8000 [engineering] run `npm i` during postinstall in parallel by tmm1 · Pull Request #252068 · microsoft/vscode · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[engineering] run npm i during postinstall in parallel #252068

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

tmm1
Copy link
Contributor
@tmm1 tmm1 commented Jun 22, 2025

this speeds up npm i considerably on all platforms

i have been using this for a few months so all the bugs should be worked out

one major issue was running node-gyp in parallel can cause random corruption nodejs/node-gyp#3165

cc @deepak1556 #250554 #250981

@tmm1
Copy link
Contributor Author
tmm1 commented Jun 22, 2025

The parallel parts work, but the node-gyp override is still problematic.

According to the npm ci docs:

These [scripts] all run after the actual installation of modules into node_modules, in order, with no internal actions happening in between

So setting up build/npm/gyp in preinstall.js doesn't help with the main npm ci at all, because it happens after node_modules has been constructed.

@deepak1556 deepak1556 added this to the June 2025 milestone Jun 23, 2025
@deepak1556
Copy link
Collaborator

So setting up build/npm/gyp in preinstall.js doesn't help with the main npm ci at all, because it happens after node_modules has been constructed.

Agreed, an option would be to check-in node-gyp into the source tree locked to a specific version similar to how it is bundled for npm. This would avoid any preinstall script dependency but comes with it own maintenance cost.

How about moving the native module dependencies of the root folder into a subfolder say electron that would only get installed as part of the postinstall step

vscode
  |
  |-> electron
        |-> .npmrc
        |-> package.json (native module dependencies of root)

