@@ -3,10 +3,13 @@ import { walk } from 'estree-walker'
33import { MagicStringAST } from 'magic-string-ast'
44import {
55 parseAsync ,
6+ type BindingPattern ,
67 type Declaration ,
8+ type Expression ,
79 type Function ,
810 type Node ,
911 type Span ,
12+ type TSModuleDeclarationName ,
1013 type TSTypeName ,
1114 type TSTypeQuery ,
1215 type TSTypeReference ,
@@ -17,26 +20,51 @@ import type { Plugin } from 'rolldown'
1720
1821const RE_TYPE = / \b t y p e \b /
1922
23+ type Range = [ start : number , end : number ]
24+
2025export function dts ( ) : Plugin {
2126 let i = 0
22- const map = new Map < number , [ code : string , nameRange : [ number , number ] ] > ( )
27+ const symbolMap = new Map <
28+ number /* symbol id */ ,
29+ [ code : string , bindingRange : Range [ ] ]
30+ > ( )
2331
24- function register ( raw : string , idNode : Node & Span , parent : Span ) {
25- const id = i ++
26- let idNodeEnd = idNode . end
27- if ( 'typeAnnotation' in idNode && idNode . typeAnnotation ) {
28- idNodeEnd = idNode . typeAnnotation . start
32+ function register (
33+ raw : string ,
34+ binding : BindingPattern | TSModuleDeclarationName ,
35+ deps : ( Node & Span ) [ ] ,
36+ parent : Span ,
37+ ) {
38+ const symbolId = i ++
39+ let bindingEnd = binding . end
40+ if ( 'typeAnnotation' in binding && binding . typeAnnotation ) {
41+ bindingEnd = binding . typeAnnotation . start
2942 }
30- const nameRange : [ number , number ] = [
31- idNode . start - parent . start ,
32- idNodeEnd - parent . start ,
33- ]
34- map . set ( id , [ raw , nameRange ] )
35- return id
43+ symbolMap . set ( symbolId , [
44+ raw ,
45+ [
46+ [ binding . start - parent . start , bindingEnd - parent . start ] ,
47+ ...deps . map (
48+ ( d ) : Range => [ d . start - parent . start , d . end - parent . start ] ,
49+ ) ,
50+ ] ,
51+ ] )
52+ return symbolId
3653 }
37- function retrieve ( id : number , name : string ) {
38- const [ code , nameRange ] = map . get ( id ) !
39- return code . slice ( 0 , nameRange [ 0 ] ) + name + code . slice ( nameRange [ 1 ] )
54+
55+ function retrieve ( s : MagicStringAST , id : number , bindings : Span [ ] ) {
56+ const [ code , ranges ] = symbolMap . get ( id ) !
57+ if ( ! ranges . length ) return code
58+
59+ let codeIndex = 0
60+ let result = ''
61+ for ( const [ start , end ] of ranges ) {
62+ result += code . slice ( codeIndex , start )
63+ result += s . sliceNode ( bindings . shift ( ) )
64+ codeIndex = end
65+ }
66+ result += code . slice ( codeIndex )
67+ return result
4068 }
4169
4270 return {
@@ -60,32 +88,7 @@ export function dts(): Plugin {
6088
6189 const s = new MagicStringAST ( code )
6290 for ( let node of program . body as ( Node & Span ) [ ] ) {
63- // fix:
64- // - import type { ... } from '...'
65- // - import { type ... } from '...'
66- if ( node . type === 'ImportDeclaration' ) {
67- for ( const specifier of node . specifiers ) {
68- if (
69- specifier . type === 'ImportSpecifier' &&
70- specifier . importKind === 'type'
71- ) {
72- s . overwriteNode (
73- specifier ,
74- s . sliceNode ( specifier ) . replace ( RE_TYPE , '' ) ,
75- )
76- }
77- }
78-
79- const firstSpecifier = node . specifiers [ 0 ]
80- if ( node . importKind === 'type' && firstSpecifier ) {
81- s . overwrite (
82- node . start ,
83- firstSpecifier . start ,
84- s . slice ( node . start , firstSpecifier . start ) . replace ( RE_TYPE , '' ) ,
85- )
86- }
87- continue
88- }
91+ if ( rewriteImportType ( s , node ) ) continue
8992
9093 // remove `export` modifier
9194 const isDefaultExport = node . type === 'ExportDefaultDeclaration'
@@ -112,19 +115,18 @@ export function dts(): Plugin {
112115 ) . id
113116 if ( ! binding ) continue
114117 const original = s . sliceNode ( node )
115- const id = register ( original , binding , node )
116- const deps = collectDependencies ( node )
117- const typeDeps = collectTypeDeps ( node )
118- const depsString = [ ...deps , ...typeDeps ]
118+ const deps = [ ...collectDependencies ( node ) , ...collectTypeDeps ( node ) ]
119+ const depsString = deps
119120 . map ( ( node ) => `() => ${ s . sliceNode ( node ) } ` )
120121 . join ( ', ' )
122+ const symbolId = register ( original , binding , deps , node )
121123
122- const runtime = `[${ id } , ${ depsString } ]`
124+ const runtime = `[${ symbolId } , ${ depsString } ]`
123125 s . overwriteNode (
124126 node ,
125127 isDefaultExport
126128 ? runtime
127- : `var ${ s . sliceNode ( binding ) } = [${ id } , ${ depsString } ]` ,
129+ : `var ${ s . sliceNode ( binding ) } = [${ symbolId } , ${ depsString } ]` ,
128130 )
129131 }
130132 }
@@ -137,9 +139,8 @@ export function dts(): Plugin {
137139 const s = new MagicStringAST ( code )
138140
139141 for ( const node of program . body ) {
140- if ( patchImportSource ( s , node ) ) {
141- continue
142- }
142+ if ( patchImportSource ( s , node ) ) continue
143+
143144 if (
144145 node . type !== 'VariableDeclaration' ||
145146 node . declarations . length !== 1
@@ -153,14 +154,24 @@ export function dts(): Plugin {
153154 continue
154155 }
155156
156- const idNode = decl . init . elements [ 0 ]
157- if ( idNode ?. type !== 'Literal' || typeof idNode . value !== 'number' ) {
157+ const [ symbolIdNode , ...depsNodes ] = decl . init . elements as Expression [ ]
158+ if (
159+ symbolIdNode ?. type !== 'Literal' ||
160+ typeof symbolIdNode . value !== 'number'
161+ ) {
158162 patchVariableDeclarator ( s , node , decl )
159163 continue
160164 }
161165
162- const id = idNode . value
163- const type = retrieve ( id , s . sliceNode ( decl . id ) )
166+ const symbolId = symbolIdNode . value
167+ const type = retrieve ( s , symbolId , [
168+ decl . id ,
169+ ...depsNodes . map ( ( dep ) => {
170+ if ( dep . type !== 'ArrowFunctionExpression' )
171+ throw new Error ( 'Expected ArrowFunctionExpression' )
172+ return dep . body
173+ } ) ,
174+ ] )
164175
165176 s . overwriteNode ( node , type )
166177 }
@@ -185,17 +196,18 @@ export function dts(): Plugin {
185196 const [ decl ] = node . declarations
186197
187198 const raw = s . sliceNode ( node )
188- const id = register ( raw , decl . id , node )
189199 const deps = collectTypeDeps ( node )
190- . map ( ( node ) => s . sliceNode ( node ) )
200+ const symbolId = register ( raw , decl . id , deps , node )
201+ const depsString = collectTypeDeps ( node )
202+ . map ( ( node ) => `() => ${ s . sliceNode ( node ) } ` )
191203 . join ( ', ' )
192- const runtime = `[${ id } , ${ deps } ]`
204+ const runtime = `[${ symbolId } , ${ depsString } ]`
193205 s . overwriteNode ( node , `var ${ s . sliceNode ( decl . id ) } = ${ runtime } ` )
194206 }
195207}
196208
197- function collectDependencies ( node : Node ) {
198- const deps = new Set < Node > ( )
209+ function collectDependencies ( node : Node ) : ( Node & Span ) [ ] {
210+ const deps = new Set < Node & Span > ( )
199211 ; ( walk as any ) ( node , {
200212 enter ( node : Node ) {
201213 if ( node . type === 'ClassDeclaration' && node . superClass ) {
@@ -234,7 +246,7 @@ function collectTypeDeps(node: Node): TSTypeName[] {
234246 return result
235247}
236248
237- // patch let x = 1; to declare let x: typeof 1;
249+ // patch ` let x = 1;` to ` declare let x: typeof 1;`
238250function patchVariableDeclarator (
239251 s : MagicStringAST ,
240252 node : VariableDeclaration ,
@@ -265,3 +277,29 @@ function patchImportSource(s: MagicStringAST, node: Node) {
265277 return true
266278 }
267279}
280+
281+ // fix:
282+ // - import type { ... } from '...'
283+ // - import { type ... } from '...'
284+ function rewriteImportType ( s : MagicStringAST , node : Node ) {
285+ if ( node . type === 'ImportDeclaration' ) {
286+ for ( const specifier of node . specifiers ) {
287+ if (
288+ specifier . type === 'ImportSpecifier' &&
289+ specifier . importKind === 'type'
290+ ) {
291+ s . overwriteNode ( specifier , s . sliceNode ( specifier ) . replace ( RE_TYPE , '' ) )
292+ }
293+ }
294+
295+ const firstSpecifier = node . specifiers [ 0 ]
296+ if ( node . importKind === 'type' && firstSpecifier ) {
297+ s . overwrite (
298+ node . start ,
299+ firstSpecifier . start ,
300+ s . slice ( node . start , firstSpecifier . start ) . replace ( RE_TYPE , '' ) ,
301+ )
302+ }
303+ return true
304+ }
305+ }
0 commit comments