Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
shareable_builtins = {'cjs_module_lexer/lexer': 'deps/cjs-module-lexer/lexer.js',
'cjs_module_lexer/dist/lexer': 'deps/cjs-module-lexer/dist/lexer.js',
'undici/undici': 'deps/undici/undici.js',
'corepack/dist/lib/download': 'deps/corepack/dist/lib/download.js',
'amaro/dist/index': 'deps/amaro/dist/index.js'
}

Expand Down
16,396 changes: 16,396 additions & 0 deletions deps/corepack/dist/lib/download.js

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions lib/internal/main/install.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

const {
ArrayPrototypeMap,
JSONParse,
ObjectEntries,
SafePromiseAllReturnVoid,
StringPrototypeSlice,
} = primordials;

const { Buffer } = require('buffer');
const { readFileSync } = require('fs');
const { resolve } = require('path');
const {
codes: {
ERR_MANIFEST_INTEGRITY_MISMATCH,
},
} = require('internal/errors');

const {
prepareMainThreadExecution,
markBootstrapComplete,
} = require('internal/process/pre_execution');

const lockFilePath = prepareMainThreadExecution(true);
markBootstrapComplete();

const { packages } = JSONParse(readFileSync(lockFilePath, 'utf-8'));

if (!packages) {
return;
}

const { download } = require('internal/deps/corepack/dist/lib/download');
SafePromiseAllReturnVoid(ArrayPrototypeMap(ObjectEntries(packages), async ({ 0: path, 1: { resolved, integrity } }) => {
const targetDir = resolve(lockFilePath, `../${path}`);
const { tmpFolder, outputFile, hash } = await download(`${targetDir}/..`, resolved, 'sha512');
if (Buffer.from(hash, 'hex').toString('base64') !== StringPrototypeSlice(integrity, 7)) {
throw ERR_MANIFEST_INTEGRITY_MISMATCH();
}
const { rename } = require('fs/promises');
await rename(outputFile ?? tmpFolder, targetDir);
}));
1 change: 1 addition & 0 deletions node.gni
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ declare_args() {
"deps/cjs-module-lexer/dist/lexer.js",
"deps/undici/undici.js",
"deps/amaro/dist/index.js",
"deps/corepack/dist/lib/download.js",
Copy link
Member

Choose a reason for hiding this comment

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

So this would make Corepack a dependency of Node.js core? I'm not sure how I feel about that. I still have concerns about how it differs so much from the Node codebase (written in TypeScript, dependent on Yarn) and it has an uncertain future. This seems to make the project responsible for Corepack's continued development. It also seems to contradict the recent vote to remove Corepack (I understand it would become a hidden dependency and no longer available as a binary, but presumably it would be accessible via --expose-internals?)

We already bundle npm and it is the canonical dependency downloader; why not use that? Or more targeted dependencies of npm like Arborist.

Copy link
Contributor Author

@aduh95 aduh95 Mar 23, 2025

Choose a reason for hiding this comment

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

npm has arguably an even more uncertain future, it is not maintained by us. But in any case, which code does the download matters very little (Corepack as a dep, copy the code from Corepack into core, another lib, etc.), what’s more important to establish is whether we want the feature

Copy link
Member

Choose a reason for hiding this comment

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

Define “the feature”. Is it installing npm's lockfile? Yarn's or pnpm's? All of them? Something else? Does it depend on the package manager being downloaded first?

]
}

Expand Down
4 changes: 4 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
return StartExecution(env, "internal/main/inspect");
}

if (env->options()->install_from_lock_file) {
return StartExecution(env, "internal/main/install");
}

if (per_process::cli_options->print_help) {
return StartExecution(env, "internal/main/print_help");
}
Expand Down
3 changes: 3 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,9 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"set module type for string input",
&EnvironmentOptions::input_type,
kAllowedInEnvvar);
AddOption("--install",
"installs binaries defined in a lock file",
&EnvironmentOptions::install_from_lock_file);
AddOption(
"--experimental-specifier-resolution", "", NoOp{}, kAllowedInEnvvar);
AddAlias("--es-module-specifier-resolution",
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ class EnvironmentOptions : public Options {
bool report_exclude_network = false;
std::string experimental_config_file_path;
bool experimental_default_config_file = false;
bool install_from_lock_file = false;

inline DebugOptions* get_debug_options() { return &debug_options_; }
inline const DebugOptions& debug_options() const { return debug_options_; }
Expand Down
8 changes: 8 additions & 0 deletions test/fixtures/rc/dummy-lock-file.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"packages": {
"node_modules/corepack": {
"resolved": "https://registry.npmjs.org/corepack/-/corepack-0.32.0.tgz",
"integrity": "sha512-KhahVUFy7xL8OTty/ToY646hXMQhih8rnvUkA9/qnk/u4QUF2+SbQneX/zZnDxG1NiABFm5ojZCWnIv93oyhhQ=="
}
}
}