66import { mkdir , readFile , writeFile } from 'node:fs/promises'
77import path from 'node:path'
88import { createFilter } from '@rollup/pluginutils'
9- import Debug from 'debug'
109import MagicString from 'magic-string'
1110import { parseAsync } from 'oxc-parser'
1211import {
@@ -15,14 +14,20 @@ import {
1514 type UnpluginContext ,
1615 type UnpluginInstance ,
1716} from 'unplugin'
17+ import { filterImports , patchEntryAlias , type OxcImport } from './core/ast'
1818import { resolveOptions , type Options } from './core/options'
1919import {
2020 oxcTransform ,
2121 swcTransform ,
2222 tsTransform ,
2323 type TransformResult ,
2424} from './core/transformer'
25- import { lowestCommonAncestor , stripExt } from './core/utils'
25+ import {
26+ debug ,
27+ lowestCommonAncestor ,
28+ resolveEntry ,
29+ stripExt ,
30+ } from './core/utils'
2631import type {
2732 JsPlugin ,
2833 NormalizedConfig ,
@@ -37,11 +42,13 @@ import type {
3742 PluginContext ,
3843} from 'rollup'
3944
40- const debug = Debug ( 'unplugin-isolated-decl' )
41-
4245export type { Options }
4346
44- export type * from './core/types'
47+ interface Output {
48+ source : string
49+ imports : OxcImport [ ]
50+ s ?: MagicString
51+ }
4552
4653/**
4754 * The main unplugin instance.
@@ -52,9 +59,12 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
5259 const filter = createFilter ( options . include , options . exclude )
5360
5461 let farmPluginContext : UnpluginBuildContext
55- const outputFiles : Record < string , string > = { }
56- function addOutput ( filename : string , source : string ) {
57- outputFiles [ stripExt ( filename ) ] = source
62+
63+ const outputFiles : Record < string , Output > = { }
64+ function addOutput ( filename : string , output : Output ) {
65+ const name = stripExt ( filename )
66+ debug ( 'Add output:' , name )
67+ outputFiles [ name ] = output
5868 }
5969
6070 const rollup : Partial < Plugin > = {
@@ -63,6 +73,7 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
6373 const farm : Partial < JsPlugin > = {
6474 renderStart : { executor : farmRenderStart } ,
6575 }
76+
6677 return {
6778 name : 'unplugin-isolated-decl' ,
6879
@@ -92,36 +103,6 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
92103 code : string ,
93104 id : string ,
94105 ) : Promise < undefined > {
95- let program : OxcTypes . Program | undefined
96- try {
97- program = ( await parseAsync ( code , { sourceFilename : id } ) ) . program
98- } catch { }
99- if ( options . autoAddExts && program ) {
100- const imports = program . body . filter (
101- ( node ) =>
102- node . type === 'ImportDeclaration' ||
103- node . type === 'ExportAllDeclaration' ||
104- node . type === 'ExportNamedDeclaration' ,
105- )
106- const s = new MagicString ( code )
107- for ( const i of imports ) {
108- if ( ! i . source || path . basename ( i . source . value ) . includes ( '.' ) ) {
109- continue
110- }
111-
112- const resolved = await resolve ( context , i . source . value , id )
113- if ( ! resolved || resolved . external ) continue
114- if ( resolved . id . endsWith ( '.ts' ) || resolved . id . endsWith ( '.tsx' ) ) {
115- s . overwrite (
116- i . source . start ,
117- i . source . end ,
118- JSON . stringify ( `${ i . source . value } .js` ) ,
119- )
120- }
121- }
122- code = s . toString ( )
123- }
124-
125106 const label = debug . enabled && `[${ options . transformer } ]`
126107 debug ( label , 'transform' , id )
127108
@@ -140,7 +121,7 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
140121 ( options as any ) . transformOptions ,
141122 )
142123 }
143- const { code : sourceText , errors } = result
124+ let { code : dts , errors } = result
144125 debug (
145126 label ,
146127 'transformed' ,
@@ -155,12 +136,28 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
155136 return
156137 }
157138 }
158- addOutput ( id , sourceText )
159139
160- if ( ! program ) {
161- debug ( 'cannot parse' , id )
162- return
140+ const { program } = await parseAsync ( dts , { sourceFilename : id } )
141+ const imports = filterImports ( program )
142+
143+ let s : MagicString | undefined
144+ if ( options . autoAddExts ) {
145+ s = new MagicString ( dts )
146+
147+ for ( const i of imports ) {
148+ if ( path . basename ( i . source . value ) . includes ( '.' ) ) continue
149+
150+ const resolved = await resolve ( context , i . source . value , id )
151+ if ( ! resolved || resolved . external ) continue
152+ if ( resolved . id . endsWith ( '.ts' ) || resolved . id . endsWith ( '.tsx' ) ) {
153+ i . source . value = `${ i . source . value } .js`
154+ s . overwrite ( i . source . start + 1 , i . source . end - 1 , i . source . value )
155+ }
156+ }
157+ dts = s . toString ( )
163158 }
159+ addOutput ( id , { source : dts , s, imports } )
160+
164161 const typeImports = program . body . filter (
165162 (
166163 node ,
@@ -195,7 +192,6 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
195192 return false
196193 } ,
197194 )
198-
199195 for ( const i of typeImports ) {
200196 if ( ! i . source ) continue
201197 const resolved = ( await resolve ( context , i . source . value , id ) ) ?. id
@@ -234,12 +230,28 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
234230 entryFileNames = path . join ( options . extraOutdir , entryFileNames )
235231 }
236232
237- for ( let [ outname , source ] of Object . entries ( outputFiles ) ) {
238- const name : string = map ?. [ outname ] || path . relative ( outBase , outname )
239- const fileName = entryFileNames . replace ( '[name]' , name )
233+ for ( let [ outname , { source, s, imports } ] of Object . entries (
234+ outputFiles ,
235+ ) ) {
236+ const entryAlias = map ?. [ outname ]
237+ const relativeName = path . relative ( outBase , outname )
238+ const fileName = entryFileNames . replace (
239+ '[name]' ,
240+ entryAlias || relativeName ,
241+ )
242+
243+ if ( entryAlias && entryAlias !== relativeName ) {
244+ const offset = path . relative (
245+ path . dirname ( path . resolve ( outBase , fileName ) ) ,
246+ path . dirname ( outname ) ,
247+ )
248+ source = patchEntryAlias ( source , s , imports , outname , offset )
249+ }
250+
240251 if ( options . patchCjsDefaultExport && fileName . endsWith ( '.d.cts' ) ) {
241252 source = patchCjsDefaultExport ( source )
242253 }
254+
243255 debug ( '[rollup] emit dts file:' , fileName )
244256 this . emitFile ( {
245257 type : 'asset' ,
@@ -282,13 +294,28 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
282294 entryFileNames = path . join ( options . extraOutdir , entryFileNames )
283295 }
284296
285- for ( let [ outname , source ] of Object . entries ( outputFiles ) ) {
286- const name : string = map ?. [ outname ] || path . relative ( outBase , outname )
287- const fileName = entryFileNames . replace ( '[entryName]' , name )
297+ for ( let [ outname , { source, s, imports } ] of Object . entries (
298+ outputFiles ,
299+ ) ) {
300+ const entryAlias = map ?. [ outname ]
301+ const relativeName = path . relative ( outBase , outname )
302+ const fileName = entryFileNames . replace (
303+ '[name]' ,
304+ entryAlias || relativeName ,
305+ )
306+
307+ if ( entryAlias && entryAlias !== relativeName ) {
308+ const offset = path . relative (
309+ path . dirname ( path . resolve ( outBase , fileName ) ) ,
310+ path . dirname ( outname ) ,
311+ )
312+ source = patchEntryAlias ( source , s , imports , outname , offset )
313+ }
288314
289315 if ( options . patchCjsDefaultExport && fileName . endsWith ( '.d.cts' ) ) {
290316 source = patchCjsDefaultExport ( source )
291317 }
318+
292319 debug ( '[farm] emit dts file:' , fileName )
293320 farmPluginContext . emitFile ( {
294321 type : 'asset' ,
@@ -338,7 +365,7 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
338365 }
339366
340367 const textEncoder = new TextEncoder ( )
341- for ( let [ filename , source ] of Object . entries ( outputFiles ) ) {
368+ for ( let [ filename , { source } ] of Object . entries ( outputFiles ) ) {
342369 const outDir = build . initialOptions . outdir
343370 let outFile = `${ path . relative ( outBase , filename ) } .d.${ outExt } `
344371 if ( options . extraOutdir ) {
@@ -366,24 +393,6 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
366393 }
367394 } )
368395
369- function resolveEntry ( input : string [ ] | Record < string , string > ) : {
370- map : Record < string , string > | undefined
371- outBase : string
372- } {
373- const map = ! Array . isArray ( input )
374- ? Object . fromEntries (
375- Object . entries ( input ) . map ( ( [ k , v ] ) => [
376- path . resolve ( stripExt ( v as string ) ) ,
377- k ,
378- ] ) ,
379- )
380- : undefined
381- const arr = Array . isArray ( input ) && input ? input : Object . values ( input )
382- const outBase = lowestCommonAncestor ( ...arr )
383-
384- return { map, outBase }
385- }
386-
387396async function resolve (
388397 context : UnpluginBuildContext ,
389398 id : string ,
0 commit comments