diff --git a/packages/resolvers/default/src/DefaultResolver.js b/packages/resolvers/default/src/DefaultResolver.js index 835c881d4d1..6b166af04df 100644 --- a/packages/resolvers/default/src/DefaultResolver.js +++ b/packages/resolvers/default/src/DefaultResolver.js @@ -25,9 +25,8 @@ export default (new Resolver({ ? ['ts', 'tsx', 'js', 'jsx', 'json'] : [], mainFields: ['source', 'browser', 'module', 'main'], - packageManager: options.shouldAutoInstall - ? options.packageManager - : undefined, + packageManager: options.packageManager, + shouldAutoInstall: options.shouldAutoInstall, logger, }); diff --git a/packages/utils/node-resolver-core/src/NodeResolver.js b/packages/utils/node-resolver-core/src/NodeResolver.js index 7f8ddef69d8..2a622080059 100644 --- a/packages/utils/node-resolver-core/src/NodeResolver.js +++ b/packages/utils/node-resolver-core/src/NodeResolver.js @@ -46,6 +46,7 @@ type Options = {| mainFields: Array, packageManager?: PackageManager, logger?: PluginLogger, + shouldAutoInstall?: boolean, |}; type ResolvedFile = {| path: string, @@ -96,6 +97,7 @@ export default class NodeResolver { packageCache: Map; rootPackage: InternalPackageJSON | null; packageManager: ?PackageManager; + shouldAutoInstall: boolean; logger: ?PluginLogger; constructor(opts: Options) { @@ -108,6 +110,7 @@ export default class NodeResolver { this.packageCache = new Map(); this.rootPackage = null; this.packageManager = opts.packageManager; + this.shouldAutoInstall = opts.shouldAutoInstall ?? false; this.logger = opts.logger; } @@ -274,7 +277,7 @@ export default class NodeResolver { } else if (builtin === empty) { return {filePath: empty}; } else if (builtin !== undefined) { - filename = builtin; + filename = builtin.name; } if (this.shouldIncludeNodeModule(env, filename) === false) { @@ -292,46 +295,16 @@ export default class NodeResolver { // ignore } - // Auto install node builtin polyfills if not already available - if (resolved === undefined && builtin != null) { - let packageName = builtin.split('/')[0]; + // Auto install node builtin polyfills if not already available or check that the correct + // version is installed + if (builtin != null) { + // This assumes that there are no polyfill packages that are scoped + let packageName = builtin.name.split('/')[0]; let packageManager = this.packageManager; - if (packageManager) { - this.logger?.warn({ - message: md`Auto installing polyfill for Node builtin module "${specifier}"...`, - codeFrames: [ - { - filePath: ctx.loc?.filePath ?? sourceFile, - codeHighlights: ctx.loc - ? [ - { - message: 'used here', - start: ctx.loc.start, - end: ctx.loc.end, - }, - ] - : [], - }, - ], - documentationURL: - 'https://parceljs.org/features/node-emulation/#polyfilling-%26-excluding-builtin-node-modules', - }); - - await packageManager.resolve(builtin, this.projectRoot + '/index', { - saveDev: true, - shouldAutoInstall: true, - }); - - // Re-resolve - try { - resolved = this.findNodeModulePath(filename, sourceFile, ctx); - } catch (err) { - // ignore - } - } else { - throw new ThrowableDiagnostic({ - diagnostic: { - message: md`Node builtin polyfill "${packageName}" is not installed, but auto install is disabled.`, + if (resolved == null) { + if (this.shouldAutoInstall && packageManager) { + this.logger?.warn({ + message: md`Auto installing polyfill for Node builtin module "${specifier}"...`, codeFrames: [ { filePath: ctx.loc?.filePath ?? sourceFile, @@ -348,10 +321,53 @@ export default class NodeResolver { ], documentationURL: 'https://parceljs.org/features/node-emulation/#polyfilling-%26-excluding-builtin-node-modules', - hints: [ - md`Install the "${packageName}" package with your package manager, and run Parcel again.`, - ], - }, + }); + + await packageManager.resolve(packageName, sourceFile, { + saveDev: true, + shouldAutoInstall: true, + range: builtin.range, + }); + + // Re-resolve + try { + resolved = this.findNodeModulePath(filename, sourceFile, ctx); + } catch (err) { + // ignore + } + } else { + throw new ThrowableDiagnostic({ + diagnostic: { + message: md`Node builtin polyfill "${packageName}" is not installed, but auto install is disabled.`, + codeFrames: [ + { + filePath: ctx.loc?.filePath ?? sourceFile, + codeHighlights: ctx.loc + ? [ + { + message: 'used here', + start: ctx.loc.start, + end: ctx.loc.end, + }, + ] + : [], + }, + ], + documentationURL: + 'https://parceljs.org/features/node-emulation/#polyfilling-%26-excluding-builtin-node-modules', + hints: [ + md`Install the "${packageName}" package with your package manager, and run Parcel again.`, + ], + }, + }); + } + } else if (builtin.range != null) { + // TODO packageManager can be null for backwards compatibility, but that could cause invalid + // resolutions in monorepos + await packageManager?.resolve(packageName, sourceFile, { + saveDev: true, + shouldAutoInstall: true, + range: builtin.range, }); } } @@ -707,7 +723,10 @@ export default class NodeResolver { return resolvedFile; } - findBuiltin(filename: string, env: Environment): ?string { + findBuiltin( + filename: string, + env: Environment, + ): ?{|name: string, range: ?string|} { const isExplicitNode = filename.startsWith('node:'); if (isExplicitNode || builtins[filename]) { if (env.isNode()) { diff --git a/packages/utils/node-resolver-core/src/builtins.js b/packages/utils/node-resolver-core/src/builtins.js index d733e527811..d6383b43821 100644 --- a/packages/utils/node-resolver-core/src/builtins.js +++ b/packages/utils/node-resolver-core/src/builtins.js @@ -4,35 +4,36 @@ import {builtinModules} from 'module'; export const empty: string = require.resolve('./_empty.js'); -// $FlowFixMe -let builtins: {[string]: any, ...} = Object.create(null); +let builtins: {[string]: {|name: string, range: ?string|}, ...} = + // $FlowFixMe + Object.create(null); // use definite (current) list of Node builtins for (let key of builtinModules) { - builtins[key] = empty; + builtins[key] = {name: empty, range: null}; } -builtins.assert = 'assert/'; -builtins.buffer = 'buffer/'; -builtins.console = 'console-browserify'; -builtins.constants = 'constants-browserify'; -builtins.crypto = 'crypto-browserify'; -builtins.domain = 'domain-browser'; -builtins.events = 'events/'; -builtins.http = 'stream-http'; -builtins.https = 'https-browserify'; -builtins.os = 'os-browserify/browser.js'; -builtins.path = 'path-browserify'; -builtins.process = 'process/browser.js'; -builtins.punycode = 'punycode/'; -builtins.querystring = 'querystring-es3/'; -builtins.stream = 'stream-browserify'; -builtins.string_decoder = 'string_decoder/'; -builtins.sys = 'util/util.js'; -builtins.timers = 'timers-browserify'; -builtins.tty = 'tty-browserify'; -builtins.url = 'url/'; -builtins.util = 'util/util.js'; -builtins.vm = 'vm-browserify'; -builtins.zlib = 'browserify-zlib'; +builtins.assert = {name: 'assert/', range: '^2.0.0'}; +builtins.buffer = {name: 'buffer/', range: '^5.5.0'}; +builtins.console = {name: 'console-browserify', range: '^1.2.0'}; +builtins.constants = {name: 'constants-browserify', range: '^1.0.0'}; +builtins.crypto = {name: 'crypto-browserify', range: '^3.12.0'}; +builtins.domain = {name: 'domain-browser', range: '^3.5.0'}; +builtins.events = {name: 'events/', range: '^3.1.0'}; +builtins.http = {name: 'stream-http', range: '^3.1.0'}; +builtins.https = {name: 'https-browserify', range: '^1.0.0'}; +builtins.os = {name: 'os-browserify/browser.js', range: '^0.3.0'}; +builtins.path = {name: 'path-browserify', range: '^1.0.0'}; +builtins.process = {name: 'process/browser.js', range: '^0.11.10'}; +builtins.punycode = {name: 'punycode/', range: '^1.4.1'}; +builtins.querystring = {name: 'querystring-es3/', range: '^0.2.1'}; +builtins.stream = {name: 'stream-browserify', range: '^3.0.0'}; +builtins.string_decoder = {name: 'string_decoder/', range: '^1.3.0'}; +builtins.sys = {name: 'util/util.js', range: '^0.12.3'}; +builtins.timers = {name: 'timers-browserify', range: '^2.0.11'}; +builtins.tty = {name: 'tty-browserify', range: '^0.0.1'}; +builtins.url = {name: 'url/', range: '^0.11.0'}; +builtins.util = {name: 'util/util.js', range: '^0.12.3'}; +builtins.vm = {name: 'vm-browserify', range: '^1.1.2'}; +builtins.zlib = {name: 'browserify-zlib', range: '^0.2.0'}; export default builtins;