1- import path from 'node:path'
2- import type { Plugin } from 'rollup'
1+ import path from 'path'
32import { builtinModules } from 'module'
43import { findPackagePaths , findDependencies } from './dependencies'
4+ import type { Plugin } from 'rollup'
55
66export interface ExternalsOptions {
77 /** Mark node built-in modules like `path`, `fs`... as external. Defaults to `true`. */
88 builtins ?: boolean
99 /** How to treat prefixed builtins. Defaults to `true` (prefixed are considered the same as unprefixed). */
10- prefixedBuiltins ?: boolean | 'strip ' | 'add '
10+ builtinsPrefix ?: 'add ' | 'strip '
1111 /**
1212 * Path/to/your/package.json file (or array of paths).
1313 * Defaults to all package.json files found in parent directories recursively.
@@ -26,82 +26,92 @@ export interface ExternalsOptions {
2626 include ?: string | RegExp | ( string | RegExp ) [ ]
2727 /** Exclude these deps from the list of externals, regardless of other settings. Defaults to `[]` */
2828 exclude ?: string | RegExp | ( string | RegExp ) [ ]
29+ /**
30+ * @deprecated - Please use `builtinsPrefix`instead.
31+ */
32+ prefixedBuiltins ?: boolean | 'strip' | 'add'
2933}
3034
31- type IncludeExclude = keyof ( ExternalsOptions [ 'include' ] | ExternalsOptions [ 'exclude' ] )
32-
3335/**
3436 * A Rollup plugin that automatically declares NodeJS built-in modules,
3537 * and optionally npm dependencies, as 'external'.
3638 */
3739function externals ( options : ExternalsOptions = { } ) : Plugin {
3840
41+ // This will store all eventual warnings until we can display them.
42+ const warnings : string [ ] = [ ]
43+
3944 // Consolidate options
4045 const config : Required < ExternalsOptions > = {
4146 builtins : true ,
42- prefixedBuiltins : 'strip ' ,
47+ builtinsPrefix : 'add ' ,
4348 packagePath : [ ] ,
4449 deps : true ,
4550 devDeps : true ,
4651 peerDeps : true ,
4752 optDeps : true ,
4853 include : [ ] ,
4954 exclude : [ ] ,
55+
56+ prefixedBuiltins : 'strip' ,
57+
5058 ...options
5159 }
5260
53- // This will store all eventual warnings until we can display them.
54- const warnings : string [ ] = [ ]
61+ if ( 'prefixedBuiltins' in options ) {
62+ warnings . push ( "The 'prefixedBuiltins' option is now deprecated, " +
63+ "please use 'builtinsPrefix' instead to silent this warning." )
64+ }
65+ else if ( 'builtinsPrefix' in options ) {
66+ config . prefixedBuiltins = options . builtinsPrefix
67+ }
5568
5669 // Map the include and exclude options to arrays of regexes.
57- const [ include , exclude ] = [ 'include' , 'exclude' ] . map ( option => new Array < string | RegExp > ( )
58- . concat ( config [ option as IncludeExclude ] )
59- . map ( ( entry , index ) => {
60- if ( entry instanceof RegExp )
61- return entry
62-
63- if ( typeof entry === 'string' )
64- return new RegExp ( '^' + entry . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) + '$' )
65-
66- if ( entry ) {
67- warnings . push ( `Ignoring wrong entry type #${ index } in '${ option } ' option: '${ entry } '` )
68- }
69-
70- return / (? = n o ) m a t c h /
71- } )
70+ const [ include , exclude ] = [ 'include' , 'exclude' ] . map ( option =>
71+ ( [ ] as ( string | RegExp ) [ ] )
72+ . concat ( config [ option as 'include' | 'exclude' ] )
73+ . reduce ( ( result , entry , index ) => {
74+ if ( entry instanceof RegExp )
75+ result . push ( entry )
76+ else if ( typeof entry === 'string' )
77+ result . push ( new RegExp ( '^' + entry . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) + '$' ) )
78+ else if ( entry ) {
79+ warnings . push ( `Ignoring wrong entry type #${ index } in '${ option } ' option: ${ JSON . stringify ( entry ) } ` )
80+ }
81+ return result
82+ } , [ ] as RegExp [ ] )
7283 )
7384
7485 // A filter function to keep only non excluded dependencies.
7586 const isNotExcluded = ( id : string ) => ! exclude . some ( rx => rx . test ( id ) )
7687
7788 // The array of the final regexes.
78- const externals : RegExp [ ] = [ ]
89+ let externals : RegExp [ ] = [ ]
7990 const isExternal = ( id : string ) => externals . some ( rx => rx . test ( id ) )
8091
81- // Support for nodejs: prefix and sub directory.
82- const nodePrefixRx = / ^ (?: n o d e (?: j s ) ? : ) ? /
83-
84- let builtins : Set < string >
92+ // Support for builtin modules.
93+ const builtins : Set < string > = new Set ( ) ,
94+ alwaysSchemed : Set < string > = new Set ( )
8595 if ( config . builtins ) {
86- const filtered = builtinModules . filter ( isNotExcluded )
87- builtins = new Set ( [
88- ...filtered ,
89- ...filtered . map ( builtin => builtin . startsWith ( 'node:' ) ? builtin : 'node:' + builtin )
90- ] )
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+ }
91104 }
92- else builtins = new Set ( )
93105
94106 return {
95107 name : 'node-externals' ,
96108
97109 async buildStart ( ) {
98110
99- // 1) Add the include option.
100- if ( include . length > 0 ) {
101- externals . push ( ...include )
102- }
111+ // Begin with the include option as it has precedence over the other options.
112+ externals = [ ...include ]
103113
104- // 2) Find and filter dependencies, supporting potential import from a sub directory (e.g. 'lodash/map').
114+ // Find and filter dependencies, supporting potential import from a sub directory (e.g. 'lodash/map').
105115 const packagePaths : string [ ] = ( [ ] as string [ ] ) . concat ( config . packagePath )
106116 const dependencies = ( await findDependencies ( {
107117 packagePaths : packagePaths . length > 0 ? packagePaths : findPackagePaths ( ) ,
@@ -118,31 +128,33 @@ function externals(options: ExternalsOptions = {}): Plugin {
118128 externals . push ( new RegExp ( '^(?:' + dependencies . join ( '|' ) + ')(?:/.+)?$' ) )
119129 }
120130
121- // All done. Issue the warnings we may have collected.
122- while ( warnings . length > 0 ) {
123- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
124- this . warn ( warnings . shift ( ) ! )
131+ // Issue the warnings we may have collected.
132+ let warning : string | undefined
133+ while ( ( warning = warnings . shift ( ) ) ) {
134+ this . warn ( warning )
125135 }
126136 } ,
127137
128138 resolveId ( importee ) {
129139
130- // Ignore already resolved ids and relative imports.
131- if ( path . isAbsolute ( importee ) || importee . startsWith ( '.' ) || importee . charCodeAt ( 0 ) === 0 ) {
140+ // Ignore already resolved ids, relative imports and virtual modules .
141+ if ( path . isAbsolute ( importee ) || / ^ (?: \0 | \. { 1 , 2 } [ \\ / ] ) / . test ( importee ) )
132142 return null
133- }
134143
135- // Handle builtins.
144+ // Handle builtins first.
145+ if ( alwaysSchemed . has ( importee ) )
146+ return false
147+
136148 if ( builtins . has ( importee ) ) {
137- if ( config . prefixedBuiltins ) {
138- let stripped = importee . replace ( nodePrefixRx , '' )
139- if ( config . prefixedBuiltins === 'strip' )
140- importee = stripped
141- else if ( config . prefixedBuiltins === 'add' )
142- importee = 'node:' + stripped
143- }
149+ if ( config . prefixedBuiltins === false )
150+ return false
151+
152+ const stripped = importee . replace ( / ^ n o d e : / , '' )
153+ const prefixed = 'node:' + stripped
144154
145- return { id : importee , external : true }
155+ return config . prefixedBuiltins === 'strip'
156+ ? { id : stripped , external : true }
157+ : { id : prefixed , external : true }
146158 }
147159
148160 // Handle dependencies.
0 commit comments