From 755c75a61249746ea52e8e95e92dc9ecb9030dfc Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Thu, 1 May 2025 13:07:07 -0400 Subject: [PATCH 01/42] fix(powershell): use Invoke-Expression to pass args Co-authored-by: noseratio --- bin/npm.ps1 | 21 +++++++++++++++++++-- bin/npx.ps1 | 21 +++++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index 04a1fd478ef9d..8035db163c2bc 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -22,11 +22,28 @@ if (Test-Path $NPM_PREFIX_NPM_CLI_JS) { $NPM_CLI_JS=$NPM_PREFIX_NPM_CLI_JS } +function Normalize { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] + [string]$Path + ) + + $Path = [System.IO.Path]::GetFullPath($Path) + # remove trailing " or ' quotes (if any) and put back " quotes around the path + $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', '$1' + $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" + return """$Path""" +} + +$NPM_ARGS = $MyInvocation.Statement.Substring($MyInvocation.InvocationName.Length).Trim() +$INVOKE_NPM = "& $(Normalize $NODE_EXE) $(Normalize $NPM_CLI_JS) $NPM_ARGS" + # Support pipeline input if ($MyInvocation.ExpectingInput) { - $input | & $NODE_EXE $NPM_CLI_JS $args + $input | Invoke-Expression $INVOKE_NPM } else { - & $NODE_EXE $NPM_CLI_JS $args + Invoke-Expression $INVOKE_NPM } exit $LASTEXITCODE diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 28dae51b22ca9..0a24dbaf15e92 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -22,11 +22,28 @@ if (Test-Path $NPM_PREFIX_NPX_CLI_JS) { $NPX_CLI_JS=$NPM_PREFIX_NPX_CLI_JS } +function Normalize { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] + [string]$Path + ) + + $Path = [System.IO.Path]::GetFullPath($Path) + # remove trailing " or ' quotes (if any) and put back " quotes around the path + $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', '$1' + $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" + return """$Path""" +} + +$NPX_ARGS = $MyInvocation.Statement.Substring($MyInvocation.InvocationName.Length).Trim() +$INVOKE_NPX = "& $(Normalize $NODE_EXE) $(Normalize $NPX_CLI_JS) $NPX_ARGS" + # Support pipeline input if ($MyInvocation.ExpectingInput) { - $input | & $NODE_EXE $NPX_CLI_JS $args + $input | Invoke-Expression $INVOKE_NPX } else { - & $NODE_EXE $NPX_CLI_JS $args + Invoke-Expression $INVOKE_NPX } exit $LASTEXITCODE From 5981a1b200340c1098cf21242fe6b03c4ba08ac4 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Thu, 1 May 2025 14:17:47 -0400 Subject: [PATCH 02/42] `$MyInvocation.Statement` is undefined in Windows PowerShell --- bin/npm.ps1 | 2 +- bin/npx.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index 8035db163c2bc..86d00c637b883 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -36,7 +36,7 @@ function Normalize { return """$Path""" } -$NPM_ARGS = $MyInvocation.Statement.Substring($MyInvocation.InvocationName.Length).Trim() +$NPM_ARGS = $MyInvocation.Line.Substring($MyInvocation.InvocationName.Length).Trim() $INVOKE_NPM = "& $(Normalize $NODE_EXE) $(Normalize $NPM_CLI_JS) $NPM_ARGS" # Support pipeline input diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 0a24dbaf15e92..204fa78bc328c 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -36,7 +36,7 @@ function Normalize { return """$Path""" } -$NPX_ARGS = $MyInvocation.Statement.Substring($MyInvocation.InvocationName.Length).Trim() +$NPX_ARGS = $MyInvocation.Line.Substring($MyInvocation.InvocationName.Length).Trim() $INVOKE_NPX = "& $(Normalize $NODE_EXE) $(Normalize $NPX_CLI_JS) $NPX_ARGS" # Support pipeline input From d37f8f91aaff545a7d44b6518b410a98ba07a547 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Fri, 2 May 2025 07:07:02 -0400 Subject: [PATCH 03/42] `windows-shims.js`: cover passing regular and named parameters to scripts Co-authored-by: @mbtools --- test/bin/windows-shims.js | 47 ++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 8fbee609a0fab..a26590b87da4a 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -20,6 +20,9 @@ const BIN = join(ROOT, 'bin') const SHIMS = readNonJsFiles(BIN) const NODE_GYP = readNonJsFiles(join(BIN, 'node-gyp-bin')) const SHIM_EXTS = [...new Set(Object.keys(SHIMS).map(p => extname(p)))] +const PACKAGE_NAME = 'test' +const PACKAGE_VERSION = '1.0.0' +const SCRIPT_NAME = 'args.js' t.test('shim contents', t => { // these scripts should be kept in sync so this tests the contents of each @@ -99,6 +102,17 @@ t.test('run shims', t => { }, }, }, + // test script returning all command line arguments + [SCRIPT_NAME]: `process.argv.slice(2).forEach((arg) => console.log(arg))`, + // package.json for the test script + 'package.json': ` + { + "name": "${PACKAGE_NAME}", + "version": "${PACKAGE_VERSION}", + "scripts": { + "test": "node ${SCRIPT_NAME}" + } + }`, }) // The removal of this fixture causes this test to fail when done with @@ -216,7 +230,7 @@ t.test('run shims', t => { } }) - const matchCmd = (t, cmd, bin, match) => { + const matchCmd = (t, cmd, bin, match, params, expected) => { const args = [] const opts = {} @@ -234,18 +248,34 @@ t.test('run shims', t => { throw new Error('unknown shell') } - const isNpm = bin === 'npm' - const result = spawnPath(cmd, [...args, isNpm ? 'help' : '--version'], opts) + const result = spawnPath(cmd, [...args, ...params], opts) + + // skip the first 3 lines of "npm test" to get the actual script output + if (params[0].startsWith('test')) { + result.stdout = result.stdout.split('\n').slice(3).join('\n').trim() + } t.match(result, { status: 0, signal: null, stderr: '', - stdout: isNpm ? `npm@${version} ${ROOT}` : version, + stdout: expected, ...match, - }, `${cmd} ${bin}`) + }, `${cmd} ${bin} ${params[0]}`) } + // Array with command line parameters and expected output + const tests = [ + { bin: 'npm', params: ['help'], expected: `npm@${version} ${ROOT}` }, + { bin: 'npx', params: ['--version'], expected: version }, + { bin: 'npm', params: ['test'], expected: '' }, + { bin: 'npm', params: ['test -- hello world'], expected: `hello\nworld` }, + { bin: 'npm', params: ['test -- -p hello'], expected: `-p\nhello` }, + { bin: 'npm', params: ['test -- -p "hello world"'], expected: `-p\nhello world` }, + { bin: 'npm', params: ['test -- --param=hello'], expected: `--param=hello` }, + { bin: 'npm', params: ['test -- --param="hello world"'], expected: `--param=hello world` }, + ] + // ensure that all tests are either run or skipped t.plan(shells.length) @@ -259,9 +289,10 @@ t.test('run shims', t => { } return t.end() } - t.plan(2) - matchCmd(t, cmd, 'npm', match) - matchCmd(t, cmd, 'npx', match) + t.plan(tests.length) + for (const { bin, params, expected } of tests) { + matchCmd(t, cmd, bin, match, params, expected) + } }) } }) From 7a931ed4d6f87d268fdc979be250730811275588 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Fri, 2 May 2025 09:31:29 -0400 Subject: [PATCH 04/42] add tests for comma-separated values --- test/bin/windows-shims.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index a26590b87da4a..cad6393b2a03a 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -274,6 +274,9 @@ t.test('run shims', t => { { bin: 'npm', params: ['test -- -p "hello world"'], expected: `-p\nhello world` }, { bin: 'npm', params: ['test -- --param=hello'], expected: `--param=hello` }, { bin: 'npm', params: ['test -- --param="hello world"'], expected: `--param=hello world` }, + { bin: 'npm', params: ['test -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, + { bin: 'npm', params: ['test -- "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, + { bin: 'npm', params: ['test -- \'a=1,b=2,c=3\''], expected: `a=1,b=2,c=3` }, ] // ensure that all tests are either run or skipped From 6ac55415ae57b8eda59e7b355903c520f1926214 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Fri, 2 May 2025 09:37:28 -0400 Subject: [PATCH 05/42] remove test that doesn't work in command prompt --- test/bin/windows-shims.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index cad6393b2a03a..ad9a4533e9fa8 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -276,7 +276,6 @@ t.test('run shims', t => { { bin: 'npm', params: ['test -- --param="hello world"'], expected: `--param=hello world` }, { bin: 'npm', params: ['test -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, { bin: 'npm', params: ['test -- "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, - { bin: 'npm', params: ['test -- \'a=1,b=2,c=3\''], expected: `a=1,b=2,c=3` }, ] // ensure that all tests are either run or skipped From 230fcd8963da44d283be4f66d2a882fc901e363c Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Fri, 2 May 2025 10:09:40 -0400 Subject: [PATCH 06/42] add npx tests --- test/bin/windows-shims.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index ad9a4533e9fa8..48301fc508358 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -103,11 +103,12 @@ t.test('run shims', t => { }, }, // test script returning all command line arguments - [SCRIPT_NAME]: `process.argv.slice(2).forEach((arg) => console.log(arg))`, + [SCRIPT_NAME]: `#!/usr/bin/env node\n\nprocess.argv.slice(2).forEach((arg) => console.log(arg))`, // package.json for the test script 'package.json': ` { "name": "${PACKAGE_NAME}", + "bin": "${SCRIPT_NAME}", "version": "${PACKAGE_VERSION}", "scripts": { "test": "node ${SCRIPT_NAME}" @@ -276,6 +277,8 @@ t.test('run shims', t => { { bin: 'npm', params: ['test -- --param="hello world"'], expected: `--param=hello world` }, { bin: 'npm', params: ['test -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, { bin: 'npm', params: ['test -- "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, + { bin: 'npx', params: ['. a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, + { bin: 'npx', params: ['. "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, ] // ensure that all tests are either run or skipped From d18d75e41504eb4af7c88929d698fce34c764e67 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Fri, 2 May 2025 10:13:27 -0400 Subject: [PATCH 07/42] correct test --- test/bin/windows-shims.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 48301fc508358..187be883c3912 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -108,11 +108,11 @@ t.test('run shims', t => { 'package.json': ` { "name": "${PACKAGE_NAME}", - "bin": "${SCRIPT_NAME}", "version": "${PACKAGE_VERSION}", "scripts": { "test": "node ${SCRIPT_NAME}" - } + }, + "bin": "${SCRIPT_NAME}" }`, }) From f3ff347429fbfa351ca4af37b7a5b222f9c330c8 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 14:38:41 -0400 Subject: [PATCH 08/42] small change --- test/bin/windows-shims.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 187be883c3912..fe5946c07a06b 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -253,7 +253,7 @@ t.test('run shims', t => { // skip the first 3 lines of "npm test" to get the actual script output if (params[0].startsWith('test')) { - result.stdout = result.stdout.split('\n').slice(3).join('\n').trim() + result.stdout = result.stdout?.toString().split('\n').slice(3).join('\n').trim() } t.match(result, { From ece490b8175a104eaafdeb574ff3996a2651ef19 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 15:03:38 -0400 Subject: [PATCH 09/42] Update windows-shims.js --- test/bin/windows-shims.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index fe5946c07a06b..2a639b8a478a8 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -275,8 +275,8 @@ t.test('run shims', t => { { bin: 'npm', params: ['test -- -p "hello world"'], expected: `-p\nhello world` }, { bin: 'npm', params: ['test -- --param=hello'], expected: `--param=hello` }, { bin: 'npm', params: ['test -- --param="hello world"'], expected: `--param=hello world` }, - { bin: 'npm', params: ['test -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, - { bin: 'npm', params: ['test -- "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, + { bin: 'npm', params: ['help a=1,b=2,c=3'], expected: `No matches in help for: a=1,b=2,c=3` }, + { bin: 'npm', params: ['help "a=1,b=2,c=3"'], expected: `No matches in help for: a=1,b=2,c=3` }, { bin: 'npx', params: ['. a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, { bin: 'npx', params: ['. "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, ] From f446d3849313d3c69dc6f875f9313d510f9b33c0 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 15:15:13 -0400 Subject: [PATCH 10/42] Update windows-shims.js --- test/bin/windows-shims.js | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 2a639b8a478a8..f32b5debf6397 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -20,9 +20,6 @@ const BIN = join(ROOT, 'bin') const SHIMS = readNonJsFiles(BIN) const NODE_GYP = readNonJsFiles(join(BIN, 'node-gyp-bin')) const SHIM_EXTS = [...new Set(Object.keys(SHIMS).map(p => extname(p)))] -const PACKAGE_NAME = 'test' -const PACKAGE_VERSION = '1.0.0' -const SCRIPT_NAME = 'args.js' t.test('shim contents', t => { // these scripts should be kept in sync so this tests the contents of each @@ -102,18 +99,6 @@ t.test('run shims', t => { }, }, }, - // test script returning all command line arguments - [SCRIPT_NAME]: `#!/usr/bin/env node\n\nprocess.argv.slice(2).forEach((arg) => console.log(arg))`, - // package.json for the test script - 'package.json': ` - { - "name": "${PACKAGE_NAME}", - "version": "${PACKAGE_VERSION}", - "scripts": { - "test": "node ${SCRIPT_NAME}" - }, - "bin": "${SCRIPT_NAME}" - }`, }) // The removal of this fixture causes this test to fail when done with @@ -269,16 +254,10 @@ t.test('run shims', t => { const tests = [ { bin: 'npm', params: ['help'], expected: `npm@${version} ${ROOT}` }, { bin: 'npx', params: ['--version'], expected: version }, - { bin: 'npm', params: ['test'], expected: '' }, - { bin: 'npm', params: ['test -- hello world'], expected: `hello\nworld` }, - { bin: 'npm', params: ['test -- -p hello'], expected: `-p\nhello` }, - { bin: 'npm', params: ['test -- -p "hello world"'], expected: `-p\nhello world` }, - { bin: 'npm', params: ['test -- --param=hello'], expected: `--param=hello` }, - { bin: 'npm', params: ['test -- --param="hello world"'], expected: `--param=hello world` }, { bin: 'npm', params: ['help a=1,b=2,c=3'], expected: `No matches in help for: a=1,b=2,c=3` }, { bin: 'npm', params: ['help "a=1,b=2,c=3"'], expected: `No matches in help for: a=1,b=2,c=3` }, - { bin: 'npx', params: ['. a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, - { bin: 'npx', params: ['. "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, + { bin: 'npx', params: ['glob -v a=1,b=2,c=3'], expected: `a=1,b=2,c=3 [ 'a=1,b=2,c=3' ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]` }, + { bin: 'npx', params: ['glob -v a=1,b=2,c=3'], expected: `a=1,b=2,c=3 [ 'a=1,b=2,c=3' ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]` }, ] // ensure that all tests are either run or skipped From a4077f5a64cdb09758e7136548b50d8ff5ed8cd1 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 15:17:50 -0400 Subject: [PATCH 11/42] make less strict --- test/bin/windows-shims.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index f32b5debf6397..bdc2d96ebec26 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -256,8 +256,8 @@ t.test('run shims', t => { { bin: 'npx', params: ['--version'], expected: version }, { bin: 'npm', params: ['help a=1,b=2,c=3'], expected: `No matches in help for: a=1,b=2,c=3` }, { bin: 'npm', params: ['help "a=1,b=2,c=3"'], expected: `No matches in help for: a=1,b=2,c=3` }, - { bin: 'npx', params: ['glob -v a=1,b=2,c=3'], expected: `a=1,b=2,c=3 [ 'a=1,b=2,c=3' ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]` }, - { bin: 'npx', params: ['glob -v a=1,b=2,c=3'], expected: `a=1,b=2,c=3 [ 'a=1,b=2,c=3' ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]\na=1,b=2,c=3 [ [ 'a=1,b=2,c=3' ] ]` }, + { bin: 'npx', params: ['glob -v a=1,b=2,c=3'], expected: `[ 'a=1,b=2,c=3' ]` }, + { bin: 'npx', params: ['glob -v a=1,b=2,c=3'], expected: `[ 'a=1,b=2,c=3' ]` }, ] // ensure that all tests are either run or skipped From 9f8bb56359bd316ab1dbe964629061e51c19d788 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 15:19:54 -0400 Subject: [PATCH 12/42] oops --- test/bin/windows-shims.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index bdc2d96ebec26..58429695a41a3 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -257,7 +257,7 @@ t.test('run shims', t => { { bin: 'npm', params: ['help a=1,b=2,c=3'], expected: `No matches in help for: a=1,b=2,c=3` }, { bin: 'npm', params: ['help "a=1,b=2,c=3"'], expected: `No matches in help for: a=1,b=2,c=3` }, { bin: 'npx', params: ['glob -v a=1,b=2,c=3'], expected: `[ 'a=1,b=2,c=3' ]` }, - { bin: 'npx', params: ['glob -v a=1,b=2,c=3'], expected: `[ 'a=1,b=2,c=3' ]` }, + { bin: 'npx', params: ['glob -v "a=1,b=2,c=3"'], expected: `[ 'a=1,b=2,c=3' ]` }, ] // ensure that all tests are either run or skipped From fee22f1303bac6a5ff0fe19fb93736fd843e1cfb Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 15:59:44 -0400 Subject: [PATCH 13/42] `npm.ps1` fix multiple commands --- bin/npm.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index 86d00c637b883..4e5821cc3d264 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -36,7 +36,13 @@ function Normalize { return """$Path""" } -$NPM_ARGS = $MyInvocation.Line.Substring($MyInvocation.InvocationName.Length).Trim() + +$NPM_PositionMessage = $MyInvocation.PositionMessage -split "`r?`n" +$firstIndex = $NPM_PositionMessage[2].IndexOf("~") +$lastIndex = $NPM_PositionMessage[2].LastIndexOf("~") +$NPM_OG_COMMAND = $NPM_PositionMessage[1].Substring($firstIndex, $lastIndex - $firstIndex + 1) + +$NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() $INVOKE_NPM = "& $(Normalize $NODE_EXE) $(Normalize $NPM_CLI_JS) $NPM_ARGS" # Support pipeline input From 3a4bde2a6f017ac0e377301c72427a80ef75c6ee Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 16:00:25 -0400 Subject: [PATCH 14/42] `npx.ps1` fix multiple commands --- bin/npx.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 204fa78bc328c..1f69d464d3110 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -36,7 +36,13 @@ function Normalize { return """$Path""" } -$NPX_ARGS = $MyInvocation.Line.Substring($MyInvocation.InvocationName.Length).Trim() + +$NPX_PositionMessage = $MyInvocation.PositionMessage -split "`r?`n" +$firstIndex = $NPM_PositionMessage[2].IndexOf("~") +$lastIndex = $NPM_PositionMessage[2].LastIndexOf("~") +$NPX_OG_COMMAND = $NPM_PositionMessage[1].Substring($firstIndex, $lastIndex - $firstIndex + 1) + +$NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() $INVOKE_NPX = "& $(Normalize $NODE_EXE) $(Normalize $NPX_CLI_JS) $NPX_ARGS" # Support pipeline input From 16ee5120482c3d2e30e8e7a91b02cdb94348ee85 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 16:29:21 -0400 Subject: [PATCH 15/42] Update windows-shims.js --- test/bin/windows-shims.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 58429695a41a3..a0c28fcf27c21 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -256,8 +256,8 @@ t.test('run shims', t => { { bin: 'npx', params: ['--version'], expected: version }, { bin: 'npm', params: ['help a=1,b=2,c=3'], expected: `No matches in help for: a=1,b=2,c=3` }, { bin: 'npm', params: ['help "a=1,b=2,c=3"'], expected: `No matches in help for: a=1,b=2,c=3` }, - { bin: 'npx', params: ['glob -v a=1,b=2,c=3'], expected: `[ 'a=1,b=2,c=3' ]` }, - { bin: 'npx', params: ['glob -v "a=1,b=2,c=3"'], expected: `[ 'a=1,b=2,c=3' ]` }, + { bin: 'npx', params: ['glob -v a=1,b=2,c=3 2>&1'], expected: `[ 'a=1,b=2,c=3' ]` }, + { bin: 'npx', params: ['glob -v "a=1,b=2,c=3" 2>&1'], expected: `[ 'a=1,b=2,c=3' ]` }, ] // ensure that all tests are either run or skipped From d8bd5a2ae219f8148b01f0ca8dcbe2accf2acc34 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 18:33:35 -0400 Subject: [PATCH 16/42] formatting change --- bin/npm.ps1 | 1 - bin/npx.ps1 | 1 - 2 files changed, 2 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index 4e5821cc3d264..b428d3cb85756 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -36,7 +36,6 @@ function Normalize { return """$Path""" } - $NPM_PositionMessage = $MyInvocation.PositionMessage -split "`r?`n" $firstIndex = $NPM_PositionMessage[2].IndexOf("~") $lastIndex = $NPM_PositionMessage[2].LastIndexOf("~") diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 1f69d464d3110..a20a5aacf586d 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -36,7 +36,6 @@ function Normalize { return """$Path""" } - $NPX_PositionMessage = $MyInvocation.PositionMessage -split "`r?`n" $firstIndex = $NPM_PositionMessage[2].IndexOf("~") $lastIndex = $NPM_PositionMessage[2].LastIndexOf("~") From 58b9d55feb3e88d1220a999430bc510699706cf5 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 18:37:43 -0400 Subject: [PATCH 17/42] fix typos --- bin/npx.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/npx.ps1 b/bin/npx.ps1 index a20a5aacf586d..2f820872117bb 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -37,9 +37,9 @@ function Normalize { } $NPX_PositionMessage = $MyInvocation.PositionMessage -split "`r?`n" -$firstIndex = $NPM_PositionMessage[2].IndexOf("~") -$lastIndex = $NPM_PositionMessage[2].LastIndexOf("~") -$NPX_OG_COMMAND = $NPM_PositionMessage[1].Substring($firstIndex, $lastIndex - $firstIndex + 1) +$firstIndex = $NPX_PositionMessage[2].IndexOf("~") +$lastIndex = $NPX_PositionMessage[2].LastIndexOf("~") +$NPX_OG_COMMAND = $NPX_PositionMessage[1].Substring($firstIndex, $lastIndex - $firstIndex + 1) $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() $INVOKE_NPX = "& $(Normalize $NODE_EXE) $(Normalize $NPX_CLI_JS) $NPX_ARGS" From 1526dfe39f597c91ee402599b10c368a20ef816d Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 18:58:49 -0400 Subject: [PATCH 18/42] replace tests --- test/bin/windows-shims.js | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index a0c28fcf27c21..3042e369b5e4f 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -20,6 +20,9 @@ const BIN = join(ROOT, 'bin') const SHIMS = readNonJsFiles(BIN) const NODE_GYP = readNonJsFiles(join(BIN, 'node-gyp-bin')) const SHIM_EXTS = [...new Set(Object.keys(SHIMS).map(p => extname(p)))] +const PACKAGE_NAME = 'test' +const PACKAGE_VERSION = '1.0.0' +const SCRIPT_NAME = 'args.js' t.test('shim contents', t => { // these scripts should be kept in sync so this tests the contents of each @@ -99,6 +102,18 @@ t.test('run shims', t => { }, }, }, + // test script returning all command line arguments + [SCRIPT_NAME]: `#!/usr/bin/env node\n\nprocess.argv.slice(2).forEach((arg) => console.log(arg))`, + // package.json for the test script + 'package.json': ` + { + "name": "${PACKAGE_NAME}", + "version": "${PACKAGE_VERSION}", + "scripts": { + "test": "node ${SCRIPT_NAME}" + }, + "bin": "${SCRIPT_NAME}" + }`, }) // The removal of this fixture causes this test to fail when done with @@ -254,10 +269,16 @@ t.test('run shims', t => { const tests = [ { bin: 'npm', params: ['help'], expected: `npm@${version} ${ROOT}` }, { bin: 'npx', params: ['--version'], expected: version }, - { bin: 'npm', params: ['help a=1,b=2,c=3'], expected: `No matches in help for: a=1,b=2,c=3` }, - { bin: 'npm', params: ['help "a=1,b=2,c=3"'], expected: `No matches in help for: a=1,b=2,c=3` }, - { bin: 'npx', params: ['glob -v a=1,b=2,c=3 2>&1'], expected: `[ 'a=1,b=2,c=3' ]` }, - { bin: 'npx', params: ['glob -v "a=1,b=2,c=3" 2>&1'], expected: `[ 'a=1,b=2,c=3' ]` }, + { bin: 'npm', params: ['test'], expected: '' }, + { bin: 'npm', params: ['test -- hello world'], expected: `hello\nworld` }, + { bin: 'npm', params: ['test -- -p hello'], expected: `-p\nhello` }, + { bin: 'npm', params: ['test -- -p "hello world"'], expected: `-p\nhello world` }, + { bin: 'npm', params: ['test -- --param=hello'], expected: `--param=hello` }, + { bin: 'npm', params: ['test -- --param="hello world"'], expected: `--param=hello world` }, + { bin: 'npm', params: ['test -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, + { bin: 'npm', params: ['test -- "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, + { bin: 'npx', params: ['. -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, + { bin: 'npx', params: ['. -- "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, ] // ensure that all tests are either run or skipped From 860481ed9d940422906e7280c72d13ea418e4774 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 19:22:44 -0400 Subject: [PATCH 19/42] Update windows-shims.js --- test/bin/windows-shims.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 3042e369b5e4f..2ff0b7720e727 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -127,6 +127,10 @@ t.test('run shims', t => { // only cygwin *requires* the -l, but the others are ok with it args.unshift('-l') } + if (cmd.toLowerCase().endsWith('pwsh.exe')) { + // powershell requires escaping the double-quote for this test + args = args.map(elem => elem.replaceAll('"', '\\"')) + } const result = spawnSync(`"${cmd}"`, args, { // don't hit the registry for the update check env: { PATH: path, npm_config_update_notifier: 'false' }, From 50e77f7b4b82dafd98f99a9a376b8bd52c1507ad Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sat, 3 May 2025 19:24:31 -0400 Subject: [PATCH 20/42] Update windows-shims.js --- test/bin/windows-shims.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 2ff0b7720e727..0e6ac0bd5864d 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -128,7 +128,7 @@ t.test('run shims', t => { args.unshift('-l') } if (cmd.toLowerCase().endsWith('pwsh.exe')) { - // powershell requires escaping the double-quote for this test + // powershell requires escaping the double-quotes for this test args = args.map(elem => elem.replaceAll('"', '\\"')) } const result = spawnSync(`"${cmd}"`, args, { From c3f8c94666b854c9c08fd21f41c627952a6e546d Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sun, 4 May 2025 10:25:45 -0400 Subject: [PATCH 21/42] skip tests that can't pass with cygwin --- test/bin/windows-shims.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 0e6ac0bd5864d..5fbaccbf5633a 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -300,7 +300,14 @@ t.test('run shims', t => { } t.plan(tests.length) for (const { bin, params, expected } of tests) { - matchCmd(t, cmd, bin, match, params, expected) + if (name === 'cygwin bash' && ( + (bin === 'npm' && params[0].startsWith('test')) || + (bin === 'npx' && params[0].startsWith('.')) + )) { + t.skip("`cygwin bash` doesn't respect option `{ cwd: path }` when calling `spawnSync`") + } else { + matchCmd(t, cmd, bin, match, params, expected) + } } }) } From 77e5af296067df3db617a667b273248086bb1683 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sun, 4 May 2025 11:04:08 -0400 Subject: [PATCH 22/42] more consistent --- bin/npm.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index b428d3cb85756..aa27076312ffd 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -31,7 +31,7 @@ function Normalize { $Path = [System.IO.Path]::GetFullPath($Path) # remove trailing " or ' quotes (if any) and put back " quotes around the path - $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', '$1' + $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', "$1" $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" return """$Path""" } From ce0bf2b2848caa351f44ab80bce74b5459cf3fdb Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sun, 4 May 2025 11:04:35 -0400 Subject: [PATCH 23/42] more consistent --- bin/npx.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 2f820872117bb..5f2ed0934d73e 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -31,7 +31,7 @@ function Normalize { $Path = [System.IO.Path]::GetFullPath($Path) # remove trailing " or ' quotes (if any) and put back " quotes around the path - $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', '$1' + $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', "$1" $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" return """$Path""" } From bd2cd7798b5e9af4066e2091d4d413cc371d74dd Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sun, 4 May 2025 11:13:39 -0400 Subject: [PATCH 24/42] revert bad commits --- bin/npm.ps1 | 2 +- bin/npx.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index aa27076312ffd..b428d3cb85756 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -31,7 +31,7 @@ function Normalize { $Path = [System.IO.Path]::GetFullPath($Path) # remove trailing " or ' quotes (if any) and put back " quotes around the path - $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', "$1" + $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', '$1' $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" return """$Path""" } diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 5f2ed0934d73e..2f820872117bb 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -31,7 +31,7 @@ function Normalize { $Path = [System.IO.Path]::GetFullPath($Path) # remove trailing " or ' quotes (if any) and put back " quotes around the path - $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', "$1" + $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', '$1' $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" return """$Path""" } From cc9e57a8392babf04841c66272a243cf62a67c50 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Sun, 4 May 2025 11:21:24 -0400 Subject: [PATCH 25/42] add back consistency commits, add check for Windows PowerShell but don't use it in CI --- bin/npm.ps1 | 2 +- bin/npx.ps1 | 2 +- test/bin/windows-shims.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index b428d3cb85756..aa27076312ffd 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -31,7 +31,7 @@ function Normalize { $Path = [System.IO.Path]::GetFullPath($Path) # remove trailing " or ' quotes (if any) and put back " quotes around the path - $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', '$1' + $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', "$1" $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" return """$Path""" } diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 2f820872117bb..5f2ed0934d73e 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -31,7 +31,7 @@ function Normalize { $Path = [System.IO.Path]::GetFullPath($Path) # remove trailing " or ' quotes (if any) and put back " quotes around the path - $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', '$1' + $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', "$1" $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" return """$Path""" } diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 5fbaccbf5633a..0a43b5c7ec0a7 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -127,7 +127,7 @@ t.test('run shims', t => { // only cygwin *requires* the -l, but the others are ok with it args.unshift('-l') } - if (cmd.toLowerCase().endsWith('pwsh.exe')) { + if (cmd.toLowerCase().endsWith('pwsh.exe') || cmd.toLowerCase().endsWith('powershell.exe')) { // powershell requires escaping the double-quotes for this test args = args.map(elem => elem.replaceAll('"', '\\"')) } From 1bfbd785980c702a81a864bf979afecdf7e4c779 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 11:31:14 -0400 Subject: [PATCH 26/42] complete change --- bin/npm.ps1 | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index aa27076312ffd..aae6c9a33e375 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -36,10 +36,17 @@ function Normalize { return """$Path""" } -$NPM_PositionMessage = $MyInvocation.PositionMessage -split "`r?`n" -$firstIndex = $NPM_PositionMessage[2].IndexOf("~") -$lastIndex = $NPM_PositionMessage[2].LastIndexOf("~") -$NPM_OG_COMMAND = $NPM_PositionMessage[1].Substring($firstIndex, $lastIndex - $firstIndex + 1) +$firstPartOfString = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) + +$splitStringArray = $firstPartOfString -split "``;" +for ($i = 0; $i -lt $splitStringArray.Length; $i++) { + $splitString = $splitStringArray[$i] + if ($splitString.IndexOf(";") -ne -1) { + $splitStringArray[$i] = $splitString.Substring(0, $splitString.IndexOf(";")) + } +} + +$NPM_OG_COMMAND = $splitStringArray[0..$i] -join "``;" $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() $INVOKE_NPM = "& $(Normalize $NODE_EXE) $(Normalize $NPM_CLI_JS) $NPM_ARGS" From 360b09099020b0bb556b44edabb408077729e07e Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 11:34:16 -0400 Subject: [PATCH 27/42] also for npx.ps1 --- bin/npx.ps1 | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 5f2ed0934d73e..db4108bc19566 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -36,10 +36,16 @@ function Normalize { return """$Path""" } -$NPX_PositionMessage = $MyInvocation.PositionMessage -split "`r?`n" -$firstIndex = $NPX_PositionMessage[2].IndexOf("~") -$lastIndex = $NPX_PositionMessage[2].LastIndexOf("~") -$NPX_OG_COMMAND = $NPX_PositionMessage[1].Substring($firstIndex, $lastIndex - $firstIndex + 1) +$firstPartOfString = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) + +$splitStringArray = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) -split "``;" +for ($i = 0; $i -lt $splitStringArray.Length; $i++) { + $splitString = $splitStringArray[$i] + if ($splitString.IndexOf(";") -ne -1) { + $splitStringArray[$i] = $splitString.Substring(0, $splitString.IndexOf(";")) + } +} +$NPX_OG_COMMAND = $splitStringArray[0..$i] -join "``;" $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() $INVOKE_NPX = "& $(Normalize $NODE_EXE) $(Normalize $NPX_CLI_JS) $NPX_ARGS" From 668d40de8791fd6561f31a8bb7e21ca349a126e8 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 11:34:38 -0400 Subject: [PATCH 28/42] oop --- bin/npx.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/npx.ps1 b/bin/npx.ps1 index db4108bc19566..db6b24f5838b9 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -38,7 +38,7 @@ function Normalize { $firstPartOfString = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) -$splitStringArray = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) -split "``;" +$splitStringArray = $firstPartOfString -split "``;" for ($i = 0; $i -lt $splitStringArray.Length; $i++) { $splitString = $splitStringArray[$i] if ($splitString.IndexOf(";") -ne -1) { From 72c9fdde4fb46b317af8f1c41e481579a133de3d Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 11:34:52 -0400 Subject: [PATCH 29/42] formatting --- bin/npm.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index aae6c9a33e375..0cf4f51a2bde2 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -45,7 +45,6 @@ for ($i = 0; $i -lt $splitStringArray.Length; $i++) { $splitStringArray[$i] = $splitString.Substring(0, $splitString.IndexOf(";")) } } - $NPM_OG_COMMAND = $splitStringArray[0..$i] -join "``;" $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() From fb49f5be46ca1ff873fee78730cecb1e2aa20c94 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 14:10:23 -0400 Subject: [PATCH 30/42] remove normalize part --- bin/npm.ps1 | 23 ++++------------------- bin/npx.ps1 | 23 ++++------------------- 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index 0cf4f51a2bde2..76a0cb4bccec5 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -22,23 +22,9 @@ if (Test-Path $NPM_PREFIX_NPM_CLI_JS) { $NPM_CLI_JS=$NPM_PREFIX_NPM_CLI_JS } -function Normalize { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] - [string]$Path - ) - - $Path = [System.IO.Path]::GetFullPath($Path) - # remove trailing " or ' quotes (if any) and put back " quotes around the path - $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', "$1" - $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" - return """$Path""" -} - -$firstPartOfString = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) +$firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) -$splitStringArray = $firstPartOfString -split "``;" +$splitStringArray = $firstPartOfCommand -split "``;" for ($i = 0; $i -lt $splitStringArray.Length; $i++) { $splitString = $splitStringArray[$i] if ($splitString.IndexOf(";") -ne -1) { @@ -48,13 +34,12 @@ for ($i = 0; $i -lt $splitStringArray.Length; $i++) { $NPM_OG_COMMAND = $splitStringArray[0..$i] -join "``;" $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() -$INVOKE_NPM = "& $(Normalize $NODE_EXE) $(Normalize $NPM_CLI_JS) $NPM_ARGS" # Support pipeline input if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression $INVOKE_NPM + $input | Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" } else { - Invoke-Expression $INVOKE_NPM + Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" } exit $LASTEXITCODE diff --git a/bin/npx.ps1 b/bin/npx.ps1 index db6b24f5838b9..77071f30a06a2 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -22,23 +22,9 @@ if (Test-Path $NPM_PREFIX_NPX_CLI_JS) { $NPX_CLI_JS=$NPM_PREFIX_NPX_CLI_JS } -function Normalize { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] - [string]$Path - ) - - $Path = [System.IO.Path]::GetFullPath($Path) - # remove trailing " or ' quotes (if any) and put back " quotes around the path - $Path = $Path -replace '^\s*"\s*(.*?)\s*"\s*$', "$1" - $Path = $Path -replace "^\s*'\s*(.*?)\s*'\s*$", "$1" - return """$Path""" -} - -$firstPartOfString = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) +$firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) -$splitStringArray = $firstPartOfString -split "``;" +$splitStringArray = $firstPartOfCommand -split "``;" for ($i = 0; $i -lt $splitStringArray.Length; $i++) { $splitString = $splitStringArray[$i] if ($splitString.IndexOf(";") -ne -1) { @@ -48,13 +34,12 @@ for ($i = 0; $i -lt $splitStringArray.Length; $i++) { $NPX_OG_COMMAND = $splitStringArray[0..$i] -join "``;" $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() -$INVOKE_NPX = "& $(Normalize $NODE_EXE) $(Normalize $NPX_CLI_JS) $NPX_ARGS" # Support pipeline input if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression $INVOKE_NPX + $input | Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" } else { - Invoke-Expression $INVOKE_NPX + Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" } exit $LASTEXITCODE From 2d03217b5b5bd751b24c0f445385ea7acb55051e Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 14:56:43 -0400 Subject: [PATCH 31/42] check value of `$MyInvocation.OffsetInLine` --- bin/npm.ps1 | 26 +++++++++++++++----------- bin/npx.ps1 | 26 +++++++++++++++----------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index 76a0cb4bccec5..3c106add2209a 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -22,18 +22,22 @@ if (Test-Path $NPM_PREFIX_NPM_CLI_JS) { $NPM_CLI_JS=$NPM_PREFIX_NPM_CLI_JS } -$firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) - -$splitStringArray = $firstPartOfCommand -split "``;" -for ($i = 0; $i -lt $splitStringArray.Length; $i++) { - $splitString = $splitStringArray[$i] - if ($splitString.IndexOf(";") -ne -1) { - $splitStringArray[$i] = $splitString.Substring(0, $splitString.IndexOf(";")) - } +if ($MyInvocation.OffsetInLine -gt 0) { + $firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) + + $splitStringArray = $firstPartOfCommand -split "``;" + for ($i = 0; $i -lt $splitStringArray.Length; $i++) { + $splitString = $splitStringArray[$i] + if ($splitString.IndexOf(";") -ne -1) { + $splitStringArray[$i] = $splitString.Substring(0, $splitString.IndexOf(";")) + } + } + $NPM_OG_COMMAND = $splitStringArray[0..$i] -join "``;" + + $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() +} else { + $NPM_ARGS = $args } -$NPM_OG_COMMAND = $splitStringArray[0..$i] -join "``;" - -$NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() # Support pipeline input if ($MyInvocation.ExpectingInput) { diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 77071f30a06a2..f9a6bed38cd8d 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -22,18 +22,22 @@ if (Test-Path $NPM_PREFIX_NPX_CLI_JS) { $NPX_CLI_JS=$NPM_PREFIX_NPX_CLI_JS } -$firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) - -$splitStringArray = $firstPartOfCommand -split "``;" -for ($i = 0; $i -lt $splitStringArray.Length; $i++) { - $splitString = $splitStringArray[$i] - if ($splitString.IndexOf(";") -ne -1) { - $splitStringArray[$i] = $splitString.Substring(0, $splitString.IndexOf(";")) - } +if ($MyInvocation.OffsetInLine -gt 0) { + $firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) + + $splitStringArray = $firstPartOfCommand -split "``;" + for ($i = 0; $i -lt $splitStringArray.Length; $i++) { + $splitString = $splitStringArray[$i] + if ($splitString.IndexOf(";") -ne -1) { + $splitStringArray[$i] = $splitString.Substring(0, $splitString.IndexOf(";")) + } + } + $NPX_OG_COMMAND = $splitStringArray[0..$i] -join "``;" + + $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() +} else { + $NPX_ARGS = $args } -$NPX_OG_COMMAND = $splitStringArray[0..$i] -join "``;" - -$NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() # Support pipeline input if ($MyInvocation.ExpectingInput) { From 204a2284718bc0461dc5a688edc3d152501b1c33 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 15:00:59 -0400 Subject: [PATCH 32/42] finally get it to pass --- test/bin/windows-shims.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 0a43b5c7ec0a7..919536af9aea5 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -127,10 +127,31 @@ t.test('run shims', t => { // only cygwin *requires* the -l, but the others are ok with it args.unshift('-l') } - if (cmd.toLowerCase().endsWith('pwsh.exe') || cmd.toLowerCase().endsWith('powershell.exe')) { - // powershell requires escaping the double-quotes for this test + if (cmd.toLowerCase().endsWith('powershell.exe')) { + // Windows PowerShell requires escaping the double-quotes for this test args = args.map(elem => elem.replaceAll('"', '\\"')) } + if (cmd.toLowerCase().endsWith('pwsh.exe')) { + process.env.PATH = path + // don't hit the registry for the update check + process.env.npm_config_update_notifier = 'false' + const result = spawnSync(args.join(' '), [], { + cwd: path, + windowsHide: true, + shell: cmd, + ...opts, + }) + if (stdioString) { + result.stdout = result.stdout?.toString()?.trim() + result.stderr = result.stderr?.toString()?.trim() + } + return { + status: result.status, + signal: result.signal, + stdout: result.stdout, + stderr: result.stderr, + } + } const result = spawnSync(`"${cmd}"`, args, { // don't hit the registry for the update check env: { PATH: path, npm_config_update_notifier: 'false' }, From 4befbeb8abf197f98d6f7a6cc4a4046a4336d642 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 15:41:43 -0400 Subject: [PATCH 33/42] add `powershell.exe` test, have default case for PS1 scripts, revert specific pwsh.exe test --- bin/npm.ps1 | 21 +++++++++++++-------- bin/npx.ps1 | 21 +++++++++++++-------- test/bin/windows-shims.js | 23 ++--------------------- 3 files changed, 28 insertions(+), 37 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index 3c106add2209a..c798d66bb44ed 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -35,15 +35,20 @@ if ($MyInvocation.OffsetInLine -gt 0) { $NPM_OG_COMMAND = $splitStringArray[0..$i] -join "``;" $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() + + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" + } else { + Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" + } } else { - $NPM_ARGS = $args -} - -# Support pipeline input -if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" -} else { - Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & $NODE_EXE $NPM_CLI_JS $args + } else { + & $NODE_EXE $NPM_CLI_JS $args + } } exit $LASTEXITCODE diff --git a/bin/npx.ps1 b/bin/npx.ps1 index f9a6bed38cd8d..ede98473e8529 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -35,15 +35,20 @@ if ($MyInvocation.OffsetInLine -gt 0) { $NPX_OG_COMMAND = $splitStringArray[0..$i] -join "``;" $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() + + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" + } else { + Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" + } } else { - $NPX_ARGS = $args -} - -# Support pipeline input -if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" -} else { - Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & $NODE_EXE $NPX_CLI_JS $args + } else { + & $NODE_EXE $NPX_CLI_JS $args + } } exit $LASTEXITCODE diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 919536af9aea5..1390bdd795ee1 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -131,27 +131,6 @@ t.test('run shims', t => { // Windows PowerShell requires escaping the double-quotes for this test args = args.map(elem => elem.replaceAll('"', '\\"')) } - if (cmd.toLowerCase().endsWith('pwsh.exe')) { - process.env.PATH = path - // don't hit the registry for the update check - process.env.npm_config_update_notifier = 'false' - const result = spawnSync(args.join(' '), [], { - cwd: path, - windowsHide: true, - shell: cmd, - ...opts, - }) - if (stdioString) { - result.stdout = result.stdout?.toString()?.trim() - result.stderr = result.stderr?.toString()?.trim() - } - return { - status: result.status, - signal: result.signal, - stdout: result.stdout, - stderr: result.stderr, - } - } const result = spawnSync(`"${cmd}"`, args, { // don't hit the registry for the update check env: { PATH: path, npm_config_update_notifier: 'false' }, @@ -202,6 +181,7 @@ t.test('run shims', t => { const shells = Object.entries({ cmd: 'cmd', + powershell: 'powershell', pwsh: 'pwsh', git: join(ProgramFiles, 'Git', 'bin', 'bash.exe'), 'user git': join(ProgramFiles, 'Git', 'usr', 'bin', 'bash.exe'), @@ -267,6 +247,7 @@ t.test('run shims', t => { case 'bash.exe': args.push(bin) break + case 'powershell.exe': case 'pwsh.exe': args.push(`${bin}.ps1`) break From 60161d20b6b6191b01930e810927e148091eff40 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 20:01:53 -0400 Subject: [PATCH 34/42] make simpler --- bin/npm.ps1 | 36 ++++++++++++++++-------------------- bin/npx.ps1 | 36 ++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index c798d66bb44ed..b35333a468395 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -25,30 +25,26 @@ if (Test-Path $NPM_PREFIX_NPM_CLI_JS) { if ($MyInvocation.OffsetInLine -gt 0) { $firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) - $splitStringArray = $firstPartOfCommand -split "``;" - for ($i = 0; $i -lt $splitStringArray.Length; $i++) { - $splitString = $splitStringArray[$i] - if ($splitString.IndexOf(";") -ne -1) { - $splitStringArray[$i] = $splitString.Substring(0, $splitString.IndexOf(";")) + if (!$firstPartOfCommand.Contains("``")) + $NPM_OG_COMMAND = ($firstPartOfCommand -split ";")[0] + $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() + + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" + } else { + Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" } + + exit $LASTEXITCODE } - $NPM_OG_COMMAND = $splitStringArray[0..$i] -join "``;" - - $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() +} - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" - } else { - Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" - } +# Support pipeline input +if ($MyInvocation.ExpectingInput) { + $input | & $NODE_EXE $NPM_CLI_JS $args } else { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & $NODE_EXE $NPM_CLI_JS $args - } else { - & $NODE_EXE $NPM_CLI_JS $args - } + & $NODE_EXE $NPM_CLI_JS $args } exit $LASTEXITCODE diff --git a/bin/npx.ps1 b/bin/npx.ps1 index ede98473e8529..3b15cb45ebd9e 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -25,30 +25,26 @@ if (Test-Path $NPM_PREFIX_NPX_CLI_JS) { if ($MyInvocation.OffsetInLine -gt 0) { $firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) - $splitStringArray = $firstPartOfCommand -split "``;" - for ($i = 0; $i -lt $splitStringArray.Length; $i++) { - $splitString = $splitStringArray[$i] - if ($splitString.IndexOf(";") -ne -1) { - $splitStringArray[$i] = $splitString.Substring(0, $splitString.IndexOf(";")) + if (!$firstPartOfCommand.Contains("``")) + $NPX_OG_COMMAND = ($firstPartOfCommand -split ";")[0] + $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() + + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" + } else { + Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" } + + exit $LASTEXITCODE } - $NPX_OG_COMMAND = $splitStringArray[0..$i] -join "``;" - - $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() +} - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" - } else { - Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" - } +# Support pipeline input +if ($MyInvocation.ExpectingInput) { + $input | & $NODE_EXE $NPX_CLI_JS $args } else { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & $NODE_EXE $NPX_CLI_JS $args - } else { - & $NODE_EXE $NPX_CLI_JS $args - } + & $NODE_EXE $NPX_CLI_JS $args } exit $LASTEXITCODE From 622f7fe383483ab5dc60b9bfc3d24d9ceed06539 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Mon, 5 May 2025 20:08:50 -0400 Subject: [PATCH 35/42] oops --- bin/npm.ps1 | 2 +- bin/npx.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index b35333a468395..0be5f1b273435 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -25,7 +25,7 @@ if (Test-Path $NPM_PREFIX_NPM_CLI_JS) { if ($MyInvocation.OffsetInLine -gt 0) { $firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) - if (!$firstPartOfCommand.Contains("``")) + if (!$firstPartOfCommand.Contains("``")) { $NPM_OG_COMMAND = ($firstPartOfCommand -split ";")[0] $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 3b15cb45ebd9e..e4f3b3659072e 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -25,7 +25,7 @@ if (Test-Path $NPM_PREFIX_NPX_CLI_JS) { if ($MyInvocation.OffsetInLine -gt 0) { $firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) - if (!$firstPartOfCommand.Contains("``")) + if (!$firstPartOfCommand.Contains("``")) { $NPX_OG_COMMAND = ($firstPartOfCommand -split ";")[0] $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() From 09f002d52c09d34d34b707d8c6516af0a29dfc25 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Tue, 6 May 2025 09:40:09 -0400 Subject: [PATCH 36/42] properly quote `$NODE_EXE` and `$NPM_CLI_JS`/`$NPX_CLI_JS` with Invoke-Expression --- bin/npm.ps1 | 4 ++-- bin/npx.ps1 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index 0be5f1b273435..a0c87afbc0ead 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -31,9 +31,9 @@ if ($MyInvocation.OffsetInLine -gt 0) { # Support pipeline input if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" + $input | Invoke-Expression "& `"$NODE_EXE`" `"$NPM_CLI_JS`" $NPM_ARGS" } else { - Invoke-Expression "& $NODE_EXE $NPM_CLI_JS $NPM_ARGS" + Invoke-Expression "& `"$NODE_EXE`" `"$NPM_CLI_JS`" $NPM_ARGS" } exit $LASTEXITCODE diff --git a/bin/npx.ps1 b/bin/npx.ps1 index e4f3b3659072e..2d8419ac3967f 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -31,9 +31,9 @@ if ($MyInvocation.OffsetInLine -gt 0) { # Support pipeline input if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" + $input | Invoke-Expression "& `"$NODE_EXE`" `"$NPX_CLI_JS`" $NPX_ARGS" } else { - Invoke-Expression "& $NODE_EXE $NPX_CLI_JS $NPX_ARGS" + Invoke-Expression "& `"$NODE_EXE`" `"$NPX_CLI_JS`" $NPX_ARGS" } exit $LASTEXITCODE From cd4eab75a37f22c8949afe875693157be1802618 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Tue, 6 May 2025 11:47:01 -0400 Subject: [PATCH 37/42] use reflection to get the full powershell command --- bin/npm.ps1 | 41 +++++++++++++++++++++++++++-------------- bin/npx.ps1 | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index a0c87afbc0ead..c6f2da69a0fe9 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -1,5 +1,23 @@ #!/usr/bin/env pwsh +$updateTypeDataSplat = @{ + MemberType = 'ScriptProperty' + TypeName = 'System.Management.Automation.InvocationInfo' + MemberName = '_NPM_FULL_COMMAND_' +} + +if (-not $MyInvocation._NPM_FULL_COMMAND_) { + Update-TypeData @updateTypeDataSplat -Value { + if (-not $script:_NPM_ScriptPosition_) { + # cache the PropertyInfo + $script:_NPM_ScriptPosition_ = [System.Management.Automation.InvocationInfo]. + GetProperty('ScriptPosition', [System.Reflection.BindingFlags] 'Instance, NonPublic') + } + + $script:_NPM_ScriptPosition_.GetValue($this).Text + } +} + $NODE_EXE="$PSScriptRoot/node.exe" if (-not (Test-Path $NODE_EXE)) { $NODE_EXE="$PSScriptRoot/node" @@ -23,21 +41,16 @@ if (Test-Path $NPM_PREFIX_NPM_CLI_JS) { } if ($MyInvocation.OffsetInLine -gt 0) { - $firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) - - if (!$firstPartOfCommand.Contains("``")) { - $NPM_OG_COMMAND = ($firstPartOfCommand -split ";")[0] - $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() - - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression "& `"$NODE_EXE`" `"$NPM_CLI_JS`" $NPM_ARGS" - } else { - Invoke-Expression "& `"$NODE_EXE`" `"$NPM_CLI_JS`" $NPM_ARGS" - } - - exit $LASTEXITCODE + $NPM_ARGS = $MyInvocation._NPM_FULL_COMMAND_.Substring($MyInvocation.InvocationName.Length).Trim() + + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | Invoke-Expression "& `"$NODE_EXE`" `"$NPM_CLI_JS`" $NPM_ARGS" + } else { + Invoke-Expression "& `"$NODE_EXE`" `"$NPM_CLI_JS`" $NPM_ARGS" } + + exit $LASTEXITCODE } # Support pipeline input diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 2d8419ac3967f..b4ac3cafdc36b 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -1,5 +1,23 @@ #!/usr/bin/env pwsh +$updateTypeDataSplat = @{ + MemberType = 'ScriptProperty' + TypeName = 'System.Management.Automation.InvocationInfo' + MemberName = '_NPX_FULL_COMMAND_' +} + +if (-not $MyInvocation._NPX_FULL_COMMAND_) { + Update-TypeData @updateTypeDataSplat -Value { + if (-not $script:_NPX_ScriptPosition_) { + # cache the PropertyInfo + $script:_NPX_ScriptPosition_ = [System.Management.Automation.InvocationInfo]. + GetProperty('ScriptPosition', [System.Reflection.BindingFlags] 'Instance, NonPublic') + } + + $script:_NPX_ScriptPosition_.GetValue($this).Text + } +} + $NODE_EXE="$PSScriptRoot/node.exe" if (-not (Test-Path $NODE_EXE)) { $NODE_EXE="$PSScriptRoot/node" @@ -23,21 +41,16 @@ if (Test-Path $NPM_PREFIX_NPX_CLI_JS) { } if ($MyInvocation.OffsetInLine -gt 0) { - $firstPartOfCommand = $MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1, $MyInvocation.Line.length - $MyInvocation.OffsetInLine + 1) - - if (!$firstPartOfCommand.Contains("``")) { - $NPX_OG_COMMAND = ($firstPartOfCommand -split ";")[0] - $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() - - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | Invoke-Expression "& `"$NODE_EXE`" `"$NPX_CLI_JS`" $NPX_ARGS" - } else { - Invoke-Expression "& `"$NODE_EXE`" `"$NPX_CLI_JS`" $NPX_ARGS" - } - - exit $LASTEXITCODE + $NPX_ARGS = $MyInvocation._NPX_FULL_COMMAND_.Substring($MyInvocation.InvocationName.Length).Trim() + + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | Invoke-Expression "& `"$NODE_EXE`" `"$NPX_CLI_JS`" $NPX_ARGS" + } else { + Invoke-Expression "& `"$NODE_EXE`" `"$NPX_CLI_JS`" $NPX_ARGS" } + + exit $LASTEXITCODE } # Support pipeline input From a128038803daf207b3465254dcabaa95bb1a9ae7 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Tue, 6 May 2025 12:47:58 -0400 Subject: [PATCH 38/42] use `$MyInvocation.Statement` if exists already --- bin/npm.ps1 | 18 +++++++++++------- bin/npx.ps1 | 18 +++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index c6f2da69a0fe9..c1d4cb69b2c19 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -1,12 +1,12 @@ #!/usr/bin/env pwsh -$updateTypeDataSplat = @{ - MemberType = 'ScriptProperty' - TypeName = 'System.Management.Automation.InvocationInfo' - MemberName = '_NPM_FULL_COMMAND_' -} +if (-not $MyInvocation.Statement -and -not $MyInvocation._NPM_FULL_COMMAND_) { + $updateTypeDataSplat = @{ + MemberType = 'ScriptProperty' + TypeName = 'System.Management.Automation.InvocationInfo' + MemberName = '_NPM_FULL_COMMAND_' + } -if (-not $MyInvocation._NPM_FULL_COMMAND_) { Update-TypeData @updateTypeDataSplat -Value { if (-not $script:_NPM_ScriptPosition_) { # cache the PropertyInfo @@ -41,7 +41,11 @@ if (Test-Path $NPM_PREFIX_NPM_CLI_JS) { } if ($MyInvocation.OffsetInLine -gt 0) { - $NPM_ARGS = $MyInvocation._NPM_FULL_COMMAND_.Substring($MyInvocation.InvocationName.Length).Trim() + if ($MyInvocation.Statement) { + $NPM_ARGS = $MyInvocation.Statement.Substring($MyInvocation.InvocationName.Length).Trim() + } else { + $NPM_ARGS = $MyInvocation._NPM_FULL_COMMAND_.Substring($MyInvocation.InvocationName.Length).Trim() + } # Support pipeline input if ($MyInvocation.ExpectingInput) { diff --git a/bin/npx.ps1 b/bin/npx.ps1 index b4ac3cafdc36b..9aeb3cb3f3e30 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -1,12 +1,12 @@ #!/usr/bin/env pwsh -$updateTypeDataSplat = @{ - MemberType = 'ScriptProperty' - TypeName = 'System.Management.Automation.InvocationInfo' - MemberName = '_NPX_FULL_COMMAND_' -} +if (-not $MyInvocation.Statement -and -not $MyInvocation._NPX_FULL_COMMAND_) { + $updateTypeDataSplat = @{ + MemberType = 'ScriptProperty' + TypeName = 'System.Management.Automation.InvocationInfo' + MemberName = '_NPX_FULL_COMMAND_' + } -if (-not $MyInvocation._NPX_FULL_COMMAND_) { Update-TypeData @updateTypeDataSplat -Value { if (-not $script:_NPX_ScriptPosition_) { # cache the PropertyInfo @@ -41,7 +41,11 @@ if (Test-Path $NPM_PREFIX_NPX_CLI_JS) { } if ($MyInvocation.OffsetInLine -gt 0) { - $NPX_ARGS = $MyInvocation._NPX_FULL_COMMAND_.Substring($MyInvocation.InvocationName.Length).Trim() + if ($MyInvocation.Statement) { + $NPX_ARGS = $MyInvocation.Statement.Substring($MyInvocation.InvocationName.Length).Trim() + } else { + $NPX_ARGS = $MyInvocation._NPX_FULL_COMMAND_.Substring($MyInvocation.InvocationName.Length).Trim() + } # Support pipeline input if ($MyInvocation.ExpectingInput) { From d74a744642a74cde7551e5830ed14b658748e6ea Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Tue, 6 May 2025 13:29:05 -0400 Subject: [PATCH 39/42] combine tests --- test/bin/windows-shims.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 1390bdd795ee1..942a5e83c9a5e 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -276,15 +276,9 @@ t.test('run shims', t => { { bin: 'npm', params: ['help'], expected: `npm@${version} ${ROOT}` }, { bin: 'npx', params: ['--version'], expected: version }, { bin: 'npm', params: ['test'], expected: '' }, - { bin: 'npm', params: ['test -- hello world'], expected: `hello\nworld` }, - { bin: 'npm', params: ['test -- -p hello'], expected: `-p\nhello` }, - { bin: 'npm', params: ['test -- -p "hello world"'], expected: `-p\nhello world` }, - { bin: 'npm', params: ['test -- --param=hello'], expected: `--param=hello` }, - { bin: 'npm', params: ['test -- --param="hello world"'], expected: `--param=hello world` }, + { bin: 'npm', params: ['test -- hello world -p hello -g "hello world" --param1=hello world --param2="hello world"'], expected: `hello\nworld\n-p\nhello\n-g\nhello world\n--param1=hello\nworld\n--param1=hello world` }, { bin: 'npm', params: ['test -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, - { bin: 'npm', params: ['test -- "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, { bin: 'npx', params: ['. -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, - { bin: 'npx', params: ['. -- "a=1,b=2,c=3"'], expected: `a=1,b=2,c=3` }, ] // ensure that all tests are either run or skipped From 8235520b69072c77553c80c1c14468051a12af3a Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Tue, 6 May 2025 13:41:37 -0400 Subject: [PATCH 40/42] fix test --- test/bin/windows-shims.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index 942a5e83c9a5e..dfba7bd208c0e 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -276,7 +276,7 @@ t.test('run shims', t => { { bin: 'npm', params: ['help'], expected: `npm@${version} ${ROOT}` }, { bin: 'npx', params: ['--version'], expected: version }, { bin: 'npm', params: ['test'], expected: '' }, - { bin: 'npm', params: ['test -- hello world -p hello -g "hello world" --param1=hello world --param2="hello world"'], expected: `hello\nworld\n-p\nhello\n-g\nhello world\n--param1=hello\nworld\n--param1=hello world` }, + { bin: 'npm', params: ['test -- hello world -p hello -g "hello world" --param1=hello world --param2="hello world"'], expected: `hello\nworld\n-p\nhello\n-g\nhello world\n--param1=hello\nworld\n--param2=hello world` }, { bin: 'npm', params: ['test -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, { bin: 'npx', params: ['. -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, ] From 59e56c0c05b0aebfbf7958413e394849807040d9 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Tue, 6 May 2025 14:56:13 -0400 Subject: [PATCH 41/42] make simpler --- bin/npm.ps1 | 23 ++++------------------- bin/npx.ps1 | 23 ++++------------------- 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index c1d4cb69b2c19..4ef60f8949a9b 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -1,23 +1,5 @@ #!/usr/bin/env pwsh -if (-not $MyInvocation.Statement -and -not $MyInvocation._NPM_FULL_COMMAND_) { - $updateTypeDataSplat = @{ - MemberType = 'ScriptProperty' - TypeName = 'System.Management.Automation.InvocationInfo' - MemberName = '_NPM_FULL_COMMAND_' - } - - Update-TypeData @updateTypeDataSplat -Value { - if (-not $script:_NPM_ScriptPosition_) { - # cache the PropertyInfo - $script:_NPM_ScriptPosition_ = [System.Management.Automation.InvocationInfo]. - GetProperty('ScriptPosition', [System.Reflection.BindingFlags] 'Instance, NonPublic') - } - - $script:_NPM_ScriptPosition_.GetValue($this).Text - } -} - $NODE_EXE="$PSScriptRoot/node.exe" if (-not (Test-Path $NODE_EXE)) { $NODE_EXE="$PSScriptRoot/node" @@ -44,7 +26,10 @@ if ($MyInvocation.OffsetInLine -gt 0) { if ($MyInvocation.Statement) { $NPM_ARGS = $MyInvocation.Statement.Substring($MyInvocation.InvocationName.Length).Trim() } else { - $NPM_ARGS = $MyInvocation._NPM_FULL_COMMAND_.Substring($MyInvocation.InvocationName.Length).Trim() + $NPM_OG_COMMAND = ( + [System.Management.Automation.InvocationInfo].GetProperty('ScriptPosition', [System.Reflection.BindingFlags] 'Instance, NonPublic') + ).GetValue($MyInvocation).Text + $NPM_ARGS = $NPM_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() } # Support pipeline input diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 9aeb3cb3f3e30..0957bbadb23f0 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -1,23 +1,5 @@ #!/usr/bin/env pwsh -if (-not $MyInvocation.Statement -and -not $MyInvocation._NPX_FULL_COMMAND_) { - $updateTypeDataSplat = @{ - MemberType = 'ScriptProperty' - TypeName = 'System.Management.Automation.InvocationInfo' - MemberName = '_NPX_FULL_COMMAND_' - } - - Update-TypeData @updateTypeDataSplat -Value { - if (-not $script:_NPX_ScriptPosition_) { - # cache the PropertyInfo - $script:_NPX_ScriptPosition_ = [System.Management.Automation.InvocationInfo]. - GetProperty('ScriptPosition', [System.Reflection.BindingFlags] 'Instance, NonPublic') - } - - $script:_NPX_ScriptPosition_.GetValue($this).Text - } -} - $NODE_EXE="$PSScriptRoot/node.exe" if (-not (Test-Path $NODE_EXE)) { $NODE_EXE="$PSScriptRoot/node" @@ -44,7 +26,10 @@ if ($MyInvocation.OffsetInLine -gt 0) { if ($MyInvocation.Statement) { $NPX_ARGS = $MyInvocation.Statement.Substring($MyInvocation.InvocationName.Length).Trim() } else { - $NPX_ARGS = $MyInvocation._NPX_FULL_COMMAND_.Substring($MyInvocation.InvocationName.Length).Trim() + $NPX_OG_COMMAND = ( + [System.Management.Automation.InvocationInfo].GetProperty('ScriptPosition', [System.Reflection.BindingFlags] 'Instance, NonPublic') + ).GetValue($MyInvocation).Text + $NPX_ARGS = $NPX_OG_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim() } # Support pipeline input From 81f24266e5890ce207a2ed0874edc70bf9ecfc74 Mon Sep 17 00:00:00 2001 From: Alex Schwartz Date: Wed, 7 May 2025 05:56:22 -0400 Subject: [PATCH 42/42] single exit for ps1 bin files, single-quote test --- bin/npm.ps1 | 14 ++++++-------- bin/npx.ps1 | 14 ++++++-------- test/bin/windows-shims.js | 2 +- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/bin/npm.ps1 b/bin/npm.ps1 index 4ef60f8949a9b..eb9b6b3794fba 100644 --- a/bin/npm.ps1 +++ b/bin/npm.ps1 @@ -38,15 +38,13 @@ if ($MyInvocation.OffsetInLine -gt 0) { } else { Invoke-Expression "& `"$NODE_EXE`" `"$NPM_CLI_JS`" $NPM_ARGS" } - - exit $LASTEXITCODE -} - -# Support pipeline input -if ($MyInvocation.ExpectingInput) { - $input | & $NODE_EXE $NPM_CLI_JS $args } else { - & $NODE_EXE $NPM_CLI_JS $args + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & $NODE_EXE $NPM_CLI_JS $args + } else { + & $NODE_EXE $NPM_CLI_JS $args + } } exit $LASTEXITCODE diff --git a/bin/npx.ps1 b/bin/npx.ps1 index 0957bbadb23f0..870c27157584f 100644 --- a/bin/npx.ps1 +++ b/bin/npx.ps1 @@ -38,15 +38,13 @@ if ($MyInvocation.OffsetInLine -gt 0) { } else { Invoke-Expression "& `"$NODE_EXE`" `"$NPX_CLI_JS`" $NPX_ARGS" } - - exit $LASTEXITCODE -} - -# Support pipeline input -if ($MyInvocation.ExpectingInput) { - $input | & $NODE_EXE $NPX_CLI_JS $args } else { - & $NODE_EXE $NPX_CLI_JS $args + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & $NODE_EXE $NPX_CLI_JS $args + } else { + & $NODE_EXE $NPX_CLI_JS $args + } } exit $LASTEXITCODE diff --git a/test/bin/windows-shims.js b/test/bin/windows-shims.js index dfba7bd208c0e..d942c8d64e6ea 100644 --- a/test/bin/windows-shims.js +++ b/test/bin/windows-shims.js @@ -276,7 +276,7 @@ t.test('run shims', t => { { bin: 'npm', params: ['help'], expected: `npm@${version} ${ROOT}` }, { bin: 'npx', params: ['--version'], expected: version }, { bin: 'npm', params: ['test'], expected: '' }, - { bin: 'npm', params: ['test -- hello world -p hello -g "hello world" --param1=hello world --param2="hello world"'], expected: `hello\nworld\n-p\nhello\n-g\nhello world\n--param1=hello\nworld\n--param2=hello world` }, + { bin: 'npm', params: [`test -- hello -p1 world -p2 "hello world" -p3 'hello world' --q1=hello world --q2="hello world" --q3='hello world'`], expected: `hello\n-p1\nworld\n-p2\nhello world\n-p3\nhello world\n--q1=hello\nworld\n--q2=hello world\n--q3=hello world` }, { bin: 'npm', params: ['test -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, { bin: 'npx', params: ['. -- a=1,b=2,c=3'], expected: `a=1,b=2,c=3` }, ]