@@ -42,9 +42,10 @@ function externals(options: ExternalsOptions = {}): Plugin {
4242 const warnings : string [ ] = [ ]
4343
4444 // Consolidate options
45- const config : Required < ExternalsOptions > = {
45+ const config : Required < ExternalsOptions > & { _builtinsPrefix : 'ignore' | 'add' | 'strip' } = {
4646 builtins : true ,
47- builtinsPrefix : 'add' ,
47+ builtinsPrefix : 'strip' , // Will be be 'add' v5
48+ prefixedBuiltins : 'strip' , // Will be removed in v5
4849 packagePath : [ ] ,
4950 deps : true ,
5051 devDeps : true ,
@@ -53,23 +54,31 @@ function externals(options: ExternalsOptions = {}): Plugin {
5354 include : [ ] ,
5455 exclude : [ ] ,
5556
56- prefixedBuiltins : 'strip' ,
57+ ... options ,
5758
58- ... options
59+ _builtinsPrefix : 'strip' , // Used to handle prefixes until v5
5960 }
6061
61- if ( 'prefixedBuiltins' in options ) {
62+ if ( 'builtinsPrefix' in options ) {
63+ config . _builtinsPrefix = options . builtinsPrefix
64+ }
65+ else if ( 'prefixedBuiltins' in options ) {
6266 warnings . push ( "The 'prefixedBuiltins' option is now deprecated, " +
6367 "please use 'builtinsPrefix' instead to silent this warning." )
64- }
65- else if ( 'builtinsPrefix' in options ) {
66- config . prefixedBuiltins = options . builtinsPrefix
68+
69+ const { prefixedBuiltins } = options
70+ config . _builtinsPrefix =
71+ prefixedBuiltins === false
72+ ? 'ignore'
73+ : prefixedBuiltins === true || prefixedBuiltins === 'add'
74+ ? 'add'
75+ : 'strip'
6776 }
6877
6978 // Map the include and exclude options to arrays of regexes.
7079 const [ include , exclude ] = [ 'include' , 'exclude' ] . map ( option =>
7180 ( [ ] as ( string | RegExp ) [ ] )
72- . concat ( config [ option as 'include' | 'exclude' ] )
81+ . concat ( config [ option as 'include' | 'exclude' ] )
7382 . reduce ( ( result , entry , index ) => {
7483 if ( entry instanceof RegExp )
7584 result . push ( entry )
@@ -82,85 +91,102 @@ function externals(options: ExternalsOptions = {}): Plugin {
8291 } , [ ] as RegExp [ ] )
8392 )
8493
94+ // Support for builtin modules.
95+ const nodePrefix = 'node:'
96+ const nodePrefixRx = / ^ n o d e : /
97+ const _builtinModules = {
98+ bare : [ ] as string [ ] , // w/o schemed-only builtins
99+ schemed : [ ] as string [ ] , // w/ schemed-only builtins
100+ alwaysSchemed : [ ] as string [ ] // e.g., node:test in node 18+
101+ }
102+
103+ builtinModules . forEach ( builtin => {
104+ if ( builtin . startsWith ( nodePrefix ) ) {
105+ _builtinModules . schemed . push ( builtin )
106+ _builtinModules . alwaysSchemed . push ( builtin )
107+ }
108+ else {
109+ _builtinModules . bare . push ( builtin )
110+ _builtinModules . schemed . push ( nodePrefix + builtin )
111+ }
112+ } )
113+
114+ const builtins = new Set ( [
115+ ..._builtinModules . bare ,
116+ ..._builtinModules . schemed
117+ ] )
118+ const alwaysSchemed = new Set ( _builtinModules . alwaysSchemed )
119+
85120 // A filter function to keep only non excluded dependencies.
86121 const isNotExcluded = ( id : string ) => ! exclude . some ( rx => rx . test ( id ) )
87122
88123 // The array of the final regexes.
89124 let externals : RegExp [ ] = [ ]
90125 const isExternal = ( id : string ) => externals . some ( rx => rx . test ( id ) )
91126
92- // Support for builtin modules.
93- const builtins : Set < string > = new Set ( ) ,
94- alwaysSchemed : Set < string > = new Set ( )
95- if ( config . builtins ) {
96- const filtered = builtinModules . filter ( b => isNotExcluded ( b ) && isNotExcluded ( 'node:' + b ) )
97- for ( const builtin of filtered ) {
98- builtins . add ( builtin )
99- if ( builtin . startsWith ( 'node:' ) )
100- alwaysSchemed . add ( builtin )
101- else
102- builtins . add ( 'node:' + builtin )
103- }
104- }
105-
106127 return {
107128 name : 'node-externals' ,
108129
109130 async buildStart ( ) {
110131
111- // Begin with the include option as it has precedence over the other options.
132+ // Begin with the include option as it has precedence over the other inclusion options.
112133 externals = [ ...include ]
113134
135+ // Add builtins
136+ if ( config . builtins ) {
137+ externals . push (
138+ new RegExp ( '^(?:' + _builtinModules . bare . filter ( isNotExcluded ) . join ( '|' ) + ')$' ) ,
139+ new RegExp ( '^node:(?:' + _builtinModules . schemed . filter ( isNotExcluded ) . map ( id => id . replace ( nodePrefixRx , '' ) ) . join ( '|' ) + ')$' ) ,
140+ )
141+ }
142+
114143 // Find and filter dependencies, supporting potential import from a sub directory (e.g. 'lodash/map').
115- const packagePaths : string [ ] = ( [ ] as string [ ] ) . concat ( config . packagePath )
116- const dependencies = ( await findDependencies ( {
117- packagePaths : packagePaths . length > 0 ? packagePaths : findPackagePaths ( ) ,
118- keys : [
119- config . deps && 'dependencies' ,
120- config . devDeps && 'devDependencies' ,
121- config . peerDeps && 'peerDependencies' ,
122- config . optDeps && 'optionalDependencies'
123- ] . filter ( Boolean ) as string [ ] ,
124- warnings
125- } ) ) . filter ( isNotExcluded )
126-
127- if ( dependencies . length > 0 ) {
128- externals . push ( new RegExp ( '^(?:' + dependencies . join ( '|' ) + ')(?:/.+)?$' ) )
144+ if ( config . deps || config . devDeps || config . peerDeps || config . optDeps ) {
145+ const packagePaths : string [ ] = ( [ ] as string [ ] ) . concat ( config . packagePath )
146+ const dependencies = ( await findDependencies ( {
147+ packagePaths : packagePaths . length > 0 ? packagePaths : findPackagePaths ( ) ,
148+ keys : [
149+ config . deps && 'dependencies' ,
150+ config . devDeps && 'devDependencies' ,
151+ config . peerDeps && 'peerDependencies' ,
152+ config . optDeps && 'optionalDependencies'
153+ ] . filter ( Boolean ) as string [ ] ,
154+ warnings
155+ } ) ) . filter ( isNotExcluded )
156+
157+ if ( dependencies . length > 0 ) {
158+ externals . push ( new RegExp ( '^(?:' + dependencies . join ( '|' ) + ')(?:/.+)?$' ) )
159+ }
129160 }
130161
131162 // Issue the warnings we may have collected.
132- let warning : string | undefined
133- while ( ( warning = warnings . shift ( ) ) ) {
134- this . warn ( warning )
163+ while ( warnings . length > 0 ) {
164+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
165+ this . warn ( warnings . shift ( ) ! )
135166 }
136167 } ,
137168
138- resolveId ( importee ) {
169+ resolveId ( id ) {
139170
140171 // Ignore already resolved ids, relative imports and virtual modules.
141- if ( path . isAbsolute ( importee ) || / ^ (?: \0 | \. { 1 , 2 } [ \\ / ] ) / . test ( importee ) )
172+ if ( path . isAbsolute ( id ) || / ^ (?: \0 | \. { 1 , 2 } [ \\ / ] ) / . test ( id ) )
142173 return null
143174
144- // Handle builtins first.
145- if ( alwaysSchemed . has ( importee ) )
146- return false
175+ // Check for externality.
176+ const external = isExternal ( id ) && isNotExcluded ( id )
147177
148- if ( builtins . has ( importee ) ) {
149- if ( config . prefixedBuiltins === false )
150- return false
178+ // If not a builtin, or we're told not to handle prefixes, return status immediately.
179+ if ( ! builtins . has ( id ) || config . _builtinsPrefix === 'ignore' )
180+ return external ? false : null
151181
152- const stripped = importee . replace ( / ^ n o d e : / , '' )
153- const prefixed = 'node:' + stripped
154-
155- return config . prefixedBuiltins === 'strip'
156- ? { id : stripped , external : true }
157- : { id : prefixed , external : true }
182+ // Otherwise, handle prefix.
183+ const stripped = id . replace ( nodePrefixRx , '' )
184+ return {
185+ id : alwaysSchemed . has ( id ) || config . _builtinsPrefix === 'add'
186+ ? 'node:' + stripped
187+ : stripped ,
188+ external
158189 }
159-
160- // Handle dependencies.
161- return isExternal ( importee ) && isNotExcluded ( importee )
162- ? false
163- : null
164190 }
165191 }
166192}
0 commit comments