Skip to content

Commit bd22c4b

Browse files
committed
Use node:module's isBuiltin; various adjustments
1 parent fa1c75f commit bd22c4b

File tree

2 files changed

+33
-40
lines changed

2 files changed

+33
-40
lines changed

source/index.ts

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import path from 'node:path'
22
import fs from 'node:fs/promises'
3-
import { builtinModules } from 'node:module'
3+
import { createRequire, isBuiltin } from 'node:module'
44
import type { Plugin } from 'rollup'
55

66
type MaybeFalsy<T> = (T) | undefined | null | false
@@ -78,28 +78,19 @@ export interface ExternalsOptions {
7878

7979
// Fields of interest in package.json
8080
interface 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)
9092
const nodePrefix = 'node:'
9193
const nodePrefixRx = /^node:/
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.
10596
const 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

260256
export default nodeExternals
261-
export {
262-
nodeExternals, // Named export since 6.1
263-
nodeExternals as externals // For backwards compatibility
264-
}
257+
export { nodeExternals }

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
"skipDefaultLibCheck": true,
1111

1212
// Input
13-
"moduleResolution": "Node16",
1413
"allowSyntheticDefaultImports": true,
14+
"moduleResolution": "Node16",
1515

1616
// Output
1717
"outDir": "dist",

0 commit comments

Comments
 (0)