From 4800485095695745d76129d534547aaae404cbe2 Mon Sep 17 00:00:00 2001 From: Diogo Cruz Date: Sun, 22 Mar 2026 18:05:23 +0000 Subject: [PATCH 1/3] Fix #231076: Restore NODE_OPTIONS in Windows terminal VS Code on Windows did not propagate NODE_OPTIONS to integrated terminals when launched from an external shell. Electron strips most NODE_OPTIONS from the app process. This fix preserves the value as VSCODE_NODE_OPTIONS before Electron startup in launcher scripts and restores it when creating terminal env. Changes included: - preserve NODE_OPTIONS and NODE_REPL_EXTERNAL_MODULE in launchers - restore variables for Windows in terminal environment logic - add test coverage for NODE_OPTIONS restoration behavior Signed-off-by: Diogo Cruz --- scripts/code.bat | 3 +++ scripts/code.sh | 7 +++++++ src/vs/code/electron-main/main.ts | 17 +++++++++++++++++ .../terminal/common/terminalEnvironment.ts | 3 ++- .../test/common/terminalEnvironment.test.ts | 14 ++++++++++++++ 5 files changed, 43 insertions(+), 1 deletion(-) diff --git a/scripts/code.bat b/scripts/code.bat index 62cfd0b4c4908..553355352500f 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -26,6 +26,9 @@ set VSCODE_CLI=1 set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 +if defined NODE_OPTIONS set "VSCODE_NODE_OPTIONS=%NODE_OPTIONS%" +if defined NODE_REPL_EXTERNAL_MODULE set "VSCODE_NODE_REPL_EXTERNAL_MODULE=%NODE_REPL_EXTERNAL_MODULE%" + set DISABLE_TEST_EXTENSION="--disable-extension=vscode.vscode-api-tests" for %%A in (%*) do ( if "%%~A"=="--extensionTestsPath" ( diff --git a/scripts/code.sh b/scripts/code.sh index 16fdefde55208..c0720467e52ff 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -43,6 +43,13 @@ function code() { export ELECTRON_ENABLE_STACK_DUMPING=1 export ELECTRON_ENABLE_LOGGING=1 + if [[ -n "${NODE_OPTIONS}" ]]; then + export VSCODE_NODE_OPTIONS="${NODE_OPTIONS}" + fi + if [[ -n "${NODE_REPL_EXTERNAL_MODULE}" ]]; then + export VSCODE_NODE_REPL_EXTERNAL_MODULE="${NODE_REPL_EXTERNAL_MODULE}" + fi + DISABLE_TEST_EXTENSION="--disable-extension=vscode.vscode-api-tests" if [[ "$@" == *"--extensionTestsPath"* ]]; then DISABLE_TEST_EXTENSION="" diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index eb7e0193373b8..a325eda0e6991 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -75,6 +75,11 @@ import { addUNCHostToAllowlist, getUNCHost } from '../../base/node/unc.js'; import { ThemeMainService } from '../../platform/theme/electron-main/themeMainServiceImpl.js'; import { LINUX_SYSTEM_POLICY_FILE_PATH } from '../../base/common/policy.js'; +// Capture NODE_OPTIONS early before Electron strips it (https://github.com/microsoft/vscode/issues/231076) +// Store in a global so patchEnvironment can access it +const CAPTURED_NODE_OPTIONS = process.env['NODE_OPTIONS']; +const CAPTURED_NODE_REPL_EXTERNAL_MODULE = process.env['NODE_REPL_EXTERNAL_MODULE']; + /** * The main VS Code entry point. * @@ -259,6 +264,18 @@ class CodeMain { } }); + // Preserve NODE_OPTIONS for terminals on Windows and macOS (https://github.com/microsoft/vscode/issues/231076) + // Electron restricts NODE_OPTIONS early, so we use globally captured values + // and restore them in the terminal environment + if (isMacintosh || isWindows) { + if (CAPTURED_NODE_OPTIONS) { + instanceEnvironment['VSCODE_NODE_OPTIONS'] = CAPTURED_NODE_OPTIONS; + } + if (CAPTURED_NODE_REPL_EXTERNAL_MODULE) { + instanceEnvironment['VSCODE_NODE_REPL_EXTERNAL_MODULE'] = CAPTURED_NODE_REPL_EXTERNAL_MODULE; + } + } + Object.assign(process.env, instanceEnvironment); return instanceEnvironment; diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index a724394106afd..5930d811669b4 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -271,12 +271,13 @@ export async function createTerminalEnvironment( } // Workaround for https://github.com/microsoft/vscode/issues/204005 + // and https://github.com/microsoft/vscode/issues/231076 // We should restore the following environment variables when a user // launches the application using the CLI so that integrated terminal // can still inherit these variables. // We are not bypassing the restrictions implied in https://github.com/electron/electron/pull/40770 // since this only affects integrated terminal and not the application itself. - if (isMacintosh) { + if (isMacintosh || isWindows) { // Restore NODE_OPTIONS if it was set if (env['VSCODE_NODE_OPTIONS']) { env['NODE_OPTIONS'] = env['VSCODE_NODE_OPTIONS']; diff --git a/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts b/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts index f06292f5e1d84..1d17b442d9163 100644 --- a/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts @@ -321,6 +321,20 @@ suite('Workbench - TerminalEnvironment', () => { { foo: 'bar', empty: '', ...commonVariables } ); }); + test('should restore NODE_OPTIONS from VSCODE_NODE_OPTIONS on macOS and Windows', async () => { + const env = await createTerminalEnvironment({}, undefined, undefined, undefined, 'off', { + VSCODE_NODE_OPTIONS: '--max-old-space-size=8192', + VSCODE_NODE_REPL_EXTERNAL_MODULE: 'test-module' + }); + // On macOS and Windows, VSCODE_NODE_OPTIONS should be restored to NODE_OPTIONS + if (process.platform === 'darwin' || process.platform === 'win32') { + strictEqual(env['NODE_OPTIONS'], '--max-old-space-size=8192'); + strictEqual(env['NODE_REPL_EXTERNAL_MODULE'], 'test-module'); + strictEqual(env['VSCODE_NODE_OPTIONS'], undefined); + strictEqual(env['VSCODE_NODE_REPL_EXTERNAL_MODULE'], undefined); + } + // On other platforms, behavior may differ (currently not implemented) + }); }); suite('getWorkspaceForTerminal', () => { test('should resolve workspace folder from cwd, not last active workspace', () => { From ebf1fa8d37ba7e769d2929b0d7531c6103faf8e6 Mon Sep 17 00:00:00 2001 From: Diogo Cruz Date: Fri, 27 Mar 2026 15:08:01 +0000 Subject: [PATCH 2/3] Address PR review: prefer existing VSCODE_* vars and harden tests --- scripts/code.bat | 10 ++++++++-- scripts/code.sh | 2 ++ src/vs/code/electron-main/main.ts | 15 +++++++++++---- .../test/common/terminalEnvironment.test.ts | 7 ++++++- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/scripts/code.bat b/scripts/code.bat index 553355352500f..6f814a4bab0c9 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -26,8 +26,14 @@ set VSCODE_CLI=1 set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 -if defined NODE_OPTIONS set "VSCODE_NODE_OPTIONS=%NODE_OPTIONS%" -if defined NODE_REPL_EXTERNAL_MODULE set "VSCODE_NODE_REPL_EXTERNAL_MODULE=%NODE_REPL_EXTERNAL_MODULE%" +if defined NODE_OPTIONS ( + set "VSCODE_NODE_OPTIONS=%NODE_OPTIONS%" + set NODE_OPTIONS= +) +if defined NODE_REPL_EXTERNAL_MODULE ( + set "VSCODE_NODE_REPL_EXTERNAL_MODULE=%NODE_REPL_EXTERNAL_MODULE%" + set NODE_REPL_EXTERNAL_MODULE= +) set DISABLE_TEST_EXTENSION="--disable-extension=vscode.vscode-api-tests" for %%A in (%*) do ( diff --git a/scripts/code.sh b/scripts/code.sh index c0720467e52ff..9376778ffdac2 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -45,9 +45,11 @@ function code() { if [[ -n "${NODE_OPTIONS}" ]]; then export VSCODE_NODE_OPTIONS="${NODE_OPTIONS}" + unset NODE_OPTIONS fi if [[ -n "${NODE_REPL_EXTERNAL_MODULE}" ]]; then export VSCODE_NODE_REPL_EXTERNAL_MODULE="${NODE_REPL_EXTERNAL_MODULE}" + unset NODE_REPL_EXTERNAL_MODULE fi DISABLE_TEST_EXTENSION="--disable-extension=vscode.vscode-api-tests" diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index a325eda0e6991..6598815ffcf0d 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -265,13 +265,20 @@ class CodeMain { }); // Preserve NODE_OPTIONS for terminals on Windows and macOS (https://github.com/microsoft/vscode/issues/231076) - // Electron restricts NODE_OPTIONS early, so we use globally captured values - // and restore them in the terminal environment + // Electron restricts NODE_OPTIONS early, so we capture globally and restore + // in the terminal environment, but prefer existing VSCODE_* values if set. if (isMacintosh || isWindows) { - if (CAPTURED_NODE_OPTIONS) { + const existingNodeOptions = process.env['VSCODE_NODE_OPTIONS']; + if (typeof existingNodeOptions === 'string') { + instanceEnvironment['VSCODE_NODE_OPTIONS'] = existingNodeOptions; + } else if (CAPTURED_NODE_OPTIONS) { instanceEnvironment['VSCODE_NODE_OPTIONS'] = CAPTURED_NODE_OPTIONS; } - if (CAPTURED_NODE_REPL_EXTERNAL_MODULE) { + + const existingNodeReplExternalModule = process.env['VSCODE_NODE_REPL_EXTERNAL_MODULE']; + if (typeof existingNodeReplExternalModule === 'string') { + instanceEnvironment['VSCODE_NODE_REPL_EXTERNAL_MODULE'] = existingNodeReplExternalModule; + } else if (CAPTURED_NODE_REPL_EXTERNAL_MODULE) { instanceEnvironment['VSCODE_NODE_REPL_EXTERNAL_MODULE'] = CAPTURED_NODE_REPL_EXTERNAL_MODULE; } } diff --git a/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts b/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts index 1d17b442d9163..d53f477fb63d1 100644 --- a/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts @@ -332,8 +332,13 @@ suite('Workbench - TerminalEnvironment', () => { strictEqual(env['NODE_REPL_EXTERNAL_MODULE'], 'test-module'); strictEqual(env['VSCODE_NODE_OPTIONS'], undefined); strictEqual(env['VSCODE_NODE_REPL_EXTERNAL_MODULE'], undefined); + } else { + // On other platforms, VSCODE_* and NODE_* vars should not be restored + strictEqual(env['NODE_OPTIONS'], undefined); + strictEqual(env['NODE_REPL_EXTERNAL_MODULE'], undefined); + strictEqual(env['VSCODE_NODE_OPTIONS'], undefined); + strictEqual(env['VSCODE_NODE_REPL_EXTERNAL_MODULE'], undefined); } - // On other platforms, behavior may differ (currently not implemented) }); }); suite('getWorkspaceForTerminal', () => { From 275b5b22b0fab022aa79ac54d932f81d6635854f Mon Sep 17 00:00:00 2001 From: Diogo Cruz Date: Thu, 16 Apr 2026 16:44:00 +0100 Subject: [PATCH 3/3] Fix #231076: Restore NODE_OPTIONS in Windows terminal Preserve NODE_OPTIONS and NODE_REPL_EXTERNAL_MODULE as VSCODE_* variables in launch scripts. Prefer existing VSCODE_* values during terminal environment creation. Add regression test to ensure restore behavior works correctly on macOS and Windows. Signed-off-by: Diogo Cruz --- scripts/code.bat | 4 ++-- scripts/code.sh | 8 ++++++-- .../test/common/terminalEnvironment.test.ts | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/scripts/code.bat b/scripts/code.bat index 6f814a4bab0c9..cc906e1083297 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -27,11 +27,11 @@ set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 if defined NODE_OPTIONS ( - set "VSCODE_NODE_OPTIONS=%NODE_OPTIONS%" + if not defined VSCODE_NODE_OPTIONS set "VSCODE_NODE_OPTIONS=%NODE_OPTIONS%" set NODE_OPTIONS= ) if defined NODE_REPL_EXTERNAL_MODULE ( - set "VSCODE_NODE_REPL_EXTERNAL_MODULE=%NODE_REPL_EXTERNAL_MODULE%" + if not defined VSCODE_NODE_REPL_EXTERNAL_MODULE set "VSCODE_NODE_REPL_EXTERNAL_MODULE=%NODE_REPL_EXTERNAL_MODULE%" set NODE_REPL_EXTERNAL_MODULE= ) diff --git a/scripts/code.sh b/scripts/code.sh index 9376778ffdac2..989228461d0f8 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -44,11 +44,15 @@ function code() { export ELECTRON_ENABLE_LOGGING=1 if [[ -n "${NODE_OPTIONS}" ]]; then - export VSCODE_NODE_OPTIONS="${NODE_OPTIONS}" + if [[ -z "${VSCODE_NODE_OPTIONS+x}" ]]; then + export VSCODE_NODE_OPTIONS="${NODE_OPTIONS}" + fi unset NODE_OPTIONS fi if [[ -n "${NODE_REPL_EXTERNAL_MODULE}" ]]; then - export VSCODE_NODE_REPL_EXTERNAL_MODULE="${NODE_REPL_EXTERNAL_MODULE}" + if [[ -z "${VSCODE_NODE_REPL_EXTERNAL_MODULE+x}" ]]; then + export VSCODE_NODE_REPL_EXTERNAL_MODULE="${NODE_REPL_EXTERNAL_MODULE}" + fi unset NODE_REPL_EXTERNAL_MODULE fi diff --git a/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts b/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts index d53f477fb63d1..cfc9f510c218a 100644 --- a/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts @@ -340,6 +340,25 @@ suite('Workbench - TerminalEnvironment', () => { strictEqual(env['VSCODE_NODE_REPL_EXTERNAL_MODULE'], undefined); } }); + test('should prefer existing VSCODE_* values when restoring NODE_OPTIONS', async () => { + const env = await createTerminalEnvironment({}, undefined, undefined, undefined, 'off', { + NODE_OPTIONS: '--from-node-options', + NODE_REPL_EXTERNAL_MODULE: 'from-node-repl', + VSCODE_NODE_OPTIONS: '--from-vscode-options', + VSCODE_NODE_REPL_EXTERNAL_MODULE: 'from-vscode-repl' + }); + if (process.platform === 'darwin' || process.platform === 'win32') { + strictEqual(env['NODE_OPTIONS'], '--from-vscode-options'); + strictEqual(env['NODE_REPL_EXTERNAL_MODULE'], 'from-vscode-repl'); + strictEqual(env['VSCODE_NODE_OPTIONS'], undefined); + strictEqual(env['VSCODE_NODE_REPL_EXTERNAL_MODULE'], undefined); + } else { + strictEqual(env['NODE_OPTIONS'], undefined); + strictEqual(env['NODE_REPL_EXTERNAL_MODULE'], undefined); + strictEqual(env['VSCODE_NODE_OPTIONS'], undefined); + strictEqual(env['VSCODE_NODE_REPL_EXTERNAL_MODULE'], undefined); + } + }); }); suite('getWorkspaceForTerminal', () => { test('should resolve workspace folder from cwd, not last active workspace', () => {