Skip to content

Commit

Permalink
deps: add corepack
Browse files Browse the repository at this point in the history
Corepack provides shims for Yarn and pnpm in order to soften the
developer experience when working on Node projects.

Refs: nodejs#15244
Refs: nodejs/TSC#904
  • Loading branch information
arcanis committed Sep 28, 2020
1 parent a74a6e3 commit 5f978e2
Show file tree
Hide file tree
Showing 23 changed files with 507 additions and 23 deletions.
7 changes: 7 additions & 0 deletions Makefile
Expand Up @@ -1029,6 +1029,13 @@ $(PKG): release-only
# Builds the macOS installer for releases.
pkg: $(PKG)

corepack-update:
rm -rf /tmp/node-corepack-clone
git clone 'https://github.com/arcanis/pmm.git' /tmp/node-corepack-clone
cd /tmp/node-corepack-clone && yarn pack
rm -rf deps/corepack && mkdir -p deps/corepack
cd deps/corepack && tar xf /tmp/node-corepack-clone/package.tgz --strip-components=1

# Note: this is strictly for release builds on release machines only.
pkg-upload: pkg
ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)"
Expand Down
121 changes: 121 additions & 0 deletions deps/corepack/README.md
@@ -0,0 +1,121 @@
# <img src="./icon.svg" height="25" /> corepack

Corepack is a zero-runtime-dependency Node script that acts as a bridge between Node projects and the package managers they are intended to be used with during development.

**Important:** At the moment, Corepack only covers Yarn and pnpm. Given that we have little control on the npm project, we prefer to focus on the Yarn and pnpm use cases. As a result, Corepack doesn't have any effect at all on the way you use npm.

## How to Install

### Default Installs

Corepack isn't intended to be installed manually. While it's certainly possible, we're working with the Node TSC to provide Corepack by default starting from Node 15, thus ensuring that all package managers can be used with little to no friction.

### Manual Installs

<details>
<summary>Click here to see how to install Corepack using npm</summary>

First uninstall your global Yarn and pnpm binaries (just leave npm). In general, you'd do this by running the following command:

```shell
npm uninstall -g yarn pnpm

# That should be enough, but if you installed Yarn without going through npm it might
# be more tedious - for example, you might need to run `brew uninstall yarn` as well.
```

Then install Corepack:

```shell
npm install -g corepack
```

We do acknowledge the irony and overhead of using npm to install Corepack, which is at least part of why the preferred option is to use the Corepack version that will be distributed along with Node itself.

</details>

### Prebuilt Binaries

<details>
<summary>Click here to see how to download prebuilt Corepack Node distributions</summary>

