UNPKG

rollup-plugin-node-externals

Version:

Automatically declare NodeJS built-in modules and npm dependencies as 'external' in Rollup/Vite config

169 lines 7.13 kB
import assert from 'node:assert'; import path from 'node:path'; import fs from 'node:fs/promises'; import cp from 'node:child_process'; import { isBuiltin } from 'node:module'; import self from '#package.json' with { type: 'json' }; const monorepoRootFiles = [ 'pnpm-workspace.yaml', 'lerna.json', 'rush.json', ]; const emptyArray = []; function nodeExternals(options = {}) { let config = undefined; return { name: self.name.replace(/^rollup-plugin-/, ''), version: self.version, apply: 'build', enforce: 'pre', async buildStart() { config ??= await getConfig.call(this); config.packages.forEach(pkg => this.addWatchFile(pkg)); }, watchChange(id) { if (config?.packages.has(id)) config = undefined; }, resolveId(specifier, _, { isEntry }) { if (isEntry || /^(?:\0|\.{1,2}\/)/.test(specifier) || path.isAbsolute(specifier)) { return null; } assert(config); if (isBuiltin(specifier)) { const stripped = specifier.replace(/^node:/, ''); const prefixed = 'node:' + stripped; return { id: config.prefix ? prefixed : config.prefix === false ? isBuiltin(stripped) ? stripped : prefixed : specifier, external: (config.builtins || config.isIncluded(specifier)) && !config.isExcluded(specifier), moduleSideEffects: false }; } return config.isIncluded(specifier) && !config.isExcluded(specifier) ? false : null; } }; async function getConfig() { const config = { builtins: true, prefix: true, packages: new Set(), deps: true, peerDeps: true, optDeps: true, devDeps: false, include: [], exclude: [], isIncluded, isExcluded }; for (const [key, value] of Object.entries(options)) { switch (key) { case 'include': case 'exclude': emptyArray.concat(value).forEach((entry, index) => { if (entry instanceof RegExp) config[key].push(entry); else if (isString(entry)) config[key].push(new RegExp('^' + RegExp.escape(entry) + '$')); else if (entry) this.warn(`Ignoring wrong entry type #${index} in '${key}' option: ${JSON.stringify(entry)}.`); }); continue; case 'packagePath': emptyArray.concat(value).forEach((entry, index) => { if (isString(entry)) config.packages.add(path.resolve(entry)); else if (entry) this.warn(`Ignoring wrong entry type #${index} in '${key}' option: ${JSON.stringify(entry)}.`); }); continue; case 'builtinsPrefix': if (value === 'add' || value === true) config.prefix = true; else if (value === 'strip' || value === false) config.prefix = false; else if (value === 'ignore') config.prefix = null; else this.warn(`Ignoring bad value ${JSON.stringify(value)} for option '${key}', using default of 'add'.`); continue; case 'builtins': case 'deps': case 'devDeps': case 'optDeps': case 'peerDeps': config[key] = Boolean(value); continue; default: this.warn(`Ignoring unknown option ${JSON.stringify(key)}.`); continue; } } if (config.packages.size === 0) { const gitTopLevel = await new Promise(resolve => { cp.execFile('git', ['rev-parse', '--show-toplevel'], (error, stdout) => resolve(error ? typeof error.code === 'string' ? undefined : null : path.normalize(stdout.trim()))); }); walk: for (const cwd of walkUp(process.cwd())) { let file = path.join(cwd, 'package.json'); if (await fileExists(file)) config.packages.add(file); if (cwd === gitTopLevel || (gitTopLevel === undefined && await directoryExists(path.join(cwd, '.git')))) break; for (file of monorepoRootFiles) { if (await fileExists(path.join(cwd, file))) break walk; } } } const externalDependencies = {}; for (const pkg of config.packages) { const json = await fs.readFile(pkg).then(buffer => JSON.parse(buffer.toString()), (err) => err); if (json instanceof Error) { const message = json instanceof SyntaxError ? `File ${JSON.stringify(pkg)} does not look like a valid package.json.` : `Cannot read ${JSON.stringify(pkg)}, error: ${json.code}.`; this.error({ message, cause: json }); } Object.assign(externalDependencies, config.deps ? json.dependencies : undefined, config.devDeps ? json.devDependencies : undefined, config.peerDeps ? json.peerDependencies : undefined, config.optDeps ? json.optionalDependencies : undefined); if (Array.isArray(json.workspaces)) break; } const names = Object.keys(externalDependencies); if (names.length > 0) config.include.push(new RegExp('^(?:' + names.map(RegExp.escape).join('|') + ')(?:/.+)?$')); return config; function isString(str) { return typeof str === 'string' && str.length > 0; } function* walkUp(from) { let previous = ''; while (previous !== from) { yield from; previous = from; from = path.dirname(from); } } function fileExists(name) { return fs.stat(name).then(stat => stat.isFile(), () => false); } function directoryExists(name) { return fs.stat(name).then(stat => stat.isDirectory(), () => false); } function isIncluded(id) { return this.include.length > 0 && this.include.some(rx => rx.test(id)); } function isExcluded(id) { return this.exclude.length > 0 && this.exclude.some(rx => rx.test(id)); } } } export default nodeExternals; export { nodeExternals }; //# sourceMappingURL=index.js.map