The following additionally need to be covered,

  1. When running out of sources we need to inject the module paths, we already have infra for this to support our remote case
    /**
    * Add support for redirecting the loading of node modules
    *
    * Note: only applies when running out of sources.
    */
    export function devInjectNodeModuleLookupPath(injectPath: string): void {
    if (!process.env['VSCODE_DEV']) {
    return; // only applies running out of sources
    }
    if (!injectPath) {
    throw new Error('Missing injectPath');
    }
    // register a loader hook
    const Module = require('node:module');
    Module.register('./bootstrap-import.js', { parentURL: import.meta.url, data: injectPath });
    }
    and

    vscode/src/server-main.ts

    Lines 241 to 245 in f20278e

    if (process.env['VSCODE_DEV']) {
    // When running out of sources, we need to load node modules from remote/node_modules,
    // which are compiled against nodejs, not electron
    process.env['VSCODE_DEV_INJECT_NODE_MODULE_LOOKUP_PATH'] = process.env['VSCODE_DEV_INJECT_NODE_MODULE_LOOKUP_PATH'] || path.join(__dirname, '..', 'remote', 'node_modules');
    devInjectNodeModuleLookupPath(process.env['VSCODE_DEV_INJECT_NODE_MODULE_LOOKUP_PATH']);
  2. At build time ensure the modules from electron are packaged as part of the application via
    const deps = gulp.src(dependenciesSrc, { base: '.', dot: true })
    .pipe(filter(['**', `!**/${config.version}/**`, '!**/bin/darwin-arm64-87/**', '!**/package-lock.json', '!**/yarn.lock', '!**/*.{js,css}.map']))
    .pipe(util.cleanNodeModules(path.join(__dirname, '.moduleignore')))
    .pipe(util.cleanNodeModules(path.join(__dirname, `.moduleignore.${process.platform}`)))
    .pipe(jsFilter)
    .pipe(util.rewriteSourceMappingURL(sourceMappingURLBase))
    .pipe(jsFilter.restore)
    .pipe(createAsar(path.join(process.cwd(), 'node_modules'), [
    '**/*.node',
    '**/@vscode/ripgrep/bin/*',
    '**/node-pty/build/Release/*',
    '**/node-pty/build/Release/conpty/*',
    '**/node-pty/lib/worker/conoutSocketWorker.js',
    '**/node-pty/lib/shared/conout.js',
    '**/*.wasm',
    '**/@vscode/vsce-sign/bin/*',
    ], [
    '**/*.mk',
    '!node_modules/vsda/**' // stay compatible with extensions that depend on us shipping `vsda` into ASAR
    ], [
    'node_modules/vsda/**' // retain copy of `vsda` in node_modules for internal use
    ], 'node_modules.asar'));

But this seems like a good path forward, considering npm/cli#8153 coming our way which requires us to inject the npm config env variables rather than them being auto injected via the rc file we need a way for the root native modules to be adapted and this seems like a good solution to cover both. Thoughts ?

@deepak1556
Copy link
Collaborator

opened #252171 for discussion on the dependency structure.

@wraithgar
Copy link

Added a comment to the PR for the node-gyp config but perhaps it's better to continue here. The syntax in the .npmrc file is wrong as-is.

But this seems like a good path forward, considering npm/cli#8153 coming our way which requires us to inject the npm config env variables rather than them being auto injected via the rc file we need a way for the root native modules to be adapted and this s 8000 eems like a good solution to cover both. Thoughts ?

node-gyp is valid npm config so this is not a concern. There are other values in this project's .npmrc that are valid npm config and will need to be addressed, but node-gyp is not one of them.

The node-gyp config was fixed in npm recently via npm/run-script#230 and was included in npm@11.3.0 so folks would need to be on that version at least to benefit from this.

@deepak1556
Copy link
Collaborator

@wraithgar thanks for the context, we did notice the config name was incorrect in #250554 (comment) :)

node-gyp is valid npm config so this is not a concern. There are other values in this project's .npmrc that are valid npm config and will need to be addressed, but node-gyp is not one of them.

👍

Copy link
@dcq01 dcq01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! I'm a grad student working on a research project about using large language models to automate code review. Based on your commit d962a12 and the changes in build/npm/postinstall.js, my tool generated this comment:

  1. Null Checks for opts: The opts parameter is spread into a new object without checking if it is null or undefined. Consider adding a check to ensure opts is an object before spreading it.
  2. User Info Handling: Validate that the uid and gid properties from os.userInfo() exist before using them.
  3. Error Handling: The patch does not introduce any error handling for the run function calls. It would be beneficial to include error handling to catch potential issues during command execution.
  4. Concatenation of PATH: Ensure that opts.env[pathVar] is defined before concatenating. If opts.env[pathVar] is undefined, the resulting opts.env[pathVar] will only be set to the new path.
  5. Potential for Path Delimiter Issues: If opts.env[pathVar] is undefined, the concatenation will not include a delimiter. It might be safer to conditionally add the delimiter only if opts.env[pathVar] is defined.
  6. Environment Variable Case Sensitivity: The patch correctly handles the case sensitivity of the PATH variable on Windows by using Path instead of PATH.
  7. Redundant Path Construction: Store the path in a variable and reuse it to avoid redundancy:
  8. ```javascript
    
  9. const nodeGypPath = process.platform === 'win32'
    
  10.     ? path.join(__dirname, 'gyp', 'node_modules', '.bin', 'node-gyp.cmd')
    
  11.     : path.join(__dirname, 'gyp', 'node_modules', '.bin', 'node-gyp');
    
  12. opts.env['npm_config_node_gyp'] = nodeGypPath;
    
  13. opts.env[pathVar] = nodeGypPath + path.delimiter + opts.env[pathVar];
    
  14. ```
    
  15. String Concatenation: Use template literals for better readability:
  16. opts.env[pathVar] = ${path.join(__dirname, 'gyp', 'node_modules', '.bin')}${path.delimiter}${opts.env[pathVar]};
  17. Logging Sensitive Information: Ensure that sensitive data, such as GITHUB_TOKEN, is not logged or is masked 80AB appropriately to prevent leakage.

As part of my research, I'm trying to understand how useful these comments are in real-world development. If you have a moment, I'd be super grateful if you could quickly reply to these two yes/no questions:

  1. Does this comment provide suggestions from a dimension you hadn’t considered?
    1. Do you find this comment helpful?

Thanks a lot for your time and feedback! And sorry again if this message is a bother.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants
0