11import path from 'node:path'
22import fs from 'node:fs/promises'
3- import { builtinModules } from 'node:module'
3+ import { createRequire , isBuiltin } from 'node:module'
44import type { Plugin } from 'rollup'
55
66type MaybeFalsy < T > = ( T ) | undefined | null | false
@@ -78,28 +78,19 @@ export interface ExternalsOptions {
7878
7979// Fields of interest in package.json
8080interface PackageJson {
81+ version : string
8182 dependencies ?: Record < string , string >
8283 devDependencies ?: Record < string , string >
8384 peerDependencies ?: Record < string , string >
8485 optionalDependencies ?: Record < string , string >
8586}
8687
88+ // Get our own version
89+ const { version } = createRequire ( import . meta. url ) ( '../package.json' ) as PackageJson
90+
8791// Prepare node built-in modules lists.
88- // Note: node:test is currently not part of builtinModules... and may well never be
89- // (see https://github.com/nodejs/node/issues/42785)
9092const nodePrefix = 'node:'
9193const nodePrefixRx = / ^ n o d e : /
92- const builtins = {
93- all : new Set ( builtinModules ) ,
94- alwaysPrefixed : new Set (
95- builtinModules . filter ( mod => nodePrefixRx . test ( mod ) )
96- )
97- }
98-
99- for ( const extra of [ 'node:test' ] ) {
100- builtins . all . add ( extra )
101- builtins . alwaysPrefixed . add ( extra )
102- }
10394
10495// Files that mark the root of a workspace.
10596const workspaceRootFiles = new Set ( [
@@ -139,13 +130,15 @@ function nodeExternals(options: ExternalsOptions = {}): Plugin {
139130 let include : RegExp [ ] ,
140131 exclude : RegExp [ ]
141132
142- const isIncluded = ( id : string ) => include . some ( rx => rx . test ( id ) ) ,
143- isExcluded = ( id : string ) => exclude . some ( rx => rx . test ( id ) )
133+ const isIncluded = ( id : string ) => include . length === 0 || include . some ( rx => rx . test ( id ) ) ,
134+ isExcluded = ( id : string ) => exclude . length > 0 && exclude . some ( rx => rx . test ( id ) )
144135
145136 return {
146137 name : 'node-externals' ,
138+ version,
147139
148140 async buildStart ( ) {
141+
149142 // Map the include and exclude options to arrays of regexes.
150143 [ include , exclude ] = ( [ 'include' , 'exclude' ] as const ) . map ( option =>
151144 ( [ ] as Array < string | RegExp | null | undefined | false > )
@@ -155,9 +148,9 @@ function nodeExternals(options: ExternalsOptions = {}): Plugin {
155148 result . push ( entry )
156149 else if ( isString ( entry ) )
157150 result . push ( new RegExp ( '^' + entry . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) + '$' ) )
158- else if ( entry ) {
151+ else if ( entry )
159152 this . warn ( `Ignoring wrong entry type #${ index } in '${ option } ' option: ${ JSON . stringify ( entry ) } ` )
160- }
153+
161154 return result
162155 } , [ ] as RegExp [ ] )
163156 )
@@ -166,22 +159,25 @@ function nodeExternals(options: ExternalsOptions = {}): Plugin {
166159 // from cwd up to the root of the git repo, the root of the monorepo,
167160 // or the root of the volume, whichever comes first.
168161 const packagePaths = ( [ ] as string [ ] )
169- . concat ( config [ ' packagePath' ] )
162+ . concat ( config . packagePath )
170163 . filter ( isString )
164+ . map ( packagePath => path . resolve ( packagePath ) )
171165 if ( packagePaths . length === 0 ) {
172166 for (
173- let current = process . cwd ( ) , previous : string | undefined ;
167+ let current = process . cwd ( ) , previous : string | undefined = undefined ;
174168 previous !== current ;
175169 previous = current , current = path . dirname ( current )
176170 ) {
177- const entries = await fs . readdir ( current , { withFileTypes : true } )
171+ const entries = await fs . readdir ( current , { withFileTypes : true } ) . catch ( ( ) => null )
172+ if ( entries === null )
173+ this . error ( `Could not read contents of directory ${ JSON . stringify ( current ) } .` )
178174
179175 // Gather package.json files.
180- if ( entries . some ( entry => entry . name === 'package.json' && entry . isFile ( ) ) )
176+ if ( entries ! . some ( entry => entry . name === 'package.json' && entry . isFile ( ) ) )
181177 packagePaths . push ( path . join ( current , 'package.json' ) )
182178
183179 // Break early if this is a git repo root or there is a known workspace root file.
184- if ( entries . some ( entry =>
180+ if ( entries ! . some ( entry =>
185181 ( entry . name === '.git' && entry . isDirectory ( ) )
186182 || ( workspaceRootFiles . has ( entry . name ) && entry . isFile ( ) )
187183 ) ) {
@@ -193,19 +189,18 @@ function nodeExternals(options: ExternalsOptions = {}): Plugin {
193189 // Gather dependencies names.
194190 const dependencies : Record < string , string > = { }
195191 for ( const packagePath of packagePaths ) {
192+ this . addWatchFile ( packagePath )
196193 try {
197194 const json = ( await fs . readFile ( packagePath ) ) . toString ( )
198195 try {
199- const pkg : PackageJson = JSON . parse ( json )
196+ const pkg = JSON . parse ( json ) as PackageJson
200197 Object . assign ( dependencies ,
201198 config . deps ? pkg . dependencies : undefined ,
202199 config . devDeps ? pkg . devDependencies : undefined ,
203200 config . peerDeps ? pkg . peerDependencies : undefined ,
204201 config . optDeps ? pkg . optionalDependencies : undefined
205202 )
206203
207- this . addWatchFile ( packagePath )
208-
209204 // Break early if this is a npm/yarn workspace root.
210205 if ( 'workspaces' in pkg )
211206 break
@@ -225,40 +220,38 @@ function nodeExternals(options: ExternalsOptions = {}): Plugin {
225220 }
226221 }
227222
223+ // Add all dependencies as an include RegEx.
228224 const names = Object . keys ( dependencies )
229225 if ( names . length > 0 )
230226 include . push ( new RegExp ( '^(?:' + names . join ( '|' ) + ')(?:/.+)?$' ) )
231227 } ,
232228
233- async resolveId ( id ) {
234- // Ignore already resolved ids, relative imports and virtual modules.
235- if ( / ^ (?: \0 | \. { 0 , 2 } \/ ) / . test ( id ) || path . isAbsolute ( id ) )
229+ async resolveId ( specifier ) {
230+ // Ignore absolute ( already resolved) ids, relative imports and virtual modules.
231+ if ( / ^ (?: \0 | \. { 0 , 2 } \/ ) / . test ( specifier ) || path . isAbsolute ( specifier ) )
236232 return null
237233
238234 // Handle node builtins.
239- if ( id . startsWith ( nodePrefix ) || builtins . all . has ( id ) ) {
240- const stripped = id . replace ( nodePrefixRx , '' )
235+ if ( isBuiltin ( specifier ) ) {
236+ const stripped = specifier . replace ( nodePrefixRx , '' )
241237 return {
242238 id : config . builtinsPrefix === 'ignore'
243- ? id
244- : config . builtinsPrefix === 'add' || builtins . alwaysPrefixed . has ( id )
239+ ? specifier
240+ : config . builtinsPrefix === 'add' || ( specifier . startsWith ( nodePrefix ) && ! isBuiltin ( stripped ) )
245241 ? nodePrefix + stripped
246242 : stripped ,
247- external : ( config . builtins || isIncluded ( id ) ) && ! isExcluded ( id ) ,
243+ external : ( config . builtins || isIncluded ( specifier ) ) && ! isExcluded ( specifier ) ,
248244 moduleSideEffects : false
249245 }
250246 }
251247
252248 // Handle npm dependencies.
253- return isIncluded ( id ) && ! isExcluded ( id )
249+ return isIncluded ( specifier ) && ! isExcluded ( specifier )
254250 ? false // external
255251 : null // normal handling
256252 }
257253 }
258254}
259255
260256export default nodeExternals
261- export {
262- nodeExternals , // Named export since 6.1
263- nodeExternals as externals // For backwards compatibility
264- }
257+ export { nodeExternals }
0 commit comments