diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index 694ef44c325331..57b06c156ecf04 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -15,7 +15,10 @@ const { const { reconnectZeroFillToggle } = require('internal/buffer'); const { Buffer } = require('buffer'); -const { ERR_MANIFEST_ASSERT_INTEGRITY } = require('internal/errors').codes; +const { + ERR_MANIFEST_ASSERT_INTEGRITY, + ERR_PACKAGE_NOT_ALLOWED +} = require('internal/errors').codes; const assert = require('internal/assert'); function prepareMainThreadExecution(expandArgv1 = false) { @@ -97,14 +100,34 @@ function patchProcessObject(expandArgv1) { }); process.argv[0] = process.execPath; + let base; if (expandArgv1 && process.argv[1] && !StringPrototypeStartsWith(process.argv[1], '-')) { // Expand process.argv[1] into a full path. const path = require('path'); try { process.argv[1] = path.resolve(process.argv[1]); + base = path.dirname(process.argv[1]); } catch {} } + const implicitPackageJSONPath = getOptionValue('--package'); + if (implicitPackageJSONPath) { + const cwd = process.cwd(); + if (!base) { + base = cwd; + } + const path = require('path'); + const { read, clobber } = require('internal/modules/package_json_reader'); + const cwdPackage = read(path.join(cwd, 'package.json')); + const entryPackage = read(path.join(base, 'package.json')); + if ( + typeof cwdPackage.string === 'string' || + typeof entryPackage.string === 'string' + ) { + throw ERR_PACKAGE_NOT_ALLOWED(); + } + clobber(path.join(base, 'package.json'), implicitPackageJSONPath); + } // TODO(joyeecheung): most of these should be deprecated and removed, // except some that we need to be able to mutate during run time. diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 34c168ce38ab91..81a731f0b60d71 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1355,6 +1355,8 @@ E('ERR_OUT_OF_RANGE', msg += ` It must be ${range}. Received ${received}`; return msg; }, RangeError); +E('ERR_PACKAGE_NOT_ALLOWED', '--package can only be used when there is not an' + + ' existing package.json file', Error); E('ERR_PACKAGE_IMPORT_NOT_DEFINED', (specifier, packagePath, base) => { return `Package import specifier "${specifier}" is not defined${packagePath ? ` in package ${packagePath}package.json` : ''} imported from ${base}`; diff --git a/lib/internal/modules/package_json_reader.js b/lib/internal/modules/package_json_reader.js index 09eb12bd1533bf..b7433b4b4d7e12 100644 --- a/lib/internal/modules/package_json_reader.js +++ b/lib/internal/modules/package_json_reader.js @@ -38,4 +38,14 @@ function read(jsonPath) { return result; } -module.exports = { read }; +/** + * + * @param {string} jsonPath + * @param {string} srcPath + */ +function clobber(jsonPath, srcPath) { + const value = cache.has(srcPath) ? cache.get(srcPath) : read(srcPath); + cache.set(jsonPath, value); +} + +module.exports = { clobber, read }; diff --git a/src/node_options.cc b/src/node_options.cc index d5c2600688767b..7c13c7dcc94132 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -395,6 +395,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "silence all process warnings", &EnvironmentOptions::no_warnings, kAllowedInEnvironment); + AddOption("--package", + "set file to act as package for entry point", + &EnvironmentOptions::implicit_package, + kAllowedInEnvironment); AddOption("--force-context-aware", "disable loading non-context-aware addons", &EnvironmentOptions::force_context_aware, diff --git a/src/node_options.h b/src/node_options.h index b45a93520dfdad..98b032b656f08d 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -175,6 +175,8 @@ class EnvironmentOptions : public Options { bool tls_max_v1_3 = false; std::string tls_keylog; + std::string implicit_package; + std::vector preload_modules; std::vector user_argv;