We have a few prebuilt Node binaries (based on the [following branch](https://github.com/arcanis/node/tree/mael/pmm)) that you can just download, unpack somewhere, and add to your `PATH` environment variable.

1. Go to [this page](https://github.com/arcanis/pmm/actions?query=workflow%3ABuild)
2. Open the latest build (the one at the top)
3. Download the right artifact (Linux or Darwin)
4. Unzip the artifact, then untar it
5. Add the `node-v15.0.0-nightlyYYYY-MM-DDXXXX-linux-x64/bin` directory to your `$PATH`

</details>

## Usage

Just use your package managers as you usually would. Run `yarn install` in Yarn projects, `pnpm install` in pnpm projects, and `npm` in npm projects. Corepack will catch these calls, and depending on the situation:

- **If the local project is configured for the package manager you're using**, Corepack will silently download and cache the latest compatible version.

- **If the local project is configured for a different package manager**, Corepack will request you to run the command again using the right package manager - thus avoiding corruptions of your install artifacts.

- **If the local project isn't configured for any package manager**, Corepack will assume that you know what you're doing, and will use whatever package manager version has been pinned as "known good release". Check the relevant section for more details.

## Known Good Releases

When running Yarn or pnpm within projects that don't list a supported package manager, Corepack will default to a set of Known Good Releases. In a way, you can compare this to Node, where each version ships with a specific version of npm.

The Known Good Releases can be updated system-wide using the `--activate` flag from the `corepack prepare` and `corepack hydrate` commands.

## Offline Workflow

The utility commands detailed in the next section.

- Either you can use the network while building your container image, in which case you'll simply run `corepack prepare --cache-only <name>` to make sure that your image includes the Last Known Good release for the specified package manager.

- If you want to have *all* Last Known Good releases for all package managers, just use the `--all` flag which will do just that.

- Or you're publishing your project to a system where the network is unavailable, in which case you'll preemptively generate a package manager archive from your local computer (using `corepack prepare`) before storing it somewhere your container will be able to access (for example within your repository). After that, it's just a matter of running `corepack hydrate <path/to/corepack>` to setup the cache.

## Utility Commands

### `corepack prepare [name@version]`

| Option | Description |
| --- | --- |
| `--all` | Prepare the "Last Known Good" version of all supported package managers |
| `--cache-only` | Just populate the cache, don't generate an archive |
| `--activate` | Also update the "Last Known Good" release |

This command will download the given package manager (or the one configured for the local project if no argument is passed in parameter) and store it within the Corepack cache. Unless the `--cache-only` flag is set, an archive will also be generated that can be used by the `corepack hydrate` command.

### `corepack hydrate <path/to/corepack.tgz>`

| Option | Description |
| --- | --- |
| `--activate` | Also update the "Last Known Good" release |

This command will retrieve the given package manager from the specified archive and will install it within the Corepack cache, ready to be used without further network interaction.

## Contributing

If you want to build corepack yourself things yourself, you can build the project like this:

1. Clone this repository
2. Run `yarn build` (no need for `yarn install`)
3. The `dist/` directory now contains the pmm build and the shims
4. Call `node ./dist/pmm --version` and behold

You can also run the tests with `yarn jest` (still no install needed).

## Design

Various tidbits about Corepack's design are explained in more details in [DESIGN.md](/DESIGN.md).

## License (MIT)

> **Copyright © 2019 Maël Nison**
>
> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 changes: 17 additions & 0 deletions deps/corepack/dist/corepack.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions deps/corepack/dist/npm.js
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./corepack').runMain(['npm', 'npm', ...process.argv.slice(2)]);
2 changes: 2 additions & 0 deletions deps/corepack/dist/npx.js
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./corepack').runMain(['npm', 'npx', ...process.argv.slice(2)]);
2 changes: 2 additions & 0 deletions deps/corepack/dist/pnpm.js
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./corepack').runMain(['pnpm', 'pnpm', ...process.argv.slice(2)]);
2 changes: 2 additions & 0 deletions deps/corepack/dist/pnpx.js
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./corepack').runMain(['pnpm', 'pnpx', ...process.argv.slice(2)]);
2 changes: 2 additions & 0 deletions deps/corepack/dist/yarn.js
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./corepack').runMain(['yarn', 'yarn', ...process.argv.slice(2)]);
2 changes: 2 additions & 0 deletions deps/corepack/dist/yarnpkg.js
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./corepack').runMain(['yarn', 'yarnpkg', ...process.argv.slice(2)]);
77 changes: 77 additions & 0 deletions deps/corepack/package.json
@@ -0,0 +1,77 @@
{
"name": "corepack",
"version": "0.3.0",
"bin": {
"corepack": "./dist/corepack.js",
"pnpm": "./dist/pnpm.js",
"pnpx": "./dist/pnpx.js",
"yarn": "./dist/yarn.js",
"yarnpkg": "./dist/yarnpkg.js"
},
"packageManager": "yarn@^2.0.0-rc.29",
"devDependencies": {
"@babel/core": "^7.11.0",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-proposal-decorators": "^7.10.5",
"@babel/plugin-transform-modules-commonjs": "^7.8.3",
"@babel/preset-typescript": "^7.10.4",
"@types/debug": "^4.1.5",
"@types/jest": "^25.1.4",
"@types/node": "^13.9.2",
"@types/semver": "^7.1.0",
"@types/tar": "^4.0.3",
"@typescript-eslint/eslint-plugin": "^2.0.0",
"@typescript-eslint/parser": "^4.2.0",
"@yarnpkg/eslint-config": "^0.1.0",
"@yarnpkg/fslib": "^2.1.0",
"@zkochan/cmd-shim": "^5.0.0",
"clipanion": "^2.4.4",
"debug": "^4.1.1",
"enquirer": "^2.3.6",
"eslint": "^7.10.0",
"eslint-plugin-arca": "^0.9.5",
"jest": "^25.1.0",
"semver": "^7.1.3",
"supports-color": "^7.1.0",
"tar": "^6.0.1",
"terser-webpack-plugin": "^3.1.0",
"ts-loader": "^8.0.2",
"ts-node": "^8.10.2",
"typescript": "^3.9.7",
"webpack": "next",
"webpack-cli": "^3.3.11"
},
"scripts": {
"build": "rm -rf dist && yarn webpack && yarn ts-node ./mkshims.ts",
"corepack": "ts-node ./sources/main.ts",
"prepack": "yarn build",
"postpack": "rm -rf dist shims"
},
"files": [
"dist",
"shims"
],
"publishConfig": {
"executableFiles": [
"./dist/npm.js",
"./dist/npx.js",
"./dist/pnpm.js",
"./dist/pnpx.js",
"./dist/yarn.js",
"./dist/yarnpkg.js",
"./dist/corepack.js",
"./shims/npm",
"./shims/npm.ps1",
"./shims/npx",
"./shims/npx.ps1",
"./shims/pnpm",
"./shims/pnpm.ps1",
"./shims/pnpx",
"./shims/pnpx.ps1",
"./shims/yarn",
"./shims/yarn.ps1",
"./shims/yarnpkg",
"./shims/yarnpkg.ps1"
]
}
}
12 changes: 12 additions & 0 deletions deps/corepack/shims/npm
@@ -0,0 +1,12 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../dist/npm.js" "$@"
else
exec node "$basedir/../dist/npm.js" "$@"
fi
28 changes: 28 additions & 0 deletions deps/corepack/shims/npm.ps1
@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../dist/npm.js" $args
} else {
& "$basedir/node$exe" "$basedir/../dist/npm.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../dist/npm.js" $args
} else {
& "node$exe" "$basedir/../dist/npm.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret
12 changes: 12 additions & 0 deletions deps/corepack/shims/npx
@@ -0,0 +1,12 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../dist/npx.js" "$@"
else
exec node "$basedir/../dist/npx.js" "$@"
fi
28 changes: 28 additions & 0 deletions deps/corepack/shims/npx.ps1
@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../dist/npx.js" $args
} else {
& "$basedir/node$exe" "$basedir/../dist/npx.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../dist/npx.js" $args
} else {
& "node$exe" "$basedir/../dist/npx.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret
12 changes: 12 additions & 0 deletions deps/corepack/shims/pnpm
@@ -0,0 +1,12 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../dist/pnpm.js" "$@"
else
exec node "$basedir/../dist/pnpm.js" "$@"
fi
28 changes: 28 additions & 0 deletions deps/corepack/shims/pnpm.ps1
@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../dist/pnpm.js" $args
} else {
& "$basedir/node$exe" "$basedir/../dist/pnpm.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../dist/pnpm.js" $args
} else {
& "node$exe" "$basedir/../dist/pnpm.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret
12 changes: 12 additions & 0 deletions deps/corepack/shims/pnpx
@@ -0,0 +1,12 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../dist/pnpx.js" "$@"
else
exec node "$basedir/../dist/pnpx.js" "$@"
fi

0 comments on commit 5f978e2

Please sign in to comment.