1+ import path from 'node:path'
12import type { Plugin } from 'rollup'
23import { builtinModules } from 'module'
34import { findPackagePaths , findDependencies } from './dependencies'
@@ -6,7 +7,7 @@ export interface ExternalsOptions {
67 /** Mark node built-in modules like `path`, `fs`... as external. Defaults to `true`. */
78 builtins ?: boolean
89 /** How to treat prefixed builtins. Defaults to `true` (prefixed are considered the same as unprefixed). */
9- prefixedBuiltins ?: boolean | 'strip'
10+ prefixedBuiltins ?: boolean | 'strip' | 'add'
1011 /**
1112 * Path/to/your/package.json file (or array of paths).
1213 * Defaults to all package.json files found in parent directories recursively.
@@ -27,7 +28,7 @@ export interface ExternalsOptions {
2728 exclude ?: string | RegExp | ( string | RegExp ) [ ]
2829}
2930
30- type IncludeExclude = Pick < ExternalsOptions , 'include' | 'exclude' >
31+ type IncludeExclude = keyof ( ExternalsOptions [ 'include' ] | ExternalsOptions [ 'exclude' ] )
3132
3233/**
3334 * A Rollup plugin that automatically declares NodeJS built-in modules,
@@ -54,7 +55,7 @@ function externals(options: ExternalsOptions = {}): Plugin {
5455
5556 // Map the include and exclude options to arrays of regexes.
5657 const [ include , exclude ] = [ 'include' , 'exclude' ] . map ( option => new Array < string | RegExp > ( )
57- . concat ( config [ option as keyof IncludeExclude ] )
58+ . concat ( config [ option as IncludeExclude ] )
5859 . map ( ( entry , index ) => {
5960 if ( entry instanceof RegExp )
6061 return entry
@@ -77,15 +78,27 @@ function externals(options: ExternalsOptions = {}): Plugin {
7778 const externals : RegExp [ ] = [ ]
7879 const isExternal = ( id : string ) => externals . some ( rx => rx . test ( id ) )
7980
81+ // Support for nodejs: prefix and sub directory.
82+ const nodePrefixRx = / ^ (?: n o d e (?: j s ) ? : ) ? /
83+
84+ let builtins : Set < string >
85+ 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+ ] )
91+ }
92+ else builtins = new Set ( )
93+
8094 return {
8195 name : 'node-externals' ,
8296
8397 async buildStart ( ) {
8498
85- // 1) Filter NodeJS builtins, supporting potential import from a sub directory (e.g. 'fs/promises').
86- const builtins = ( config . builtins ? builtinModules : [ ] ) . filter ( isNotExcluded )
87- if ( builtins . length > 0 ) {
88- externals . push ( new RegExp ( `^(?:${ builtins . join ( '|' ) } )(?:/.+)?$` ) )
99+ // 1) Add the include option.
100+ if ( include . length > 0 ) {
101+ externals . push ( ...include )
89102 }
90103
91104 // 2) Find and filter dependencies, supporting potential import from a sub directory (e.g. 'lodash/map').
@@ -102,12 +115,7 @@ function externals(options: ExternalsOptions = {}): Plugin {
102115 } ) ) . filter ( isNotExcluded )
103116
104117 if ( dependencies . length > 0 ) {
105- externals . push ( new RegExp ( `^(?:${ dependencies . join ( '|' ) } )(?:/.+)?$` ) )
106- }
107-
108- // 3) Add the include option.
109- if ( include . length > 0 ) {
110- externals . push ( ...include )
118+ externals . push ( new RegExp ( '^(?:' + dependencies . join ( '|' ) + ')(?:/.+)?$' ) )
111119 }
112120
113121 // All done. Issue the warnings we may have collected.
@@ -117,28 +125,29 @@ function externals(options: ExternalsOptions = {}): Plugin {
117125 }
118126 } ,
119127
120- resolveId ( importee , importer , { isEntry } ) {
128+ resolveId ( importee ) {
121129
122- // Ignore entry chunks & don't mess with other plugins .
123- if ( isEntry || ! importee . charCodeAt ( 0 ) || ! importer ? .charCodeAt ( 0 ) ) {
130+ // Ignore already resolved ids and relative imports .
131+ if ( path . isAbsolute ( importee ) || importee . startsWith ( '.' ) || importee . charCodeAt ( 0 ) === 0 ) {
124132 return null
125133 }
126134
127- // Remove node:/nodejs: prefix so builtins resolve to their unprefixed equivalent .
128- let stripped = importee
129- if ( config . prefixedBuiltins ) {
130- if ( importee . startsWith ( 'node:' ) ) {
131- stripped = importee . slice ( 5 )
132- }
133- else if ( importee . startsWith ( 'nodejs:' ) ) {
134- stripped = importee . slice ( 7 )
135+ // Handle builtins.
136+ 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
135143 }
144+
145+ return { id : importee , external : true }
136146 }
137147
138- // Return object if importee should be treated as an external module,
139- // otherwise return `null` to let Rollup and other plugins handle it.
140- return isExternal ( stripped ) && isNotExcluded ( stripped )
141- ? { id : config . prefixedBuiltins === 'strip' ? stripped : importee , external : true }
148+ // Handle dependencies.
149+ return isExternal ( importee ) && isNotExcluded ( importee )
150+ ? false
142151 : null
143152 }
144153 }
0 commit comments