diff --git a/.changeset/late-laws-approve.md b/.changeset/late-laws-approve.md new file mode 100644 index 00000000000..bc0bb99dacb --- /dev/null +++ b/.changeset/late-laws-approve.md @@ -0,0 +1,6 @@ +--- +"@pnpm/plugin-commands-env": patch +"pnpm": patch +--- + +Use hard links to link the node executable on Windows machines [#4315](https://github.com/pnpm/pnpm/issues/4315). diff --git a/env/plugin-commands-env/package.json b/env/plugin-commands-env/package.json index e9880e7ce2d..c0f688e90d2 100644 --- a/env/plugin-commands-env/package.json +++ b/env/plugin-commands-env/package.json @@ -41,6 +41,7 @@ "@pnpm/store-path": "workspace:*", "@zkochan/cmd-shim": "^6.0.0", "@zkochan/rimraf": "^2.1.2", + "is-windows": "^1.0.2", "load-json-file": "^6.2.0", "render-help": "^1.0.3", "semver": "^7.4.0", @@ -51,6 +52,7 @@ "@pnpm/plugin-commands-env": "workspace:*", "@pnpm/prepare": "workspace:*", "@types/adm-zip": "^0.5.0", + "@types/is-windows": "^1.0.0", "@types/semver": "7.3.13", "adm-zip": "^0.5.10", "execa": "npm:safe-execa@0.1.2", diff --git a/env/plugin-commands-env/src/envUse.ts b/env/plugin-commands-env/src/envUse.ts index b5c147722b9..6f09a06b689 100644 --- a/env/plugin-commands-env/src/envUse.ts +++ b/env/plugin-commands-env/src/envUse.ts @@ -4,6 +4,7 @@ import { PnpmError } from '@pnpm/error' import { createFetchFromRegistry } from '@pnpm/fetch' import { resolveNodeVersion } from '@pnpm/node.resolver' import cmdShim from '@zkochan/cmd-shim' +import isWindows from 'is-windows' import { getNodeDir, type NvmNodeCommandOptions } from './node' import { getNodeMirror } from './getNodeMirror' import { parseNodeEditionSpecifier } from './parseNodeEditionSpecifier' @@ -30,7 +31,7 @@ export async function envUse (opts: NvmNodeCommandOptions, params: string[]) { try { await fs.unlink(dest) } catch (err) {} - await fs.symlink(src, dest, 'file') + await symlinkOrHardLink(src, dest) try { let npmDir = nodeDir if (process.platform !== 'win32') { @@ -52,3 +53,12 @@ export async function envUse (opts: NvmNodeCommandOptions, params: string[]) { return `Node.js ${nodeVersion as string} is activated ${dest} -> ${src}` } + +// On Windows, symlinks only work with developer mode enabled +// or with admin permissions. So it is better to use hard links on Windows. +async function symlinkOrHardLink (existingPath: string, newPath: string) { + if (isWindows()) { + return fs.link(existingPath, newPath) + } + return fs.symlink(existingPath, newPath, 'file') +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2552aaa970..abe0b167d9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -855,6 +855,9 @@ importers: '@zkochan/rimraf': specifier: ^2.1.2 version: 2.1.2 + is-windows: + specifier: ^1.0.2 + version: 1.0.2 load-json-file: specifier: ^6.2.0 version: 6.2.0 @@ -877,6 +880,9 @@ importers: '@types/adm-zip': specifier: ^0.5.0 version: 0.5.0 + '@types/is-windows': + specifier: ^1.0.0 + version: 1.0.0 '@types/semver': specifier: 7.3.13 version: 7.3.13 @@ -8804,7 +8810,7 @@ packages: /@types/byline@4.2.33: resolution: {integrity: sha512-LJYez7wrWcJQQDknqZtrZuExMGP0IXmPl1rOOGDqLbu+H7UNNRfKNuSxCBcQMLH1EfjeWidLedC/hCc5dDfBog==} dependencies: - '@types/node': 18.15.11 + '@types/node': 14.18.42 dev: true /@types/cacheable-request@6.0.3: @@ -8914,7 +8920,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.15.11 + '@types/node': 14.18.42 /@types/lodash@4.14.181: resolution: {integrity: sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==} @@ -8957,7 +8963,6 @@ packages: /@types/node@14.18.42: resolution: {integrity: sha512-xefu+RBie4xWlK8hwAzGh3npDz/4VhF6icY/shU+zv/1fNn+ZVG7T7CRwe9LId9sAYRPxI+59QBPuKL3WpyGRg==} - dev: true /@types/node@18.15.11: resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==} @@ -13501,7 +13506,7 @@ packages: jws: 3.2.2 lodash: 4.17.21 ms: 2.1.3 - semver: 7.3.8 + semver: 7.4.0 /jsprim@1.4.2: resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}