@@ -16,9 +16,15 @@ import {
1616 tsTransform ,
1717 type TransformResult ,
1818} from './core/transformer'
19+ import { lowestCommonAncestor , stripExt } from './core/utils'
1920import type * as OxcTypes from '@oxc-project/types'
2021import type { PluginBuild } from 'esbuild'
21- import type { Plugin , PluginContext } from 'rollup'
22+ import type {
23+ NormalizedInputOptions ,
24+ NormalizedOutputOptions ,
25+ Plugin ,
26+ PluginContext ,
27+ } from 'rollup'
2228
2329export type { Options }
2430
@@ -28,70 +34,23 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
2834 const filter = createFilter ( options . include , options . exclude )
2935
3036 const outputFiles : Record < string , string > = { }
31-
3237 function addOutput ( filename : string , source : string ) {
3338 outputFiles [ stripExt ( filename ) ] = source
3439 }
3540
3641 const rollup : Partial < Plugin > = {
37- renderStart ( outputOptions , inputOptions ) {
38- const { input } = inputOptions
39- const inputMap = ! Array . isArray ( input )
40- ? Object . fromEntries (
41- Object . entries ( input ) . map ( ( [ k , v ] ) => [
42- path . resolve ( stripExt ( v ) ) ,
43- k ,
44- ] ) ,
45- )
46- : undefined
47- const normalizedInput = Array . isArray ( input )
48- ? input
49- : Object . values ( input )
50- const outBase = lowestCommonAncestor ( ...normalizedInput )
51-
52- if ( typeof outputOptions . entryFileNames !== 'string' ) {
53- return this . error ( 'entryFileNames must be a string' )
54- }
55-
56- let entryFileNames = outputOptions . entryFileNames . replace (
57- / \. ( .) ? [ j t ] s x ? $ / ,
58- ( _ , s ) => `.d.${ s || '' } ts` ,
59- )
60-
61- if ( options . extraOutdir ) {
62- entryFileNames = path . join ( options . extraOutdir , entryFileNames )
63- }
64-
65- for ( let [ outname , source ] of Object . entries ( outputFiles ) ) {
66- const name : string =
67- inputMap ?. [ outname ] || path . relative ( outBase , outname )
68- const fileName = entryFileNames . replace ( '[name]' , name )
69- if ( options . patchCjsDefaultExport && fileName . endsWith ( '.d.cts' ) ) {
70- source = patchCjsDefaultExport ( source )
71- }
72- this . emitFile ( {
73- type : 'asset' ,
74- fileName,
75- source,
76- } )
77- }
78- } ,
42+ renderStart : rollupRenderStart ,
7943 }
8044
8145 return {
8246 name : 'unplugin-isolated-decl' ,
8347
84- transformInclude ( id ) {
85- return filter ( id )
86- } ,
87-
48+ transformInclude : ( id ) => filter ( id ) ,
8849 transform ( code , id ) : Promise < undefined > {
8950 return transform ( this , code , id )
9051 } ,
9152
92- esbuild : {
93- setup : esbuildSetup ,
94- } ,
53+ esbuild : { setup : esbuildSetup } ,
9554 rollup,
9655 rolldown : rollup as any ,
9756 vite : {
@@ -214,6 +173,53 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
214173 }
215174 }
216175
176+ function rollupRenderStart (
177+ this : PluginContext ,
178+ outputOptions : NormalizedOutputOptions ,
179+ inputOptions : NormalizedInputOptions ,
180+ ) {
181+ const { input } = inputOptions
182+ const inputMap = ! Array . isArray ( input )
183+ ? Object . fromEntries (
184+ Object . entries ( input ) . map ( ( [ k , v ] ) => [
185+ path . resolve ( stripExt ( v ) ) ,
186+ k ,
187+ ] ) ,
188+ )
189+ : undefined
190+ const normalizedInput = Array . isArray ( input )
191+ ? input
192+ : Object . values ( input )
193+ const outBase = lowestCommonAncestor ( ...normalizedInput )
194+
195+ if ( typeof outputOptions . entryFileNames !== 'string' ) {
196+ return this . error ( 'entryFileNames must be a string' )
197+ }
198+
199+ let entryFileNames = outputOptions . entryFileNames . replace (
200+ / \. ( .) ? [ j t ] s x ? $ / ,
201+ ( _ , s ) => `.d.${ s || '' } ts` ,
202+ )
203+
204+ if ( options . extraOutdir ) {
205+ entryFileNames = path . join ( options . extraOutdir , entryFileNames )
206+ }
207+
208+ for ( let [ outname , source ] of Object . entries ( outputFiles ) ) {
209+ const name : string =
210+ inputMap ?. [ outname ] || path . relative ( outBase , outname )
211+ const fileName = entryFileNames . replace ( '[name]' , name )
212+ if ( options . patchCjsDefaultExport && fileName . endsWith ( '.d.cts' ) ) {
213+ source = patchCjsDefaultExport ( source )
214+ }
215+ this . emitFile ( {
216+ type : 'asset' ,
217+ fileName,
218+ source,
219+ } )
220+ }
221+ }
222+
217223 function esbuildSetup ( build : PluginBuild ) {
218224 build . onEnd ( async ( result ) => {
219225 const esbuildOptions = build . initialOptions
@@ -297,38 +303,9 @@ async function resolve(
297303 return { id : resolved . id , external : ! ! resolved . external }
298304}
299305
300- function stripExt ( filename : string ) {
301- return filename . replace ( / \. ( .? ) [ j t ] s x ? $ / , '' )
302- }
303-
304306function patchCjsDefaultExport ( source : string ) {
305307 return source . replace (
306308 / (?< = (?: [ ; } ] | ^ ) \s * e x p o r t \s * ) (?: \{ \s * ( [ \w $ ] + ) \s * a s \s + d e f a u l t \s * \} | d e f a u l t \s + ( [ \w $ ] + ) ) / ,
307309 ( _ , s1 , s2 ) => `= ${ s1 || s2 } ` ,
308310 )
309311}
310-
311- export function lowestCommonAncestor ( ...filepaths : string [ ] ) : string {
312- if ( filepaths . length === 0 ) return ''
313- if ( filepaths . length === 1 ) return path . dirname ( filepaths [ 0 ] )
314- filepaths = filepaths . map ( ( p ) => p . replaceAll ( '\\' , '/' ) )
315- const [ first , ...rest ] = filepaths
316- let ancestor = first . split ( '/' )
317- for ( const filepath of rest ) {
318- const directories = filepath . split ( '/' , ancestor . length )
319- let index = 0
320- for ( const directory of directories ) {
321- if ( directory === ancestor [ index ] ) {
322- index += 1
323- } else {
324- ancestor = ancestor . slice ( 0 , index )
325- break
326- }
327- }
328- ancestor = ancestor . slice ( 0 , index )
329- }
330-
331- return ancestor . length <= 1 && ancestor [ 0 ] === ''
332- ? `/${ ancestor [ 0 ] } `
333- : ancestor . join ( '/' )
334- }
0 commit comments