Skip to content
Open
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
36 changes: 36 additions & 0 deletions .yarn/versions/fix-7210.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
releases:
"@yarnpkg/core": patch

declined:
- "@yarnpkg/plugin-catalog"
- "@yarnpkg/plugin-compat"
- "@yarnpkg/plugin-constraints"
- "@yarnpkg/plugin-dlx"
- "@yarnpkg/plugin-essentials"
- "@yarnpkg/plugin-exec"
- "@yarnpkg/plugin-file"
- "@yarnpkg/plugin-git"
- "@yarnpkg/plugin-github"
- "@yarnpkg/plugin-http"
- "@yarnpkg/plugin-init"
- "@yarnpkg/plugin-interactive-tools"
- "@yarnpkg/plugin-jsr"
- "@yarnpkg/plugin-link"
- "@yarnpkg/plugin-nm"
- "@yarnpkg/plugin-npm"
- "@yarnpkg/plugin-npm-cli"
- "@yarnpkg/plugin-pack"
- "@yarnpkg/plugin-patch"
- "@yarnpkg/plugin-pnp"
- "@yarnpkg/plugin-pnpm"
- "@yarnpkg/plugin-stage"
- "@yarnpkg/plugin-typescript"
- "@yarnpkg/plugin-version"
- "@yarnpkg/plugin-workspace-tools"
- "@yarnpkg/builder"
- "@yarnpkg/cli"
- "@yarnpkg/doctor"
- "@yarnpkg/extensions"
- "@yarnpkg/nm"
- "@yarnpkg/pnpify"
- "@yarnpkg/sdks"
129 changes: 113 additions & 16 deletions packages/yarnpkg-core/sources/scriptUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,27 @@ interface PackageManagerSelection {
reason: string;
}

async function makePathWrapper(location: PortablePath, name: Filename, argv0: NativePath, args: Array<string> = []) {
if (process.platform === `win32`) {
// https://github.com/microsoft/terminal/issues/217#issuecomment-737594785
const cmdScript = `@goto #_undefined_# 2>NUL || @title %COMSPEC% & @setlocal & @"${argv0}" ${args.map(arg => `"${arg.replace(`"`, `""`)}"`).join(` `)} %*`;
await xfs.writeFilePromise(ppath.format({dir: location, name, ext: `.cmd`}), cmdScript);
}
async function makePathWrapper(location: PortablePath, name: Filename, argv0: NativePath, args: Array<string> = [], nodeWrapperPath?: NativePath) {
if (nodeWrapperPath && (argv0 === process.execPath || npath.toPortablePath(argv0) === npath.toPortablePath(process.execPath))) {
if (process.platform === `win32`) {
const cmdScript = `@goto #_undefined_# 2>NUL || @title %COMSPEC% & @setlocal & @set "WRAPPER_NODE_OPTIONS=%NODE_OPTIONS%" & @set "NODE_OPTIONS=" & @"${process.execPath}" "${nodeWrapperPath}" "${argv0}" ${args.map(arg => `"${arg.replace(`"`, `""`)}"`).join(` `)} %*`;
await xfs.writeFilePromise(ppath.format({dir: location, name, ext: `.cmd`}), cmdScript);
}

await xfs.writeFilePromise(ppath.join(location, name), `#!/bin/sh\nexec "${argv0}" ${args.map(arg => `'${arg.replace(/'/g, `'"'"'`)}'`).join(` `)} "$@"\n`, {
mode: 0o755,
});
await xfs.writeFilePromise(ppath.join(location, name), `#!/bin/sh\nexport WRAPPER_NODE_OPTIONS="$NODE_OPTIONS"\nexport NODE_OPTIONS=""\nexec "${process.execPath}" "${nodeWrapperPath}" "${argv0}" ${args.map(arg => `'${arg.replace(/'/g, `'"'"'`)}'`).join(` `)} "$@"\n`, {
mode: 0o755,
});
} else {
if (process.platform === `win32`) {
// https://github.com/microsoft/terminal/issues/217#issuecomment-737594785
const cmdScript = `@goto #_undefined_# 2>NUL || @title %COMSPEC% & @setlocal & @"${argv0}" ${args.map(arg => `"${arg.replace(`"`, `""`)}"`).join(` `)} %*`;
await xfs.writeFilePromise(ppath.format({dir: location, name, ext: `.cmd`}), cmdScript);
}

await xfs.writeFilePromise(ppath.join(location, name), `#!/bin/sh\nexec "${argv0}" ${args.map(arg => `'${arg.replace(/'/g, `'"'"'`)}'`).join(` `)} "$@"\n`, {
mode: 0o755,
});
}
}

