Compare commits

...

70 Commits

Author SHA1 Message Date
Jonathan Netley 15498ddb9a
docs(corepack-version): reference node 24 as latest default (#723)
Co-authored-by: Steven <steven@ceriously.com>
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
2025-06-27 08:00:56 +00:00
Antoine du Hamel 9a1794a59f
chore: fix permissions in the `publish` workflow (#721) 2025-06-14 22:26:33 +02:00
Mike McCready 783a42fbe3
chore!: remove Node.js 18.x and 23.x usage, add 24.x (#718)
BREAKING CHANGE: drop Node.js 18.x and 23.x support

In CI add Node.js 24.x tests with Windows exclusion
2025-06-06 20:21:48 +02:00
github-actions[bot] 98fd966176
chore(main): release 0.33.0 (#699)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-02 13:06:02 +00:00
Maël Nison 5fc3691354
feat: Adds guard to avoid stepping on Yarn's feet (#714)
* feat: Adds guard to avoid stepping on Yarn's feet

* Makes the path more specific

* Apply feedback

* Fixes test
2025-06-02 09:40:35 +02:00
github-actions[bot] b45b3a3244
feat: update package manager versions (#671)
* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* Trigger actions?

* Fixes mocks

* nock again

---------

Co-authored-by: Create or Update Pull Request Action <create-or-update-pull-request@users.noreply.github.com>
Co-authored-by: Maël Nison <nison.mael@gmail.com>
2025-05-27 09:37:08 +02:00
Maël Nison 77fff3c1f3
Disables auto-pinning by default (#709)
* Disables auto-pinning by default

* Updates tests

* Updates the README
2025-05-16 19:14:38 +02:00
dependabot[bot] 273237a110
build(deps): bump vite from 6.2.6 to 6.3.4 (#705)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.6 to 6.3.4.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.3.4/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.3.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-01 00:37:14 +02:00
dependabot[bot] 2b43f26135
build(deps): bump vite from 6.2.3 to 6.2.6 (#702)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.3 to 6.2.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.2.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-30 00:19:14 +02:00
Antoine du Hamel aefde28a63
chore(publish): add `--provenance` (#701) 2025-04-09 17:48:07 +02:00
Mike McCready 0b94797f96
fix: debug text typo (#698) 2025-04-08 11:46:08 +02:00
dependabot[bot] 679bcefda5
build(deps): bump vite from 6.2.0 to 6.2.3 (#689)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.0 to 6.2.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.3/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-28 14:55:25 +01:00
Mike McCready 633764f7c4
chore(deps): update eslint to 9.22.0 (#676)
update @yarnpkg/eslint-config to 3.0.0

fix fixable eslint formatting errors

add @typescript-eslint/no-unused-vars caughtErrors:none

change VScode setting to eslint.useFlatConfig true
2025-03-26 20:26:06 +01:00
github-actions[bot] 19e3c6861a
chore(main): release 0.32.0 (#641) 2025-02-28 20:00:24 +00:00
github-actions[bot] b83bb5ec15
feat: update package manager versions (#617) 2025-02-28 19:46:59 +00:00
Antoine du Hamel 7deb39ead0
chore(deps): remove duplicate esbuild version, bump other deps (#667) 2025-02-28 19:34:05 +00:00
Antoine du Hamel 9b95b46f05
feat: add support for `.corepack.env` (#642) 2025-02-28 19:25:03 +00:00
Antoine du Hamel b456268851
feat: add limited support for `devEngines` (#643) 2025-02-28 20:06:33 +01:00
Antoine du Hamel 53b1fe75c4
feat: add more informative error when fetching latest stable fails (#644) 2025-02-28 20:05:59 +01:00
Antoine du Hamel 4be72f6941
fix(use): do not throw on invalid `packageManager` (#663) 2025-02-28 19:20:09 +01:00
Antoine du Hamel bb16184b7b
chore(tests): remove temporary directories in test teardown (#666) 2025-02-28 11:21:15 +00:00
Thomas Scholtes b0608d1a60
chore(tests): remove temporary directories in test teardown (#662) 2025-02-28 12:09:16 +01:00
Mike McCready 351d86c202
fix: replace explicit with specify as verb (#665) 2025-02-26 12:25:46 +01:00
Mike McCready 48ce89827c
chore(ci): enable node 23 ci tests for Windows (#660) 2025-02-15 09:25:51 +01:00
Thomas Scholtes da96687e69
chore(ci): reject linter warnings (#655) 2025-02-14 22:34:18 +00:00
Antoine du Hamel f8de7214e5
chore: fix lint warning (#657) 2025-02-14 23:20:20 +01:00
dependabot[bot] 7f7336b9bd
build(deps-dev): bump esbuild from 0.24.2 to 0.25.0 (#653)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.24.2 to 0.25.0.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2024.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.24.2...v0.25.0)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-12 12:59:59 +01:00
Antoine du Hamel d26a552daa
chore(tests): split `invalid_signature` test in two (#651)
Ranges and tags are not the same thing, it can be useful to have two
different tests in case only one of them fail.
2025-02-12 10:14:34 +00:00
Thomas Scholtes 7b193d8498
chore(tests): fix string matching expectations (#648)
Vitest does not support matching a regex with a string, it would fail to report unmatched output.
2025-02-12 10:01:48 +00:00
Antoine du Hamel 91ea527475
chore(test): fix some custom registry tests (#649)
Co-authored-by: Thomas Scholtes <geigerzaehler@axiom.fm>
2025-02-11 18:56:39 +01:00
Thomas Scholtes c388c64805
chore(tests): don’t keep HTTP connections alive (#645)
This change almost halves the time it takes to run `tests/main.test.ts`.
2025-02-10 23:37:21 +01:00
Antoine du Hamel 12e77e5069
fix: do not resolve fallback descriptor when `packageManager` is defined (#632) 2025-02-08 18:55:54 +01:00
Mike McCready b0c46078f1
docs: add instructions to update corepack (#637) 2025-02-07 19:08:34 +01:00
Antoine du Hamel bae0839794
fix(doc): fix link to proxy library (#636) 2025-02-07 16:18:04 +01:00
Antoine du Hamel d5e938795b
chore(deps): bump all dependencies (#638) 2025-02-07 15:40:14 +01:00
Antoine du Hamel 9317593dda
chore(tests): await promise in `config.test.js` (#639) 2025-02-07 14:35:44 +00:00
Maël Nison 8163608e49
chore(build): bump TypeScript to 5.7 (#630)
* Updates TypeScript

* Updates the SDK
2025-02-03 17:07:57 +01:00
Mike McCready 721eeec8c3
chore: update yarn to 4.6.0 (#619) 2025-02-03 14:57:01 +01:00
Mike McCready a7cb9f603c
docs: add contributor windows os non-homedrive usage (#606) 2025-01-29 21:03:23 +01:00
github-actions[bot] c11891152c
chore(main): release 0.31.0 (#596) 2025-01-27 22:52:21 +00:00
Antoine du Hamel ab38255f24
chore(tests): do not fail tests when `DEBUG` is set in the env (#615) 2025-01-27 22:42:16 +00:00
dependabot[bot] 34abfcaee8
build(deps): bump vite from 5.4.6 to 5.4.14 (#609)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.6 to 5.4.14.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.14/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.14/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
2025-01-27 22:36:47 +00:00
Antoine du Hamel 72a588c237
fix: only print message for `UsageError`s (#602) 2025-01-27 23:22:51 +01:00
Mike McCready 8c90caab7f
fix: update npm registry keys (#614)
Align with https://registry.npmjs.org/-/npm/v1/keys
2025-01-27 23:15:35 +01:00
Mike McCready 9dfbe18003
docs: add `DEBUG=corepack` logging instructions (#605) 2025-01-26 01:15:28 +01:00
Mike McCready 2a04e7c8da
docs: fix new shims contributing typo (#599) 2025-01-20 09:46:47 +01:00
Mike McCready 8bebc0c0a5
chore!: drop support for Node.js 21.x (#594)
Node.js 21.x EOL was June 6th 2024.

* chore(deps): update better-sqlite3 to 11.7.2

Required for running tests with Node.js 23.x.

* chore(ci): add Node.js 23 to tested versions

Windows tests are excluded due to an upstream bug.
2025-01-19 13:47:59 +01:00
github-actions[bot] c7a9bde16d
feat: update package manager versions (#595)
* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* feat: update package manager versions

* update Nock files

---------

Co-authored-by: Create or Update Pull Request Action <create-or-update-pull-request@users.noreply.github.com>
Co-authored-by: GitHub Actions <actions@github.com>
Co-authored-by: Kristoffer K. <merceyz@users.noreply.github.com>
2025-01-14 13:09:52 +01:00
dependabot[bot] 0ae3c3c1f5
build(deps): bump nanoid from 3.3.7 to 3.3.8 (#582)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 18:02:19 +01:00
Mike McCready afd939011f
chore(ci): add `pull_requests: write` permissions to `sync.yml` (#593) 2025-01-13 16:40:29 +00:00
Hamir Mahal 256cf8aaa4
chore(ci): move away from archived `google-github` action (#581) 2024-12-05 15:31:16 +01:00
github-actions[bot] 1bb0fb5d67
chore(main): release 0.30.0 (#577) 2024-11-23 12:39:40 +00:00
Antoine du Hamel a286c8f553
feat: update package manager versions (#578) 2024-11-23 12:25:31 +00:00
Kristoffer K. cba690575b
perf: prefer `module.enableCompileCache` over `v8-compile-cache` (#574) 2024-11-23 11:40:37 +01:00
dependabot[bot] 72ed05b775
build(deps): bump cross-spawn from 7.0.3 to 7.0.6 (#575)
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-23 11:39:07 +01:00
dependabot[bot] 98d0e43cbc
build(deps): bump vite from 5.3.3 to 5.4.6 (#562)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.3.3 to 5.4.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-23 11:38:24 +01:00
chlorine d732259faf
doc: update directories to directory in corepack --help (#565) 2024-09-29 08:12:08 +00:00
github-actions[bot] 095903b3a8
chore(main): release 0.29.4 (#558) 2024-09-07 11:14:44 +00:00
Antoine du Hamel 8ba6e0b3ba
chore: fix permissions in `publish.yml` (#557)
Refs: https://github.com/googleapis/release-please-action#workflow-permissions
2024-09-07 12:58:33 +02:00
github-actions[bot] b819e404db
feat: update package manager versions (#543) 2024-09-07 10:54:43 +00:00
Antoine du Hamel 2c2005cc42
chore: fix permissions of `update-nock-files.yml` (#556) 2024-09-07 10:50:48 +00:00
dependabot[bot] cb2c063dd6
build(deps): bump micromatch from 4.0.7 to 4.0.8 (#555)
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.7 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.7...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-07 10:35:12 +00:00
Antoine du Hamel 4ca33d3974
chore: fix `sync.yml` permissions (#554) 2024-09-07 12:20:26 +02:00
github-actions[bot] bc13d40037
chore(main): release 0.29.3 (#539) 2024-07-21 14:57:40 +00:00
Antoine du Hamel eb63873c6c
fix: fallback to `shasum` when `integrity` is not defined (#542)
Some npm registries do not define an `integrity` field, in which case
we can try using the `shasum` field instead.
2024-07-21 16:38:30 +02:00
Wojciech Maj 93a49c8b2b
chore(deps): bump semver from 7.6.2 to 7.6.3 (#534)
This bugfix version of semver includes significant performance improvements that I guess we would like to see in corepack.

See: https://github.com/npm/node-semver/pull/726
2024-07-20 11:33:00 +02:00
Wojciech Maj 67463b2a3b
chore(deps): bump tar from 6.x to 7.x, selectively import required functions (#533)
Changes from 06e5872189 made it possible to update tar from 6.x to 7.x and use new `package.json` exports that are included in 7.x, allowing us to further reduce our bundle size.
2024-07-18 13:53:41 +02:00
Antoine du Hamel 6019d7b56e
fix: make `DEBUG=corepack` more useful (#538) 2024-07-18 13:52:19 +02:00
Wojciech Maj 06e5872189
chore: adjust tsconfig.json settings (#532)
Because Corepack is bundled with esbuild, the correct, recommended tsconfig.json settings are:
- module: preserve
- moduleResolution: bundler

This ensures that modules are resolved the way esbuild resolves them, and opens us to using e.g. package.json exports (which will come in handy in the next PR in which I update tar dependency).
2024-07-17 11:19:03 +02:00
Kristoffer K. 85e556a1a9
test: migrate to vitest (#349) 2024-07-16 23:31:15 +02:00
54 changed files with 3628 additions and 3887 deletions

View File

@ -39,22 +39,28 @@ jobs:
run: corepack yarn typecheck
- name: "Check for linting errors"
run: corepack yarn lint
run: corepack yarn lint --max-warnings=0
build-and-test:
strategy:
fail-fast: false
matrix:
node:
- 18
- 20
- 21
- 22
- 24
platform:
- ubuntu-latest
- macos-latest
- windows-latest
# Temporarily skipping Node.js 24 under Windows due to issue
# https://github.com/nodejs/corepack/issues/715
# vitest fails "handle integrity checks" on Windows with Node.js 24.x
exclude:
- node: 24
platform: windows-latest
name: "${{matrix.platform}} w/ Node.js ${{matrix.node}}.x"
runs-on: ${{matrix.platform}}

View File

@ -7,14 +7,20 @@ on:
env:
YARN_ENABLE_GLOBAL_CACHE: false
permissions:
contents: read
jobs:
release-please:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
outputs:
release_created: ${{ steps.release.outputs.release_created }}
release_tag: ${{ steps.release.outputs.tag_name }}
steps:
- uses: google-github-actions/release-please-action@v3
- uses: googleapis/release-please-action@v3
id: release
with:
release-type: node
@ -25,6 +31,9 @@ jobs:
needs: release-please
if: ${{ needs.release-please.outputs.release_created }}
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
@ -44,10 +53,10 @@ jobs:
restore-keys: |
${{runner.os}}-yarn-
- run: corepack yarn install --immutable
- name: Publish to the npm registry
run: |
corepack yarn install --immutable
corepack yarn npm publish
run: corepack yarn npm publish --provenance
env:
YARN_NPM_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

View File

@ -8,6 +8,9 @@ on:
jobs:
fetch-latest-versions:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:

View File

@ -17,6 +17,8 @@ env:
jobs:
build-and-update-nock-files:
permissions:
contents: write
runs-on: ubuntu-latest
steps:

2
.gitignore vendored
View File

@ -13,3 +13,5 @@
/shims
/coverage
/node_modules

View File

@ -4,7 +4,7 @@
"**/.yarn": true,
"**/.pnp.*": true
},
"eslint.experimental.useFlatConfig": true,
"eslint.useFlatConfig": true,
"eslint.nodePath": ".yarn/sdks",
"typescript.enablePromptUseWorkspaceTsdk": true,
"editor.codeActionsOnSave": {

View File

@ -0,0 +1,10 @@
diff --git a/lib/platform/package.json b/lib/platform/package.json
index 5ea9d43740d1bdb509612376c0e9ce91d20f8b20..08d4f7327dee5e605107a343d73b2254ed08ddb9 100644
--- a/lib/platform/package.json
+++ b/lib/platform/package.json
@@ -1,4 +1,4 @@
{
- "main": "./node",
+ "main": "./node.js",
"browser": "./browser"
}

View File

@ -1,20 +1,32 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint/bin/eslint.js
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real eslint/bin/eslint.js your application uses
module.exports = absRequire(`eslint/bin/eslint.js`);
module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`));

View File

@ -1,20 +1,32 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real eslint your application uses
module.exports = absRequire(`eslint`);
module.exports = wrapWithUserWrapper(absRequire(`eslint`));

View File

@ -1,20 +1,32 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint/use-at-your-own-risk
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real eslint/use-at-your-own-risk your application uses
module.exports = absRequire(`eslint/use-at-your-own-risk`);
module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`));

View File

@ -1,6 +1,6 @@
{
"name": "eslint",
"version": "8.56.0-sdk",
"version": "8.57.0-sdk",
"main": "./lib/api.js",
"type": "commonjs",
"bin": {

View File

@ -1,20 +1,32 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/bin/tsc
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real typescript/bin/tsc your application uses
module.exports = absRequire(`typescript/bin/tsc`);
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsc`));

View File

@ -1,20 +1,32 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/bin/tsserver
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real typescript/bin/tsserver your application uses
module.exports = absRequire(`typescript/bin/tsserver`);
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsserver`));

View File

@ -1,20 +1,32 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsc.js
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real typescript/lib/tsc.js your application uses
module.exports = absRequire(`typescript/lib/tsc.js`);
module.exports = wrapWithUserWrapper(absRequire(`typescript/lib/tsc.js`));

View File

@ -1,15 +1,38 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const moduleWrapper = tsserver => {
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsserver.js
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
const moduleWrapper = exports => {
return wrapWithUserWrapper(moduleWrapperFn(exports));
};
const moduleWrapperFn = tsserver => {
if (!process.versions.pnp) {
return tsserver;
}
@ -214,11 +237,11 @@ const moduleWrapper = tsserver => {
return tsserver;
};
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsserver.js
require(absPnpApiPath).setup();
}
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
// Ref https://github.com/microsoft/TypeScript/pull/55326
if (major > 5 || (major === 5 && minor >= 5)) {
moduleWrapper(absRequire(`typescript`));
}
// Defer to the real typescript/lib/tsserver.js your application uses

View File

@ -1,15 +1,38 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const moduleWrapper = tsserver => {
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
const moduleWrapper = exports => {
return wrapWithUserWrapper(moduleWrapperFn(exports));
};
const moduleWrapperFn = tsserver => {
if (!process.versions.pnp) {
return tsserver;
}
@ -214,11 +237,11 @@ const moduleWrapper = tsserver => {
return tsserver;
};
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
require(absPnpApiPath).setup();
}
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
// Ref https://github.com/microsoft/TypeScript/pull/55326
if (major > 5 || (major === 5 && minor >= 5)) {
moduleWrapper(absRequire(`typescript`));
}
// Defer to the real typescript/lib/tsserverlibrary.js your application uses

View File

@ -1,20 +1,32 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real typescript your application uses
module.exports = absRequire(`typescript`);
module.exports = wrapWithUserWrapper(absRequire(`typescript`));

View File

@ -1,6 +1,6 @@
{
"name": "typescript",
"version": "5.3.3-sdk",
"version": "5.7.3-sdk",
"main": "./lib/typescript.js",
"type": "commonjs",
"bin": {

View File

@ -1,5 +1,85 @@
# Changelog
## [0.33.0](https://github.com/nodejs/corepack/compare/v0.32.0...v0.33.0) (2025-06-02)
### Features
* Adds guard to avoid stepping on Yarn's feet ([#714](https://github.com/nodejs/corepack/issues/714)) ([5fc3691](https://github.com/nodejs/corepack/commit/5fc3691354eb5bdeca17a9495b234584353f0151))
* update package manager versions ([#671](https://github.com/nodejs/corepack/issues/671)) ([b45b3a3](https://github.com/nodejs/corepack/commit/b45b3a3244bacfbaf65188ae8c04209a1e98307d))
### Bug Fixes
* debug text typo ([#698](https://github.com/nodejs/corepack/issues/698)) ([0b94797](https://github.com/nodejs/corepack/commit/0b94797f96e30e46e466873fe7d437d0471cd92c))
## [0.32.0](https://github.com/nodejs/corepack/compare/v0.31.0...v0.32.0) (2025-02-28)
### Features
* add limited support for `devEngines` ([#643](https://github.com/nodejs/corepack/issues/643)) ([b456268](https://github.com/nodejs/corepack/commit/b4562688513f23e37e37b0d69a0daff33ca84c8d))
* add more informative error when fetching latest stable fails ([#644](https://github.com/nodejs/corepack/issues/644)) ([53b1fe7](https://github.com/nodejs/corepack/commit/53b1fe75c47c06bd72a8b8f8bb699a47c9ca32fb))
* add support for `.corepack.env` ([#642](https://github.com/nodejs/corepack/issues/642)) ([9b95b46](https://github.com/nodejs/corepack/commit/9b95b46f05e50fe1c60f05309c210ba8fe4e23c5))
* update package manager versions ([#617](https://github.com/nodejs/corepack/issues/617)) ([b83bb5e](https://github.com/nodejs/corepack/commit/b83bb5ec150980c998b9c7053dff307d912cb508))
### Bug Fixes
* do not resolve fallback descriptor when `packageManager` is defined ([#632](https://github.com/nodejs/corepack/issues/632)) ([12e77e5](https://github.com/nodejs/corepack/commit/12e77e506946d42a0de9ce8e68d75af8454d6776))
* **doc:** fix link to proxy library ([#636](https://github.com/nodejs/corepack/issues/636)) ([bae0839](https://github.com/nodejs/corepack/commit/bae08397943d4b99437389b4286546361091f4b3))
* replace explicit with specify as verb ([#665](https://github.com/nodejs/corepack/issues/665)) ([351d86c](https://github.com/nodejs/corepack/commit/351d86c20226a8c18bfe212be27401f2908b1595))
* **use:** do not throw on invalid `packageManager` ([#663](https://github.com/nodejs/corepack/issues/663)) ([4be72f6](https://github.com/nodejs/corepack/commit/4be72f6941afa0c9b2b7d26635016bb7b560df8a))
## [0.31.0](https://github.com/nodejs/corepack/compare/v0.30.0...v0.31.0) (2025-01-27)
### ⚠ BREAKING CHANGES
* drop support for Node.js 21.x ([#594](https://github.com/nodejs/corepack/issues/594))
### Features
* update package manager versions ([#595](https://github.com/nodejs/corepack/issues/595)) ([c7a9bde](https://github.com/nodejs/corepack/commit/c7a9bde16dcbbb7e6ef03fef740656cde7ade360))
### Bug Fixes
* only print message for `UsageError`s ([#602](https://github.com/nodejs/corepack/issues/602)) ([72a588c](https://github.com/nodejs/corepack/commit/72a588c2370c17e415b24fe389efdafb3c84e90b))
* update npm registry keys ([#614](https://github.com/nodejs/corepack/issues/614)) ([8c90caa](https://github.com/nodejs/corepack/commit/8c90caab7f1c5c9b89f1de113bc1dfc441bf25d2))
### Miscellaneous Chores
* drop support for Node.js 21.x ([#594](https://github.com/nodejs/corepack/issues/594)) ([8bebc0c](https://github.com/nodejs/corepack/commit/8bebc0c0a5cbcdeec41673dcbaf581e6e1c1be11))
## [0.30.0](https://github.com/nodejs/corepack/compare/v0.29.4...v0.30.0) (2024-11-23)
### Features
* update package manager versions ([#578](https://github.com/nodejs/corepack/issues/578)) ([a286c8f](https://github.com/nodejs/corepack/commit/a286c8f5537ea9ecf9b6ff53c7bc3e8da4e3c8bb))
### Performance Improvements
* prefer `module.enableCompileCache` over `v8-compile-cache` ([#574](https://github.com/nodejs/corepack/issues/574)) ([cba6905](https://github.com/nodejs/corepack/commit/cba690575bd606faeee54bd512ccb8797d49055f))
## [0.29.4](https://github.com/nodejs/corepack/compare/v0.29.3...v0.29.4) (2024-09-07)
### Features
* update package manager versions ([#543](https://github.com/nodejs/corepack/issues/543)) ([b819e40](https://github.com/nodejs/corepack/commit/b819e404dbb23c4ae3d5dbe55e21de74d714ee9c))
## [0.29.3](https://github.com/nodejs/corepack/compare/v0.29.2...v0.29.3) (2024-07-21)
### Bug Fixes
* fallback to `shasum` when `integrity` is not defined ([#542](https://github.com/nodejs/corepack/issues/542)) ([eb63873](https://github.com/nodejs/corepack/commit/eb63873c6c617a2f8ac7106e26ccfe8aa3ae1fb9))
* make `DEBUG=corepack` more useful ([#538](https://github.com/nodejs/corepack/issues/538)) ([6019d7b](https://github.com/nodejs/corepack/commit/6019d7b56e85bd8b7b58a1a487922c707e70e30e))
## [0.29.2](https://github.com/nodejs/corepack/compare/v0.29.1...v0.29.2) (2024-07-13)

View File

@ -2,7 +2,7 @@
If you want to build Corepack yourself, you can build the project like this:
1. Clone this repository.
1. Clone this repository. Some restrictions on the cloning location apply to Windows users, read [Contributing on Windows](#contributing-on-windows) for more information.
2. Run `yarn install` (or `corepack yarn install` if the global version of
`yarn` is not provided by Corepack).
3. Run `yarn build` (or `corepack yarn build`).
@ -11,6 +11,12 @@ The `dist/` directory now contains the corepack build and the shims.
Call `node ./dist/corepack --help` and behold.
You can also run the tests with `yarn test`.
## Contributing on Windows
If you are cloning the repo to a directory on a Microsoft Windows operating system, it is recommended to use the same drive as your Windows `HOMEDRIVE` to avoid build and other issues related to a current restriction with Yarn Plug'n'Play caching.
If you are unable to use your `HOMEDRIVE`, you may be able to work around the issue by setting the environment variable `YARN_ENABLE_GLOBAL_CACHE` to `false` before running `yarn install` (or `corepack yarn install`).
# Adding a new package manager
New package managers can be added by editing the following files:
@ -18,7 +24,7 @@ New package managers can be added by editing the following files:
- [`config.json`](./config.json),
- [`.github/workflows/sync.yml`](./.github/workflows/sync.yml) that keeps pinned
versions up-to-date,
- [`package.json`](./package.json) to add to add the added shims to the list of
- [`package.json`](./package.json) to add the new shims to the list of
`"publishConfig/bin"` and `"executableFiles"`,
- [`sources/types.ts`](./sources/types.ts) to add the package manager to the
`SupportedPackageManagers` enum,

View File

@ -11,7 +11,7 @@ and pnpm without having to install them**.
### Default Installs
Corepack is [distributed by default with all recent Node.js versions](https://nodejs.org/api/corepack.html).
Corepack is distributed with Node.js from version 14.19.0 up to (but not including) 25.0.0.
Run `corepack enable` to install the required Yarn and pnpm binaries on your path.
### Manual Installs
@ -41,6 +41,25 @@ is distributed along with Node.js itself.
</details>
<details><summary>Update Corepack using npm</summary>
To install the latest version of Corepack, use:
```shell
npm install -g corepack@latest
```
If Corepack was installed on your system using a Node.js Windows Installer
`.msi` package then you might need to remove it before attempting to install a
different version of Corepack using npm. You can select the Modify option of the
Node.js app settings to access the Windows Installer feature selection, and on
the "corepack manager" feature of the Node.js `.msi` package by selecting
"Entire feature will be unavailable". See
[Repair apps and programs in Windows](https://support.microsoft.com/en-us/windows/repair-apps-and-programs-in-windows-e90eefe4-d0a2-7c1b-dd59-949a9030f317)
for instructions on accessing the Windows apps page to modify settings.
</details>
<details><summary>Install Corepack from source</summary>
See [`CONTRIBUTING.md`](./CONTRIBUTING.md).
@ -94,6 +113,35 @@ use in the archive).
}
```
#### `devEngines.packageManager`
When a `devEngines.packageManager` field is defined, and is an object containing
a `"name"` field (can also optionally contain `version` and `onFail` fields),
Corepack will use it to validate you're using a compatible package manager.
Depending on the value of `devEngines.packageManager.onFail`:
- if set to `ignore`, Corepack won't print any warning or error.
- if unset or set to `error`, Corepack will throw an error in case of a mismatch.
- if set to `warn` or some other value, Corepack will print a warning in case
of mismatch.
If the top-level `packageManager` field is missing, Corepack will use the
package manager defined in `devEngines.packageManager` in which case you must
provide a specific version in `devEngines.packageManager.version`, ideally with
a hash, as explained in the previous section:
```json
{
"devEngines":{
"packageManager": {
"name": "yarn",
"version": "3.2.3+sha224.953c8233f7a92884eee2de69a1b92d1f2ec1655e66d08071ba9a02fa"
}
}
}
```
## Known Good Releases
When running Corepack within projects that don't list a supported package
@ -227,6 +275,7 @@ it.
Unlike `corepack use` this command doesn't take a package manager name nor a
version range, as it will always select the latest available version from the
range specified in `devEngines.packageManager.version`, or fallback to the
same major line. Should you need to upgrade to a new major, use an explicit
`corepack use {name}@latest` call (or simply `corepack use {name}`).
@ -237,8 +286,8 @@ same major line. Should you need to upgrade to a new major, use an explicit
package manager, and to not update the Last Known Good version when it
downloads a new version of the same major line.
- `COREPACK_ENABLE_AUTO_PIN` can be set to `0` to prevent Corepack from
updating the `packageManager` field when it detects that the local package
- `COREPACK_ENABLE_AUTO_PIN` can be set to `1` to instruct Corepack to
update the `packageManager` field when it detects that the local package
doesn't list it. In general we recommend to always list a `packageManager`
field (which you can easily set through `corepack use [name]@[version]`), as
it ensures that your project installs are always deterministic.
@ -248,6 +297,7 @@ same major line. Should you need to upgrade to a new major, use an explicit
set to `1` to have the URL shown. By default, when Corepack is called
explicitly (e.g. `corepack pnpm …`), it is set to `0`; when Corepack is called
implicitly (e.g. `pnpm …`), it is set to `1`.
The default value cannot be overridden in a `.corepack.env` file.
When standard input is a TTY and no CI environment is detected, Corepack will
ask for user input before starting the download.
@ -273,6 +323,14 @@ same major line. Should you need to upgrade to a new major, use an explicit
project. This means that it will always use the system-wide package manager
regardless of what is being specified in the project's `packageManager` field.
- `COREPACK_ENV_FILE` can be set to `0` to request Corepack to not attempt to
load `.corepack.env`; it can be set to a path to specify a different env file.
Only keys that start with `COREPACK_` and are not in the exception list
(`COREPACK_ENABLE_DOWNLOAD_PROMPT` and `COREPACK_ENV_FILE` are ignored)
will be taken into account.
For Node.js 18.x users, this setting has no effect as that version doesn't
support parsing of `.env` files.
- `COREPACK_HOME` can be set in order to define where Corepack should install
the package managers. By default it is set to `%LOCALAPPDATA%\node\corepack`
on Windows, and to `$HOME/.cache/node/corepack` everywhere else.
@ -294,7 +352,7 @@ same major line. Should you need to upgrade to a new major, use an explicit
empty password, explicitly set `COREPACK_NPM_PASSWORD` to an empty string.
- `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` are supported through
[`node-proxy-agent`](https://github.com/TooTallNate/node-proxy-agent).
[`proxy-from-env`](https://github.com/Rob--W/proxy-from-env).
- `COREPACK_INTEGRITY_KEYS` can be set to an empty string or `0` to
instruct Corepack to skip integrity checks, or to a JSON string containing
@ -302,6 +360,8 @@ same major line. Should you need to upgrade to a new major, use an explicit
## Troubleshooting
The environment variable `DEBUG` can be set to `corepack` to enable additional debug logging.
### Networking
There are a wide variety of networking issues that can occur while running

View File

@ -1,9 +0,0 @@
module.exports = {
presets: [
`@babel/preset-typescript`,
],
plugins: [
[`@babel/plugin-transform-modules-commonjs`],
[`babel-plugin-dynamic-import-node`],
],
};

View File

@ -1,7 +1,7 @@
{
"definitions": {
"npm": {
"default": "10.8.2+sha1.3c123c7f14409dc0395478e7269fdbc32ae179d8",
"default": "11.4.1+sha1.80350af543069991de20657ebcd07d9624cfad06",
"fetchLatestFrom": {
"type": "npm",
"package": "npm"
@ -38,7 +38,7 @@
}
},
"pnpm": {
"default": "9.5.0+sha1.8c155dc114e1689d18937974f6571e0ceee66f1d",
"default": "10.11.0+sha1.4048eeefd564ff1ab248fac3e2854d38245fe2f1",
"fetchLatestFrom": {
"type": "npm",
"package": "pnpm"
@ -102,7 +102,7 @@
"package": "yarn"
},
"transparent": {
"default": "4.3.1+sha224.934d21773e22af4b69a7032a2d3b4cb38c1f7c019624777cc9916b23",
"default": "4.9.1+sha224.4285002185abb91fe2b781f27fd1e078086c37a7b095f6ea4ee25971",
"commands": [
[
"yarn",
@ -165,11 +165,18 @@
"keys": {
"npm": [
{
"expires": null,
"expires": "2025-01-29T00:00:00.000Z",
"keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA",
"keytype": "ecdsa-sha2-nistp256",
"scheme": "ecdsa-sha2-nistp256",
"key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg=="
},
{
"expires": null,
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U",
"keytype": "ecdsa-sha2-nistp256",
"scheme": "ecdsa-sha2-nistp256",
"key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY6Ya7W++7aUPzvMTrezH6Ycx3c+HOKYCcNGybJZSCJq/fd7Qa8uuAKtdIkUQtQiEKERhAmE5lMMJhP8OkDOa2g=="
}
]
}

View File

@ -13,11 +13,13 @@ export default [
...yarnpkg,
{
rules: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'no-restricted-globals': [`error`, {
name: `fetch`,
message: `Use fetch from sources/httpUtils.ts`,
}],
'@typescript-eslint/no-unused-vars': [`error`, {
caughtErrors: `none`,
}],
},
},
];

View File

@ -1,4 +0,0 @@
module.exports = {
setupFilesAfterEnv: [`./tests/setupTests.js`],
testTimeout: 120000,
};

View File

@ -22,6 +22,7 @@ async function main() {
fs.writeFileSync(corepackPath, [
`#!/usr/bin/env node`,
`process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT??='0';`,
`require('module').enableCompileCache?.();`,
`require('./lib/corepack.cjs').runMain(process.argv.slice(2));`,
].join(`\n`));
fs.chmodSync(corepackPath, 0o755);
@ -34,6 +35,7 @@ async function main() {
const entryScript = [
`#!/usr/bin/env node`,
`process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT??='1'`,
`require('module').enableCompileCache?.();`,
`require('./lib/corepack.cjs').runMain(['${binaryName}', ...process.argv.slice(2)]);`,
].join(`\n`);

View File

@ -1,6 +1,6 @@
{
"name": "corepack",
"version": "0.29.2",
"version": "0.33.0",
"homepage": "https://github.com/nodejs/corepack#readme",
"bugs": {
"url": "https://github.com/nodejs/corepack/issues"
@ -10,44 +10,37 @@
"url": "https://github.com/nodejs/corepack.git"
},
"engines": {
"node": "^18.17.1 || >=20.10.0"
"node": "^20.10.0 || ^22.11.0 || >=24.0.0"
},
"exports": {
"./package.json": "./package.json"
},
"license": "MIT",
"packageManager": "yarn@4.3.1+sha224.934d21773e22af4b69a7032a2d3b4cb38c1f7c019624777cc9916b23",
"packageManager": "yarn@4.9.0+sha224.dce6c5df199861784bd9b0eecb2a228df97e3f18a02b1bb75ff98383",
"devDependencies": {
"@babel/core": "^7.14.3",
"@babel/plugin-transform-modules-commonjs": "^7.14.0",
"@babel/preset-typescript": "^7.13.0",
"@jest/globals": "^29.0.0",
"@types/debug": "^4.1.5",
"@types/jest": "^29.0.0",
"@types/node": "^20.4.6",
"@types/proxy-from-env": "^1",
"@types/semver": "^7.1.0",
"@types/tar": "^6.0.0",
"@types/which": "^3.0.0",
"@yarnpkg/eslint-config": "^2.0.0",
"@yarnpkg/eslint-config": "^3.0.0",
"@yarnpkg/fslib": "^3.0.0-rc.48",
"@zkochan/cmd-shim": "^6.0.0",
"babel-plugin-dynamic-import-node": "^2.3.3",
"better-sqlite3": "^10.0.0",
"clipanion": "^3.0.1",
"better-sqlite3": "^11.7.2",
"clipanion": "patch:clipanion@npm%3A3.2.1#~/.yarn/patches/clipanion-npm-3.2.1-fc9187f56c.patch",
"debug": "^4.1.1",
"esbuild": "^0.21.0",
"eslint": "^8.57.0",
"jest": "^29.0.0",
"esbuild": "^0.25.0",
"eslint": "^9.22.0",
"proxy-from-env": "^1.1.0",
"semver": "^7.5.2",
"supports-color": "^9.0.0",
"tar": "^6.2.1",
"semver": "^7.6.3",
"supports-color": "^10.0.0",
"tar": "^7.4.0",
"tsx": "^4.16.2",
"typescript": "^5.3.3",
"typescript": "^5.7.3",
"undici": "^6.19.2",
"v8-compile-cache": "^2.3.0",
"which": "^4.0.0"
"vitest": "^3.0.5",
"which": "^5.0.0"
},
"resolutions": {
"undici-types": "6.x"
@ -62,7 +55,7 @@
"postpack": "run clean",
"rimraf": "node -e 'for(let i=2;i<process.argv.length;i++)fs.rmSync(process.argv[i],{recursive:true,force:true});'",
"typecheck": "tsc --noEmit",
"test": "jest"
"test": "vitest"
},
"files": [
"dist",

View File

@ -14,9 +14,9 @@ import * as folderUtils from './folderUtil
import type {NodeError} from './nodeUtils';
import * as semverUtils from './semverUtils';
import * as specUtils from './specUtils';
import {Config, Descriptor, Locator, PackageManagerSpec} from './types';
import {Config, Descriptor, LazyLocator, Locator} from './types';
import {SupportedPackageManagers, SupportedPackageManagerSet} from './types';
import {isSupportedPackageManager} from './types';
import {isSupportedPackageManager, PackageManagerSpec} from './types';
export type PreparedPackageManagerInfo = Awaited<ReturnType<Engine[`ensurePackageManager`]>>;
@ -27,7 +27,9 @@ export type PackageManagerRequest = {
};
function getLastKnownGoodFilePath() {
return path.join(folderUtils.getCorepackHomeFolder(), `lastKnownGood.json`);
const lkg = path.join(folderUtils.getCorepackHomeFolder(), `lastKnownGood.json`);
debugUtils.log(`LastKnownGood file would be located at ${lkg}`);
return lkg;
}
export async function getLastKnownGood(): Promise<Record<string, string>> {
@ -35,23 +37,34 @@ export async function getLastKnownGood(): Promise<Record<string, string>> {
try {
raw = await fs.promises.readFile(getLastKnownGoodFilePath(), `utf8`);
} catch (err) {
if ((err as NodeError)?.code === `ENOENT`) return {};
if ((err as NodeError)?.code === `ENOENT`) {
debugUtils.log(`No LastKnownGood version found in Corepack home.`);
return {};
}
throw err;
}
try {
const parsed = JSON.parse(raw);
if (!parsed) return {};
if (typeof parsed !== `object`) return {};
if (!parsed) {
debugUtils.log(`Invalid LastKnowGood file in Corepack home (JSON parsable, but falsy)`);
return {};
}
if (typeof parsed !== `object`) {
debugUtils.log(`Invalid LastKnowGood file in Corepack home (JSON parsable, but non-object)`);
return {};
}
Object.entries(parsed).forEach(([key, value]) => {
if (typeof value !== `string`) {
// Ensure that all entries are strings.
debugUtils.log(`Ignoring key ${key} in LastKnownGood file as its value is not a string`);
delete parsed[key];
}
});
return parsed;
} catch {
// Ignore errors; too bad
debugUtils.log(`Invalid LastKnowGood file in Corepack home (maybe not JSON parsable)`);
return {};
}
}
@ -161,13 +174,18 @@ export class Engine {
const lastKnownGood = await getLastKnownGood();
const lastKnownGoodForThisPackageManager = getLastKnownGoodFromFileContent(lastKnownGood, packageManager);
if (lastKnownGoodForThisPackageManager)
if (lastKnownGoodForThisPackageManager) {
debugUtils.log(`Search for default version: Found ${packageManager}@${lastKnownGoodForThisPackageManager} in LastKnownGood file`);
return lastKnownGoodForThisPackageManager;
}
if (process.env.COREPACK_DEFAULT_TO_LATEST === `0`)
if (process.env.COREPACK_DEFAULT_TO_LATEST === `0`) {
debugUtils.log(`Search for default version: As defined in environment, defaulting to internal config ${packageManager}@${definition.default}`);
return definition.default;
}
const reference = await corepackUtils.fetchLatestStableVersion(definition.fetchLatestFrom);
debugUtils.log(`Search for default version: found in remote registry ${packageManager}@${reference}`);
try {
await activatePackageManager(lastKnownGood, {
@ -175,6 +193,7 @@ export class Engine {
reference,
});
} catch {
debugUtils.log(`Search for default version: could not activate registry version`);
// If for some reason, we cannot update the last known good file, we
// ignore the error.
}
@ -218,19 +237,23 @@ export class Engine {
*
* If the project doesn't include a specification file, we just assume that
* whatever the user uses is exactly what they want to use. Since the version
* isn't explicited, we fallback on known good versions.
* isn't specified, we fallback on known good versions.
*
* Finally, if the project doesn't exist at all, we ask the user whether they
* want to create one in the current project. If they do, we initialize a new
* project using the default package managers, and configure it so that we
* don't need to ask again in the future.
*/
async findProjectSpec(initialCwd: string, locator: Locator, {transparent = false}: {transparent?: boolean} = {}): Promise<Descriptor> {
async findProjectSpec(initialCwd: string, locator: Locator | LazyLocator, {transparent = false}: {transparent?: boolean} = {}): Promise<Descriptor> {
// A locator is a valid descriptor (but not the other way around)
const fallbackDescriptor = {name: locator.name, range: `${locator.reference}`};
if (process.env.COREPACK_ENABLE_PROJECT_SPEC === `0`)
if (process.env.COREPACK_ENABLE_PROJECT_SPEC === `0`) {
if (typeof locator.reference === `function`)
fallbackDescriptor.range = await locator.reference();
return fallbackDescriptor;
}
if (process.env.COREPACK_ENABLE_STRICT === `0`)
transparent = true;
@ -239,11 +262,19 @@ export class Engine {
const result = await specUtils.loadSpec(initialCwd);
switch (result.type) {
case `NoProject`:
case `NoProject`: {
if (typeof locator.reference === `function`)
fallbackDescriptor.range = await locator.reference();
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} as no project manifest were found`);
return fallbackDescriptor;
}
case `NoSpec`: {
if (process.env.COREPACK_ENABLE_AUTO_PIN !== `0`) {
if (typeof locator.reference === `function`)
fallbackDescriptor.range = await locator.reference();
if (process.env.COREPACK_ENABLE_AUTO_PIN === `1`) {
const resolved = await this.resolveDescriptor(fallbackDescriptor, {allowTags: true});
if (resolved === null)
throw new UsageError(`Failed to successfully resolve '${fallbackDescriptor.range}' to a valid ${fallbackDescriptor.name} release`);
@ -257,18 +288,25 @@ export class Engine {
await specUtils.setLocalPackageManager(path.dirname(result.target), installSpec);
}
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} in the absence of "packageManager" field in ${result.target}`);
return fallbackDescriptor;
}
case `Found`: {
if (result.spec.name !== locator.name) {
const spec = result.getSpec();
if (spec.name !== locator.name) {
if (transparent) {
if (typeof locator.reference === `function`)
fallbackDescriptor.range = await locator.reference();
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} in a ${spec.name}@${spec.range} project`);
return fallbackDescriptor;
} else {
throw new UsageError(`This project is configured to use ${result.spec.name} because ${result.target} has a "packageManager" field`);
throw new UsageError(`This project is configured to use ${spec.name} because ${result.target} has a "packageManager" field`);
}
} else {
return result.spec;
debugUtils.log(`Using ${spec.name}@${spec.range} as defined in project manifest ${result.target}`);
return spec;
}
}
}
@ -276,14 +314,14 @@ export class Engine {
}
async executePackageManagerRequest({packageManager, binaryName, binaryVersion}: PackageManagerRequest, {cwd, args}: {cwd: string, args: Array<string>}): Promise<void> {
let fallbackLocator: Locator = {
let fallbackLocator: Locator | LazyLocator = {
name: binaryName as SupportedPackageManagers,
reference: undefined as any,
};
let isTransparentCommand = false;
if (packageManager != null) {
const defaultVersion = binaryVersion || await this.getDefaultVersion(packageManager);
const defaultVersion = binaryVersion || (() => this.getDefaultVersion(packageManager));
const definition = this.config.definitions[packageManager]!;
// If all leading segments match one of the patterns defined in the `transparent`
@ -302,7 +340,7 @@ export class Engine {
fallbackLocator = {
name: packageManager,
reference: fallbackReference,
reference: fallbackReference as string,
};
}

View File

@ -13,13 +13,13 @@ export abstract class BaseCommand extends Command<Context> {
const lookup = await specUtils.loadSpec(this.context.cwd);
switch (lookup.type) {
case `NoProject`:
throw new UsageError(`Couldn't find a project in the local directory - please explicit the package manager to pack, or run this command from a valid project`);
throw new UsageError(`Couldn't find a project in the local directory - please specify the package manager to pack, or run this command from a valid project`);
case `NoSpec`:
throw new UsageError(`The local project doesn't feature a 'packageManager' field - please explicit the package manager to pack, or update the manifest to reference it`);
throw new UsageError(`The local project doesn't feature a 'packageManager' field nor a 'devEngines.packageManager' field - please specify the package manager to pack, or update the manifest to reference it`);
default: {
return [lookup.spec];
return [lookup.range ?? lookup.getSpec()];
}
}
}

View File

@ -3,6 +3,7 @@ import fs from 'f
import path from 'path';
import which from 'which';
import * as corepackUtils from '../corepackUtils';
import {Context} from '../main';
import type {NodeError} from '../nodeUtils';
import {isSupportedPackageManager, SupportedPackageManagerSetWithoutNpm} from '../types';
@ -70,6 +71,11 @@ export class DisableCommand extends Command<Context> {
async removePosixLink(installDirectory: string, binName: string) {
const file = path.join(installDirectory, binName);
try {
if (binName.includes(`yarn`) && corepackUtils.isYarnSwitchPath(await fs.promises.realpath(file))) {
console.warn(`${binName} is already installed in ${file} and points to a Yarn Switch install - skipping`);
return;
}
await fs.promises.unlink(file);
} catch (err) {
if ((err as NodeError).code !== `ENOENT`) {

View File

@ -4,6 +4,7 @@ import fs from 'f
import path from 'path';
import which from 'which';
import * as corepackUtils from '../corepackUtils';
import {Context} from '../main';
import {isSupportedPackageManager, SupportedPackageManagerSetWithoutNpm} from '../types';
@ -13,7 +14,7 @@ export class EnableCommand extends Command<Context> {
];
static usage = Command.Usage({
description: `Add the Corepack shims to the install directories`,
description: `Add the Corepack shims to the install directory`,
details: `
When run, this command will check whether the shims for the specified package managers can be found with the correct values inside the install directory. If not, or if they don't exist, they will be created.
@ -83,6 +84,12 @@ export class EnableCommand extends Command<Context> {
if (fs.existsSync(file)) {
const currentSymlink = await fs.promises.readlink(file);
if (binName.includes(`yarn`) && corepackUtils.isYarnSwitchPath(await fs.promises.realpath(file))) {
console.warn(`${binName} is already installed in ${file} and points to a Yarn Switch install - skipping`);
return;
}
if (currentSymlink !== symlink) {
await fs.promises.unlink(file);
} else {

View File

@ -77,11 +77,11 @@ export class InstallGlobalCommand extends BaseCommand {
const installFolder = folderUtils.getInstallFolder();
const archiveEntries = new Map<string, Set<string>>();
const {default: tar} = await import(`tar`);
const {list: tarT} = await import(`tar/list`);
let hasShortEntries = false;
await tar.t({file: p, onentry: entry => {
await tarT({file: p, onentry: entry => {
const segments = entry.path.split(/\//g);
if (segments.length > 0 && segments[segments.length - 1] !== `.corepack`)
return;
@ -100,6 +100,8 @@ export class InstallGlobalCommand extends BaseCommand {
if (hasShortEntries || archiveEntries.size < 1)
throw new UsageError(`Invalid archive format; did it get generated by 'corepack pack'?`);
const {extract: tarX} = await import(`tar/extract`);
for (const [name, references] of archiveEntries) {
for (const reference of references) {
if (!isSupportedPackageManager(name))
@ -110,7 +112,7 @@ export class InstallGlobalCommand extends BaseCommand {
// Recreate the folder in case it was deleted somewhere else:
await fs.promises.mkdir(installFolder, {recursive: true});
await tar.x({file: p, cwd: installFolder}, [`${name}/${reference}`]);
await tarX({file: p, cwd: installFolder}, [`${name}/${reference}`]);
if (!this.cacheOnly) {
await this.context.engine.activatePackageManager({name, reference});

View File

@ -62,11 +62,11 @@ export class PackCommand extends BaseCommand {
this.context.stdout.write(`Packing the selected tools in ${path.basename(outputPath)}...\n`);
}
const {default: tar} = await import(`tar`);
const {create: tarC} = await import(`tar/create`);
// Recreate the folder in case it was deleted somewhere else:
await mkdir(baseInstallFolder, {recursive: true});
await tar.c({gzip: true, cwd: baseInstallFolder, file: path.resolve(outputPath)}, installLocations.map(location => {
await tarC({gzip: true, cwd: baseInstallFolder, file: path.resolve(outputPath)}, installLocations.map(location => {
return path.relative(baseInstallFolder, location);
}));

View File

@ -24,9 +24,9 @@ export class HydrateCommand extends Command<Context> {
const archiveEntries = new Map<string, Set<string>>();
let hasShortEntries = false;
const {default: tar} = await import(`tar`);
const {list: tarT} = await import(`tar/list`);
await tar.t({file: fileName, onentry: entry => {
await tarT({file: fileName, onentry: entry => {
const segments = entry.path.split(/\//g);
if (segments.length < 3) {
@ -43,6 +43,8 @@ export class HydrateCommand extends Command<Context> {
if (hasShortEntries || archiveEntries.size < 1)
throw new UsageError(`Invalid archive format; did it get generated by 'corepack prepare'?`);
const {extract: tarX} = await import(`tar/extract`);
for (const [name, references] of archiveEntries) {
for (const reference of references) {
if (!isSupportedPackageManager(name))
@ -56,7 +58,7 @@ export class HydrateCommand extends Command<Context> {
// Recreate the folder in case it was deleted somewhere else:
await mkdir(installFolder, {recursive: true});
await tar.x({file: fileName, cwd: installFolder}, [`${name}/${reference}`]);
await tarX({file: fileName, cwd: installFolder}, [`${name}/${reference}`]);
if (this.activate) {
await this.context.engine.activatePackageManager({name, reference});

View File

@ -36,13 +36,13 @@ export class PrepareCommand extends Command<Context> {
const lookup = await specUtils.loadSpec(this.context.cwd);
switch (lookup.type) {
case `NoProject`:
throw new UsageError(`Couldn't find a project in the local directory - please explicit the package manager to pack, or run this command from a valid project`);
throw new UsageError(`Couldn't find a project in the local directory - please specify the package manager to pack, or run this command from a valid project`);
case `NoSpec`:
throw new UsageError(`The local project doesn't feature a 'packageManager' field - please explicit the package manager to pack, or update the manifest to reference it`);
throw new UsageError(`The local project doesn't feature a 'packageManager' field - please specify the package manager to pack, or update the manifest to reference it`);
default: {
specs.push(lookup.spec);
specs.push(lookup.getSpec());
}
}
}
@ -83,10 +83,10 @@ export class PrepareCommand extends Command<Context> {
if (!this.json)
this.context.stdout.write(`Packing the selected tools in ${path.basename(outputPath)}...\n`);
const {default: tar} = await import(`tar`);
const {create: tarC} = await import(`tar/create`);
// Recreate the folder in case it was deleted somewhere else:
await mkdir(baseInstallFolder, {recursive: true});
await tar.c({gzip: true, cwd: baseInstallFolder, file: path.resolve(outputPath)}, installLocations.map(location => {
await tarC({gzip: true, cwd: baseInstallFolder, file: path.resolve(outputPath)}, installLocations.map(location => {
return path.relative(baseInstallFolder, location);
}));

View File

@ -19,6 +19,12 @@ import * as npmRegistryUtils from './npmRegist
import {RegistrySpec, Descriptor, Locator, PackageManagerSpec} from './types';
import {BinList, BinSpec, InstallSpec, DownloadSpec} from './types';
const YARN_SWITCH_REGEX = /[/\\]switch[/\\]bin[/\\]/;
export function isYarnSwitchPath(p: string) {
return YARN_SWITCH_REGEX.test(p);
}
export function getRegistryFromPackageManagerSpec(spec: PackageManagerSpec) {
return process.env.COREPACK_NPM_REGISTRY
? spec.npmRegistry ?? spec.registry
@ -151,8 +157,8 @@ async function download(installTarget: string, url: string, algo: string, binPat
let sendTo: any;
if (ext === `.tgz`) {
const {default: tar} = await import(`tar`);
sendTo = tar.x({
const {extract: tarX} = await import(`tar/extract`);
sendTo = tarX({
strip: 1,
cwd: tmpFolder,
filter: binPath ? path => {
@ -207,7 +213,7 @@ export async function installVersion(installTarget: string, locator: Locator, {s
const corepackData = JSON.parse(corepackContent);
debugUtils.log(`Reusing ${locator.name}@${locator.reference}`);
debugUtils.log(`Reusing ${locator.name}@${locator.reference} found in ${installFolder}`);
return {
hash: corepackData.hash as string,
@ -333,7 +339,7 @@ export async function installVersion(installTarget: string, locator: Locator, {s
}
}
debugUtils.log(`Install finished`);
debugUtils.log(`Download and install of ${locator.name}@${locator.reference} is finished`);
return {
location: installFolder,
@ -400,12 +406,16 @@ export async function runVersion(locator: Locator, installSpec: InstallSpec & {s
if (!binPath)
throw new Error(`Assertion failed: Unable to locate path for bin '${binName}'`);
// Node.js segfaults when using npm@>=9.7.0 and v8-compile-cache
// $ docker run -it node:20.3.0-slim corepack npm@9.7.1 --version
// [SIGSEGV]
if (locator.name !== `npm` || semverLt(locator.reference, `9.7.0`))
// @ts-expect-error - No types
await import(`v8-compile-cache`);
// @ts-expect-error - Missing types
if (!Module.enableCompileCache) {
// Node.js segfaults when using npm@>=9.7.0 and v8-compile-cache
// $ docker run -it node:20.3.0-slim corepack npm@9.7.1 --version
// [SIGSEGV]
if (locator.name !== `npm` || semverLt(locator.reference, `9.7.0`)) {
// @ts-expect-error - No types
await import(`v8-compile-cache`);
}
}
// We load the binary into the current process,
// while making it think it was spawned.
@ -429,6 +439,12 @@ export async function runVersion(locator: Locator, installSpec: InstallSpec & {s
// Use nextTick to unwind the stack, and consequently remove Corepack from
// the stack trace of the package manager.
process.nextTick(Module.runMain, binPath);
// @ts-expect-error - No types
if (Module.flushCompileCache) {
// @ts-expect-error - No types
setImmediate(Module.flushCompileCache);
}
}
export function shouldSkipIntegrityCheck() {

View File

@ -1,4 +1,5 @@
import {BaseContext, Builtins, Cli} from 'clipanion';
import type {UsageError} from 'clipanion';
import {version as corepackVersion} from '../package.json';
@ -37,6 +38,10 @@ function getPackageManagerRequestFromCli(parameter: string | undefined, engine:
};
}
function isUsageError(error: any): error is UsageError {
return error?.name === `UsageError`;
}
export async function runMain(argv: Array<string>) {
const engine = new Engine();
@ -79,9 +84,17 @@ export async function runMain(argv: Array<string>) {
process.exitCode ??= code;
}
} else {
await engine.executePackageManagerRequest(request, {
cwd: process.cwd(),
args: restArgs,
});
try {
await engine.executePackageManagerRequest(request, {
cwd: process.cwd(),
args: restArgs,
});
} catch (error) {
if (isUsageError(error)) {
console.error(error.message);
process.exit(1);
}
throw error;
}
}
}

View File

@ -38,19 +38,27 @@ export function verifySignature({signatures, integrity, packageName, version}: {
packageName: string;
version: string;
}) {
const {npm: keys} = process.env.COREPACK_INTEGRITY_KEYS ?
if (!Array.isArray(signatures) || !signatures.length) throw new Error(`No compatible signature found in package metadata`);
const {npm: trustedKeys} = process.env.COREPACK_INTEGRITY_KEYS ?
JSON.parse(process.env.COREPACK_INTEGRITY_KEYS) as typeof defaultConfig.keys :
defaultConfig.keys;
const key = keys.find(({keyid}) => signatures.some(s => s.keyid === keyid));
const signature = signatures.find(({keyid}) => keyid === key?.keyid);
if (key == null || signature == null) throw new Error(`Cannot find matching keyid: ${JSON.stringify({signatures, keys})}`);
let signature: typeof signatures[0] | undefined;
let key!: string;
for (const k of trustedKeys) {
signature = signatures.find(({keyid}) => keyid === k.keyid);
if (signature != null) {
key = k.key;
break;
}
}
if (signature?.sig == null) throw new UsageError(`The package was not signed by any trusted keys: ${JSON.stringify({signatures, trustedKeys}, undefined, 2)}`);
const verifier = createVerify(`SHA256`);
verifier.end(`${packageName}@${version}:${integrity}`);
const valid = verifier.verify(
`-----BEGIN PUBLIC KEY-----\n${key.key}\n-----END PUBLIC KEY-----`,
`-----BEGIN PUBLIC KEY-----\n${key}\n-----END PUBLIC KEY-----`,
signature.sig,
`base64`,
);
@ -62,16 +70,25 @@ export function verifySignature({signatures, integrity, packageName, version}: {
export async function fetchLatestStableVersion(packageName: string) {
const metadata = await fetchAsJson(packageName, `latest`);
const {version, dist: {integrity, signatures}} = metadata;
const {version, dist: {integrity, signatures, shasum}} = metadata;
if (!shouldSkipIntegrityCheck()) {
verifySignature({
packageName, version,
integrity, signatures,
});
try {
verifySignature({
packageName, version,
integrity, signatures,
});
} catch (cause) {
// TODO: consider switching to `UsageError` when https://github.com/arcanis/clipanion/issues/157 is fixed
throw new Error(`Corepack cannot download the latest stable version of ${packageName}; you can disable signature verification by setting COREPACK_INTEGRITY_CHECK to 0 in your env, or instruct Corepack to use the latest stable release known by this version of Corepack by setting COREPACK_USE_LATEST to 0`, {cause});
}
}
return `${version}+sha512.${Buffer.from(integrity.slice(7), `base64`).toString(`hex`)}`;
return `${version}+${
integrity ?
`sha512.${Buffer.from(integrity.slice(7), `base64`).toString(`hex`)}` :
`sha1.${shasum}`
}`;
}
export async function fetchAvailableTags(packageName: string) {

View File

@ -1,12 +1,17 @@
import {UsageError} from 'clipanion';
import fs from 'fs';
import path from 'path';
import semverSatisfies from 'semver/functions/satisfies';
import semverValid from 'semver/functions/valid';
import semverValidRange from 'semver/ranges/valid';
import {parseEnv} from 'util';
import {PreparedPackageManagerInfo} from './Engine';
import * as debugUtils from './debugUtils';
import {NodeError} from './nodeUtils';
import * as nodeUtils from './nodeUtils';
import {Descriptor, isSupportedPackageManager} from './types';
import type {LocalEnvFile} from './types';
const nodeModulesRegExp = /[\\/]node_modules[\\/](@[^\\/]*[\\/])?([^@\\/][^\\/]*)$/;
@ -51,16 +56,87 @@ export function parseSpec(raw: unknown, source: string, {enforceExactVersion = t
};
}
type CorepackPackageJSON = {
packageManager?: string;
devEngines?: {packageManager?: DevEngineDependency};
};
interface DevEngineDependency {
name: string;
version: string;
onFail?: `ignore` | `warn` | `error`;
}
function warnOrThrow(errorMessage: string, onFail?: DevEngineDependency[`onFail`]) {
switch (onFail) {
case `ignore`:
break;
case `error`:
case undefined:
throw new UsageError(errorMessage);
default:
console.warn(`! Corepack validation warning: ${errorMessage}`);
}
}
function parsePackageJSON(packageJSONContent: CorepackPackageJSON) {
const {packageManager: pm} = packageJSONContent;
if (packageJSONContent.devEngines?.packageManager != null) {
const {packageManager} = packageJSONContent.devEngines;
if (typeof packageManager !== `object`) {
console.warn(`! Corepack only supports objects as valid value for devEngines.packageManager. The current value (${JSON.stringify(packageManager)}) will be ignored.`);
return pm;
}
if (Array.isArray(packageManager)) {
console.warn(`! Corepack does not currently support array values for devEngines.packageManager`);
return pm;
}
const {name, version, onFail} = packageManager;
if (typeof name !== `string` || name.includes(`@`)) {
warnOrThrow(`The value of devEngines.packageManager.name ${JSON.stringify(name)} is not a supported string value`, onFail);
return pm;
}
if (version != null && (typeof version !== `string` || !semverValidRange(version))) {
warnOrThrow(`The value of devEngines.packageManager.version ${JSON.stringify(version)} is not a valid semver range`, onFail);
return pm;
}
debugUtils.log(`devEngines.packageManager defines that ${name}@${version} is the local package manager`);
if (pm) {
if (!pm.startsWith?.(`${name}@`))
warnOrThrow(`"packageManager" field is set to ${JSON.stringify(pm)} which does not match the "devEngines.packageManager" field set to ${JSON.stringify(name)}`, onFail);
else if (version != null && !semverSatisfies(pm.slice(packageManager.name.length + 1), version))
warnOrThrow(`"packageManager" field is set to ${JSON.stringify(pm)} which does not match the value defined in "devEngines.packageManager" for ${JSON.stringify(name)} of ${JSON.stringify(version)}`, onFail);
return pm;
}
return `${name}@${version ?? `*`}`;
}
return pm;
}
export async function setLocalPackageManager(cwd: string, info: PreparedPackageManagerInfo) {
const lookup = await loadSpec(cwd);
const range = `range` in lookup && lookup.range;
if (range) {
if (info.locator.name !== range.name || !semverSatisfies(info.locator.reference, range.range)) {
warnOrThrow(`The requested version of ${info.locator.name}@${info.locator.reference} does not match the devEngines specification (${range.name}@${range.range})`, range.onFail);
}
}
const content = lookup.type !== `NoProject`
? await fs.promises.readFile(lookup.target, `utf8`)
: ``;
const {data, indent} = nodeUtils.readPackageJson(content);
const previousPackageManager = data.packageManager ?? `unknown`;
const previousPackageManager = data.packageManager ?? (range ? `${range.name}@${range.range}` : `unknown`);
data.packageManager = `${info.locator.name}@${info.locator.reference}`;
const newContent = nodeUtils.normalizeLineEndings(content, `${JSON.stringify(data, null, indent)}\n`);
@ -71,10 +147,17 @@ export async function setLocalPackageManager(cwd: string, info: PreparedPackageM
};
}
interface FoundSpecResult {
type: `Found`;
target: string;
getSpec: () => Descriptor;
range?: Descriptor & {onFail?: DevEngineDependency[`onFail`]};
envFilePath?: string;
}
export type LoadSpecResult =
| {type: `NoProject`, target: string}
| {type: `NoSpec`, target: string}
| {type: `Found`, target: string, spec: Descriptor};
| FoundSpecResult;
export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
let nextCwd = initialCwd;
@ -83,6 +166,8 @@ export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
let selection: {
data: any;
manifestPath: string;
envFilePath?: string;
localEnv: LocalEnvFile;
} | null = null;
while (nextCwd !== currCwd && (!selection || !selection.data.packageManager)) {
@ -93,6 +178,7 @@ export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
continue;
const manifestPath = path.join(currCwd, `package.json`);
debugUtils.log(`Checking ${manifestPath}`);
let content: string;
try {
content = await fs.promises.readFile(manifestPath, `utf8`);
@ -109,19 +195,60 @@ export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
if (typeof data !== `object` || data === null)
throw new UsageError(`Invalid package.json in ${path.relative(initialCwd, manifestPath)}`);
selection = {data, manifestPath};
let localEnv: LocalEnvFile;
const envFilePath = path.resolve(currCwd, process.env.COREPACK_ENV_FILE ?? `.corepack.env`);
if (process.env.COREPACK_ENV_FILE == `0`) {
debugUtils.log(`Skipping env file as configured with COREPACK_ENV_FILE`);
localEnv = process.env;
} else if (typeof parseEnv !== `function`) {
// TODO: remove this block when support for Node.js 18.x is dropped.
debugUtils.log(`Skipping env file as it is not supported by the current version of Node.js`);
localEnv = process.env;
} else {
debugUtils.log(`Checking ${envFilePath}`);
try {
localEnv = {
...Object.fromEntries(Object.entries(parseEnv(await fs.promises.readFile(envFilePath, `utf8`))).filter(e => e[0].startsWith(`COREPACK_`))),
...process.env,
};
debugUtils.log(`Successfully loaded env file found at ${envFilePath}`);
} catch (err) {
if ((err as NodeError)?.code !== `ENOENT`)
throw err;
debugUtils.log(`No env file found at ${envFilePath}`);
localEnv = process.env;
}
}
selection = {data, manifestPath, localEnv, envFilePath};
}
if (selection === null)
return {type: `NoProject`, target: path.join(initialCwd, `package.json`)};
const rawPmSpec = selection.data.packageManager;
let envFilePath: string | undefined;
if (selection.localEnv !== process.env) {
envFilePath = selection.envFilePath;
process.env = selection.localEnv;
}
const rawPmSpec = parsePackageJSON(selection.data);
if (typeof rawPmSpec === `undefined`)
return {type: `NoSpec`, target: selection.manifestPath};
debugUtils.log(`${selection.manifestPath} defines ${rawPmSpec} as local package manager`);
return {
type: `Found`,
target: selection.manifestPath,
spec: parseSpec(rawPmSpec, path.relative(initialCwd, selection.manifestPath)),
envFilePath,
range: selection.data.devEngines?.packageManager?.version && {
name: selection.data.devEngines.packageManager.name,
range: selection.data.devEngines.packageManager.version,
onFail: selection.data.devEngines.packageManager.onFail,
},
// Lazy-loading it so we do not throw errors on commands that do not need valid spec.
getSpec: () => parseSpec(rawPmSpec, path.relative(initialCwd, selection.manifestPath)),
};
}

View File

@ -71,7 +71,7 @@ export interface DownloadSpec {
*/
export interface Config {
definitions: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[name in SupportedPackageManagers]?: {
/**
* Defines the version that needs to be used when running commands within
@ -145,3 +145,20 @@ export interface Locator {
*/
reference: string;
}
/**
*
*/
export interface LazyLocator {
/**
* The name of the package manager required.
*/
name: string;
/**
* The exact version required.
*/
reference: () => Promise<string>;
}
export type LocalEnvFile = Record<string, string | undefined>;

View File

@ -1,7 +1,7 @@
import {describe, beforeEach, it, expect} from '@jest/globals';
import {Filename, ppath, xfs, npath} from '@yarnpkg/fslib';
import {delimiter} from 'node:path';
import process from 'node:process';
import {describe, beforeEach, it, expect} from 'vitest';
import {Engine} from '../sources/Engine';
import {SupportedPackageManagerSetWithoutNpm} from '../sources/types';
@ -97,4 +97,23 @@ describe(`DisableCommand`, () => {
await expect(sortedEntries).resolves.toEqual([...binNames].sort());
});
});
it(`shouldn't remove Yarn binaries if they are in a /switch/ folder`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.mkdirPromise(ppath.join(cwd, `switch/bin`), {recursive: true});
await xfs.writeFilePromise(ppath.join(cwd, `switch/bin/yarn`), `hello`);
await xfs.linkPromise(
ppath.join(cwd, `switch/bin/yarn`),
ppath.join(cwd, `yarn`),
);
await expect(runCli(cwd, [`disable`])).resolves.toMatchObject({
exitCode: 0,
});
const file = await xfs.readFilePromise(ppath.join(cwd, `yarn`), `utf8`);
expect(file).toBe(`hello`);
});
});
});

View File

@ -1,7 +1,7 @@
import {describe, beforeEach, it, expect} from '@jest/globals';
import {Filename, ppath, xfs, npath} from '@yarnpkg/fslib';
import {delimiter} from 'node:path';
import process from 'node:process';
import {describe, beforeEach, it, expect, test} from 'vitest';
import {Engine} from '../sources/Engine';
import {SupportedPackageManagers, SupportedPackageManagerSetWithoutNpm} from '../sources/types';
@ -87,4 +87,42 @@ describe(`EnableCommand`, () => {
await expect(sortedEntries).resolves.toEqual(expectedEntries.sort());
});
});
test.skipIf(process.platform === `win32`)(`should overwrite existing files`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.writeFilePromise(ppath.join(cwd, `yarn`), `hello`);
process.env.PATH = `${npath.fromPortablePath(cwd)}${delimiter}${process.env.PATH}`;
await expect(runCli(cwd, [`enable`])).resolves.toMatchObject({
stdout: ``,
stderr: ``,
exitCode: 0,
});
const file = await xfs.readFilePromise(ppath.join(cwd, `yarn`), `utf8`);
expect(file).toBe(`hello`);
});
});
test.skipIf(process.platform === `win32`)(`shouldn't overwrite Yarn files if they are in a /switch/ folder`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.mkdirPromise(ppath.join(cwd, `switch/bin`), {recursive: true});
await xfs.writeFilePromise(ppath.join(cwd, `switch/bin/yarn`), `hello`);
await xfs.linkPromise(
ppath.join(cwd, `switch/bin/yarn`),
ppath.join(cwd, `yarn`),
);
process.env.PATH = `${npath.fromPortablePath(cwd)}${delimiter}${process.env.PATH}`;
await expect(runCli(cwd, [`enable`])).resolves.toMatchObject({
stdout: ``,
stderr: ``,
exitCode: 0,
});
const file = await xfs.readFilePromise(ppath.join(cwd, `yarn`), `utf8`);
expect(file).toBe(`hello`);
});
});
});

View File

@ -1,34 +1,135 @@
import {describe, beforeEach, it, expect} from '@jest/globals';
import {ppath, xfs, npath} from '@yarnpkg/fslib';
import process from 'node:process';
import {describe, beforeEach, it, expect} from 'vitest';
import {runCli} from './_runCli';
beforeEach(async () => {
const home = await xfs.mktempPromise();
// `process.env` is reset after each tests in setupTests.js.
process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise());
process.env.COREPACK_HOME = npath.fromPortablePath(home);
process.env.COREPACK_DEFAULT_TO_LATEST = `0`;
return async () => {
await xfs.removePromise(home, {recursive: true});
};
});
describe(`UpCommand`, () => {
it(`should upgrade the package manager from the current project`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
packageManager: `yarn@2.1.0`,
});
describe(`should update the "packageManager" field from the current project`, () => {
it(`to the same major if no devEngines range`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
packageManager: `yarn@2.1.0`,
});
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
exitCode: 0,
stderr: ``,
});
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
exitCode: 0,
stderr: ``,
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
});
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `2.4.3\n`,
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `2.4.3\n`,
stderr: ``,
});
});
});
it(`to whichever range devEngines defines`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
packageManager: `yarn@1.1.0`,
devEngines: {
packageManager: {
name: `yarn`,
version: `1.x || 2.x`,
},
},
});
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
exitCode: 0,
stderr: ``,
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
});
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `2.4.3\n`,
stderr: ``,
});
});
});
it(`to whichever range devEngines defines even if onFail is set to ignore`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
packageManager: `pnpm@10.1.0`,
devEngines: {
packageManager: {
name: `yarn`,
version: `1.x || 2.x`,
onFail: `ignore`,
},
},
});
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
exitCode: 0,
stderr: ``,
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
});
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `2.4.3\n`,
stderr: ``,
});
});
});
it(`should succeed even if no 'packageManager' field`, async () => {
await xfs.mktempPromise(async cwd => {
process.env.NO_COLOR = `1`;
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
devEngines: {
packageManager: {
name: `yarn`,
version: `1.x || 2.x`,
},
},
});
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
exitCode: 0,
stderr: ``,
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
});
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `2.4.3\n`,
stderr: ``,
});
});
});
});

View File

@ -1,33 +1,116 @@
import {describe, beforeEach, it, expect} from '@jest/globals';
import {ppath, xfs, npath} from '@yarnpkg/fslib';
import process from 'node:process';
import {describe, beforeEach, it, expect} from 'vitest';
import {runCli} from './_runCli';
beforeEach(async () => {
const home = await xfs.mktempPromise();
// `process.env` is reset after each tests in setupTests.js.
process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise());
process.env.COREPACK_HOME = npath.fromPortablePath(home);
process.env.COREPACK_DEFAULT_TO_LATEST = `0`;
return async () => {
await xfs.removePromise(home, {recursive: true});
};
});
describe(`UseCommand`, () => {
it(`should set the package manager in the current project`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
packageManager: `yarn@1.0.0`,
});
describe(`should set the package manager in the current project`, () => {
it(`With an existing 'packageManager' field`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
packageManager: `yarn@1.0.0`,
license: `MIT`,
});
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
exitCode: 0,
});
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
exitCode: 0,
stdout: expect.stringMatching(/^Installing yarn@1\.22\.4 in the project\.\.\.\n\n/),
stderr: ``,
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
packageManager: `yarn@1.22.4+sha512.a1833b862fe52169bd6c2a033045a07df5bc6a23595c259e675fed1b2d035ab37abe6ce309720abb6636d68f03615054b6292dc0a70da31c8697fda228b50d18`,
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
packageManager: `yarn@1.22.4+sha512.a1833b862fe52169bd6c2a033045a07df5bc6a23595c259e675fed1b2d035ab37abe6ce309720abb6636d68f03615054b6292dc0a70da31c8697fda228b50d18`,
});
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `1.22.4\n`,
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `1.22.4\n`,
stderr: ``,
});
});
});
it(`with 'devEngines.packageManager' field`, async () => {
await xfs.mktempPromise(async cwd => {
process.env.NO_COLOR = `1`;
const devEngines = {packageManager: {name: `yarn`, version: `2.x`}};
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
devEngines,
});
// Should refuse to install an incompatible version:
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
exitCode: 1,
stderr: ``,
stdout: `Installing yarn@1.22.4 in the project...\nUsage Error: The requested version of yarn@1.22.4+sha512.a1833b862fe52169bd6c2a033045a07df5bc6a23595c259e675fed1b2d035ab37abe6ce309720abb6636d68f03615054b6292dc0a70da31c8697fda228b50d18 does not match the devEngines specification (yarn@2.x)\n\n$ corepack use <pattern>\n`,
});
// Should accept setting to a compatible version:
await expect(runCli(cwd, [`use`, `yarn@2.4.3`])).resolves.toMatchObject({
exitCode: 0,
stderr: ``,
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
devEngines,
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
});
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `2.4.3\n`,
stderr: ``,
});
});
});
it(`with 'devEngines.packageManager' and 'packageManager' fields`, async () => {
await xfs.mktempPromise(async cwd => {
process.env.NO_COLOR = `1`;
const devEngines = {packageManager: {name: `yarn`, version: `1.x || 2.x`}};
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
devEngines,
packageManager: `yarn@1.1.0`,
license: `MIT`,
});
// Should refuse to install an incompatible version:
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
exitCode: 0,
stderr: ``,
stdout: expect.stringMatching(/^Installing yarn@1\.22\.4 in the project\.\.\.\n\n/),
});
// Should accept setting to a compatible version:
await expect(runCli(cwd, [`use`, `yarn@2.4.3`])).resolves.toMatchObject({
exitCode: 0,
stderr: ``,
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
devEngines,
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
});
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `2.4.3\n`,
stderr: ``,
});
});
});
});
@ -64,4 +147,50 @@ describe(`UseCommand`, () => {
});
});
});
describe(`should not care if packageManager is set to an invalid value`, () => {
for (const {description, packageManager} of [
{
description: `when a version range is given`,
packageManager: `yarn@1.x`,
},
{
description: `when only the pm name is given`,
packageManager: `yarn`,
},
{
description: `when the version is missing`,
packageManager: `yarn@`,
},
{
description: `when the field is not a string`,
packageManager: [],
},
]) {
it(description, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
packageManager,
license: `MIT`, // To avoid warning
});
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
exitCode: 0,
stderr: ``,
stdout: expect.stringMatching(/^Installing yarn@1\.22\.4 in the project\.\.\.\n\n/),
});
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
packageManager: `yarn@1.22.4+sha512.a1833b862fe52169bd6c2a033045a07df5bc6a23595c259e675fed1b2d035ab37abe6ce309720abb6636d68f03615054b6292dc0a70da31c8697fda228b50d18`,
});
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `1.22.4\n`,
stderr: ``,
});
});
});
}
});
});

View File

@ -79,7 +79,7 @@ const registry = {
__proto__: null,
yarn: [`1.9998.9999`],
pnpm: [`1.9998.9999`],
// eslint-disable-next-line @typescript-eslint/naming-convention
'@yarnpkg/cli-dist': [`5.9999.9999`],
customPkgManager: [`1.0.0`],
};
@ -87,7 +87,7 @@ const registry = {
function generateSignature(packageName, version) {
if (privateKey == null) return undefined;
const sign = createSign(`SHA256`).end(`${packageName}@${version}:${integrity}`);
return {signatures: [{
return {integrity, signatures: [{
keyid,
sig: sign.sign(privateKey, `base64`),
}]};
@ -100,10 +100,8 @@ function generateVersionMetadata(packageName, version) {
[packageName]: `./bin/${packageName}.js`,
},
dist: {
integrity,
shasum,
size: mockPackageTarGz.length,
noattachment: false,
tarball: `https://registry.npmjs.org/${packageName}/-/${packageName}-${version}.tgz`,
...generateSignature(packageName, version),
},
@ -113,6 +111,8 @@ function generateVersionMetadata(packageName, version) {
const TOKEN_MOCK = `SOME_DUMMY_VALUE`;
const server = createServer((req, res) => {
res.setHeader(`connection`, `close`);
const auth = req.headers.authorization;
if (
@ -131,7 +131,6 @@ const server = createServer((req, res) => {
const packageName = req.url.slice(1, slashPosition === -1 ? undefined : slashPosition);
if (packageName in registry) {
if (req.url === `/${packageName}`) {
// eslint-disable-next-line @typescript-eslint/naming-convention
res.end(JSON.stringify({"dist-tags": {
latest: registry[packageName].at(-1),
}, versions: Object.fromEntries(registry[packageName].map(version =>

View File

@ -1,14 +1,14 @@
import {jest, describe, it, expect} from '@jest/globals';
import {vi, describe, it, expect} from 'vitest';
import defaultConfig from '../config.json';
import {DEFAULT_NPM_REGISTRY_URL} from '../sources/npmRegistryUtils';
import defaultConfig from '../config.json';
import {DEFAULT_NPM_REGISTRY_URL} from '../sources/npmRegistryUtils';
jest.mock(`../sources/httpUtils`);
vi.mock(`../sources/httpUtils`);
describe(`key store should be up-to-date`, () => {
it(`should contain up-to-date npm keys`, async () => {
const r = await globalThis.fetch(new URL(`/-/npm/v1/keys`, DEFAULT_NPM_REGISTRY_URL));
expect(r.ok).toBe(true);
expect(r.json()).resolves.toMatchObject({keys: defaultConfig.keys.npm});
await expect(r.json()).resolves.toMatchObject({keys: defaultConfig.keys.npm});
});
});

View File

@ -1,4 +1,4 @@
import {describe, it, expect} from '@jest/globals';
import {describe, it, expect} from 'vitest';
import {shouldSkipIntegrityCheck} from '../sources/corepackUtils';

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,15 +1,15 @@
import {jest, describe, beforeEach, it, expect} from '@jest/globals';
import {Buffer} from 'node:buffer';
import process from 'node:process';
import {describe, beforeEach, it, expect, vi} from 'vitest';
import {fetchAsJson as httpFetchAsJson} from '../sources/httpUtils';
import {DEFAULT_HEADERS, DEFAULT_NPM_REGISTRY_URL, fetchAsJson} from '../sources/npmRegistryUtils';
jest.mock(`../sources/httpUtils`);
vi.mock(`../sources/httpUtils`);
describe(`npm registry utils fetchAsJson`, () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});
it(`throw usage error if COREPACK_ENABLE_NETWORK env is set to 0`, async () => {

View File

@ -1,15 +1,12 @@
/* global jest, beforeEach, afterAll */
const process = require(`process`);
jest.retryTimes(2, {logErrorsBeforeRetry: true});
import process from 'process';
import {beforeEach, afterAll} from 'vitest';
const OLD_ENV = process.env;
// To ensure we test the default behavior, we must remove these env vars
// in case the local machine already set these values.
const processEnv = Object.fromEntries(
Object.entries(process.env).filter(
([key]) => key !== `FORCE_COLOR` && !key.startsWith(`COREPACK_`),
([key]) => key !== `FORCE_COLOR` && key !== `DEBUG` && !key.startsWith(`COREPACK_`),
),
);

View File

@ -4,11 +4,11 @@
"baseUrl": ".",
"esModuleInterop": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"noEmit": true,
"forceConsistentCasingInFileNames": true,
"lib": ["ES2023"],
"module": "commonjs",
"module": "preserve",
"moduleResolution": "bundler",
"noEmit": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,

13
vitest.config.mts Normal file
View File

@ -0,0 +1,13 @@
import {defineConfig} from 'vitest/config';
// eslint-disable-next-line arca/no-default-export
export default defineConfig({
test: {
setupFiles: [`./tests/setupTests.ts`],
testTimeout: 120000,
retry: 2,
},
esbuild: {
target: `node${process.versions.node}`,
},
});

5432
yarn.lock

File diff suppressed because it is too large Load Diff