From 9cebd463c8b4bc3faf3d2be7b0f30c8d8c59b60e Mon Sep 17 00:00:00 2001 From: Devraj Mehta Date: Wed, 15 Oct 2025 16:27:49 -0400 Subject: [PATCH 1/4] Load native addons directly from prebuilds directory --- src/conpty_console_list_agent.ts | 8 ++------ src/index.ts | 3 ++- src/unixTerminal.ts | 21 ++++----------------- src/utils.ts | 20 ++++++++++++++++++++ src/windowsPtyAgent.ts | 25 +++---------------------- 5 files changed, 31 insertions(+), 46 deletions(-) diff --git a/src/conpty_console_list_agent.ts b/src/conpty_console_list_agent.ts index d7700cff3..181ccabbb 100644 --- a/src/conpty_console_list_agent.ts +++ b/src/conpty_console_list_agent.ts @@ -6,13 +6,9 @@ * single console attached to a process. */ -let getConsoleProcessList: any; -try { - getConsoleProcessList = require('../build/Release/conpty_console_list.node').getConsoleProcessList; -} catch (err) { - getConsoleProcessList = require('../build/Debug/conpty_console_list.node').getConsoleProcessList; -} +import { loadNativeModule } from './utils'; +const getConsoleProcessList = loadNativeModule('conpty_console_list').module.getConsoleProcessList; const shellPid = parseInt(process.argv[2], 10); const consoleProcessList = getConsoleProcessList(shellPid); process.send!({ consoleProcessList }); diff --git a/src/index.ts b/src/index.ts index 53d0322e5..8a7e95051 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { ITerminal, IPtyOpenOptions, IPtyForkOptions, IWindowsPtyForkOptions } from './interfaces'; import { ArgvOrCommandLine } from './types'; +import { loadNativeModule } from './utils'; let terminalCtor: any; if (process.platform === 'win32') { @@ -48,4 +49,4 @@ export function open(options: IPtyOpenOptions): ITerminal { * Expose the native API when not Windows, note that this is not public API and * could be removed at any time. */ -export const native = (process.platform !== 'win32' ? require('../build/Release/pty.node') : null); +export const native = (process.platform !== 'win32' ? loadNativeModule('pty').module : null); diff --git a/src/unixTerminal.ts b/src/unixTerminal.ts index b745d762d..05120dbdb 100644 --- a/src/unixTerminal.ts +++ b/src/unixTerminal.ts @@ -9,24 +9,11 @@ import * as tty from 'tty'; import { Terminal, DEFAULT_COLS, DEFAULT_ROWS } from './terminal'; import { IProcessEnv, IPtyForkOptions, IPtyOpenOptions } from './interfaces'; import { ArgvOrCommandLine } from './types'; -import { assign } from './utils'; - -let pty: IUnixNative; -let helperPath: string; -try { - pty = require('../build/Release/pty.node'); - helperPath = '../build/Release/spawn-helper'; -} catch (outerError) { - try { - pty = require('../build/Debug/pty.node'); - helperPath = '../build/Debug/spawn-helper'; - } catch (innerError) { - console.error('innerError', innerError); - // Re-throw the exception from the Release require if the Debug require fails as well - throw outerError; - } -} +import { assign, loadNativeModule } from './utils'; +const native = loadNativeModule('pty'); +const pty: IUnixNative = native.module; +let helperPath = native.dir + '/spawn-helper'; helperPath = path.resolve(__dirname, helperPath); helperPath = helperPath.replace('app.asar', 'app.asar.unpacked'); helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked'); diff --git a/src/utils.ts b/src/utils.ts index 6306c8ba2..246a9a0ca 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,3 +7,23 @@ export function assign(target: any, ...sources: any[]): any { sources.forEach(source => Object.keys(source).forEach(key => target[key] = source[key])); return target; } + + +export function loadNativeModule(name: string): {dir: string, module: any} { + // Check prebuild, build, and then debug build. + const dirs = [`prebuilds/${process.platform}-${process.arch}`, 'build/Release', 'build/Debug']; + // Check relative to the current dir for bundled and to the parent dir for unbundled. + const relative = ['.', '..']; + let lastError: unknown; + for (const d of dirs) { + for (const r of relative) { + const dir = `${r}/${d}/`; + try { + return { dir, module: require(`${dir}/${name}.node`) }; + } catch (e) { + lastError = e; + } + } + } + throw new Error(`Failed to load native module: ${name}.node, checked: ${dirs.join(', ')}: ${lastError}`); +} diff --git a/src/windowsPtyAgent.ts b/src/windowsPtyAgent.ts index ba5dbdbc9..d70544495 100644 --- a/src/windowsPtyAgent.ts +++ b/src/windowsPtyAgent.ts @@ -11,6 +11,7 @@ import { fork } from 'child_process'; import { Socket } from 'net'; import { ArgvOrCommandLine } from './types'; import { ConoutConnection } from './windowsConoutConnection'; +import { loadNativeModule } from './utils'; let conptyNative: IConptyNative; let winptyNative: IWinptyNative; @@ -62,31 +63,11 @@ export class WindowsPtyAgent { } if (this._useConpty) { if (!conptyNative) { - try { - conptyNative = require('../build/Release/conpty.node'); - } catch (outerError) { - try { - conptyNative = require('../build/Debug/conpty.node'); - } catch (innerError) { - console.error('innerError', innerError); - // Re-throw the exception from the Release require if the Debug require fails as well - throw outerError; - } - } + conptyNative = loadNativeModule('conpty').module; } } else { if (!winptyNative) { - try { - winptyNative = require('../build/Release/pty.node'); - } catch (outerError) { - try { - winptyNative = require('../build/Debug/pty.node'); - } catch (innerError) { - console.error('innerError', innerError); - // Re-throw the exception from the Release require if the Debug require fails as well - throw outerError; - } - } + winptyNative = loadNativeModule('pty').module; } } this._ptyNative = this._useConpty ? conptyNative : winptyNative; From 603f97508eeabbd7596ef1fec06cf97e1dc3efe3 Mon Sep 17 00:00:00 2001 From: Devraj Mehta Date: Thu, 16 Oct 2025 22:26:39 -0400 Subject: [PATCH 2/4] feat: remove prebuilds when npm_config_build_from_source is set --- scripts/prebuild.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/prebuild.js b/scripts/prebuild.js index a8fc93029..e8ad43bbf 100644 --- a/scripts/prebuild.js +++ b/scripts/prebuild.js @@ -6,20 +6,21 @@ const path = require('path'); /** * This script copies the prebuilt binaries from the prebuild directory * for the current platform and architecture to the build/Release directory. - * + * * Usage: * node scripts/prebuild.js */ +const PREBUILD_DIR = path.join(__dirname, '..', 'prebuilds', `${process.platform}-${process.arch}`); +const RELEASE_DIR = path.join(__dirname, '../build/Release'); + // Skip copying prebuilds when npm_config_build_from_source is set if (process.env.npm_config_build_from_source === 'true') { - console.log('\x1b[33m> Skipping prebuild copy because npm_config_build_from_source is set\x1b[0m'); + console.log('\x1b[33m> Removing prebuilds and rebuilding because npm_config_build_from_source is set\x1b[0m'); + fs.rmSync(PREBUILD_DIR, { recursive: true }); process.exit(1); } -const PREBUILD_DIR = path.join(__dirname, '..', 'prebuilds', `${process.platform}-${process.arch}`); -const RELEASE_DIR = path.join(__dirname, '../build/Release'); - /* Copy prebuild files to build/Release */ console.log('\x1b[32m> Copying prebuilds to release folder...\x1b[0m'); if (!fs.existsSync(PREBUILD_DIR)) { @@ -27,4 +28,4 @@ if (!fs.existsSync(PREBUILD_DIR)) { // Exit with 1 to fall back on node-gyp building the native modules process.exit(1); } -fs.cpSync(PREBUILD_DIR, RELEASE_DIR, { recursive: true }); \ No newline at end of file +fs.cpSync(PREBUILD_DIR, RELEASE_DIR, { recursive: true }); From e8c25c91ded18ca879d92b8ac88ad42d42fc4b6a Mon Sep 17 00:00:00 2001 From: Devraj Mehta Date: Fri, 17 Oct 2025 00:55:40 -0400 Subject: [PATCH 3/4] feat: change the load order to local builds and unbundled first --- src/utils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 246a9a0ca..81a70c77a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,10 +10,10 @@ export function assign(target: any, ...sources: any[]): any { export function loadNativeModule(name: string): {dir: string, module: any} { - // Check prebuild, build, and then debug build. - const dirs = [`prebuilds/${process.platform}-${process.arch}`, 'build/Release', 'build/Debug']; - // Check relative to the current dir for bundled and to the parent dir for unbundled. - const relative = ['.', '..']; + // Check build, debug, and then prebuilds. + const dirs = ['build/Release', 'build/Debug', `prebuilds/${process.platform}-${process.arch}`]; + // Check relative to the parent dir for unbundled and then the current dir for bundled + const relative = ['..', '.']; let lastError: unknown; for (const d of dirs) { for (const r of relative) { From af7fa263f770a2acc590b9540eca7e8b83a762de Mon Sep 17 00:00:00 2001 From: Devraj Mehta Date: Fri, 17 Oct 2025 12:26:12 -0400 Subject: [PATCH 4/4] chore: skip copying prebuilds to build dir --- scripts/prebuild.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/prebuild.js b/scripts/prebuild.js index e8ad43bbf..e830ed619 100644 --- a/scripts/prebuild.js +++ b/scripts/prebuild.js @@ -4,15 +4,17 @@ const fs = require('fs'); const path = require('path'); /** - * This script copies the prebuilt binaries from the prebuild directory - * for the current platform and architecture to the build/Release directory. + * This script checks for the prebuilt binaries for the current platform and + * architecture. It exits with 0 if prebuilds are found and 1 if not. + * + * If npm_config_build_from_source is set then it removes the prebuilds for the + * current platform so they are not loaded at runtime. * * Usage: * node scripts/prebuild.js */ const PREBUILD_DIR = path.join(__dirname, '..', 'prebuilds', `${process.platform}-${process.arch}`); -const RELEASE_DIR = path.join(__dirname, '../build/Release'); // Skip copying prebuilds when npm_config_build_from_source is set if (process.env.npm_config_build_from_source === 'true') { @@ -28,4 +30,5 @@ if (!fs.existsSync(PREBUILD_DIR)) { // Exit with 1 to fall back on node-gyp building the native modules process.exit(1); } -fs.cpSync(PREBUILD_DIR, RELEASE_DIR, { recursive: true }); +// Exit with 0 to skip node-gyp building from source +process.exit(0);