From 60044e6b6fe0aca8177f861efa6fa19bac5483a4 Mon Sep 17 00:00:00 2001 From: Bradley Meck Farias Date: Fri, 14 Jun 2024 14:43:14 -0500 Subject: [PATCH 1/6] cli: make run and watch modes friends --- lib/internal/main/watch_mode.js | 98 ++++++++++++++++----- lib/internal/modules/package_json_reader.js | 7 +- src/node.cc | 2 +- src/node_modules.cc | 22 +++-- src/node_modules.h | 3 +- src/node_options.cc | 2 +- 6 files changed, 96 insertions(+), 38 deletions(-) diff --git a/lib/internal/main/watch_mode.js b/lib/internal/main/watch_mode.js index d70908e6e3cd9b..ae835c49e70adf 100644 --- a/lib/internal/main/watch_mode.js +++ b/lib/internal/main/watch_mode.js @@ -3,7 +3,7 @@ const { ArrayPrototypeForEach, ArrayPrototypeJoin, ArrayPrototypeMap, - ArrayPrototypePush, + ArrayPrototypeFilter, ArrayPrototypePushApply, ArrayPrototypeSlice, StringPrototypeStartsWith, @@ -21,11 +21,14 @@ const { getOptionValue } = require('internal/options'); const { FilesWatcher } = require('internal/watch_mode/files_watcher'); const { green, blue, red, white, clear } = require('internal/util/colors'); -const { spawn } = require('child_process'); +const { spawn, exec } = require('child_process'); const { inspect } = require('util'); const { setTimeout, clearTimeout } = require('timers'); -const { resolve } = require('path'); +const { resolve, join } = require('path'); const { once } = require('events'); +const { getNearestParentPackageJSON } = require('internal/modules/package_json_reader') +const { ChildProcess } = require('internal/child_process') +const { readFileSync } = require('fs') prepareMainThreadExecution(false, false); markBootstrapComplete(); @@ -35,23 +38,28 @@ const kKillSignal = 'SIGTERM'; const kShouldFilterModules = getOptionValue('--watch-path').length === 0; const kWatchedPaths = ArrayPrototypeMap(getOptionValue('--watch-path'), (path) => resolve(path)); const kPreserveOutput = getOptionValue('--watch-preserve-output'); +const kRun = getOptionValue('--run'); const kCommand = ArrayPrototypeSlice(process.argv, 1); -const kCommandStr = inspect(ArrayPrototypeJoin(kCommand, ' ')); +let kCommandStr = inspect(ArrayPrototypeJoin(kCommand, ' ')); -const argsWithoutWatchOptions = []; - -for (let i = 0; i < process.execArgv.length; i++) { - const arg = process.execArgv[i]; - if (StringPrototypeStartsWith(arg, '--watch')) { - i++; - const nextArg = process.execArgv[i]; - if (nextArg && StringPrototypeStartsWith(nextArg, '-')) { - ArrayPrototypePush(argsWithoutWatchOptions, nextArg); +const argsWithoutWatchOptions = ArrayPrototypeFilter(process.execArgv, (v, i) => { + if (StringPrototypeStartsWith(v, '--watch')) { + return false + } + if (v === '--run') { + return false + } + if (i > 0 && v[0] !== '-') { + let prevArg = process.execArgv[i - 1] + if (StringPrototypeStartsWith(prevArg, '--watch')) { + return false + } + if (prevArg === '--run') { + return false } - continue; } - ArrayPrototypePush(argsWithoutWatchOptions, arg); -} + return true +}); ArrayPrototypePushApply(argsWithoutWatchOptions, kCommand); @@ -59,19 +67,63 @@ const watcher = new FilesWatcher({ debounce: 200, mode: kShouldFilterModules ? ' ArrayPrototypeForEach(kWatchedPaths, (p) => watcher.watchPath(p)); let graceTimer; +/** + * @type {ChildProcess} + */ let child; let exited; function start() { exited = false; const stdio = kShouldFilterModules ? ['inherit', 'inherit', 'inherit', 'ipc'] : 'inherit'; - child = spawn(process.execPath, argsWithoutWatchOptions, { - stdio, - env: { - ...process.env, - WATCH_REPORT_DEPENDENCIES: '1', - }, - }); + if (!kRun) { + child = spawn(process.execPath, argsWithoutWatchOptions, { + stdio, + env: { + ...process.env, + WATCH_REPORT_DEPENDENCIES: '1', + }, + }); + } else { + if (kCommand.length !== 0) { + throw new Error('cannot provide positional argument to both --run and --watch') + } + // Always get new data in case it changed + const pkgJSON = getNearestParentPackageJSON(join(process.cwd(), 'abc'), false) + if (!pkgJSON) { + throw new Error(`unable to find package.json from directory ${process.cwd()}`) + } + let rawJSON + try { + rawJSON = JSON.parse(readFileSync(pkgJSON.data.pjsonPath, { + encoding: 'utf8' + })) + } catch (e) { + console.error(e) + } + if (rawJSON) { + if (!('scripts' in rawJSON)) { + throw new Error(`package.json from directory ${process.cwd()} (${pkgJSON.data.pjsonPath}) has no "scripts" field`) + } + if (!(kRun in rawJSON.scripts)) { + throw new Error(`package.json from directory ${process.cwd()} (${pkgJSON.data.pjsonPath}) has no entry in "scripts": ${JSON.stringify(kRun)}`) + } + const script = rawJSON.scripts[kRun] + const newCommandStr = inspect(script) + if (newCommandStr != kCommandStr) { + kCommandStr = newCommandStr + process.stdout.write(`${blue}Running ${kCommandStr}${white}\n`); + } + child = spawn(script, kCommand, { + stdio, + shell: true, + env: { + ...process.env, + WATCH_REPORT_DEPENDENCIES: '1', + } + }) + } + } watcher.watchChildProcessModules(child); child.once('exit', (code) => { exited = true; diff --git a/lib/internal/modules/package_json_reader.js b/lib/internal/modules/package_json_reader.js index 9a9dcebb799c00..2b95f03064e5d5 100644 --- a/lib/internal/modules/package_json_reader.js +++ b/lib/internal/modules/package_json_reader.js @@ -77,7 +77,7 @@ function deserializePackageJSON(path, contents) { * }} options * @returns {import('typings/internalBinding/modules').PackageConfig} */ -function read(jsonPath, { base, specifier, isESM } = kEmptyObject) { +function read(jsonPath, { base, specifier, isESM, cached = true } = kEmptyObject) { // This function will be called by both CJS and ESM, so we need to make sure // non-null attributes are converted to strings. const parsed = modulesBinding.readPackageJSON( @@ -85,6 +85,7 @@ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) { isESM, base == null ? undefined : `${base}`, specifier == null ? undefined : `${specifier}`, + cached, ); return deserializePackageJSON(jsonPath, parsed); @@ -107,8 +108,8 @@ function readPackage(requestPath) { * @param {string} checkPath The path to start searching from. * @returns {undefined | {data: import('typings/internalBinding/modules').PackageConfig, path: string}} */ -function getNearestParentPackageJSON(checkPath) { - const result = modulesBinding.getNearestParentPackageJSON(checkPath); +function getNearestParentPackageJSON(checkPath, cached = true) { + const result = modulesBinding.getNearestParentPackageJSON(checkPath, undefined, undefined, undefined, cached); if (result === undefined) { return undefined; diff --git a/src/node.cc b/src/node.cc index a13c5a45496fd0..d707de2ee54a66 100644 --- a/src/node.cc +++ b/src/node.cc @@ -992,7 +992,7 @@ InitializeOncePerProcessInternal(const std::vector& args, } } - if (!per_process::cli_options->run.empty()) { + if (!per_process::cli_options->run.empty() && !per_process::cli_options->per_isolate->per_env->watch_mode) { // TODO(@anonrig): Handle NODE_NO_WARNINGS, NODE_REDIRECT_WARNINGS, // --disable-warning and --redirect-warnings. if (per_process::cli_options->per_isolate->per_env->warnings) { diff --git a/src/node_modules.cc b/src/node_modules.cc index 2e80ff4e95a5c1..f9972dc7d534af 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -85,12 +85,14 @@ Local BindingData::PackageConfig::Serialize(Realm* realm) const { } const BindingData::PackageConfig* BindingData::GetPackageJSON( - Realm* realm, std::string_view path, ErrorContext* error_context) { + Realm* realm, std::string_view path, ErrorContext* error_context, bool cached) { auto binding_data = realm->GetBindingData(); - auto cache_entry = binding_data->package_configs_.find(path.data()); - if (cache_entry != binding_data->package_configs_.end()) { - return &cache_entry->second; + if (cached == true) { + auto cache_entry = binding_data->package_configs_.find(path.data()); + if (cache_entry != binding_data->package_configs_.end()) { + return &cache_entry->second; + } } PackageConfig package_config{}; @@ -228,14 +230,14 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON( } // package_config could be quite large, so we should move it instead of // copying it. - auto cached = binding_data->package_configs_.insert( + auto after_cached = binding_data->package_configs_.insert( {std::string(path), std::move(package_config)}); - return &cached.first->second; + return &after_cached.first->second; } void BindingData::ReadPackageJSON(const FunctionCallbackInfo& args) { - CHECK_GE(args.Length(), 1); // path, [is_esm, base, specifier] + CHECK_GE(args.Length(), 1); // path, [is_esm, base, specifier, cached = true] CHECK(args[0]->IsString()); // path Realm* realm = Realm::GetCurrent(args); @@ -255,6 +257,8 @@ void BindingData::ReadPackageJSON(const FunctionCallbackInfo& args) { Utf8Value specifier(isolate, args[3]); error_context.specifier = specifier.ToString(); } + // everything except exactly false default to cached + auto cached = !(args[4]->IsFalse()); THROW_IF_INSUFFICIENT_PERMISSIONS( realm->env(), @@ -265,10 +269,10 @@ void BindingData::ReadPackageJSON(const FunctionCallbackInfo& args) { // path.toNamespacedPath logic is ported to C++ #ifdef _WIN32 auto package_json = GetPackageJSON( - realm, "\\\\?\\" + path.ToString(), is_esm ? &error_context : nullptr); + realm, "\\\\?\\" + path.ToString(), is_esm ? &error_context : nullptr, cached); #else auto package_json = - GetPackageJSON(realm, path.ToString(), is_esm ? &error_context : nullptr); + GetPackageJSON(realm, path.ToString(), is_esm ? &error_context : nullptr, cached); #endif if (package_json == nullptr) { return; diff --git a/src/node_modules.h b/src/node_modules.h index 17909b2270454b..3a153d688d8c39 100644 --- a/src/node_modules.h +++ b/src/node_modules.h @@ -79,7 +79,8 @@ class BindingData : public SnapshotableObject { static const PackageConfig* GetPackageJSON( Realm* realm, std::string_view path, - ErrorContext* error_context = nullptr); + ErrorContext* error_context = nullptr, + bool cached = true); static const PackageConfig* TraverseParent( Realm* realm, const std::filesystem::path& check_path); }; diff --git a/src/node_options.cc b/src/node_options.cc index b47ee5fea16e2e..62d1ea008fa683 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -176,7 +176,7 @@ void EnvironmentOptions::CheckOptions(std::vector* errors, } else if (test_runner_force_exit) { errors->push_back("either --watch or --test-force-exit " "can be used, not both"); - } else if (!test_runner && (argv->size() < 1 || (*argv)[1].empty())) { + } else if (!(test_runner || !per_process::cli_options->run.empty()) && (argv->size() < 1 || (*argv)[1].empty())) { errors->push_back("--watch requires specifying a file"); } From 8f6021bc44ef4f2e143879fa8f0cce1b723ba2a6 Mon Sep 17 00:00:00 2001 From: Bradley Meck Farias Date: Fri, 21 Jun 2024 14:43:17 -0500 Subject: [PATCH 2/6] docs --- doc/api/cli.md | 6 ++++ test/sequential/test-watch-mode.mjs | 43 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/doc/api/cli.md b/doc/api/cli.md index 684627637b5a0b..bfadc6d36e8389 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -2596,8 +2596,14 @@ Use `--watch-path` to specify what paths to watch. This flag cannot be combined with `--check`, `--eval`, `--interactive`, or the REPL. +If using `--run` and `--watch` Node.js will read the `package.json` scripts +field each restart. +If using `--run` and `--watch` Node.js positional arguments +will throw an error. + ```bash node --watch index.js +node --watch --run start ``` ### `--watch-path` diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index d1dbd75323ddc5..df9843950766b8 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -29,6 +29,12 @@ function createTmpFile(content = 'console.log("running");', ext = '.js', basenam writeFileSync(file, content); return file; } +function createPackageJSON(content = {}, basename = tmpdir.path) { + const file = path.join(basename, 'package.json'); + console.error(file) + writeFileSync(file, JSON.stringify(content, null, 2)); + return file; +} async function runWriteSucceed({ file, @@ -550,6 +556,43 @@ console.log(values.random); ]); }); + it('should run when `--watch --run`', async () => { + const script = Math.random() + const output = Math.random() + const command = `echo ${output}` + const file = createPackageJSON({ + scripts: { + [script]: command + } + }); + const args = ['--watch', '--run', script]; + const { stdout, stderr } = await runWriteSucceed({ file, watchedFile: file, watchFlag: null, args, options: { + cwd: path.dirname(file) + } }); + + assert.strictEqual(stderr, ''); + assert.deepStrictEqual(stdout, [ + `Running '${command}'`, + `${output}`, + `Completed running '${command}'`, + ]); + }); + + it('should error when `--watch --run` with positional arguments', async () => { + const file = createPackageJSON({ + scripts: { + start: 'echo 123' + } + }); + const args = ['--watch', '--run', 'start', 'positional']; + const { stdout, stderr } = await runWriteSucceed({ file, watchedFile: file, watchFlag: null, args, options: { + cwd: path.dirname(file) + } }); + + assert.match(stderr, /cannot provide positional argument to both --run and --watch/); + assert.deepStrictEqual(stdout, []); + }); + it('should run when `--watch -r ./foo.js`', async () => { const projectDir = tmpdir.resolve('project7'); mkdirSync(projectDir); From 26f13d1146a3f658d7a4d5c4064356c8fd37dce6 Mon Sep 17 00:00:00 2001 From: Bradley Meck Farias Date: Fri, 21 Jun 2024 14:58:44 -0500 Subject: [PATCH 3/6] lint --- src/node.cc | 3 ++- src/node_modules.cc | 16 +++++++++++++--- src/node_options.cc | 8 +++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/node.cc b/src/node.cc index d707de2ee54a66..ae6383a48c13fd 100644 --- a/src/node.cc +++ b/src/node.cc @@ -992,7 +992,8 @@ InitializeOncePerProcessInternal(const std::vector& args, } } - if (!per_process::cli_options->run.empty() && !per_process::cli_options->per_isolate->per_env->watch_mode) { + if (!per_process::cli_options->run.empty() && + !per_process::cli_options->per_isolate->per_env->watch_mode) { // TODO(@anonrig): Handle NODE_NO_WARNINGS, NODE_REDIRECT_WARNINGS, // --disable-warning and --redirect-warnings. if (per_process::cli_options->per_isolate->per_env->warnings) { diff --git a/src/node_modules.cc b/src/node_modules.cc index f9972dc7d534af..942170ff0c6537 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -85,7 +85,10 @@ Local BindingData::PackageConfig::Serialize(Realm* realm) const { } const BindingData::PackageConfig* BindingData::GetPackageJSON( - Realm* realm, std::string_view path, ErrorContext* error_context, bool cached) { + Realm* realm, + std::string_view path, + ErrorContext* error_context, + bool cached) { auto binding_data = realm->GetBindingData(); if (cached == true) { @@ -269,10 +272,17 @@ void BindingData::ReadPackageJSON(const FunctionCallbackInfo& args) { // path.toNamespacedPath logic is ported to C++ #ifdef _WIN32 auto package_json = GetPackageJSON( - realm, "\\\\?\\" + path.ToString(), is_esm ? &error_context : nullptr, cached); + realm, + "\\\\?\\" + path.ToString(), + is_esm ? &error_context : nullptr, + cached); #else auto package_json = - GetPackageJSON(realm, path.ToString(), is_esm ? &error_context : nullptr, cached); + GetPackageJSON( + realm, + path.ToString(), + is_esm ? &error_context : nullptr, + cached); #endif if (package_json == nullptr) { return; diff --git a/src/node_options.cc b/src/node_options.cc index 62d1ea008fa683..49a4b1341a31cc 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -176,7 +176,13 @@ void EnvironmentOptions::CheckOptions(std::vector* errors, } else if (test_runner_force_exit) { errors->push_back("either --watch or --test-force-exit " "can be used, not both"); - } else if (!(test_runner || !per_process::cli_options->run.empty()) && (argv->size() < 1 || (*argv)[1].empty())) { + } else if (!( + test_runner || + !per_process::cli_options->run.empty()) && + ( + argv->size() < 1 || + (*argv)[1].empty()) + ) { errors->push_back("--watch requires specifying a file"); } From d808a6b7370e7b12da0f85fc6bcdf8a5a8e471ec Mon Sep 17 00:00:00 2001 From: Bradley Meck Farias Date: Fri, 21 Jun 2024 15:24:57 -0500 Subject: [PATCH 4/6] lint --- doc/api/errors.md | 16 ++++++++ lib/internal/errors.js | 9 +++++ lib/internal/main/watch_mode.js | 60 ++++++++++++++++------------- src/node_modules.cc | 17 +++----- src/node_options.cc | 9 +---- test/sequential/test-watch-mode.mjs | 9 +++-- 6 files changed, 71 insertions(+), 49 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index 4d3829f78e7b95..49c13db5d2fc3f 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -2359,6 +2359,22 @@ The `package.json` [`"exports"`][] field does not export the requested subpath. Because exports are encapsulated, private internal modules that are not exported cannot be imported through the package resolution, unless using an absolute URL. + + +### `ERR_PACKAGE_SCRIPT_MISSING` + +The `package.json` [`"script"`][] field does not contain the requested script. +In order to use `--run` ensure that the contents are an object containing a +`"scripts"` field that contains the missing script name as a field. + +```json +{ + "scripts": { + "start": "node server.js" + } +} +``` + ### `ERR_PARSE_ARGS_INVALID_OPTION_VALUE` diff --git a/lib/internal/errors.js b/lib/internal/errors.js index f78be397d5dce2..229e1254d8f824 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1633,6 +1633,15 @@ E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath, base = undefined) => { return `Package subpath '${subpath}' is not defined by "exports" in ${ pkgPath}package.json${base ? ` imported from ${base}` : ''}`; }, Error); +E('ERR_PACKAGE_SCRIPT_MISSING', (pjsonPath, script, parsedJSON, base) => { + if (!pjsonPath) { + return `unable to find package.json from directory ${base}`; + } + if (!('scripts' in parsedJSON)) { + return `package.json from directory ${base} (${pjsonPath}) has no "scripts" field`; + } + return `package.json from directory ${base} (${pjsonPath}) has no entry in "scripts": ${JSONStringify(script)}`; +}, Error); E('ERR_PARSE_ARGS_INVALID_OPTION_VALUE', '%s', TypeError); E('ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL', "Unexpected argument '%s'. This " + 'command does not take positional arguments', TypeError); diff --git a/lib/internal/main/watch_mode.js b/lib/internal/main/watch_mode.js index ae835c49e70adf..b344a7713e0d63 100644 --- a/lib/internal/main/watch_mode.js +++ b/lib/internal/main/watch_mode.js @@ -1,11 +1,12 @@ 'use strict'; const { + ArrayPrototypeFilter, ArrayPrototypeForEach, ArrayPrototypeJoin, ArrayPrototypeMap, - ArrayPrototypeFilter, ArrayPrototypePushApply, ArrayPrototypeSlice, + JSONParse, StringPrototypeStartsWith, } = primordials; @@ -18,17 +19,21 @@ const { exitCodes: { kNoFailure }, } = internalBinding('errors'); const { getOptionValue } = require('internal/options'); +const { + ERR_PACKAGE_SCRIPT_MISSING, + ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL, +} = require('internal/errors').codes; const { FilesWatcher } = require('internal/watch_mode/files_watcher'); const { green, blue, red, white, clear } = require('internal/util/colors'); -const { spawn, exec } = require('child_process'); +const { spawn } = require('child_process'); const { inspect } = require('util'); const { setTimeout, clearTimeout } = require('timers'); const { resolve, join } = require('path'); const { once } = require('events'); -const { getNearestParentPackageJSON } = require('internal/modules/package_json_reader') -const { ChildProcess } = require('internal/child_process') -const { readFileSync } = require('fs') +const { getNearestParentPackageJSON } = require('internal/modules/package_json_reader'); +const { readFileSync } = require('fs'); +const { emitWarning } = require('internal/process/warning'); prepareMainThreadExecution(false, false); markBootstrapComplete(); @@ -44,21 +49,21 @@ let kCommandStr = inspect(ArrayPrototypeJoin(kCommand, ' ')); const argsWithoutWatchOptions = ArrayPrototypeFilter(process.execArgv, (v, i) => { if (StringPrototypeStartsWith(v, '--watch')) { - return false + return false; } if (v === '--run') { - return false + return false; } if (i > 0 && v[0] !== '-') { - let prevArg = process.execArgv[i - 1] + const prevArg = process.execArgv[i - 1]; if (StringPrototypeStartsWith(prevArg, '--watch')) { - return false + return false; } if (prevArg === '--run') { - return false + return false; } } - return true + return true; }); ArrayPrototypePushApply(argsWithoutWatchOptions, kCommand); @@ -86,32 +91,33 @@ function start() { }); } else { if (kCommand.length !== 0) { - throw new Error('cannot provide positional argument to both --run and --watch') + throw new ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL('cannot provide positional argument to both --run and --watch'); } // Always get new data in case it changed - const pkgJSON = getNearestParentPackageJSON(join(process.cwd(), 'abc'), false) + const base = process.cwd(); + const pkgJSON = getNearestParentPackageJSON(join(base, 'package.json'), false); if (!pkgJSON) { - throw new Error(`unable to find package.json from directory ${process.cwd()}`) + throw new ERR_PACKAGE_SCRIPT_MISSING(null, kRun, undefined, base); } - let rawJSON + let rawJSON; try { - rawJSON = JSON.parse(readFileSync(pkgJSON.data.pjsonPath, { - encoding: 'utf8' - })) + rawJSON = JSONParse(readFileSync(pkgJSON.data.pjsonPath, { + encoding: 'utf8', + })); } catch (e) { - console.error(e) + emitWarning(`Error parsing JSON in ${pkgJSON.data.pjsonPath}. ${e}`); } if (rawJSON) { if (!('scripts' in rawJSON)) { - throw new Error(`package.json from directory ${process.cwd()} (${pkgJSON.data.pjsonPath}) has no "scripts" field`) + throw new ERR_PACKAGE_SCRIPT_MISSING(pkgJSON.data.pjsonPath, kRun, rawJSON, base); } if (!(kRun in rawJSON.scripts)) { - throw new Error(`package.json from directory ${process.cwd()} (${pkgJSON.data.pjsonPath}) has no entry in "scripts": ${JSON.stringify(kRun)}`) + throw new ERR_PACKAGE_SCRIPT_MISSING(pkgJSON.data.pjsonPath, kRun, rawJSON, base); } - const script = rawJSON.scripts[kRun] - const newCommandStr = inspect(script) - if (newCommandStr != kCommandStr) { - kCommandStr = newCommandStr + const script = rawJSON.scripts[kRun]; + const newCommandStr = inspect(script); + if (newCommandStr !== kCommandStr) { + kCommandStr = newCommandStr; process.stdout.write(`${blue}Running ${kCommandStr}${white}\n`); } child = spawn(script, kCommand, { @@ -120,8 +126,8 @@ function start() { env: { ...process.env, WATCH_REPORT_DEPENDENCIES: '1', - } - }) + }, + }); } } watcher.watchChildProcessModules(child); diff --git a/src/node_modules.cc b/src/node_modules.cc index 942170ff0c6537..f9f62b886fa35f 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -271,18 +271,13 @@ void BindingData::ReadPackageJSON(const FunctionCallbackInfo& args) { // TODO(StefanStojanovic): Remove ifdef after // path.toNamespacedPath logic is ported to C++ #ifdef _WIN32 - auto package_json = GetPackageJSON( - realm, - "\\\\?\\" + path.ToString(), - is_esm ? &error_context : nullptr, - cached); + auto package_json = GetPackageJSON(realm, + "\\\\?\\" + path.ToString(), + is_esm ? &error_context : nullptr, + cached); #else - auto package_json = - GetPackageJSON( - realm, - path.ToString(), - is_esm ? &error_context : nullptr, - cached); + auto package_json = GetPackageJSON( + realm, path.ToString(), is_esm ? &error_context : nullptr, cached); #endif if (package_json == nullptr) { return; diff --git a/src/node_options.cc b/src/node_options.cc index 49a4b1341a31cc..681dabfdd83364 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -176,13 +176,8 @@ void EnvironmentOptions::CheckOptions(std::vector* errors, } else if (test_runner_force_exit) { errors->push_back("either --watch or --test-force-exit " "can be used, not both"); - } else if (!( - test_runner || - !per_process::cli_options->run.empty()) && - ( - argv->size() < 1 || - (*argv)[1].empty()) - ) { + } else if (!(test_runner || !per_process::cli_options->run.empty()) && + (argv->size() < 1 || (*argv)[1].empty())) { errors->push_back("--watch requires specifying a file"); } diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index df9843950766b8..fb2851fa644060 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -29,9 +29,10 @@ function createTmpFile(content = 'console.log("running");', ext = '.js', basenam writeFileSync(file, content); return file; } + function createPackageJSON(content = {}, basename = tmpdir.path) { const file = path.join(basename, 'package.json'); - console.error(file) + console.error(file); writeFileSync(file, JSON.stringify(content, null, 2)); return file; } @@ -557,9 +558,9 @@ console.log(values.random); }); it('should run when `--watch --run`', async () => { - const script = Math.random() - const output = Math.random() - const command = `echo ${output}` + const script = Math.random(); + const output = Math.random(); + const command = `echo ${output}`; const file = createPackageJSON({ scripts: { [script]: command From ba06f89f0a7bef57765ce4f00723485dd6fed7f9 Mon Sep 17 00:00:00 2001 From: Bradley Meck Farias Date: Fri, 21 Jun 2024 15:35:59 -0500 Subject: [PATCH 5/6] lint --- doc/api/errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index 49c13db5d2fc3f..9cbdce450cc5f6 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -2363,7 +2363,7 @@ cannot be imported through the package resolution, unless using an absolute URL. ### `ERR_PACKAGE_SCRIPT_MISSING` -The `package.json` [`"script"`][] field does not contain the requested script. +The `package.json` "scripts" field does not contain the requested script. In order to use `--run` ensure that the contents are an object containing a `"scripts"` field that contains the missing script name as a field. From 35540cb1b32e400074e726421c5eef22ba872800 Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Mon, 24 Jun 2024 12:11:45 -0500 Subject: [PATCH 6/6] Update test/sequential/test-watch-mode.mjs --- test/sequential/test-watch-mode.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index fb2851fa644060..1b3eaf54e34999 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -32,7 +32,6 @@ function createTmpFile(content = 'console.log("running");', ext = '.js', basenam function createPackageJSON(content = {}, basename = tmpdir.path) { const file = path.join(basename, 'package.json'); - console.error(file); writeFileSync(file, JSON.stringify(content, null, 2)); return file; }