/**
Expand Down Expand Up @@ -119,6 +130,87 @@ export async function makeScriptEnv({project, locator, binFolder, ignoreCorepack
// binaries for the dependencies of the active package
scriptEnv.BERRY_BIN_FOLDER = npath.fromPortablePath(nBinFolder);

let nodeWrapperNativePath: string | undefined;
if (project) {
scriptEnv.BERRY_PROJECT_ROOT = npath.fromPortablePath(project.cwd);

const nodeWrapperPath = ppath.join(binFolder, `node-wrapper.js` as Filename);
const nodeWrapperContent = `const child_process = require('child_process');
const fs = require('fs');
const path = require('path');

const realNode = process.argv[2];
const args = process.argv.slice(3);

let strip = false;
const projectRoot = process.env.BERRY_PROJECT_ROOT;
if (projectRoot) {
const resolvedProjectRoot = path.resolve(projectRoot);
for (const arg of args) {
if (arg.startsWith('-'))
continue;

try {
if (fs.existsSync(arg) && fs.statSync(arg).isFile()) {
const resolvedPath = path.resolve(arg);
const relative = path.relative(resolvedProjectRoot, resolvedPath);
const isOutside = relative.startsWith('..') || path.isAbsolute(relative);
if (isOutside) {
strip = true;
break;
}
}
} catch {}
}
}

const env = { ...process.env };
let nodeOptions = env.WRAPPER_NODE_OPTIONS || '';
delete env.WRAPPER_NODE_OPTIONS;

if (strip && nodeOptions) {
nodeOptions = nodeOptions
.replace(/--experimental-package-map(?:="[^"]*"|='[^']*'|=\\S+)/g, '')
.trim();
}

env.NODE_OPTIONS = nodeOptions;

const cp = child_process.spawn(realNode, args, {
stdio: 'inherit',
env,
});

cp.on('error', err => {
console.error(err);
process.exit(1);
});

cp.on('exit', (code, signal) => {
if (signal) {
process.kill(process.pid, signal);
} else {
process.exit(code ?? 0);
}
});

const signals = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK'];
for (const sig of signals) {
try {
process.on(sig, () => {
if (cp.pid) {
try {
process.kill(cp.pid, sig);
} catch {}
}
});
} catch {}
}
`;
await xfs.writeFilePromise(nodeWrapperPath, nodeWrapperContent);
nodeWrapperNativePath = npath.fromPortablePath(nodeWrapperPath);
}

// Otherwise we'd override the Corepack binaries, and thus break the detection
// of the `packageManager` field when running Yarn in other directories.
const yarnBin = process.env.COREPACK_ROOT && !ignoreCorepack
Expand All @@ -128,12 +220,12 @@ export async function makeScriptEnv({project, locator, binFolder, ignoreCorepack
// Register some binaries that must be made available in all subprocesses
// spawned by Yarn (we thus ensure that they always use the right version)
await Promise.all([
makePathWrapper(binFolder, `node` as Filename, process.execPath),
makePathWrapper(binFolder, `node` as Filename, process.execPath, [], nodeWrapperNativePath),
...YarnVersion !== null ? [
makePathWrapper(binFolder, `run` as Filename, process.execPath, [yarnBin, `run`]),
makePathWrapper(binFolder, `yarn` as Filename, process.execPath, [yarnBin]),
makePathWrapper(binFolder, `yarnpkg` as Filename, process.execPath, [yarnBin]),
makePathWrapper(binFolder, `node-gyp` as Filename, process.execPath, [yarnBin, `run`, `--top-level`, `node-gyp`]),
makePathWrapper(binFolder, `run` as Filename, process.execPath, [yarnBin, `run`], nodeWrapperNativePath),
makePathWrapper(binFolder, `yarn` as Filename, process.execPath, [yarnBin], nodeWrapperNativePath),
makePathWrapper(binFolder, `yarnpkg` as Filename, process.execPath, [yarnBin], nodeWrapperNativePath),
makePathWrapper(binFolder, `node-gyp` as Filename, process.execPath, [yarnBin, `run`, `--top-level`, `node-gyp`], nodeWrapperNativePath),
] : [],
]);

Expand Down Expand Up @@ -729,11 +821,16 @@ export async function getWorkspaceAccessibleBinaries(workspace: Workspace) {
}

async function installBinaries(target: PortablePath, binaries: PackageAccessibleBinaries) {
const nodeWrapperPath = ppath.join(target, `node-wrapper.js` as Filename);
const nodeWrapperNativePath = xfs.existsSync(nodeWrapperPath)
? npath.fromPortablePath(nodeWrapperPath)
: undefined;

await Promise.all(
Array.from(binaries, ([binaryName, [, binaryPath, isScript]]) => {
return isScript
? makePathWrapper(target, binaryName as Filename, process.execPath, [binaryPath])
: makePathWrapper(target, binaryName as Filename, binaryPath, []);
? makePathWrapper(target, binaryName as Filename, process.execPath, [binaryPath], nodeWrapperNativePath)
: makePathWrapper(target, binaryName as Filename, binaryPath, [], nodeWrapperNativePath);
}),
);
}
Expand Down
Loading