mirror of https://github.com/nodejs/node.git
deps: upgrade npm to 7.11.2
PR-URL: https://github.com/nodejs/node/pull/38475 Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com>
This commit is contained in:
parent
ff4e782c08
commit
c975dff3c0
|
@ -770,3 +770,5 @@ Yash Singh <saiansh2525@gmail.com>
|
|||
Danielle Church <dani.church@gmail.com>
|
||||
Seth Thomas <seth@emailseth.com>
|
||||
Andreas <andreas@bielk.se>
|
||||
Felipe Santos <felipecassiors@gmail.com>
|
||||
Luigi Pinca <luigipinca@gmail.com>
|
||||
|
|
|
@ -1,3 +1,125 @@
|
|||
## v7.11.2 (2021-04-29)
|
||||
|
||||
### BUG FIXES
|
||||
|
||||
* [`c371f183e`](https://github.com/npm/cli/commit/c371f183ebe833c2439e98b679f14e7a59f22c34)
|
||||
[#3137](https://github.com/npm/cli/issues/3137)
|
||||
[#3140](https://github.com/npm/cli/issues/3140)
|
||||
fix(ls): do not warn on missing optional deps
|
||||
([@isaacs](https://github.com/isaacs))
|
||||
* [`861f606c7`](https://github.com/npm/cli/commit/861f606c7609d177c644814a171581afbb72f6db)
|
||||
[#3156](https://github.com/npm/cli/issues/3156)
|
||||
fix(build): make prune rule work on case-sensitive file systems
|
||||
([@lpinca](https://github.com/lpinca))
|
||||
|
||||
### DEPENDENCIES
|
||||
|
||||
* [`fb79d89a0`](https://github.com/npm/cli/commit/fb79d89a07ef03e76633db275463f701d3dae42f)
|
||||
`tap@15.0.6`
|
||||
* [`ce3820043`](https://github.com/npm/cli/commit/ce38200437e9ed527df973794909b2699909bc9b)
|
||||
`@npmcli/arborist@2.4.1`
|
||||
* fix: prevent and eliminate unnecessary duplicates
|
||||
* fix: support resolvable partial intersecting peerSets
|
||||
|
||||
### DOCUMENTATION
|
||||
|
||||
* [`e479f1dac`](https://github.com/npm/cli/commit/e479f1dac9a7639304d20116583034861635b2b1)
|
||||
[#3146](https://github.com/npm/cli/issues/3146)
|
||||
mention `directories.bin` in `bin`
|
||||
([@felipecrs](https://github.com/felipecrs))
|
||||
|
||||
## v7.11.1 (2021-04-23)
|
||||
|
||||
### DEPENDENCIES
|
||||
|
||||
* [`7925cca24`](https://github.com/npm/cli/commit/7925cca24543d9e1a8297844b3e53e11057643ef)
|
||||
`pacote@11.3.3`:
|
||||
* fix(registry): normalize manfest
|
||||
* [`b61eac693`](https://github.com/npm/cli/commit/b61eac693df82c52b955e6c18ec4dcf4cedea8a3)
|
||||
[#3130](https://github.com/npm/cli/issues/3130)
|
||||
`@npmcli/config@2.2.0`
|
||||
* [`c74e67fc6`](https://github.com/npm/cli/commit/c74e67fc6572bb001d74c7486c05d211a0e03de8)
|
||||
[#3130](https://github.com/npm/cli/issues/3130)
|
||||
`npm-registry-fetch@10.1.1`
|
||||
|
||||
### DOCUMENTATION
|
||||
|
||||
* [`efdd7dd44`](https://github.com/npm/cli/commit/efdd7dd4427a0ee856c18aab1df2d3d30a307997)
|
||||
Remove unused and incorrectly documented `--always-auth` config definition
|
||||
([@isaacs](https://github.com/isaacs))
|
||||
|
||||
## v7.11.0 (2021-04-22)
|
||||
|
||||
### FEATURES
|
||||
|
||||
* [`4c1f16d2c`](https://github.com/npm/cli/commit/4c1f16d2c29a7a56c19b97f2820e6305a6075083)
|
||||
[#3095](https://github.com/npm/cli/issues/3095)
|
||||
feat(init): add workspaces support
|
||||
([@ruyadorno](https://github.com/ruyadorno))
|
||||
|
||||
### BUG FIXES
|
||||
|
||||
* [`42ca59eee`](https://github.com/npm/cli/commit/42ca59eeedd3e402aa1c606941f7f52864e6039b)
|
||||
[#3086](https://github.com/npm/cli/issues/3086)
|
||||
fix(ls): do not exit with error when all problems are extraneous deps
|
||||
([@nlf](https://github.com/nlf))
|
||||
* [`2aecec591`](https://github.com/npm/cli/commit/2aecec591df6866e27d0b17dc49cef8f7d738d77)
|
||||
[#2724](https://github.com/npm/cli/issues/2724)
|
||||
[#3119](https://github.com/npm/cli/issues/3119)
|
||||
fix(ls): make --long work when missing deps
|
||||
([@ruyadorno](https://github.com/ruyadorno))
|
||||
* [`42e0587a9`](https://github.com/npm/cli/commit/42e0587a9ea6940a5d5be5903370ad1113feef21)
|
||||
[#3115](https://github.com/npm/cli/issues/3115)
|
||||
fix(pack): refuse to pack invalid packument
|
||||
([@wraithgar](https://github.com/wraithgar))
|
||||
* [`1c4eff7b5`](https://github.com/npm/cli/commit/1c4eff7b513b8e84876818ede014d3ab19d203c6)
|
||||
[#3126](https://github.com/npm/cli/issues/3126)
|
||||
fix(logout): use isBasicAuth attribute
|
||||
([@wraithgar](https://github.com/wraithgar))
|
||||
### DOCUMENTATION
|
||||
|
||||
|
||||
* [`c93f1c39e`](https://github.com/npm/cli/commit/c93f1c39e326feff0857712a10ef6183fbafe1ab)
|
||||
[#3101](https://github.com/npm/cli/issues/3101)
|
||||
chore(docs): update view docs
|
||||
([@wraithgar](https://github.com/wraithgar))
|
||||
* [`c4ff4bc11`](https://github.com/npm/cli/commit/c4ff4bc113c3a5b6ee5d74ab0b1adee95169ed32)
|
||||
[npm/statusboard#313](https://github.com/npm/statusboard/issues/313)
|
||||
[#3109](https://github.com/npm/cli/issues/3109)
|
||||
fix(usage): fix refs to ws shorthand
|
||||
([@ruyadorno](https://github.com/ruyadorno))
|
||||
|
||||
|
||||
### DEPENDENCIES
|
||||
|
||||
* [`83166ebcc`](https://github.com/npm/cli/commit/83166ebcc4ba5e3bf215f08151437d96637f4f33)
|
||||
`npm-registry-fetch@10.1.0`
|
||||
* feat(auth): set isBasicAuth
|
||||
* [`e02bda6da`](https://github.com/npm/cli/commit/e02bda6da68b8e8f490bf270cb5d6adec81685ea)
|
||||
`npm-registry-fetch@10.0.0`
|
||||
* feat(auth) load/send based on URI, not registry
|
||||
* [`a0382deba`](https://github.com/npm/cli/commit/a0382deba346b09834e75db89e1fd4527f1f07dd)
|
||||
`@npmcli/run-script@1.8.5`
|
||||
* fix: windows ComSpec env variable name
|
||||
* [`7f82ef5a8`](https://github.com/npm/cli/commit/7f82ef5a84d70e28983ed43ba1d8aced0fb4ba45)
|
||||
`pacote@11.3.2`
|
||||
* [`35e49b94f`](https://github.com/npm/cli/commit/35e49b94fba478a63df6cc9b62816eafe5f1fbdd)
|
||||
`@npmcli/arborist@2.4.0`
|
||||
* [`95faf8ce6`](https://github.com/npm/cli/commit/95faf8ce6c007082a02c160977da194c08ee9d82)
|
||||
`libnpmaccess@4.0.2`
|
||||
* [`17fffc0e4`](https://github.com/npm/cli/commit/17fffc0e42b2a9e7b84691093e45ba511906cbfa)
|
||||
`libnpmhook@6.0.2`
|
||||
* [`1b5a213aa`](https://github.com/npm/cli/commit/1b5a213aaf39652661ba72ba2e8751f049b170fb)
|
||||
`libnpmorg@2.0.2`
|
||||
* [`9f83e6484`](https://github.com/npm/cli/commit/9f83e6484aa163d066f318df42ec89c8234b614e)
|
||||
`libnpmpublish@4.0.1`
|
||||
* [`251f788c5`](https://github.com/npm/cli/commit/251f788c554a198ab42682453fa5504f8abe93fe)
|
||||
`libnpmsearch@3.1.1`
|
||||
* [`35873a989`](https://github.com/npm/cli/commit/35873a989fe67041ddcf30a0a278ed77ace5ee3c)
|
||||
`libnpmteam@2.0.3`
|
||||
* [`23e12b4d8`](https://github.com/npm/cli/commit/23e12b4d8f63d765a48036e7bb08f53319c73304)
|
||||
`npm-profile@5.0.3`
|
||||
|
||||
## v7.10.0 (2021-04-15)
|
||||
|
||||
### FEATURES
|
||||
|
|
|
@ -7,7 +7,7 @@ description: Add a registry user account
|
|||
### Synopsis
|
||||
|
||||
```bash
|
||||
npm adduser [--registry=url] [--scope=@orgname] [--always-auth] [--auth-type=legacy]
|
||||
npm adduser [--registry=url] [--scope=@orgname] [--auth-type=legacy]
|
||||
|
||||
aliases: login, add-user
|
||||
```
|
||||
|
@ -58,23 +58,6 @@ npm adduser --registry=http://myregistry.example.com --scope=@myco
|
|||
This will set a registry for the given scope and login or create a user for
|
||||
that registry at the same time.
|
||||
|
||||
#### always-auth
|
||||
|
||||
Default: false
|
||||
|
||||
If specified, save configuration indicating that all requests to the given
|
||||
registry should include authorization information. Useful for private
|
||||
registries. Can be used with `--registry` and / or `--scope`, e.g.
|
||||
|
||||
```bash
|
||||
npm adduser --registry=http://private-registry.example.com --always-auth
|
||||
```
|
||||
|
||||
This will ensure that all requests to that registry (including for tarballs)
|
||||
include an authorization header. This setting may be necessary for use with
|
||||
private registries where metadata and package tarballs are stored on hosts with
|
||||
different hostnames. See `always-auth` in [`config`](/using-npm/config) for more details on always-auth. Registry-specific configuration of `always-auth` takes precedence over any global configuration.
|
||||
|
||||
#### auth-type
|
||||
|
||||
* Default: `'legacy'`
|
||||
|
|
|
@ -11,7 +11,7 @@ npm exec -- <pkg>[@<version>] [args...]
|
|||
npm exec --package=<pkg>[@<version>] -- <cmd> [args...]
|
||||
npm exec -c '<cmd> [args...]'
|
||||
npm exec --package=foo -c '<cmd> [args...]'
|
||||
npm exec [-ws] [-w <workspace-name] [args...]
|
||||
npm exec [--ws] [-w <workspace-name] [args...]
|
||||
|
||||
npx <pkg>[@<specifier>] [args...]
|
||||
npx -p <pkg>[@<specifier>] <cmd> [args...]
|
||||
|
@ -184,7 +184,7 @@ in this example we're using **eslint** to lint any js file found within each
|
|||
workspace folder:
|
||||
|
||||
```
|
||||
npm exec -ws -- eslint ./*.js
|
||||
npm exec --ws -- eslint ./*.js
|
||||
```
|
||||
|
||||
#### Filtering workspaces
|
||||
|
@ -275,7 +275,7 @@ children workspaces)
|
|||
|
||||
#### workspaces
|
||||
|
||||
* Alias: `-ws`
|
||||
* Alias: `--ws`
|
||||
* Type: Boolean
|
||||
* Default: `false`
|
||||
|
||||
|
@ -291,3 +291,4 @@ project.
|
|||
* [npm restart](/commands/npm-restart)
|
||||
* [npm stop](/commands/npm-stop)
|
||||
* [npm config](/commands/npm-config)
|
||||
* [npm workspaces](/using-npm/workspaces)
|
||||
|
|
|
@ -8,8 +8,9 @@ description: Create a package.json file
|
|||
|
||||
```bash
|
||||
npm init [--force|-f|--yes|-y|--scope]
|
||||
npm init <@scope> (same as `npx <@scope>/create`)
|
||||
npm init [<@scope>/]<name> (same as `npx [<@scope>/]create-<name>`)
|
||||
npm init <@scope> (same as `npm exec <@scope>/create`)
|
||||
npm init [<@scope>/]<name> (same as `npm exec [<@scope>/]create-<name>`)
|
||||
npm init [-w <dir>] [args...]
|
||||
```
|
||||
|
||||
### Description
|
||||
|
@ -18,19 +19,16 @@ npm init [<@scope>/]<name> (same as `npx [<@scope>/]create-<name>`)
|
|||
package.
|
||||
|
||||
`initializer` in this case is an npm package named `create-<initializer>`,
|
||||
which will be installed by [`npx`](https://npm.im/npx), and then have its
|
||||
which will be installed by [`npm-exec`](/commands/npm-exec), and then have its
|
||||
main bin executed -- presumably creating or updating `package.json` and
|
||||
running any other initialization-related operations.
|
||||
|
||||
The init command is transformed to a corresponding `npx` operation as
|
||||
The init command is transformed to a corresponding `npm exec` operation as
|
||||
follows:
|
||||
|
||||
* `npm init foo` -> `npx create-foo`
|
||||
* `npm init @usr/foo` -> `npx @usr/create-foo`
|
||||
* `npm init @usr` -> `npx @usr/create`
|
||||
|
||||
Any additional options will be passed directly to the command, so `npm init
|
||||
foo -- --hello` will map to `npx create-foo --hello`.
|
||||
* `npm init foo` -> `npm exec create-foo`
|
||||
* `npm init @usr/foo` -> `npm exec @usr/create-foo`
|
||||
* `npm init @usr` -> `npm exec @usr/create`
|
||||
|
||||
If the initializer is omitted (by just calling `npm init`), init will fall
|
||||
back to legacy init behavior. It will ask you a bunch of questions, and
|
||||
|
@ -40,6 +38,18 @@ strictly additive, so it will keep any fields and values that were already
|
|||
set. You can also use `-y`/`--yes` to skip the questionnaire altogether. If
|
||||
you pass `--scope`, it will create a scoped package.
|
||||
|
||||
#### Forwarding additional options
|
||||
|
||||
Any additional options will be passed directly to the command, so `npm init
|
||||
foo -- --hello` will map to `npm exec -- create-foo --hello`.
|
||||
|
||||
To better illustrate how options are forwarded, here's a more evolved
|
||||
example showing options passed to both the **npm cli** and a create package,
|
||||
both following commands are equivalent:
|
||||
|
||||
- `npm init foo -y --registry=<url> -- --hello -a`
|
||||
- `npm exec -y --registry=<url> -- create-foo --hello -a`
|
||||
|
||||
### Examples
|
||||
|
||||
Create a new React-based project using
|
||||
|
@ -71,6 +81,68 @@ Generate it without having it ask any questions:
|
|||
$ npm init -y
|
||||
```
|
||||
|
||||
### Workspaces support
|
||||
|
||||
It's possible to create a new workspace within your project by using the
|
||||
`workspace` config option. When using `npm init -w <dir>` the cli will
|
||||
create the folders and boilerplate expected while also adding a reference
|
||||
to your project `package.json` `"workspaces": []` property in order to make
|
||||
sure that new generated **workspace** is properly set up as such.
|
||||
|
||||
Given a project with no workspaces, e.g:
|
||||
|
||||
```
|
||||
.
|
||||
+-- package.json
|
||||
```
|
||||
|
||||
You may generate a new workspace using the legacy init:
|
||||
|
||||
```bash
|
||||
$ npm init -w packages/a
|
||||
```
|
||||
|
||||
That will generate a new folder and `package.json` file, while also updating
|
||||
your top-level `package.json` to add the reference to this new workspace:
|
||||
|
||||
```
|
||||
.
|
||||
+-- package.json
|
||||
`-- packages
|
||||
`-- a
|
||||
`-- package.json
|
||||
```
|
||||
|
||||
The workspaces init also supports the `npm init <initializer> -w <dir>`
|
||||
syntax, following the same set of rules explained earlier in the initial
|
||||
**Description** section of this page. Similar to the previous example of
|
||||
creating a new React-based project using
|
||||
[`create-react-app`](https://npm.im/create-react-app), the following syntax
|
||||
will make sure to create the new react app as a nested **workspace** within your
|
||||
project and configure your `package.json` to recognize it as such:
|
||||
|
||||
```bash
|
||||
npm init -w packages/my-react-app react-app .
|
||||
```
|
||||
|
||||
This will make sure to generate your react app as expected, one important
|
||||
consideration to have in mind is that `npm exec` is going to be run in the
|
||||
context of the newly created folder for that workspace, and that's the reason
|
||||
why in this example the initializer uses the initializer name followed with a
|
||||
dot to represent the current directory in that context, e.g: `react-app .`:
|
||||
|
||||
```
|
||||
.
|
||||
+-- package.json
|
||||
`-- packages
|
||||
+-- a
|
||||
| `-- package.json
|
||||
`-- my-react-app
|
||||
+-- README
|
||||
+-- package.json
|
||||
`-- ...
|
||||
```
|
||||
|
||||
### A note on caching
|
||||
|
||||
The npm cli utilizes its internal package cache when using the package
|
||||
|
@ -93,6 +165,33 @@ requested from the server. To force full offline mode, use `offline`.
|
|||
Forces full offline mode. Any packages not locally cached will result in
|
||||
an error.
|
||||
|
||||
#### workspace
|
||||
|
||||
* Alias: `-w`
|
||||
* Type: Array
|
||||
* Default: `[]`
|
||||
|
||||
Enable running `npm init` in the context of workspaces, creating any missing
|
||||
folders, generating files and adding/updating the `"workspaces"` property of
|
||||
the project `package.json`.
|
||||
|
||||
the provided names or paths provided.
|
||||
|
||||
Valid values for the `workspace` config are either:
|
||||
- Workspace names
|
||||
- Path to a workspace directory
|
||||
- Path to a parent workspace directory (will result to selecting all of the
|
||||
children workspaces)
|
||||
|
||||
#### workspaces
|
||||
|
||||
* Alias: `-ws`
|
||||
* Type: Boolean
|
||||
* Default: `false`
|
||||
|
||||
Run `npm init` in the context of all configured workspaces for the
|
||||
current project.
|
||||
|
||||
### See Also
|
||||
|
||||
* [init-package-json module](http://npm.im/init-package-json)
|
||||
|
@ -100,3 +199,4 @@ an error.
|
|||
* [npm version](/commands/npm-version)
|
||||
* [npm scope](/using-npm/scope)
|
||||
* [npm exec](/commands/npm-exec)
|
||||
* [npm workspaces](/using-npm/workspaces)
|
||||
|
|
|
@ -189,7 +189,7 @@ children workspaces)
|
|||
|
||||
#### workspaces
|
||||
|
||||
* Alias: `-ws`
|
||||
* Alias: `--ws`
|
||||
* Type: Boolean
|
||||
* Default: `false`
|
||||
|
||||
|
@ -204,3 +204,4 @@ project.
|
|||
* [npm restart](/commands/npm-restart)
|
||||
* [npm stop](/commands/npm-stop)
|
||||
* [npm config](/commands/npm-config)
|
||||
* [npm workspaces](/using-npm/workspaces)
|
||||
|
|
|
@ -94,6 +94,25 @@ this:
|
|||
npm view connect versions
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
#### json
|
||||
|
||||
Show information in JSON format. See [`Output`](#output) below.
|
||||
|
||||
#### workspaces
|
||||
|
||||
Enables workspaces context while searching the `package.json` in the
|
||||
current folder. Information about packages named in each workspace will
|
||||
be viewed.
|
||||
|
||||
#### workspace
|
||||
|
||||
Enables workspaces context and limits results to only those specified by
|
||||
this config item. Only the information about packages named in the
|
||||
workspaces given here will be viewed.
|
||||
|
||||
|
||||
### Output
|
||||
|
||||
If only a single string field for a single version is output, then it
|
||||
|
|
|
@ -386,6 +386,8 @@ Please make sure that your file(s) referenced in `bin` starts with
|
|||
`#!/usr/bin/env node`, otherwise the scripts are started without the node
|
||||
executable!
|
||||
|
||||
Note that you can also set the executable files using [directories.bin](#directoriesbin).
|
||||
|
||||
### man
|
||||
|
||||
Specify either a single file or an array of filenames to put in place for
|
||||
|
|
|
@ -165,14 +165,6 @@ upon by the current project.
|
|||
Prevents throwing an error when `npm version` is used to set the new version
|
||||
to the same value as the current version.
|
||||
|
||||
#### `always-auth`
|
||||
|
||||
* Default: false
|
||||
* Type: Boolean
|
||||
|
||||
Force npm to always require authentication when accessing the registry, even
|
||||
for `GET` requests.
|
||||
|
||||
#### `audit`
|
||||
|
||||
* Default: true
|
||||
|
@ -1087,7 +1079,8 @@ installation of packages specified according to the pattern
|
|||
* Default: '/bin/sh' on POSIX systems, 'cmd.exe' on Windows
|
||||
* Type: null or String
|
||||
|
||||
The shell to use for scripts run with the `npm run` command.
|
||||
The shell to use for scripts run with the `npm exec`, `npm run` and `npm
|
||||
init <pkg>` commands.
|
||||
|
||||
#### `searchexclude`
|
||||
|
||||
|
|
|
@ -141,11 +141,11 @@ npm command-line interface
|
|||
|
||||
<section id="table_of_contents">
|
||||
<h2 id="table-of-contents">Table of contents</h2>
|
||||
<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#configuration">Configuration</a></li><ul><li><a href="#registry">registry</a></li><li><a href="#scope">scope</a></li><li><a href="#always-auth">always-auth</a></li><li><a href="#auth-type">auth-type</a></li></ul><li><a href="#see-also">See Also</a></li></ul></div>
|
||||
<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#configuration">Configuration</a></li><ul><li><a href="#registry">registry</a></li><li><a href="#scope">scope</a></li><li><a href="#auth-type">auth-type</a></li></ul><li><a href="#see-also">See Also</a></li></ul></div>
|
||||
</section>
|
||||
|
||||
<div id="_content"><h3 id="synopsis">Synopsis</h3>
|
||||
<pre lang="bash"><code>npm adduser [--registry=url] [--scope=@orgname] [--always-auth] [--auth-type=legacy]
|
||||
<pre lang="bash"><code>npm adduser [--registry=url] [--scope=@orgname] [--auth-type=legacy]
|
||||
|
||||
aliases: login, add-user
|
||||
</code></pre>
|
||||
|
@ -177,17 +177,6 @@ e.g.</p>
|
|||
</code></pre>
|
||||
<p>This will set a registry for the given scope and login or create a user for
|
||||
that registry at the same time.</p>
|
||||
<h4 id="always-auth">always-auth</h4>
|
||||
<p>Default: false</p>
|
||||
<p>If specified, save configuration indicating that all requests to the given
|
||||
registry should include authorization information. Useful for private
|
||||
registries. Can be used with <code>--registry</code> and / or <code>--scope</code>, e.g.</p>
|
||||
<pre lang="bash"><code>npm adduser --registry=http://private-registry.example.com --always-auth
|
||||
</code></pre>
|
||||
<p>This will ensure that all requests to that registry (including for tarballs)
|
||||
include an authorization header. This setting may be necessary for use with
|
||||
private registries where metadata and package tarballs are stored on hosts with
|
||||
different hostnames. See <code>always-auth</code> in <a href="../using-npm/config.html"><code>config</code></a> for more details on always-auth. Registry-specific configuration of <code>always-auth</code> takes precedence over any global configuration.</p>
|
||||
<h4 id="auth-type">auth-type</h4>
|
||||
<ul>
|
||||
<li>Default: <code>'legacy'</code></li>
|
||||
|
|
|
@ -149,7 +149,7 @@ npm command-line interface
|
|||
npm exec --package=<pkg>[@<version>] -- <cmd> [args...]
|
||||
npm exec -c '<cmd> [args...]'
|
||||
npm exec --package=foo -c '<cmd> [args...]'
|
||||
npm exec [-ws] [-w <workspace-name] [args...]
|
||||
npm exec [--ws] [-w <workspace-name] [args...]
|
||||
|
||||
npx <pkg>[@<specifier>] [args...]
|
||||
npx -p <pkg>[@<specifier>] <cmd> [args...]
|
||||
|
@ -276,7 +276,7 @@ at a time.</p>
|
|||
the configured workspaces when using the <code>workspaces</code> configuration options,
|
||||
in this example we’re using <strong>eslint</strong> to lint any js file found within each
|
||||
workspace folder:</p>
|
||||
<pre><code>npm exec -ws -- eslint ./*.js
|
||||
<pre><code>npm exec --ws -- eslint ./*.js
|
||||
</code></pre>
|
||||
<h4 id="filtering-workspaces">Filtering workspaces</h4>
|
||||
<p>It’s also possible to execute a command in a single workspace using the
|
||||
|
@ -348,7 +348,7 @@ children workspaces)</li>
|
|||
</ul>
|
||||
<h4 id="workspaces">workspaces</h4>
|
||||
<ul>
|
||||
<li>Alias: <code>-ws</code></li>
|
||||
<li>Alias: <code>--ws</code></li>
|
||||
<li>Type: Boolean</li>
|
||||
<li>Default: <code>false</code></li>
|
||||
</ul>
|
||||
|
@ -363,6 +363,7 @@ project.</p>
|
|||
<li><a href="../commands/npm-restart.html">npm restart</a></li>
|
||||
<li><a href="../commands/npm-stop.html">npm stop</a></li>
|
||||
<li><a href="../commands/npm-config.html">npm config</a></li>
|
||||
<li><a href="../using-npm/workspaces.html">npm workspaces</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -141,29 +141,29 @@ npm command-line interface
|
|||
|
||||
<section id="table_of_contents">
|
||||
<h2 id="table-of-contents">Table of contents</h2>
|
||||
<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#examples">Examples</a></li><li><a href="#a-note-on-caching">A note on caching</a></li><ul><li><a href="#prefer-online">prefer-online</a></li><li><a href="#prefer-offline">prefer-offline</a></li><li><a href="#offline">offline</a></li></ul><li><a href="#see-also">See Also</a></li></ul></div>
|
||||
<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><ul><li><a href="#forwarding-additional-options">Forwarding additional options</a></li></ul><li><a href="#examples">Examples</a></li><li><a href="#workspaces-support">Workspaces support</a></li><li><a href="#a-note-on-caching">A note on caching</a></li><ul><li><a href="#prefer-online">prefer-online</a></li><li><a href="#prefer-offline">prefer-offline</a></li><li><a href="#offline">offline</a></li><li><a href="#workspace">workspace</a></li><li><a href="#workspaces">workspaces</a></li></ul><li><a href="#see-also">See Also</a></li></ul></div>
|
||||
</section>
|
||||
|
||||
<div id="_content"><h3 id="synopsis">Synopsis</h3>
|
||||
<pre lang="bash"><code>npm init [--force|-f|--yes|-y|--scope]
|
||||
npm init <@scope> (same as `npx <@scope>/create`)
|
||||
npm init [<@scope>/]<name> (same as `npx [<@scope>/]create-<name>`)
|
||||
npm init <@scope> (same as `npm exec <@scope>/create`)
|
||||
npm init [<@scope>/]<name> (same as `npm exec [<@scope>/]create-<name>`)
|
||||
npm init [-w <dir>] [args...]
|
||||
</code></pre>
|
||||
<h3 id="description">Description</h3>
|
||||
<p><code>npm init <initializer></code> can be used to set up a new or existing npm
|
||||
package.</p>
|
||||
<p><code>initializer</code> in this case is an npm package named <code>create-<initializer></code>,
|
||||
which will be installed by <a href="https://npm.im/npx"><code>npx</code></a>, and then have its
|
||||
which will be installed by <a href="../commands/npm-exec.html"><code>npm-exec</code></a>, and then have its
|
||||
main bin executed – presumably creating or updating <code>package.json</code> and
|
||||
running any other initialization-related operations.</p>
|
||||
<p>The init command is transformed to a corresponding <code>npx</code> operation as
|
||||
<p>The init command is transformed to a corresponding <code>npm exec</code> operation as
|
||||
follows:</p>
|
||||
<ul>
|
||||
<li><code>npm init foo</code> -> <code>npx create-foo</code></li>
|
||||
<li><code>npm init @usr/foo</code> -> <code>npx @usr/create-foo</code></li>
|
||||
<li><code>npm init @usr</code> -> <code>npx @usr/create</code></li>
|
||||
<li><code>npm init foo</code> -> <code>npm exec create-foo</code></li>
|
||||
<li><code>npm init @usr/foo</code> -> <code>npm exec @usr/create-foo</code></li>
|
||||
<li><code>npm init @usr</code> -> <code>npm exec @usr/create</code></li>
|
||||
</ul>
|
||||
<p>Any additional options will be passed directly to the command, so <code>npm init foo -- --hello</code> will map to <code>npx create-foo --hello</code>.</p>
|
||||
<p>If the initializer is omitted (by just calling <code>npm init</code>), init will fall
|
||||
back to legacy init behavior. It will ask you a bunch of questions, and
|
||||
then write a package.json for you. It will attempt to make reasonable
|
||||
|
@ -171,6 +171,15 @@ guesses based on existing fields, dependencies, and options selected. It is
|
|||
strictly additive, so it will keep any fields and values that were already
|
||||
set. You can also use <code>-y</code>/<code>--yes</code> to skip the questionnaire altogether. If
|
||||
you pass <code>--scope</code>, it will create a scoped package.</p>
|
||||
<h4 id="forwarding-additional-options">Forwarding additional options</h4>
|
||||
<p>Any additional options will be passed directly to the command, so <code>npm init foo -- --hello</code> will map to <code>npm exec -- create-foo --hello</code>.</p>
|
||||
<p>To better illustrate how options are forwarded, here’s a more evolved
|
||||
example showing options passed to both the <strong>npm cli</strong> and a create package,
|
||||
both following commands are equivalent:</p>
|
||||
<ul>
|
||||
<li><code>npm init foo -y --registry=<url> -- --hello -a</code></li>
|
||||
<li><code>npm exec -y --registry=<url> -- create-foo --hello -a</code></li>
|
||||
</ul>
|
||||
<h3 id="examples">Examples</h3>
|
||||
<p>Create a new React-based project using
|
||||
<a href="https://npm.im/create-react-app"><code>create-react-app</code></a>:</p>
|
||||
|
@ -189,6 +198,51 @@ $ npm init
|
|||
<p>Generate it without having it ask any questions:</p>
|
||||
<pre lang="bash"><code>$ npm init -y
|
||||
</code></pre>
|
||||
<h3 id="workspaces-support">Workspaces support</h3>
|
||||
<p>It’s possible to create a new workspace within your project by using the
|
||||
<code>workspace</code> config option. When using <code>npm init -w <dir></code> the cli will
|
||||
create the folders and boilerplate expected while also adding a reference
|
||||
to your project <code>package.json</code> <code>"workspaces": []</code> property in order to make
|
||||
sure that new generated <strong>workspace</strong> is properly set up as such.</p>
|
||||
<p>Given a project with no workspaces, e.g:</p>
|
||||
<pre><code>.
|
||||
+-- package.json
|
||||
</code></pre>
|
||||
<p>You may generate a new workspace using the legacy init:</p>
|
||||
<pre lang="bash"><code>$ npm init -w packages/a
|
||||
</code></pre>
|
||||
<p>That will generate a new folder and <code>package.json</code> file, while also updating
|
||||
your top-level <code>package.json</code> to add the reference to this new workspace:</p>
|
||||
<pre><code>.
|
||||
+-- package.json
|
||||
`-- packages
|
||||
`-- a
|
||||
`-- package.json
|
||||
</code></pre>
|
||||
<p>The workspaces init also supports the <code>npm init <initializer> -w <dir></code>
|
||||
syntax, following the same set of rules explained earlier in the initial
|
||||
<strong>Description</strong> section of this page. Similar to the previous example of
|
||||
creating a new React-based project using
|
||||
<a href="https://npm.im/create-react-app"><code>create-react-app</code></a>, the following syntax
|
||||
will make sure to create the new react app as a nested <strong>workspace</strong> within your
|
||||
project and configure your <code>package.json</code> to recognize it as such:</p>
|
||||
<pre lang="bash"><code>npm init -w packages/my-react-app react-app .
|
||||
</code></pre>
|
||||
<p>This will make sure to generate your react app as expected, one important
|
||||
consideration to have in mind is that <code>npm exec</code> is going to be run in the
|
||||
context of the newly created folder for that workspace, and that’s the reason
|
||||
why in this example the initializer uses the initializer name followed with a
|
||||
dot to represent the current directory in that context, e.g: <code>react-app .</code>:</p>
|
||||
<pre><code>.
|
||||
+-- package.json
|
||||
`-- packages
|
||||
+-- a
|
||||
| `-- package.json
|
||||
`-- my-react-app
|
||||
+-- README
|
||||
+-- package.json
|
||||
`-- ...
|
||||
</code></pre>
|
||||
<h3 id="a-note-on-caching">A note on caching</h3>
|
||||
<p>The npm cli utilizes its internal package cache when using the package
|
||||
name specified. You can use the following to change how and when the
|
||||
|
@ -203,6 +257,31 @@ requested from the server. To force full offline mode, use <code>offline</code>.
|
|||
<h4 id="offline">offline</h4>
|
||||
<p>Forces full offline mode. Any packages not locally cached will result in
|
||||
an error.</p>
|
||||
<h4 id="workspace">workspace</h4>
|
||||
<ul>
|
||||
<li>Alias: <code>-w</code></li>
|
||||
<li>Type: Array</li>
|
||||
<li>Default: <code>[]</code></li>
|
||||
</ul>
|
||||
<p>Enable running <code>npm init</code> in the context of workspaces, creating any missing
|
||||
folders, generating files and adding/updating the <code>"workspaces"</code> property of
|
||||
the project <code>package.json</code>.</p>
|
||||
<p>the provided names or paths provided.</p>
|
||||
<p>Valid values for the <code>workspace</code> config are either:</p>
|
||||
<ul>
|
||||
<li>Workspace names</li>
|
||||
<li>Path to a workspace directory</li>
|
||||
<li>Path to a parent workspace directory (will result to selecting all of the
|
||||
children workspaces)</li>
|
||||
</ul>
|
||||
<h4 id="workspaces">workspaces</h4>
|
||||
<ul>
|
||||
<li>Alias: <code>-ws</code></li>
|
||||
<li>Type: Boolean</li>
|
||||
<li>Default: <code>false</code></li>
|
||||
</ul>
|
||||
<p>Run <code>npm init</code> in the context of all configured workspaces for the
|
||||
current project.</p>
|
||||
<h3 id="see-also">See Also</h3>
|
||||
<ul>
|
||||
<li><a href="http://npm.im/init-package-json">init-package-json module</a></li>
|
||||
|
@ -210,6 +289,7 @@ an error.</p>
|
|||
<li><a href="../commands/npm-version.html">npm version</a></li>
|
||||
<li><a href="../using-npm/scope.html">npm scope</a></li>
|
||||
<li><a href="../commands/npm-exec.html">npm exec</a></li>
|
||||
<li><a href="../using-npm/workspaces.html">npm workspaces</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ tree at all, use <a href="../commands/npm-explain.html"><code>npm explain</code>
|
|||
the results to only the paths to the packages named. Note that nested
|
||||
packages will <em>also</em> show the paths to the specified packages. For
|
||||
example, running <code>npm ls promzard</code> in npm’s source tree will show:</p>
|
||||
<pre lang="bash"><code>npm@7.10.0 /path/to/npm
|
||||
<pre lang="bash"><code>npm@7.11.2 /path/to/npm
|
||||
└─┬ init-package-json@0.0.4
|
||||
└── promzard@0.1.5
|
||||
</code></pre>
|
||||
|
|
|
@ -282,7 +282,7 @@ children workspaces)</li>
|
|||
</ul>
|
||||
<h4 id="workspaces">workspaces</h4>
|
||||
<ul>
|
||||
<li>Alias: <code>-ws</code></li>
|
||||
<li>Alias: <code>--ws</code></li>
|
||||
<li>Type: Boolean</li>
|
||||
<li>Default: <code>false</code></li>
|
||||
</ul>
|
||||
|
@ -296,6 +296,7 @@ project.</p>
|
|||
<li><a href="../commands/npm-restart.html">npm restart</a></li>
|
||||
<li><a href="../commands/npm-stop.html">npm stop</a></li>
|
||||
<li><a href="../commands/npm-config.html">npm config</a></li>
|
||||
<li><a href="../using-npm/workspaces.html">npm workspaces</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ npm command-line interface
|
|||
|
||||
<section id="table_of_contents">
|
||||
<h2 id="table-of-contents">Table of contents</h2>
|
||||
<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#output">Output</a></li><li><a href="#see-also">See Also</a></li></ul></div>
|
||||
<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#configuration">Configuration</a></li><ul><li><a href="#json">json</a></li><li><a href="#workspaces">workspaces</a></li><li><a href="#workspace">workspace</a></li></ul><li><a href="#output">Output</a></li><li><a href="#see-also">See Also</a></li></ul></div>
|
||||
</section>
|
||||
|
||||
<div id="_content"><h3 id="synopsis">Synopsis</h3>
|
||||
|
@ -198,6 +198,17 @@ was required by each matching version of <code>yui3</code>:</p>
|
|||
this:</p>
|
||||
<pre lang="bash"><code>npm view connect versions
|
||||
</code></pre>
|
||||
<h3 id="configuration">Configuration</h3>
|
||||
<h4 id="json">json</h4>
|
||||
<p>Show information in JSON format. See <a href="#output"><code>Output</code></a> below.</p>
|
||||
<h4 id="workspaces">workspaces</h4>
|
||||
<p>Enables workspaces context while searching the <code>package.json</code> in the
|
||||
current folder. Information about packages named in each workspace will
|
||||
be viewed.</p>
|
||||
<h4 id="workspace">workspace</h4>
|
||||
<p>Enables workspaces context and limits results to only those specified by
|
||||
this config item. Only the information about packages named in the
|
||||
workspaces given here will be viewed.</p>
|
||||
<h3 id="output">Output</h3>
|
||||
<p>If only a single string field for a single version is output, then it
|
||||
will not be colorized or quoted, to enable piping the output to
|
||||
|
|
|
@ -148,7 +148,7 @@ npm command-line interface
|
|||
<pre lang="bash"><code>npm <command> [args]
|
||||
</code></pre>
|
||||
<h3 id="version">Version</h3>
|
||||
<p>7.10.0</p>
|
||||
<p>7.11.2</p>
|
||||
<h3 id="description">Description</h3>
|
||||
<p>npm is the package manager for the Node JavaScript platform. It puts
|
||||
modules in place so that node can find them, and manages dependency
|
||||
|
|
|
@ -435,6 +435,7 @@ package, then you can just supply it as a string. For example:</p>
|
|||
<p>Please make sure that your file(s) referenced in <code>bin</code> starts with
|
||||
<code>#!/usr/bin/env node</code>, otherwise the scripts are started without the node
|
||||
executable!</p>
|
||||
<p>Note that you can also set the executable files using <a href="#directoriesbin">directories.bin</a>.</p>
|
||||
<h3 id="man">man</h3>
|
||||
<p>Specify either a single file or an array of filenames to put in place for
|
||||
the <code>man</code> program to find.</p>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,7 +21,6 @@ class AddUser extends BaseCommand {
|
|||
return [
|
||||
'registry',
|
||||
'scope',
|
||||
'always-auth',
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,6 @@
|
|||
const { promisify } = require('util')
|
||||
const read = promisify(require('read'))
|
||||
const chalk = require('chalk')
|
||||
const mkdirp = require('mkdirp-infer-owner')
|
||||
const readPackageJson = require('read-package-json-fast')
|
||||
const Arborist = require('@npmcli/arborist')
|
||||
const runScript = require('@npmcli/run-script')
|
||||
const { resolve, delimiter } = require('path')
|
||||
const ciDetect = require('@npmcli/ci-detect')
|
||||
const crypto = require('crypto')
|
||||
const pacote = require('pacote')
|
||||
const npa = require('npm-package-arg')
|
||||
const fileExists = require('./utils/file-exists.js')
|
||||
const PATH = require('./utils/path.js')
|
||||
const libexec = require('libnpmexec')
|
||||
const BaseCommand = require('./base-command.js')
|
||||
const getLocationMsg = require('./exec/get-workspace-location-msg.js')
|
||||
const getWorkspaces = require('./workspaces/get-workspaces.js')
|
||||
|
||||
// it's like this:
|
||||
|
@ -40,13 +28,6 @@ const getWorkspaces = require('./workspaces/get-workspaces.js')
|
|||
// runScript({ pkg, event: 'npx', ... })
|
||||
// process.env.npm_lifecycle_event = 'npx'
|
||||
|
||||
const nocolor = {
|
||||
reset: s => s,
|
||||
bold: s => s,
|
||||
dim: s => s,
|
||||
green: s => s,
|
||||
}
|
||||
|
||||
class Exec extends BaseCommand {
|
||||
/* istanbul ignore next - see test/lib/load-all-commands.js */
|
||||
static get description () {
|
||||
|
@ -86,276 +67,50 @@ class Exec extends BaseCommand {
|
|||
// When commands go async and we can dump the boilerplate exec methods this
|
||||
// can be named correctly
|
||||
async _exec (_args, { locationMsg, path, runPath }) {
|
||||
const args = [..._args]
|
||||
const cache = this.npm.config.get('cache')
|
||||
const call = this.npm.config.get('call')
|
||||
const shell = this.npm.config.get('shell')
|
||||
// dereferenced because we manipulate it later
|
||||
const packages = [...this.npm.config.get('package')]
|
||||
const color = this.npm.config.get('color')
|
||||
const {
|
||||
flatOptions,
|
||||
localBin,
|
||||
log,
|
||||
globalBin,
|
||||
output,
|
||||
} = this.npm
|
||||
const scriptShell = this.npm.config.get('script-shell') || undefined
|
||||
const packages = this.npm.config.get('package')
|
||||
const yes = this.npm.config.get('yes')
|
||||
|
||||
if (call && _args.length)
|
||||
throw this.usage
|
||||
|
||||
const args = [..._args]
|
||||
const pathArr = [...PATH]
|
||||
|
||||
// nothing to maybe install, skip the arborist dance
|
||||
if (!call && !args.length && !packages.length) {
|
||||
return await this.run({
|
||||
args,
|
||||
call,
|
||||
locationMsg,
|
||||
shell,
|
||||
path,
|
||||
pathArr,
|
||||
runPath,
|
||||
})
|
||||
}
|
||||
|
||||
const needPackageCommandSwap = args.length && !packages.length
|
||||
// if there's an argument and no package has been explicitly asked for
|
||||
// check the local and global bin paths for a binary named the same as
|
||||
// the argument and run it if it exists, otherwise fall through to
|
||||
// the behavior of treating the single argument as a package name
|
||||
if (needPackageCommandSwap) {
|
||||
let binExists = false
|
||||
if (await fileExists(`${this.npm.localBin}/${args[0]}`)) {
|
||||
pathArr.unshift(this.npm.localBin)
|
||||
binExists = true
|
||||
} else if (await fileExists(`${this.npm.globalBin}/${args[0]}`)) {
|
||||
pathArr.unshift(this.npm.globalBin)
|
||||
binExists = true
|
||||
}
|
||||
|
||||
if (binExists) {
|
||||
return await this.run({
|
||||
args,
|
||||
call,
|
||||
locationMsg,
|
||||
path,
|
||||
pathArr,
|
||||
runPath,
|
||||
shell,
|
||||
})
|
||||
}
|
||||
|
||||
packages.push(args[0])
|
||||
}
|
||||
|
||||
// If we do `npm exec foo`, and have a `foo` locally, then we'll
|
||||
// always use that, so we don't really need to fetch the manifest.
|
||||
// So: run npa on each packages entry, and if it is a name with a
|
||||
// rawSpec==='', then try to readPackageJson at
|
||||
// node_modules/${name}/package.json, and only pacote fetch if
|
||||
// that fails.
|
||||
const manis = await Promise.all(packages.map(async p => {
|
||||
const spec = npa(p, path)
|
||||
if (spec.type === 'tag' && spec.rawSpec === '') {
|
||||
// fall through to the pacote.manifest() approach
|
||||
try {
|
||||
const pj = resolve(path, 'node_modules', spec.name)
|
||||
return await readPackageJson(pj)
|
||||
} catch (er) {}
|
||||
}
|
||||
// Force preferOnline to true so we are making sure to pull in the latest
|
||||
// This is especially useful if the user didn't give us a version, and
|
||||
// they expect to be running @latest
|
||||
return await pacote.manifest(p, {
|
||||
...this.npm.flatOptions,
|
||||
preferOnline: true,
|
||||
})
|
||||
}))
|
||||
|
||||
if (needPackageCommandSwap)
|
||||
args[0] = this.getBinFromManifest(manis[0])
|
||||
|
||||
// figure out whether we need to install stuff, or if local is fine
|
||||
const localArb = new Arborist({
|
||||
...this.npm.flatOptions,
|
||||
path,
|
||||
})
|
||||
const tree = await localArb.loadActual()
|
||||
|
||||
// do we have all the packages in manifest list?
|
||||
const needInstall = manis.some(mani => this.manifestMissing(tree, mani))
|
||||
|
||||
if (needInstall) {
|
||||
const installDir = this.cacheInstallDir(packages)
|
||||
await mkdirp(installDir)
|
||||
const arb = new Arborist({
|
||||
...this.npm.flatOptions,
|
||||
log: this.npm.log,
|
||||
path: installDir,
|
||||
})
|
||||
const tree = await arb.loadActual()
|
||||
|
||||
// at this point, we have to ensure that we get the exact same
|
||||
// version, because it's something that has only ever been installed
|
||||
// by npm exec in the cache install directory
|
||||
const add = manis.filter(mani => this.manifestMissing(tree, {
|
||||
...mani,
|
||||
_from: `${mani.name}@${mani.version}`,
|
||||
}))
|
||||
.map(mani => mani._from)
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
|
||||
// no need to install if already present
|
||||
if (add.length) {
|
||||
if (!this.npm.config.get('yes')) {
|
||||
// set -n to always say no
|
||||
if (this.npm.config.get('yes') === false)
|
||||
throw new Error('canceled')
|
||||
|
||||
if (!process.stdin.isTTY || ciDetect()) {
|
||||
this.npm.log.warn('exec', `The following package${
|
||||
add.length === 1 ? ' was' : 's were'
|
||||
} not found and will be installed: ${
|
||||
add.map((pkg) => pkg.replace(/@$/, '')).join(', ')
|
||||
}`)
|
||||
} else {
|
||||
const addList = add.map(a => ` ${a.replace(/@$/, '')}`)
|
||||
.join('\n') + '\n'
|
||||
const prompt = `Need to install the following packages:\n${
|
||||
addList
|
||||
}Ok to proceed? `
|
||||
const confirm = await read({ prompt, default: 'y' })
|
||||
if (confirm.trim().toLowerCase().charAt(0) !== 'y')
|
||||
throw new Error('canceled')
|
||||
}
|
||||
}
|
||||
await arb.reify({
|
||||
...this.npm.flatOptions,
|
||||
log: this.npm.log,
|
||||
add,
|
||||
})
|
||||
}
|
||||
pathArr.unshift(resolve(installDir, 'node_modules/.bin'))
|
||||
}
|
||||
|
||||
return await this.run({
|
||||
return libexec({
|
||||
...flatOptions,
|
||||
args,
|
||||
call,
|
||||
cache,
|
||||
color,
|
||||
localBin,
|
||||
locationMsg,
|
||||
log,
|
||||
globalBin,
|
||||
output,
|
||||
packages,
|
||||
path,
|
||||
pathArr,
|
||||
runPath,
|
||||
shell,
|
||||
scriptShell,
|
||||
yes,
|
||||
})
|
||||
}
|
||||
|
||||
async run ({ args, call, locationMsg, path, pathArr, runPath, shell }) {
|
||||
// turn list of args into command string
|
||||
const script = call || args.shift() || shell
|
||||
|
||||
// do the fakey runScript dance
|
||||
// still should work if no package.json in cwd
|
||||
const realPkg = await readPackageJson(`${path}/package.json`)
|
||||
.catch(() => ({}))
|
||||
const pkg = {
|
||||
...realPkg,
|
||||
scripts: {
|
||||
...(realPkg.scripts || {}),
|
||||
npx: script,
|
||||
},
|
||||
}
|
||||
|
||||
this.npm.log.disableProgress()
|
||||
try {
|
||||
if (script === shell) {
|
||||
if (process.stdin.isTTY) {
|
||||
if (ciDetect())
|
||||
return this.npm.log.warn('exec', 'Interactive mode disabled in CI environment')
|
||||
|
||||
const color = this.npm.config.get('color')
|
||||
const colorize = color ? chalk : nocolor
|
||||
|
||||
locationMsg = locationMsg || ` at location:\n${colorize.dim(runPath)}`
|
||||
|
||||
this.npm.output(`${
|
||||
colorize.reset('\nEntering npm script environment')
|
||||
}${
|
||||
colorize.reset(locationMsg)
|
||||
}${
|
||||
colorize.bold('\nType \'exit\' or ^D when finished\n')
|
||||
}`)
|
||||
}
|
||||
}
|
||||
return await runScript({
|
||||
...this.npm.flatOptions,
|
||||
pkg,
|
||||
banner: false,
|
||||
// we always run in cwd, not --prefix
|
||||
path: runPath,
|
||||
stdioString: true,
|
||||
event: 'npx',
|
||||
args,
|
||||
env: {
|
||||
PATH: pathArr.join(delimiter),
|
||||
},
|
||||
stdio: 'inherit',
|
||||
})
|
||||
} finally {
|
||||
this.npm.log.enableProgress()
|
||||
}
|
||||
}
|
||||
|
||||
manifestMissing (tree, mani) {
|
||||
// if the tree doesn't have a child by that name/version, return true
|
||||
// true means we need to install it
|
||||
const child = tree.children.get(mani.name)
|
||||
// if no child, we have to load it
|
||||
if (!child)
|
||||
return true
|
||||
|
||||
// if no version/tag specified, allow whatever's there
|
||||
if (mani._from === `${mani.name}@`)
|
||||
return false
|
||||
|
||||
// otherwise the version has to match what we WOULD get
|
||||
return child.version !== mani.version
|
||||
}
|
||||
|
||||
getBinFromManifest (mani) {
|
||||
// if we have a bin matching (unscoped portion of) packagename, use that
|
||||
// otherwise if there's 1 bin or all bin value is the same (alias), use
|
||||
// that, otherwise fail
|
||||
const bin = mani.bin || {}
|
||||
if (new Set(Object.values(bin)).size === 1)
|
||||
return Object.keys(bin)[0]
|
||||
|
||||
// XXX probably a util to parse this better?
|
||||
const name = mani.name.replace(/^@[^/]+\//, '')
|
||||
if (bin[name])
|
||||
return name
|
||||
|
||||
// XXX need better error message
|
||||
throw Object.assign(new Error('could not determine executable to run'), {
|
||||
pkgid: mani._id,
|
||||
})
|
||||
}
|
||||
|
||||
cacheInstallDir (packages) {
|
||||
// only packages not found in ${prefix}/node_modules
|
||||
return resolve(this.npm.config.get('cache'), '_npx', this.getHash(packages))
|
||||
}
|
||||
|
||||
getHash (packages) {
|
||||
return crypto.createHash('sha512')
|
||||
.update(packages.sort((a, b) => a.localeCompare(b)).join('\n'))
|
||||
.digest('hex')
|
||||
.slice(0, 16)
|
||||
}
|
||||
|
||||
async _execWorkspaces (args, filters) {
|
||||
const workspaces =
|
||||
await getWorkspaces(filters, { path: this.npm.localPrefix })
|
||||
const getLocationMsg = async path => {
|
||||
const color = this.npm.config.get('color')
|
||||
const colorize = color ? chalk : nocolor
|
||||
const { _id } = await readPackageJson(`${path}/package.json`)
|
||||
return ` in workspace ${colorize.green(_id)} at location:\n${colorize.dim(path)}`
|
||||
}
|
||||
const color = this.npm.config.get('color')
|
||||
|
||||
for (const workspacePath of workspaces.values()) {
|
||||
const locationMsg = await getLocationMsg(workspacePath)
|
||||
const locationMsg = await getLocationMsg({ color, path: workspacePath })
|
||||
await this._exec(args, {
|
||||
locationMsg,
|
||||
path: workspacePath,
|
||||
|
@ -364,4 +119,5 @@ class Exec extends BaseCommand {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Exec
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
const chalk = require('chalk')
|
||||
const readPackageJson = require('read-package-json-fast')
|
||||
|
||||
const nocolor = {
|
||||
dim: s => s,
|
||||
green: s => s,
|
||||
}
|
||||
|
||||
const getLocationMsg = async ({ color, path }) => {
|
||||
const colorize = color ? chalk : nocolor
|
||||
const { _id } =
|
||||
await readPackageJson(`${path}/package.json`)
|
||||
.catch(() => ({}))
|
||||
|
||||
const workspaceMsg = _id
|
||||
? ` in workspace ${colorize.green(_id)}`
|
||||
: ` in a ${colorize.green('new')} workspace`
|
||||
const locationMsg = ` at location:\n${
|
||||
colorize.dim(path)
|
||||
}`
|
||||
|
||||
return `${workspaceMsg}${locationMsg}`
|
||||
}
|
||||
|
||||
module.exports = getLocationMsg
|
|
@ -1,6 +1,14 @@
|
|||
const fs = require('fs')
|
||||
const { relative, resolve } = require('path')
|
||||
const mkdirp = require('mkdirp-infer-owner')
|
||||
const initJson = require('init-package-json')
|
||||
const npa = require('npm-package-arg')
|
||||
const rpj = require('read-package-json-fast')
|
||||
const libexec = require('libnpmexec')
|
||||
const parseJSON = require('json-parse-even-better-errors')
|
||||
const mapWorkspaces = require('@npmcli/map-workspaces')
|
||||
|
||||
const getLocationMsg = require('./exec/get-workspace-location-msg.js')
|
||||
const BaseCommand = require('./base-command.js')
|
||||
|
||||
class Init extends BaseCommand {
|
||||
|
@ -9,6 +17,11 @@ class Init extends BaseCommand {
|
|||
return 'Create a package.json file'
|
||||
}
|
||||
|
||||
/* istanbul ignore next - see test/lib/load-all-commands.js */
|
||||
static get params () {
|
||||
return ['workspace', 'workspaces']
|
||||
}
|
||||
|
||||
/* istanbul ignore next - see test/lib/load-all-commands.js */
|
||||
static get name () {
|
||||
return 'init'
|
||||
|
@ -27,42 +40,107 @@ class Init extends BaseCommand {
|
|||
this.init(args).then(() => cb()).catch(cb)
|
||||
}
|
||||
|
||||
execWorkspaces (args, filters, cb) {
|
||||
this.initWorkspaces(args, filters).then(() => cb()).catch(cb)
|
||||
}
|
||||
|
||||
async init (args) {
|
||||
// the new npx style way
|
||||
// npm exec style
|
||||
if (args.length)
|
||||
return (await this.execCreate({ args, path: process.cwd() }))
|
||||
|
||||
// no args, uses classic init-package-json boilerplate
|
||||
await this.template()
|
||||
}
|
||||
|
||||
async initWorkspaces (args, filters) {
|
||||
// reads package.json for the top-level folder first, by doing this we
|
||||
// ensure the command throw if no package.json is found before trying
|
||||
// to create a workspace package.json file or its folders
|
||||
const pkg = await rpj(resolve(this.npm.localPrefix, 'package.json'))
|
||||
const wPath = filterArg => resolve(this.npm.localPrefix, filterArg)
|
||||
|
||||
// npm-exec style, runs in the context of each workspace filter
|
||||
if (args.length) {
|
||||
const initerName = args[0]
|
||||
let packageName = initerName
|
||||
if (/^@[^/]+$/.test(initerName))
|
||||
packageName = initerName + '/create'
|
||||
else {
|
||||
const req = npa(initerName)
|
||||
if (req.type === 'git' && req.hosted) {
|
||||
const { user, project } = req.hosted
|
||||
packageName = initerName
|
||||
.replace(user + '/' + project, user + '/create-' + project)
|
||||
} else if (req.registry) {
|
||||
packageName = req.name.replace(/^(@[^/]+\/)?/, '$1create-')
|
||||
if (req.rawSpec)
|
||||
packageName += '@' + req.rawSpec
|
||||
} else {
|
||||
throw Object.assign(new Error(
|
||||
'Unrecognized initializer: ' + initerName +
|
||||
'\nFor more package binary executing power check out `npx`:' +
|
||||
'\nhttps://www.npmjs.com/package/npx'
|
||||
), { code: 'EUNSUPPORTED' })
|
||||
}
|
||||
for (const filterArg of filters) {
|
||||
const path = wPath(filterArg)
|
||||
await mkdirp(path)
|
||||
await this.execCreate({ args, path })
|
||||
await this.setWorkspace({ pkg, workspacePath: path })
|
||||
}
|
||||
this.npm.config.set('package', [])
|
||||
const newArgs = [packageName, ...args.slice(1)]
|
||||
return new Promise((res, rej) => {
|
||||
this.npm.commands.exec(newArgs, er => er ? rej(er) : res())
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// the old way
|
||||
const dir = process.cwd()
|
||||
// no args, uses classic init-package-json boilerplate
|
||||
for (const filterArg of filters) {
|
||||
const path = wPath(filterArg)
|
||||
await mkdirp(path)
|
||||
await this.template(path)
|
||||
await this.setWorkspace({ pkg, workspacePath: path })
|
||||
}
|
||||
}
|
||||
|
||||
async execCreate ({ args, path }) {
|
||||
const [initerName, ...otherArgs] = args
|
||||
let packageName = initerName
|
||||
|
||||
if (/^@[^/]+$/.test(initerName))
|
||||
packageName = initerName + '/create'
|
||||
else {
|
||||
const req = npa(initerName)
|
||||
if (req.type === 'git' && req.hosted) {
|
||||
const { user, project } = req.hosted
|
||||
packageName = initerName
|
||||
.replace(user + '/' + project, user + '/create-' + project)
|
||||
} else if (req.registry) {
|
||||
packageName = req.name.replace(/^(@[^/]+\/)?/, '$1create-')
|
||||
if (req.rawSpec)
|
||||
packageName += '@' + req.rawSpec
|
||||
} else {
|
||||
throw Object.assign(new Error(
|
||||
'Unrecognized initializer: ' + initerName +
|
||||
'\nFor more package binary executing power check out `npx`:' +
|
||||
'\nhttps://www.npmjs.com/package/npx'
|
||||
), { code: 'EUNSUPPORTED' })
|
||||
}
|
||||
}
|
||||
|
||||
const newArgs = [packageName, ...otherArgs]
|
||||
const cache = this.npm.config.get('cache')
|
||||
const { color } = this.npm.flatOptions
|
||||
const {
|
||||
flatOptions,
|
||||
localBin,
|
||||
log,
|
||||
globalBin,
|
||||
output,
|
||||
} = this.npm
|
||||
const locationMsg = await getLocationMsg({ color, path })
|
||||
const runPath = path
|
||||
const scriptShell = this.npm.config.get('script-shell') || undefined
|
||||
const yes = this.npm.config.get('yes')
|
||||
|
||||
await libexec({
|
||||
...flatOptions,
|
||||
args: newArgs,
|
||||
cache,
|
||||
color,
|
||||
localBin,
|
||||
locationMsg,
|
||||
log,
|
||||
globalBin,
|
||||
output,
|
||||
path,
|
||||
runPath,
|
||||
scriptShell,
|
||||
yes,
|
||||
})
|
||||
}
|
||||
|
||||
async template (path = process.cwd()) {
|
||||
this.npm.log.pause()
|
||||
this.npm.log.disableProgress()
|
||||
|
||||
const initFile = this.npm.config.get('init-module')
|
||||
if (!this.npm.config.get('yes') && !this.npm.config.get('force')) {
|
||||
this.npm.output([
|
||||
|
@ -78,9 +156,10 @@ class Init extends BaseCommand {
|
|||
'Press ^C at any time to quit.',
|
||||
].join('\n'))
|
||||
}
|
||||
|
||||
// XXX promisify init-package-json
|
||||
await new Promise((res, rej) => {
|
||||
initJson(dir, initFile, this.npm.config, (er, data) => {
|
||||
initJson(path, initFile, this.npm.config, (er, data) => {
|
||||
this.npm.log.resume()
|
||||
this.npm.log.enableProgress()
|
||||
this.npm.log.silly('package data', data)
|
||||
|
@ -97,5 +176,56 @@ class Init extends BaseCommand {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
async setWorkspace ({ pkg, workspacePath }) {
|
||||
const workspaces = await mapWorkspaces({ cwd: this.npm.localPrefix, pkg })
|
||||
|
||||
// skip setting workspace if current package.json glob already satisfies it
|
||||
for (const wPath of workspaces.values()) {
|
||||
if (wPath === workspacePath)
|
||||
return
|
||||
}
|
||||
|
||||
// if a create-pkg didn't generate a package.json at the workspace
|
||||
// folder level, it might not be recognized as a workspace by
|
||||
// mapWorkspaces, so we're just going to avoid touching the
|
||||
// top-level package.json
|
||||
try {
|
||||
fs.statSync(resolve(workspacePath, 'package.json'))
|
||||
} catch (err) {
|
||||
return
|
||||
}
|
||||
|
||||
let manifest
|
||||
try {
|
||||
manifest =
|
||||
fs.readFileSync(resolve(this.npm.localPrefix, 'package.json'), 'utf-8')
|
||||
} catch (error) {
|
||||
throw new Error('package.json not found')
|
||||
}
|
||||
|
||||
try {
|
||||
manifest = parseJSON(manifest)
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid package.json: ${error}`)
|
||||
}
|
||||
|
||||
if (!manifest.workspaces)
|
||||
manifest.workspaces = []
|
||||
|
||||
manifest.workspaces.push(relative(this.npm.localPrefix, workspacePath))
|
||||
|
||||
// format content
|
||||
const {
|
||||
[Symbol.for('indent')]: indent,
|
||||
[Symbol.for('newline')]: newline,
|
||||
} = manifest
|
||||
|
||||
const content = (JSON.stringify(manifest, null, indent) + '\n')
|
||||
.replace(/\n/g, newline)
|
||||
|
||||
fs.writeFileSync(resolve(this.npm.localPrefix, 'package.json'), content)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Init
|
||||
|
|
|
@ -41,7 +41,7 @@ class Logout extends BaseCommand {
|
|||
method: 'DELETE',
|
||||
ignoreBody: true,
|
||||
})
|
||||
} else if (auth.username || auth.password)
|
||||
} else if (auth.isBasicAuth)
|
||||
log.verbose('logout', `clearing user credentials for ${reg}`)
|
||||
else {
|
||||
const msg = `not logged in to ${reg}, so can't log out!`
|
||||
|
|
|
@ -166,7 +166,10 @@ class LS extends BaseCommand {
|
|||
)
|
||||
}
|
||||
|
||||
if (problems.size) {
|
||||
const shouldThrow = problems.size &&
|
||||
![...problems].every(problem => problem.startsWith('extraneous:'))
|
||||
|
||||
if (shouldThrow) {
|
||||
throw Object.assign(
|
||||
new Error([...problems].join(EOL)),
|
||||
{ code: 'ELSPROBLEMS' }
|
||||
|
@ -302,7 +305,7 @@ const getJsonOutputItem = (node, { global, long }) => {
|
|||
if (node.isRoot && hasPackageJson)
|
||||
item.name = node.package.name || node.name
|
||||
|
||||
if (long) {
|
||||
if (long && !node[_missing]) {
|
||||
item.name = item[_name]
|
||||
const { dependencies, ...packageInfo } = node.package
|
||||
Object.assign(item, packageInfo)
|
||||
|
@ -411,9 +414,11 @@ const augmentNodesWithMetadata = ({
|
|||
path: node.path,
|
||||
isLink: node.isLink,
|
||||
realpath: node.realpath,
|
||||
[_type]: node[_type],
|
||||
[_invalid]: node[_invalid],
|
||||
[_missing]: node[_missing],
|
||||
[_dedupe]: true,
|
||||
// if it's missing, it's not deduped, it's just missing
|
||||
[_dedupe]: !node[_missing],
|
||||
}
|
||||
} else {
|
||||
// keeps track of already seen nodes in order to check for dedupes
|
||||
|
|
|
@ -45,22 +45,34 @@ class Pack extends BaseCommand {
|
|||
args = ['.']
|
||||
|
||||
const unicode = this.npm.config.get('unicode')
|
||||
const dryRun = this.npm.config.get('dry-run')
|
||||
|
||||
// clone the opts because pacote mutates it with resolved/integrity
|
||||
const tarballs = await Promise.all(args.map(async (arg) => {
|
||||
// Get the manifests and filenames first so we can bail early on manifest
|
||||
// errors before making any tarballs
|
||||
const manifests = []
|
||||
for (const arg of args) {
|
||||
const spec = npa(arg)
|
||||
const dryRun = this.npm.config.get('dry-run')
|
||||
const manifest = await pacote.manifest(spec, this.npm.flatOptions)
|
||||
if (!manifest._id)
|
||||
throw new Error('Invalid package, must have name and version')
|
||||
|
||||
const filename = `${manifest.name}-${manifest.version}.tgz`
|
||||
.replace(/^@/, '').replace(/\//, '-')
|
||||
manifests.push({ arg, filename, manifest })
|
||||
}
|
||||
|
||||
// Load tarball names up for printing afterward to isolate from the
|
||||
// noise generated during packing
|
||||
const tarballs = []
|
||||
for (const { arg, filename, manifest } of manifests) {
|
||||
const tarballData = await libpack(arg, this.npm.flatOptions)
|
||||
const pkgContents = await getContents(manifest, tarballData)
|
||||
|
||||
if (!dryRun)
|
||||
await writeFile(filename, tarballData)
|
||||
|
||||
return pkgContents
|
||||
}))
|
||||
tarballs.push(pkgContents)
|
||||
}
|
||||
|
||||
for (const tar of tarballs) {
|
||||
logTar(tar, { log, unicode })
|
||||
|
|
|
@ -199,16 +199,6 @@ define('also', {
|
|||
},
|
||||
})
|
||||
|
||||
define('always-auth', {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
description: `
|
||||
Force npm to always require authentication when accessing the registry,
|
||||
even for \`GET\` requests.
|
||||
`,
|
||||
flatten,
|
||||
})
|
||||
|
||||
define('audit', {
|
||||
default: true,
|
||||
type: Boolean,
|
||||
|
@ -1660,7 +1650,8 @@ define('script-shell', {
|
|||
`,
|
||||
type: [null, String],
|
||||
description: `
|
||||
The shell to use for scripts run with the \`npm run\` command.
|
||||
The shell to use for scripts run with the \`npm exec\`,
|
||||
\`npm run\` and \`npm init <pkg>\` commands.
|
||||
`,
|
||||
flatten (key, obj, flatOptions) {
|
||||
flatOptions.scriptShell = obj[key] || undefined
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
.P
|
||||
.RS 2
|
||||
.nf
|
||||
npm adduser [\-\-registry=url] [\-\-scope=@orgname] [\-\-always\-auth] [\-\-auth\-type=legacy]
|
||||
npm adduser [\-\-registry=url] [\-\-scope=@orgname] [\-\-auth\-type=legacy]
|
||||
|
||||
aliases: login, add\-user
|
||||
.fi
|
||||
|
@ -54,24 +54,6 @@ npm adduser \-\-registry=http://myregistry\.example\.com \-\-scope=@myco
|
|||
.P
|
||||
This will set a registry for the given scope and login or create a user for
|
||||
that registry at the same time\.
|
||||
.SS always\-auth
|
||||
.P
|
||||
Default: false
|
||||
.P
|
||||
If specified, save configuration indicating that all requests to the given
|
||||
registry should include authorization information\. Useful for private
|
||||
registries\. Can be used with \fB\-\-registry\fP and / or \fB\-\-scope\fP, e\.g\.
|
||||
.P
|
||||
.RS 2
|
||||
.nf
|
||||
npm adduser \-\-registry=http://private\-registry\.example\.com \-\-always\-auth
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
This will ensure that all requests to that registry (including for tarballs)
|
||||
include an authorization header\. This setting may be necessary for use with
|
||||
private registries where metadata and package tarballs are stored on hosts with
|
||||
different hostnames\. See \fBalways\-auth\fP in npm help \fBconfig\fP for more details on always\-auth\. Registry\-specific configuration of \fBalways\-auth\fP takes precedence over any global configuration\.
|
||||
.SS auth\-type
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
|
|
|
@ -9,7 +9,7 @@ npm exec \-\- <pkg>[@<version>] [args\.\.\.]
|
|||
npm exec \-\-package=<pkg>[@<version>] \-\- <cmd> [args\.\.\.]
|
||||
npm exec \-c '<cmd> [args\.\.\.]'
|
||||
npm exec \-\-package=foo \-c '<cmd> [args\.\.\.]'
|
||||
npm exec [\-ws] [\-w <workspace\-name] [args\.\.\.]
|
||||
npm exec [\-\-ws] [\-w <workspace\-name] [args\.\.\.]
|
||||
|
||||
npx <pkg>[@<specifier>] [args\.\.\.]
|
||||
npx \-p <pkg>[@<specifier>] <cmd> [args\.\.\.]
|
||||
|
@ -205,7 +205,7 @@ workspace folder:
|
|||
.P
|
||||
.RS 2
|
||||
.nf
|
||||
npm exec \-ws \-\- eslint \./*\.js
|
||||
npm exec \-\-ws \-\- eslint \./*\.js
|
||||
.fi
|
||||
.RE
|
||||
.SS Filtering workspaces
|
||||
|
@ -317,7 +317,7 @@ children workspaces)
|
|||
.SS workspaces
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
Alias: \fB\-ws\fP
|
||||
Alias: \fB\-\-ws\fP
|
||||
.IP \(bu 2
|
||||
Type: Boolean
|
||||
.IP \(bu 2
|
||||
|
@ -343,5 +343,7 @@ npm help restart
|
|||
npm help stop
|
||||
.IP \(bu 2
|
||||
npm help config
|
||||
.IP \(bu 2
|
||||
npm help workspaces
|
||||
|
||||
.RE
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
.RS 2
|
||||
.nf
|
||||
npm init [\-\-force|\-f|\-\-yes|\-y|\-\-scope]
|
||||
npm init <@scope> (same as `npx <@scope>/create`)
|
||||
npm init [<@scope>/]<name> (same as `npx [<@scope>/]create\-<name>`)
|
||||
npm init <@scope> (same as `npm exec <@scope>/create`)
|
||||
npm init [<@scope>/]<name> (same as `npm exec [<@scope>/]create\-<name>`)
|
||||
npm init [\-w <dir>] [args\.\.\.]
|
||||
.fi
|
||||
.RE
|
||||
.SS Description
|
||||
|
@ -16,25 +17,22 @@ npm init [<@scope>/]<name> (same as `npx [<@scope>/]create\-<name>`)
|
|||
package\.
|
||||
.P
|
||||
\fBinitializer\fP in this case is an npm package named \fBcreate\-<initializer>\fP,
|
||||
which will be installed by \fBnpx\fP \fIhttps://npm\.im/npx\fR, and then have its
|
||||
which will be installed by npm help \fBnpm\-exec\fP, and then have its
|
||||
main bin executed \-\- presumably creating or updating \fBpackage\.json\fP and
|
||||
running any other initialization\-related operations\.
|
||||
.P
|
||||
The init command is transformed to a corresponding \fBnpx\fP operation as
|
||||
The init command is transformed to a corresponding \fBnpm exec\fP operation as
|
||||
follows:
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
\fBnpm init foo\fP \-> \fBnpx create\-foo\fP
|
||||
\fBnpm init foo\fP \-> \fBnpm exec create\-foo\fP
|
||||
.IP \(bu 2
|
||||
\fBnpm init @usr/foo\fP \-> \fBnpx @usr/create\-foo\fP
|
||||
\fBnpm init @usr/foo\fP \-> \fBnpm exec @usr/create\-foo\fP
|
||||
.IP \(bu 2
|
||||
\fBnpm init @usr\fP \-> \fBnpx @usr/create\fP
|
||||
\fBnpm init @usr\fP \-> \fBnpm exec @usr/create\fP
|
||||
|
||||
.RE
|
||||
.P
|
||||
Any additional options will be passed directly to the command, so \fBnpm init
|
||||
foo \-\- \-\-hello\fP will map to \fBnpx create\-foo \-\-hello\fP\|\.
|
||||
.P
|
||||
If the initializer is omitted (by just calling \fBnpm init\fP), init will fall
|
||||
back to legacy init behavior\. It will ask you a bunch of questions, and
|
||||
then write a package\.json for you\. It will attempt to make reasonable
|
||||
|
@ -42,6 +40,21 @@ guesses based on existing fields, dependencies, and options selected\. It is
|
|||
strictly additive, so it will keep any fields and values that were already
|
||||
set\. You can also use \fB\-y\fP/\fB\-\-yes\fP to skip the questionnaire altogether\. If
|
||||
you pass \fB\-\-scope\fP, it will create a scoped package\.
|
||||
.SS Forwarding additional options
|
||||
.P
|
||||
Any additional options will be passed directly to the command, so \fBnpm init
|
||||
foo \-\- \-\-hello\fP will map to \fBnpm exec \-\- create\-foo \-\-hello\fP\|\.
|
||||
.P
|
||||
To better illustrate how options are forwarded, here's a more evolved
|
||||
example showing options passed to both the \fBnpm cli\fR and a create package,
|
||||
both following commands are equivalent:
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
\fBnpm init foo \-y \-\-registry=<url> \-\- \-\-hello \-a\fP
|
||||
.IP \(bu 2
|
||||
\fBnpm exec \-y \-\-registry=<url> \-\- create\-foo \-\-hello \-a\fP
|
||||
|
||||
.RE
|
||||
.SS Examples
|
||||
.P
|
||||
Create a new React\-based project using
|
||||
|
@ -80,6 +93,77 @@ Generate it without having it ask any questions:
|
|||
$ npm init \-y
|
||||
.fi
|
||||
.RE
|
||||
.SS Workspaces support
|
||||
.P
|
||||
It's possible to create a new workspace within your project by using the
|
||||
\fBworkspace\fP config option\. When using \fBnpm init \-w <dir>\fP the cli will
|
||||
create the folders and boilerplate expected while also adding a reference
|
||||
to your project \fBpackage\.json\fP \fB"workspaces": []\fP property in order to make
|
||||
sure that new generated \fBworkspace\fR is properly set up as such\.
|
||||
.P
|
||||
Given a project with no workspaces, e\.g:
|
||||
.P
|
||||
.RS 2
|
||||
.nf
|
||||
\|\.
|
||||
+\-\- package\.json
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
You may generate a new workspace using the legacy init:
|
||||
.P
|
||||
.RS 2
|
||||
.nf
|
||||
$ npm init \-w packages/a
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
That will generate a new folder and \fBpackage\.json\fP file, while also updating
|
||||
your top\-level \fBpackage\.json\fP to add the reference to this new workspace:
|
||||
.P
|
||||
.RS 2
|
||||
.nf
|
||||
\|\.
|
||||
+\-\- package\.json
|
||||
`\-\- packages
|
||||
`\-\- a
|
||||
`\-\- package\.json
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
The workspaces init also supports the \fBnpm init <initializer> \-w <dir>\fP
|
||||
syntax, following the same set of rules explained earlier in the initial
|
||||
\fBDescription\fR section of this page\. Similar to the previous example of
|
||||
creating a new React\-based project using
|
||||
\fBcreate\-react\-app\fP \fIhttps://npm\.im/create\-react\-app\fR, the following syntax
|
||||
will make sure to create the new react app as a nested \fBworkspace\fR within your
|
||||
project and configure your \fBpackage\.json\fP to recognize it as such:
|
||||
.P
|
||||
.RS 2
|
||||
.nf
|
||||
npm init \-w packages/my\-react\-app react\-app \.
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
This will make sure to generate your react app as expected, one important
|
||||
consideration to have in mind is that \fBnpm exec\fP is going to be run in the
|
||||
context of the newly created folder for that workspace, and that's the reason
|
||||
why in this example the initializer uses the initializer name followed with a
|
||||
dot to represent the current directory in that context, e\.g: \fBreact\-app \.\fP:
|
||||
.P
|
||||
.RS 2
|
||||
.nf
|
||||
\|\.
|
||||
+\-\- package\.json
|
||||
`\-\- packages
|
||||
+\-\- a
|
||||
| `\-\- package\.json
|
||||
`\-\- my\-react\-app
|
||||
+\-\- README
|
||||
+\-\- package\.json
|
||||
`\-\- \.\.\.
|
||||
.fi
|
||||
.RE
|
||||
.SS A note on caching
|
||||
.P
|
||||
The npm cli utilizes its internal package cache when using the package
|
||||
|
@ -98,6 +182,47 @@ requested from the server\. To force full offline mode, use \fBoffline\fP\|\.
|
|||
.P
|
||||
Forces full offline mode\. Any packages not locally cached will result in
|
||||
an error\.
|
||||
.SS workspace
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
Alias: \fB\-w\fP
|
||||
.IP \(bu 2
|
||||
Type: Array
|
||||
.IP \(bu 2
|
||||
Default: \fB[]\fP
|
||||
|
||||
.RE
|
||||
.P
|
||||
Enable running \fBnpm init\fP in the context of workspaces, creating any missing
|
||||
folders, generating files and adding/updating the \fB"workspaces"\fP property of
|
||||
the project \fBpackage\.json\fP\|\.
|
||||
.P
|
||||
the provided names or paths provided\.
|
||||
.P
|
||||
Valid values for the \fBworkspace\fP config are either:
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
Workspace names
|
||||
.IP \(bu 2
|
||||
Path to a workspace directory
|
||||
.IP \(bu 2
|
||||
Path to a parent workspace directory (will result to selecting all of the
|
||||
children workspaces)
|
||||
|
||||
.RE
|
||||
.SS workspaces
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
Alias: \fB\-ws\fP
|
||||
.IP \(bu 2
|
||||
Type: Boolean
|
||||
.IP \(bu 2
|
||||
Default: \fBfalse\fP
|
||||
|
||||
.RE
|
||||
.P
|
||||
Run \fBnpm init\fP in the context of all configured workspaces for the
|
||||
current project\.
|
||||
.SS See Also
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
|
@ -110,5 +235,7 @@ npm help version
|
|||
npm help scope
|
||||
.IP \(bu 2
|
||||
npm help exec
|
||||
.IP \(bu 2
|
||||
npm help workspaces
|
||||
|
||||
.RE
|
||||
|
|
|
@ -26,7 +26,7 @@ example, running \fBnpm ls promzard\fP in npm's source tree will show:
|
|||
.P
|
||||
.RS 2
|
||||
.nf
|
||||
npm@7\.10\.0 /path/to/npm
|
||||
npm@7\.11\.2 /path/to/npm
|
||||
└─┬ init\-package\-json@0\.0\.4
|
||||
└── promzard@0\.1\.5
|
||||
.fi
|
||||
|
|
|
@ -221,7 +221,7 @@ children workspaces)
|
|||
.SS workspaces
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
Alias: \fB\-ws\fP
|
||||
Alias: \fB\-\-ws\fP
|
||||
.IP \(bu 2
|
||||
Type: Boolean
|
||||
.IP \(bu 2
|
||||
|
@ -245,5 +245,7 @@ npm help restart
|
|||
npm help stop
|
||||
.IP \(bu 2
|
||||
npm help config
|
||||
.IP \(bu 2
|
||||
npm help workspaces
|
||||
|
||||
.RE
|
||||
|
|
|
@ -111,6 +111,20 @@ this:
|
|||
npm view connect versions
|
||||
.fi
|
||||
.RE
|
||||
.SS Configuration
|
||||
.SS json
|
||||
.P
|
||||
Show information in JSON format\. See \fBOutput\fP \fI#output\fR below\.
|
||||
.SS workspaces
|
||||
.P
|
||||
Enables workspaces context while searching the \fBpackage\.json\fP in the
|
||||
current folder\. Information about packages named in each workspace will
|
||||
be viewed\.
|
||||
.SS workspace
|
||||
.P
|
||||
Enables workspaces context and limits results to only those specified by
|
||||
this config item\. Only the information about packages named in the
|
||||
workspaces given here will be viewed\.
|
||||
.SS Output
|
||||
.P
|
||||
If only a single string field for a single version is output, then it
|
||||
|
|
|
@ -10,7 +10,7 @@ npm <command> [args]
|
|||
.RE
|
||||
.SS Version
|
||||
.P
|
||||
7\.10\.0
|
||||
7\.11\.2
|
||||
.SS Description
|
||||
.P
|
||||
npm is the package manager for the Node JavaScript platform\. It puts
|
||||
|
|
|
@ -437,6 +437,8 @@ would be the same as this:
|
|||
Please make sure that your file(s) referenced in \fBbin\fP starts with
|
||||
\fB#!/usr/bin/env node\fP, otherwise the scripts are started without the node
|
||||
executable!
|
||||
.P
|
||||
Note that you can also set the executable files using directories\.bin \fI#directoriesbin\fR\|\.
|
||||
.SS man
|
||||
.P
|
||||
Specify either a single file or an array of filenames to put in place for
|
||||
|
|
|
@ -217,17 +217,6 @@ Type: Boolean
|
|||
.P
|
||||
Prevents throwing an error when \fBnpm version\fP is used to set the new version
|
||||
to the same value as the current version\.
|
||||
.SS \fBalways\-auth\fP
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
Default: false
|
||||
.IP \(bu 2
|
||||
Type: Boolean
|
||||
|
||||
.RE
|
||||
.P
|
||||
Force npm to always require authentication when accessing the registry, even
|
||||
for \fBGET\fP requests\.
|
||||
.SS \fBaudit\fP
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
|
@ -1455,7 +1444,8 @@ Type: null or String
|
|||
|
||||
.RE
|
||||
.P
|
||||
The shell to use for scripts run with the \fBnpm run\fP command\.
|
||||
The shell to use for scripts run with the \fBnpm exec\fP, \fBnpm run\fP and \fBnpm
|
||||
init <pkg>\fP commands\.
|
||||
.SS \fBsearchexclude\fP
|
||||
.RS 0
|
||||
.IP \(bu 2
|
||||
|
|
|
@ -20,13 +20,21 @@ const levelMap = new Map(levels.reduce((set, level, index) => {
|
|||
}, []))
|
||||
|
||||
const { inspect, format } = require('util')
|
||||
const colors = process.stderr.isTTY
|
||||
const magenta = colors ? msg => `\x1B[35m${msg}\x1B[39m` : m => m
|
||||
if (loglevel !== 'silent') {
|
||||
process.on('log', (level, ...args) => {
|
||||
if (levelMap.get(level) < levelMap.get(loglevel))
|
||||
return
|
||||
const pref = `${process.pid} ${level} `
|
||||
const pref = `${process.pid} ${magenta(level)} `
|
||||
if (level === 'warn' && args[0] === 'ERESOLVE')
|
||||
args[2] = inspect(args[2], { depth: 10 })
|
||||
args[2] = inspect(args[2], { depth: 10, colors })
|
||||
else {
|
||||
args = args.map(a => {
|
||||
return typeof a === 'string' ? a
|
||||
: inspect(a, { depth: 10, colors })
|
||||
})
|
||||
}
|
||||
const msg = pref + format(...args).trim().split('\n').join(`\n${pref}`)
|
||||
console.error(msg)
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const timers = Object.create(null)
|
||||
const { format } = require('util')
|
||||
|
||||
process.on('time', name => {
|
||||
if (timers[name])
|
||||
|
@ -6,17 +7,20 @@ process.on('time', name => {
|
|||
timers[name] = process.hrtime()
|
||||
})
|
||||
|
||||
const dim = process.stderr.isTTY ? msg => `\x1B[2m${msg}\x1B[22m` : m => m
|
||||
const red = process.stderr.isTTY ? msg => `\x1B[31m${msg}\x1B[39m` : m => m
|
||||
process.on('timeEnd', name => {
|
||||
if (!timers[name])
|
||||
throw new Error('timer not started! ' + name)
|
||||
const res = process.hrtime(timers[name])
|
||||
delete timers[name]
|
||||
console.error(`${process.pid} ${name}`, res[0] * 1e3 + res[1] / 1e6)
|
||||
const msg = format(`${process.pid} ${name}`, res[0] * 1e3 + res[1] / 1e6)
|
||||
console.error(dim(msg))
|
||||
})
|
||||
|
||||
process.on('exit', () => {
|
||||
for (const name of Object.keys(timers)) {
|
||||
console.error('Dangling timer: ', name)
|
||||
console.error(red('Dangling timer:'), name)
|
||||
process.exitCode = 1
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,7 +8,8 @@ require('./lib/timers.js')
|
|||
const start = process.hrtime()
|
||||
new Arborist(options).loadVirtual().then(tree => {
|
||||
const end = process.hrtime(start)
|
||||
print(tree)
|
||||
if (!options.quiet)
|
||||
print(tree)
|
||||
if (options.save)
|
||||
tree.meta.save()
|
||||
console.error(`read ${tree.inventory.size} deps in ${end[0] * 1000 + end[1] / 1e6}ms`)
|
||||
|
|
|
@ -47,6 +47,7 @@ const _flagsSuspect = Symbol.for('flagsSuspect')
|
|||
const _workspaces = Symbol.for('workspaces')
|
||||
const _prune = Symbol('prune')
|
||||
const _preferDedupe = Symbol('preferDedupe')
|
||||
const _pruneDedupable = Symbol('pruneDedupable')
|
||||
const _legacyBundling = Symbol('legacyBundling')
|
||||
const _parseSettings = Symbol('parseSettings')
|
||||
const _initTree = Symbol('initTree')
|
||||
|
@ -1188,6 +1189,16 @@ This is a one-time fix-up, please be patient...
|
|||
}
|
||||
}
|
||||
|
||||
// There is something present already, and we're not happy about it
|
||||
// See if the thing we WOULD be happy with is also going to satisfy
|
||||
// the other dependents on the current node.
|
||||
const current = edge.to
|
||||
const dep = await this[_nodeFromEdge](edge, null, null, required)
|
||||
if (dep.canReplace(current)) {
|
||||
await this[_nodeFromEdge](edge, node.parent, null, required)
|
||||
continue
|
||||
}
|
||||
|
||||
// at this point we know that there is a dep there, and
|
||||
// we don't like it. always fail strictly, always allow forcibly or
|
||||
// in non-strict mode if it's not our fault. don't warn here, because
|
||||
|
@ -1332,6 +1343,21 @@ This is a one-time fix-up, please be patient...
|
|||
// this is an overridden peer dep
|
||||
this[_warnPeerConflict](edge)
|
||||
}
|
||||
|
||||
// if we get a KEEP in a update scenario, then we MAY have something
|
||||
// already duplicating this unnecessarily! For example:
|
||||
// ```
|
||||
// root
|
||||
// +-- x (dep: y@1.x)
|
||||
// | +-- y@1.0.0
|
||||
// +-- y@1.1.0
|
||||
// ```
|
||||
// Now say we do `reify({update:['y']})`, and the latest version is
|
||||
// 1.1.0, which we already have in the root. We'll try to place y@1.1.0
|
||||
// first in x, then in the root, ending with KEEP, because we already
|
||||
// have it. In that case, we ought to REMOVE the nm/x/nm/y node, because
|
||||
// it is an unnecessary duplicate.
|
||||
this[_pruneDedupable](target, true)
|
||||
return []
|
||||
}
|
||||
|
||||
|
@ -1387,8 +1413,8 @@ This is a one-time fix-up, please be patient...
|
|||
// MAY end up putting a better/identical node further up the tree in
|
||||
// a way that causes an unnecessary duplication. If so, remove the
|
||||
// now-unnecessary node.
|
||||
if (edge.valid && edge.to.parent !== target && newDep.canReplace(edge.to))
|
||||
edge.to.parent = null
|
||||
if (edge.valid && edge.to && edge.to !== newDep)
|
||||
this[_pruneDedupable](edge.to, false)
|
||||
|
||||
// visit any dependents who are upset by this change
|
||||
// if it's an angry overridden peer edge, however, make sure we
|
||||
|
@ -1404,30 +1430,8 @@ This is a one-time fix-up, please be patient...
|
|||
// prune anything deeper in the tree that can be replaced by this
|
||||
if (this.idealTree) {
|
||||
for (const node of this.idealTree.inventory.query('name', newDep.name)) {
|
||||
if (node !== newDep &&
|
||||
node.isDescendantOf(target) &&
|
||||
!node.inShrinkwrap &&
|
||||
!node.inBundle &&
|
||||
node.canReplaceWith(newDep)) {
|
||||
// don't prune if the dupe is necessary!
|
||||
// root (a, d)
|
||||
// +-- a (b, c2)
|
||||
// | +-- b (c2) <-- place c2 for b, lands at root
|
||||
// +-- d (e)
|
||||
// +-- e (c1, d)
|
||||
// +-- c1
|
||||
// +-- f (c2)
|
||||
// +-- c2 <-- pruning this would be bad
|
||||
|
||||
const mask = node.parent !== target &&
|
||||
node.parent &&
|
||||
node.parent.parent &&
|
||||
node.parent.parent !== target &&
|
||||
node.parent.parent.resolve(newDep.name)
|
||||
|
||||
if (!mask || mask === newDep || node.canReplaceWith(mask))
|
||||
node.parent = null
|
||||
}
|
||||
if (node.isDescendantOf(target))
|
||||
this[_pruneDedupable](node, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1460,6 +1464,21 @@ This is a one-time fix-up, please be patient...
|
|||
return placed
|
||||
}
|
||||
|
||||
// prune all the nodes in a branch of the tree that can be safely removed
|
||||
// This is only the most basic duplication detection; it finds if there
|
||||
// is another satisfying node further up the tree, and if so, dedupes.
|
||||
// Even in legacyBundling mode, we do this amount of deduplication.
|
||||
[_pruneDedupable] (node, descend = true) {
|
||||
if (node.canDedupe(this[_preferDedupe])) {
|
||||
node.root = null
|
||||
return
|
||||
}
|
||||
if (descend) {
|
||||
for (const child of node.children.values())
|
||||
this[_pruneDedupable](child)
|
||||
}
|
||||
}
|
||||
|
||||
[_pruneForReplacement] (node, oldDeps) {
|
||||
// gather up all the invalid edgesOut, and any now-extraneous
|
||||
// deps that the new node doesn't depend on but the old one did.
|
||||
|
@ -1612,32 +1631,137 @@ This is a one-time fix-up, please be patient...
|
|||
// placed here as well. the virtualRoot already has the appropriate
|
||||
// overrides applied.
|
||||
if (peerEntryEdge) {
|
||||
const peerSet = getPeerSet(current)
|
||||
OUTER: for (const p of peerSet) {
|
||||
// if any have a non-peer dep from the target, or a peer dep if
|
||||
// the target is root, then cannot safely replace and dupe deeper.
|
||||
for (const edge of p.edgesIn) {
|
||||
if (peerSet.has(edge.from))
|
||||
continue
|
||||
const currentPeerSet = getPeerSet(current)
|
||||
|
||||
// only respect valid edges, however, since we're likely trying
|
||||
// to fix the very one that's currently broken! If the virtual
|
||||
// root's replacement is ok, and doesn't have any invalid edges
|
||||
// indicating that it was an overridden peer, then ignore the
|
||||
// conflict and continue. If it WAS an override, then we need
|
||||
// to get the conflict here so that we can decide whether to
|
||||
// accept the current dep node, clobber it, or fail the install.
|
||||
if (edge.from === target && edge.valid) {
|
||||
const rep = dep.parent.children.get(edge.name)
|
||||
const override = rep && ([...rep.edgesIn].some(e => !e.valid))
|
||||
if (!rep || !rep.satisfies(edge) || override) {
|
||||
// We are effectively replacing currentPeerSet with newPeerSet
|
||||
// If there are any non-peer deps coming into the currentPeerSet,
|
||||
// which are currently valid, and are from the target, then that
|
||||
// means that we have to ensure that they're not going to be made
|
||||
// invalid by putting the newPeerSet in place.
|
||||
// If the edge comes from somewhere deeper than the target, then
|
||||
// that's fine, because we'll create an invalid edge, detect it,
|
||||
// and duplicate the node further into the tree.
|
||||
// loop through the currentPeerSet checking for valid edges on
|
||||
// the members of the peer set which will be made invalid.
|
||||
const targetEdges = new Set()
|
||||
for (const p of currentPeerSet) {
|
||||
for (const edge of p.edgesIn) {
|
||||
// edge from within the peerSet, ignore
|
||||
if (currentPeerSet.has(edge.from))
|
||||
continue
|
||||
// only care about valid edges from target.
|
||||
// edges from elsewhere can dupe if offended, invalid edges
|
||||
// are already being fixed or will be later.
|
||||
if (edge.from !== target || !edge.valid)
|
||||
continue
|
||||
targetEdges.add(edge)
|
||||
}
|
||||
}
|
||||
|
||||
for (const edge of targetEdges) {
|
||||
// see if we intend to replace this one anyway
|
||||
const rep = dep.parent.children.get(edge.name)
|
||||
const current = edge.to
|
||||
if (!rep) {
|
||||
// this isn't one we're replacing. but it WAS included in the
|
||||
// peerSet for some reason, so make sure that it's still
|
||||
// ok with the replacements in the new peerSet
|
||||
for (const curEdge of current.edgesOut.values()) {
|
||||
const newRepDep = dep.parent.children.get(curEdge.name)
|
||||
if (curEdge.valid && newRepDep && !newRepDep.satisfies(curEdge)) {
|
||||
canReplace = false
|
||||
break OUTER
|
||||
break
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// was this replacement already an override of some sort?
|
||||
const override = [...rep.edgesIn].some(e => !e.valid)
|
||||
// if we have a rep, and it's ok to put in this location, and
|
||||
// it's not already part of an override in the peerSet, then
|
||||
// we can continue with it.
|
||||
if (rep.satisfies(edge) && !override)
|
||||
continue
|
||||
// Otherwise, we cannot replace.
|
||||
canReplace = false
|
||||
break
|
||||
}
|
||||
// if we're going to be replacing the peerSet, we have to remove
|
||||
// and re-resolve any members of the old peerSet that are not
|
||||
// present in the new one, and which will have invalid edges.
|
||||
// We know that they're not depended upon by the target, or else
|
||||
// they would have caused a conflict, so they'll get landed deeper
|
||||
// in the tree, if possible.
|
||||
if (canReplace) {
|
||||
let needNesting = false
|
||||
OUTER: for (const node of currentPeerSet) {
|
||||
const rep = dep.parent.children.get(node.name)
|
||||
// has a replacement, already addressed above
|
||||
if (rep)
|
||||
continue
|
||||
|
||||
// ok, it has been placed here to dedupe, see if it needs to go
|
||||
// back deeper within the tree.
|
||||
for (const edge of node.edgesOut.values()) {
|
||||
const repDep = dep.parent.children.get(edge.name)
|
||||
// not in new peerSet, maybe fine.
|
||||
if (!repDep)
|
||||
continue
|
||||
|
||||
// new thing will be fine, no worries
|
||||
if (repDep.satisfies(edge))
|
||||
continue
|
||||
|
||||
// uhoh, we'll have to nest them.
|
||||
needNesting = true
|
||||
break OUTER
|
||||
}
|
||||
}
|
||||
|
||||
// to nest, just delete everything without a target dep
|
||||
// that's in the current peerSet, and add their dependants
|
||||
// to the _depsQueue for evaluation. Some of these MAY end
|
||||
// up in the same location again, and that's fine.
|
||||
if (needNesting) {
|
||||
// avoid mutating the tree while we're examining it
|
||||
const dependants = new Set()
|
||||
const reresolve = new Set()
|
||||
OUTER: for (const node of currentPeerSet) {
|
||||
const rep = dep.parent.children.get(node.name)
|
||||
if (rep)
|
||||
continue
|
||||
// create a separate set for each one, so we can skip any
|
||||
// that might somehow have an incoming target edge
|
||||
const deps = new Set()
|
||||
for (const edge of node.edgesIn) {
|
||||
// a target dep, skip this dep entirely, already addressed
|
||||
// ignoring for coverage, because it really ought to be
|
||||
// impossible, but I can't prove it yet, so this is here
|
||||
// for safety.
|
||||
/* istanbul ignore if - should be impossible */
|
||||
if (edge.from === target)
|
||||
continue OUTER
|
||||
// ignore this edge, it'll either be replaced or re-resolved
|
||||
if (currentPeerSet.has(edge.from))
|
||||
continue
|
||||
// ok, we care about this one.
|
||||
deps.add(edge.from)
|
||||
}
|
||||
reresolve.add(node)
|
||||
for (const d of deps)
|
||||
dependants.add(d)
|
||||
}
|
||||
for (const dependant of dependants) {
|
||||
this[_depsQueue].push(dependant)
|
||||
this[_depsSeen].delete(dependant)
|
||||
}
|
||||
for (const node of reresolve)
|
||||
node.root = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canReplace) {
|
||||
const ret = this[_canPlacePeers](dep, target, edge, REPLACE, peerEntryEdge, peerPath, isSource)
|
||||
/* istanbul ignore else - extremely rare that the peer set would
|
||||
|
|
|
@ -54,7 +54,7 @@ class Arborist extends Base {
|
|||
...options,
|
||||
path: options.path || '.',
|
||||
cache: options.cache || `${homedir()}/.npm/_cacache`,
|
||||
packumentCache: new Map(),
|
||||
packumentCache: options.packumentCache || new Map(),
|
||||
log: options.log || procLog,
|
||||
}
|
||||
this.cache = resolve(this.options.cache)
|
||||
|
|
|
@ -93,7 +93,8 @@ module.exports = cls => class VirtualLoader extends cls {
|
|||
this.virtualTree = root
|
||||
const {links, nodes} = this[resolveNodes](s, root)
|
||||
await this[resolveLinks](links, nodes)
|
||||
this[assignBundles](nodes)
|
||||
if (!(s.originalLockfileVersion >= 2))
|
||||
this[assignBundles](nodes)
|
||||
if (this[flagsSuspect])
|
||||
this[reCalcDepFlags](nodes.values())
|
||||
return root
|
||||
|
@ -220,22 +221,24 @@ module.exports = cls => class VirtualLoader extends cls {
|
|||
[assignBundles] (nodes) {
|
||||
for (const [location, node] of nodes) {
|
||||
// Skip assignment of parentage for the root package
|
||||
if (!location)
|
||||
if (!location || node.target && !node.target.location)
|
||||
continue
|
||||
const { name, parent, package: { inBundle }} = node
|
||||
|
||||
if (!parent)
|
||||
continue
|
||||
|
||||
// read inBundle from package because 'package' here is
|
||||
// actually a v2 lockfile metadata entry.
|
||||
// If the *parent* is also bundled, though, then we assume
|
||||
// that it's being pulled in just by virtue of that.
|
||||
// If the *parent* is also bundled, though, or if the parent has
|
||||
// no dependency on it, then we assume that it's being pulled in
|
||||
// just by virtue of its parent or a transitive dep being bundled.
|
||||
const { package: ppkg } = parent
|
||||
const { inBundle: parentBundled } = ppkg
|
||||
if (inBundle && !parentBundled) {
|
||||
if (inBundle && !parentBundled && parent.edgesOut.has(node.name)) {
|
||||
if (!ppkg.bundleDependencies)
|
||||
ppkg.bundleDependencies = [name]
|
||||
else if (!ppkg.bundleDependencies.includes(name))
|
||||
else
|
||||
ppkg.bundleDependencies.push(name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,10 +115,6 @@ module.exports = cls => class Builder extends cls {
|
|||
await this[_runScripts]('preinstall')
|
||||
if (this[_binLinks] && type !== 'links')
|
||||
await this[_linkAllBins]()
|
||||
if (!this[_ignoreScripts]) {
|
||||
await this[_runScripts]('install')
|
||||
await this[_runScripts]('postinstall')
|
||||
}
|
||||
|
||||
// links should also run prepare scripts and only link bins after that
|
||||
if (type === 'links') {
|
||||
|
@ -128,6 +124,11 @@ module.exports = cls => class Builder extends cls {
|
|||
await this[_linkAllBins]()
|
||||
}
|
||||
|
||||
if (!this[_ignoreScripts]) {
|
||||
await this[_runScripts]('install')
|
||||
await this[_runScripts]('postinstall')
|
||||
}
|
||||
|
||||
process.emit('timeEnd', `build:${type}`)
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,8 @@ const _loadTrees = Symbol.for('loadTrees')
|
|||
const _diffTrees = Symbol.for('diffTrees')
|
||||
const _createSparseTree = Symbol.for('createSparseTree')
|
||||
const _loadShrinkwrapsAndUpdateTrees = Symbol.for('loadShrinkwrapsAndUpdateTrees')
|
||||
const _shrinkwrapUnpacked = Symbol('shrinkwrapUnpacked')
|
||||
const _shrinkwrapInflated = Symbol('shrinkwrapInflated')
|
||||
const _bundleUnpacked = Symbol('bundleUnpacked')
|
||||
const _reifyNode = Symbol.for('reifyNode')
|
||||
const _extractOrLink = Symbol('extractOrLink')
|
||||
// defined by rebuild mixin
|
||||
|
@ -108,7 +109,7 @@ module.exports = cls => class Reifier extends cls {
|
|||
|
||||
this.diff = null
|
||||
this[_retiredPaths] = {}
|
||||
this[_shrinkwrapUnpacked] = new Set()
|
||||
this[_shrinkwrapInflated] = new Set()
|
||||
this[_retiredUnchanged] = {}
|
||||
this[_sparseTreeDirs] = new Set()
|
||||
this[_sparseTreeRoots] = new Set()
|
||||
|
@ -316,6 +317,7 @@ module.exports = cls => class Reifier extends cls {
|
|||
// find all the nodes that need to change between the actual
|
||||
// and ideal trees.
|
||||
this.diff = Diff.calculate({
|
||||
shrinkwrapInflated: this[_shrinkwrapInflated],
|
||||
filterNodes,
|
||||
actual: this.actualTree,
|
||||
ideal: this.idealTree,
|
||||
|
@ -423,7 +425,8 @@ module.exports = cls => class Reifier extends cls {
|
|||
const dirs = this.diff.leaves
|
||||
.filter(diff => {
|
||||
return (diff.action === 'ADD' || diff.action === 'CHANGE') &&
|
||||
!this[_sparseTreeDirs].has(diff.ideal.path)
|
||||
!this[_sparseTreeDirs].has(diff.ideal.path) &&
|
||||
!diff.ideal.isLink
|
||||
})
|
||||
.map(diff => diff.ideal.path)
|
||||
|
||||
|
@ -457,9 +460,9 @@ module.exports = cls => class Reifier extends cls {
|
|||
// we need to unpack them, read that shrinkwrap file, and then update
|
||||
// the tree by calling loadVirtual with the node as the root.
|
||||
[_loadShrinkwrapsAndUpdateTrees] () {
|
||||
const seen = this[_shrinkwrapUnpacked]
|
||||
const seen = this[_shrinkwrapInflated]
|
||||
const shrinkwraps = this.diff.leaves
|
||||
.filter(d => (d.action === 'CHANGE' || d.action === 'ADD') &&
|
||||
.filter(d => (d.action === 'CHANGE' || d.action === 'ADD' || !d.action) &&
|
||||
d.ideal.hasShrinkwrap && !seen.has(d.ideal) &&
|
||||
!this[_trashList].has(d.ideal.path))
|
||||
|
||||
|
@ -472,7 +475,7 @@ module.exports = cls => class Reifier extends cls {
|
|||
return promiseAllRejectLate(shrinkwraps.map(diff => {
|
||||
const node = diff.ideal
|
||||
seen.add(node)
|
||||
return this[_reifyNode](node)
|
||||
return diff.action ? this[_reifyNode](node) : node
|
||||
}))
|
||||
.then(nodes => promiseAllRejectLate(nodes.map(node => new Arborist({
|
||||
...this.options,
|
||||
|
@ -503,7 +506,7 @@ module.exports = cls => class Reifier extends cls {
|
|||
|
||||
const { npmVersion, nodeVersion } = this.options
|
||||
const p = Promise.resolve()
|
||||
.then(() => {
|
||||
.then(async () => {
|
||||
// when we reify an optional node, check the engine and platform
|
||||
// first. be sure to ignore the --force and --engine-strict flags,
|
||||
// since we always want to skip any optional packages we can't install.
|
||||
|
@ -513,11 +516,11 @@ module.exports = cls => class Reifier extends cls {
|
|||
checkEngine(node.package, npmVersion, nodeVersion, false)
|
||||
checkPlatform(node.package, false)
|
||||
}
|
||||
await this[_checkBins](node)
|
||||
await this[_extractOrLink](node)
|
||||
await this[_warnDeprecated](node)
|
||||
await this[_loadAncientPackageDetails](node)
|
||||
})
|
||||
.then(() => this[_checkBins](node))
|
||||
.then(() => this[_extractOrLink](node))
|
||||
.then(() => this[_warnDeprecated](node))
|
||||
.then(() => this[_loadAncientPackageDetails](node))
|
||||
|
||||
return this[_handleOptionalFailure](node, p)
|
||||
.then(() => {
|
||||
|
@ -563,10 +566,11 @@ module.exports = cls => class Reifier extends cls {
|
|||
})
|
||||
}
|
||||
|
||||
[_symlink] (node) {
|
||||
async [_symlink] (node) {
|
||||
const dir = dirname(node.path)
|
||||
const target = node.realpath
|
||||
const rel = relative(dir, target)
|
||||
await mkdirp(dir)
|
||||
return symlink(rel, node.path, 'junction')
|
||||
}
|
||||
|
||||
|
@ -633,8 +637,10 @@ module.exports = cls => class Reifier extends cls {
|
|||
[_loadBundlesAndUpdateTrees] (
|
||||
depth = 0, bundlesByDepth = this[_getBundlesByDepth]()
|
||||
) {
|
||||
if (depth === 0)
|
||||
if (depth === 0) {
|
||||
this[_bundleUnpacked] = new Set()
|
||||
process.emit('time', 'reify:loadBundles')
|
||||
}
|
||||
const maxBundleDepth = bundlesByDepth.get('maxBundleDepth')
|
||||
if (depth > maxBundleDepth) {
|
||||
// if we did something, then prune the tree and update the diffs
|
||||
|
@ -650,13 +656,17 @@ module.exports = cls => class Reifier extends cls {
|
|||
// shallower bundle overwriting them with a bundled meta-dep.
|
||||
const set = (bundlesByDepth.get(depth) || [])
|
||||
.filter(node => node.root === this.idealTree &&
|
||||
node.target !== node.root &&
|
||||
!this[_trashList].has(node.path))
|
||||
|
||||
if (!set.length)
|
||||
return this[_loadBundlesAndUpdateTrees](depth + 1, bundlesByDepth)
|
||||
|
||||
// extract all the nodes with bundles
|
||||
return promiseAllRejectLate(set.map(node => this[_reifyNode](node)))
|
||||
return promiseAllRejectLate(set.map(node => {
|
||||
this[_bundleUnpacked].add(node)
|
||||
return this[_reifyNode](node)
|
||||
}))
|
||||
// then load their unpacked children and move into the ideal tree
|
||||
.then(nodes =>
|
||||
promiseAllRejectLate(nodes.map(node => new this.constructor({
|
||||
|
@ -678,8 +688,13 @@ module.exports = cls => class Reifier extends cls {
|
|||
tree: this.diff,
|
||||
visit: diff => {
|
||||
const node = diff.ideal
|
||||
if (node && !node.isProjectRoot && node.package.bundleDependencies &&
|
||||
node.package.bundleDependencies.length) {
|
||||
if (!node)
|
||||
return
|
||||
if (node.isProjectRoot || (node.target && node.target.isProjectRoot))
|
||||
return
|
||||
|
||||
const { bundleDependencies } = node.package
|
||||
if (bundleDependencies && bundleDependencies.length) {
|
||||
maxBundleDepth = Math.max(maxBundleDepth, node.depth)
|
||||
if (!bundlesByDepth.has(node.depth))
|
||||
bundlesByDepth.set(node.depth, [node])
|
||||
|
@ -784,14 +799,14 @@ module.exports = cls => class Reifier extends cls {
|
|||
return
|
||||
|
||||
const node = diff.ideal
|
||||
const bd = node.package.bundleDependencies
|
||||
const sw = this[_shrinkwrapUnpacked].has(node)
|
||||
const bd = this[_bundleUnpacked].has(node)
|
||||
const sw = this[_shrinkwrapInflated].has(node)
|
||||
|
||||
// check whether we still need to unpack this one.
|
||||
// test the inDepBundle last, since that's potentially a tree walk.
|
||||
const doUnpack = node && // can't unpack if removed!
|
||||
!node.isRoot && // root node already exists
|
||||
!(bd && bd.length) && // already unpacked to read bundle
|
||||
!bd && // already unpacked to read bundle
|
||||
!sw && // already unpacked to read sw
|
||||
!node.inDepBundle // already unpacked by another dep's bundle
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
// run in debug mode if explicitly requested, running arborist tests,
|
||||
// or working in the arborist project directory.
|
||||
|
||||
const debug = process.env.ARBORIST_DEBUG !== '0' && (
|
||||
process.env.ARBORIST_DEBUG === '1' ||
|
||||
/\barborist\b/.test(process.env.NODE_DEBUG || '') ||
|
||||
|
@ -21,4 +22,10 @@ const debug = process.env.ARBORIST_DEBUG !== '0' && (
|
|||
)
|
||||
|
||||
module.exports = debug ? fn => fn() : () => {}
|
||||
module.exports.log = (...msg) => module.exports(() => console.error(...msg))
|
||||
const red = process.stderr.isTTY ? msg => `\x1B[31m${msg}\x1B[39m` : m => m
|
||||
module.exports.log = (...msg) => module.exports(() => {
|
||||
const { format } = require('util')
|
||||
const prefix = `\n${process.pid} ${red(format(msg.shift()))} `
|
||||
msg = (prefix + format(...msg).trim().split('\n').join(prefix)).trim()
|
||||
console.error(msg)
|
||||
})
|
||||
|
|
|
@ -11,8 +11,9 @@ const {existsSync} = require('fs')
|
|||
const ssri = require('ssri')
|
||||
|
||||
class Diff {
|
||||
constructor ({actual, ideal, filterSet}) {
|
||||
constructor ({actual, ideal, filterSet, shrinkwrapInflated}) {
|
||||
this.filterSet = filterSet
|
||||
this.shrinkwrapInflated = shrinkwrapInflated
|
||||
this.children = []
|
||||
this.actual = actual
|
||||
this.ideal = ideal
|
||||
|
@ -30,7 +31,7 @@ class Diff {
|
|||
this.removed = []
|
||||
}
|
||||
|
||||
static calculate ({actual, ideal, filterNodes = []}) {
|
||||
static calculate ({actual, ideal, filterNodes = [], shrinkwrapInflated = new Set()}) {
|
||||
// if there's a filterNode, then:
|
||||
// - get the path from the root to the filterNode. The root or
|
||||
// root.target should have an edge either to the filterNode or
|
||||
|
@ -77,7 +78,7 @@ class Diff {
|
|||
}
|
||||
|
||||
return depth({
|
||||
tree: new Diff({actual, ideal, filterSet}),
|
||||
tree: new Diff({actual, ideal, filterSet, shrinkwrapInflated}),
|
||||
getChildren,
|
||||
leave,
|
||||
})
|
||||
|
@ -135,7 +136,7 @@ const allChildren = node => {
|
|||
// to create the diff tree
|
||||
const getChildren = diff => {
|
||||
const children = []
|
||||
const {actual, ideal, unchanged, removed, filterSet} = diff
|
||||
const {actual, ideal, unchanged, removed, filterSet, shrinkwrapInflated} = diff
|
||||
|
||||
// Note: we DON'T diff fsChildren themselves, because they are either
|
||||
// included in the package contents, or part of some other project, and
|
||||
|
@ -144,11 +145,20 @@ const getChildren = diff => {
|
|||
// responsible for installing.
|
||||
const actualKids = allChildren(actual)
|
||||
const idealKids = allChildren(ideal)
|
||||
|
||||
if (ideal && ideal.hasShrinkwrap && !shrinkwrapInflated.has(ideal)) {
|
||||
// Guaranteed to get a diff.leaves here, because we always
|
||||
// be called with a proper Diff object when ideal has a shrinkwrap
|
||||
// that has not been inflated.
|
||||
diff.leaves.push(diff)
|
||||
return children
|
||||
}
|
||||
|
||||
const paths = new Set([...actualKids.keys(), ...idealKids.keys()])
|
||||
for (const path of paths) {
|
||||
const actual = actualKids.get(path)
|
||||
const ideal = idealKids.get(path)
|
||||
diffNode(actual, ideal, children, unchanged, removed, filterSet)
|
||||
diffNode(actual, ideal, children, unchanged, removed, filterSet, shrinkwrapInflated)
|
||||
}
|
||||
|
||||
if (diff.leaves && !children.length)
|
||||
|
@ -157,7 +167,7 @@ const getChildren = diff => {
|
|||
return children
|
||||
}
|
||||
|
||||
const diffNode = (actual, ideal, children, unchanged, removed, filterSet) => {
|
||||
const diffNode = (actual, ideal, children, unchanged, removed, filterSet, shrinkwrapInflated) => {
|
||||
if (filterSet.size && !(filterSet.has(ideal) || filterSet.has(actual)))
|
||||
return
|
||||
|
||||
|
@ -165,10 +175,10 @@ const diffNode = (actual, ideal, children, unchanged, removed, filterSet) => {
|
|||
|
||||
// if it's a match, then get its children
|
||||
// otherwise, this is the child diff node
|
||||
if (action) {
|
||||
if (action || (!shrinkwrapInflated.has(ideal) && ideal.hasShrinkwrap)) {
|
||||
if (action === 'REMOVE')
|
||||
removed.push(actual)
|
||||
children.push(new Diff({actual, ideal, filterSet}))
|
||||
children.push(new Diff({actual, ideal, filterSet, shrinkwrapInflated}))
|
||||
} else {
|
||||
unchanged.push(ideal)
|
||||
// !*! Weird dirty hack warning !*!
|
||||
|
@ -199,7 +209,7 @@ const diffNode = (actual, ideal, children, unchanged, removed, filterSet) => {
|
|||
for (const node of bundledChildren)
|
||||
node.parent = ideal
|
||||
}
|
||||
children.push(...getChildren({actual, ideal, unchanged, removed, filterSet}))
|
||||
children.push(...getChildren({actual, ideal, unchanged, removed, filterSet, shrinkwrapInflated}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,5 +3,6 @@ module.exports.Arborist = module.exports
|
|||
module.exports.Node = require('./node.js')
|
||||
module.exports.Link = require('./link.js')
|
||||
module.exports.Edge = require('./edge.js')
|
||||
module.exports.Shrinkwrap = require('./shrinkwrap.js')
|
||||
// XXX export the other classes, too. shrinkwrap, diff, etc.
|
||||
// they're handy!
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
// where we need to quickly find all instances of a given package name within a
|
||||
// tree.
|
||||
|
||||
const semver = require('semver')
|
||||
const nameFromFolder = require('@npmcli/name-from-folder')
|
||||
const Edge = require('./edge.js')
|
||||
const Inventory = require('./inventory.js')
|
||||
|
@ -885,6 +886,43 @@ class Node {
|
|||
return node.canReplaceWith(this)
|
||||
}
|
||||
|
||||
// return true if it's safe to remove this node, because anything that
|
||||
// is depending on it would be fine with the thing that they would resolve
|
||||
// to if it was removed, or nothing is depending on it in the first place.
|
||||
canDedupe (preferDedupe = false) {
|
||||
// not allowed to mess with shrinkwraps or bundles
|
||||
if (this.inDepBundle || this.inShrinkwrap)
|
||||
return false
|
||||
|
||||
// it's a top level pkg, or a dep of one
|
||||
if (!this.parent || !this.parent.parent)
|
||||
return false
|
||||
|
||||
// no one wants it, remove it
|
||||
if (this.edgesIn.size === 0)
|
||||
return true
|
||||
|
||||
const other = this.parent.parent.resolve(this.name)
|
||||
|
||||
// nothing else, need this one
|
||||
if (!other)
|
||||
return false
|
||||
|
||||
// if it's the same thing, then always fine to remove
|
||||
if (other.matches(this))
|
||||
return true
|
||||
|
||||
// if the other thing can't replace this, then skip it
|
||||
if (!other.canReplace(this))
|
||||
return false
|
||||
|
||||
// if we prefer dedupe, or if the version is greater/equal, take the other
|
||||
if (preferDedupe || semver.gte(other.version, this.version))
|
||||
return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
satisfies (requested) {
|
||||
if (requested instanceof Edge)
|
||||
return this.name === requested.name && requested.satisfiedBy(this)
|
||||
|
|
|
@ -29,6 +29,15 @@ class ArboristNode {
|
|||
this.peer = true
|
||||
if (tree.inBundle)
|
||||
this.bundled = true
|
||||
if (tree.inDepBundle)
|
||||
this.bundler = tree.getBundler().location
|
||||
const bd = tree.package && tree.package.bundleDependencies
|
||||
if (bd && bd.length)
|
||||
this.bundleDependencies = bd
|
||||
if (tree.inShrinkwrap)
|
||||
this.inShrinkwrap = true
|
||||
else if (tree.hasShrinkwrap)
|
||||
this.hasShrinkwrap = true
|
||||
if (tree.error)
|
||||
this.error = treeError(tree.error)
|
||||
if (tree.errors && tree.errors.length)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@npmcli/arborist",
|
||||
"version": "2.3.0",
|
||||
"version": "2.4.1",
|
||||
"description": "Manage node_modules trees",
|
||||
"dependencies": {
|
||||
"@npmcli/installed-package-contents": "^1.0.7",
|
||||
|
@ -19,7 +19,7 @@
|
|||
"npm-install-checks": "^4.0.0",
|
||||
"npm-package-arg": "^8.1.0",
|
||||
"npm-pick-manifest": "^6.1.0",
|
||||
"npm-registry-fetch": "^9.0.0",
|
||||
"npm-registry-fetch": "^10.0.0",
|
||||
"pacote": "^11.2.6",
|
||||
"parse-conflict-json": "^1.1.1",
|
||||
"promise-all-reject-late": "^1.0.0",
|
||||
|
@ -41,8 +41,7 @@
|
|||
"eslint-plugin-standard": "^4.0.1",
|
||||
"minify-registry-metadata": "^2.1.0",
|
||||
"mutate-fs": "^2.1.1",
|
||||
"require-inject": "^1.4.4",
|
||||
"tap": "^14.11.0",
|
||||
"tap": "^15.0.4",
|
||||
"tcompare": "^3.0.4"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -76,10 +75,8 @@
|
|||
"arborist": "bin/index.js"
|
||||
},
|
||||
"tap": {
|
||||
"100": true,
|
||||
"after": "test/fixtures/cleanup.js",
|
||||
"coverage-map": "map.js",
|
||||
"esm": false,
|
||||
"test-env": [
|
||||
"NODE_OPTIONS=--no-warnings"
|
||||
],
|
||||
|
@ -87,6 +84,6 @@
|
|||
"--no-warnings",
|
||||
"--no-deprecation"
|
||||
],
|
||||
"timeout": "120"
|
||||
"timeout": "240"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,11 +197,6 @@ class Config {
|
|||
throw new Error('call config.load() before setting values')
|
||||
if (!confTypes.has(where))
|
||||
throw new Error('invalid config location param: ' + where)
|
||||
if (key === '_auth') {
|
||||
const { email } = this.getCredentialsByURI(this.get('registry'))
|
||||
if (!email)
|
||||
throw new Error('Cannot set _auth without first setting email')
|
||||
}
|
||||
this[_checkDeprecated](key)
|
||||
const { data } = this.data.get(where)
|
||||
data[key] = val
|
||||
|
@ -282,6 +277,14 @@ class Config {
|
|||
// symbols, as that module also does a bunch of get operations
|
||||
this[_loaded] = true
|
||||
|
||||
process.emit('time', 'config:load:credentials')
|
||||
const reg = this.get('registry')
|
||||
const creds = this.getCredentialsByURI(reg)
|
||||
// ignore this error because a failed set will strip out anything that
|
||||
// might be a security hazard, which was the intention.
|
||||
try { this.setCredentialsByURI(reg, creds) } catch (_) {}
|
||||
process.emit('timeEnd', 'config:load:credentials')
|
||||
|
||||
// set proper globalPrefix now that everything is loaded
|
||||
this.globalPrefix = this.get('prefix')
|
||||
|
||||
|
@ -588,14 +591,17 @@ class Config {
|
|||
const nerfed = nerfDart(uri)
|
||||
const def = nerfDart(this.get('registry'))
|
||||
if (def === nerfed) {
|
||||
// do not delete email, that shouldn't be nerfed any more.
|
||||
// just delete the nerfed copy, if one exists.
|
||||
this.delete(`-authtoken`, 'user')
|
||||
this.delete(`_authToken`, 'user')
|
||||
this.delete(`_authtoken`, 'user')
|
||||
this.delete(`_auth`, 'user')
|
||||
this.delete(`_password`, 'user')
|
||||
this.delete(`username`, 'user')
|
||||
this.delete(`email`, 'user')
|
||||
}
|
||||
this.delete(`${nerfed}:-authtoken`, 'user')
|
||||
this.delete(`${nerfed}:_authtoken`, 'user')
|
||||
this.delete(`${nerfed}:_authToken`, 'user')
|
||||
this.delete(`${nerfed}:_auth`, 'user')
|
||||
this.delete(`${nerfed}:_password`, 'user')
|
||||
|
@ -603,7 +609,7 @@ class Config {
|
|||
this.delete(`${nerfed}:email`, 'user')
|
||||
}
|
||||
|
||||
setCredentialsByURI (uri, { token, username, password, email, alwaysAuth }) {
|
||||
setCredentialsByURI (uri, { token, username, password, email }) {
|
||||
const nerfed = nerfDart(uri)
|
||||
const def = nerfDart(this.get('registry'))
|
||||
|
||||
|
@ -611,41 +617,45 @@ class Config {
|
|||
// remove old style auth info not limited to a single registry
|
||||
this.delete('_password', 'user')
|
||||
this.delete('username', 'user')
|
||||
this.delete('email', 'user')
|
||||
this.delete('_auth', 'user')
|
||||
this.delete('_authtoken', 'user')
|
||||
this.delete('-authtoken', 'user')
|
||||
this.delete('_authToken', 'user')
|
||||
}
|
||||
|
||||
this.delete(`${nerfed}:-authtoken`)
|
||||
// email used to be nerfed always. if we're using the default
|
||||
// registry, de-nerf it.
|
||||
if (nerfed === def) {
|
||||
email = email ||
|
||||
this.get('email', 'user') ||
|
||||
this.get(`${nerfed}:email`, 'user')
|
||||
if (email)
|
||||
this.set('email', email, 'user')
|
||||
}
|
||||
|
||||
// field that hasn't been used as documented for a LONG time,
|
||||
// and as of npm 7.10.0, isn't used at all. We just always
|
||||
// send auth if we have it, only to the URIs under the nerf dart.
|
||||
this.delete(`${nerfed}:always-auth`, 'user')
|
||||
|
||||
this.delete(`${nerfed}:-authtoken`, 'user')
|
||||
this.delete(`${nerfed}:_authtoken`, 'user')
|
||||
this.delete(`${nerfed}:email`, 'user')
|
||||
if (token) {
|
||||
this.set(`${nerfed}:_authToken`, token, 'user')
|
||||
this.delete(`${nerfed}:_password`, 'user')
|
||||
this.delete(`${nerfed}:username`, 'user')
|
||||
this.delete(`${nerfed}:email`, 'user')
|
||||
this.delete(`${nerfed}:always-auth`, 'user')
|
||||
} else if (username || password || email) {
|
||||
if (username || password) {
|
||||
if (!username)
|
||||
throw new Error('must include username')
|
||||
if (!password)
|
||||
throw new Error('must include password')
|
||||
}
|
||||
if (!email)
|
||||
throw new Error('must include email')
|
||||
} else if (username || password) {
|
||||
if (!username)
|
||||
throw new Error('must include username')
|
||||
if (!password)
|
||||
throw new Error('must include password')
|
||||
this.delete(`${nerfed}:_authToken`, 'user')
|
||||
if (username || password) {
|
||||
this.set(`${nerfed}:username`, username, 'user')
|
||||
// note: not encrypted, no idea why we bothered to do this, but oh well
|
||||
// protects against shoulder-hacks if password is memorable, I guess?
|
||||
const encoded = Buffer.from(password, 'utf8').toString('base64')
|
||||
this.set(`${nerfed}:_password`, encoded, 'user')
|
||||
}
|
||||
this.set(`${nerfed}:email`, email, 'user')
|
||||
if (alwaysAuth !== undefined)
|
||||
this.set(`${nerfed}:always-auth`, alwaysAuth, 'user')
|
||||
else
|
||||
this.delete(`${nerfed}:always-auth`, 'user')
|
||||
this.set(`${nerfed}:username`, username, 'user')
|
||||
// note: not encrypted, no idea why we bothered to do this, but oh well
|
||||
// protects against shoulder-hacks if password is memorable, I guess?
|
||||
const encoded = Buffer.from(password, 'utf8').toString('base64')
|
||||
this.set(`${nerfed}:_password`, encoded, 'user')
|
||||
} else {
|
||||
throw new Error('No credentials to set.')
|
||||
}
|
||||
|
@ -656,18 +666,12 @@ class Config {
|
|||
const nerfed = nerfDart(uri)
|
||||
const creds = {}
|
||||
|
||||
// you can set always-auth for a single registry, or as a default
|
||||
const alwaysAuthReg = this.get(`${nerfed}:always-auth`)
|
||||
if (alwaysAuthReg !== undefined)
|
||||
creds.alwaysAuth = !!alwaysAuthReg
|
||||
else
|
||||
creds.alwaysAuth = this.get('always-auth')
|
||||
|
||||
const email = this.get(`${nerfed}:email`) || this.get('email')
|
||||
if (email)
|
||||
creds.email = email
|
||||
|
||||
const tokenReg = this.get(`${nerfed}:_authToken`) ||
|
||||
this.get(`${nerfed}:_authtoken`) ||
|
||||
this.get(`${nerfed}:-authtoken`) ||
|
||||
nerfed === nerfDart(this.get('registry')) && this.get('_authToken')
|
||||
|
||||
|
@ -686,6 +690,16 @@ class Config {
|
|||
return creds
|
||||
}
|
||||
|
||||
const authReg = this.get(`${nerfed}:_auth`)
|
||||
if (authReg) {
|
||||
const authDecode = Buffer.from(authReg, 'base64').toString('utf8')
|
||||
const authSplit = authDecode.split(':')
|
||||
creds.username = authSplit.shift()
|
||||
creds.password = authSplit.join(':')
|
||||
creds.auth = authReg
|
||||
return creds
|
||||
}
|
||||
|
||||
// at this point, we can only use the values if the URI is the
|
||||
// default registry.
|
||||
const defaultNerf = nerfDart(this.get('registry'))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@npmcli/config",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
|
@ -24,7 +24,7 @@
|
|||
"coverage-map": "map.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "^14.10.8"
|
||||
"tap": "^15.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"ini": "^2.0.0",
|
||||
|
|
|
@ -103,7 +103,7 @@ terminal, then it is up to the user to end it, of course.
|
|||
- The `package.json` fields described in
|
||||
[RFC183](https://github.com/npm/rfcs/pull/183/files).
|
||||
- `scriptShell` Optional, defaults to `/bin/sh` on Unix, defaults to
|
||||
`env.comspec` or `cmd` on Windows. Custom script to use to execute the
|
||||
`env.ComSpec` or `cmd` on Windows. Custom script to use to execute the
|
||||
command.
|
||||
- `stdio` Optional, defaults to `'pipe'`. The same as the `stdio` argument
|
||||
passed to `child_process` functions in Node.js. Note that if a stdio
|
||||
|
|
|
@ -8,7 +8,7 @@ const makeSpawnArgs = options => {
|
|||
const {
|
||||
event,
|
||||
path,
|
||||
scriptShell = isWindows ? process.env.comspec || 'cmd' : 'sh',
|
||||
scriptShell = isWindows ? process.env.ComSpec || 'cmd' : 'sh',
|
||||
env = {},
|
||||
stdio,
|
||||
cmd,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@npmcli/run-script",
|
||||
"version": "1.8.4",
|
||||
"version": "1.8.5",
|
||||
"description": "Run a lifecycle script for a package (descendant of npm-lifecycle)",
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (https://izs.me)",
|
||||
"license": "ISC",
|
||||
|
@ -25,7 +25,7 @@
|
|||
"eslint-plugin-standard": "^5.0.0",
|
||||
"minipass": "^3.1.1",
|
||||
"require-inject": "^1.4.4",
|
||||
"tap": "^14.11.0"
|
||||
"tap": "^15.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@npmcli/node-gyp": "^1.0.2",
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
tidelift: "npm/balanced-match"
|
||||
patreon: juliangruber
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "chalk",
|
||||
"version": "4.1.0",
|
||||
"version": "4.1.1",
|
||||
"description": "Terminal string styling done right",
|
||||
"license": "MIT",
|
||||
"repository": "chalk/chalk",
|
||||
|
|
|
@ -13,6 +13,48 @@
|
|||
|
||||
<img src="https://cdn.jsdelivr.net/gh/chalk/ansi-styles@8261697c95bf34b6c7767e2cbe9941a851d59385/screenshot.svg" width="900">
|
||||
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<p>
|
||||
<p>
|
||||
<sup>
|
||||
Sindre Sorhus' open source work is supported by the community on <a href="https://github.com/sponsors/sindresorhus">GitHub Sponsors</a> and <a href="https://stakes.social/0x44d871aebF0126Bf646753E2C976Aa7e68A66c15">Dev</a>
|
||||
</sup>
|
||||
</p>
|
||||
<sup>Special thanks to:</sup>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://standardresume.co/tech">
|
||||
<img src="https://sindresorhus.com/assets/thanks/standard-resume-logo.svg" width="160"/>
|
||||
</a>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://retool.com/?utm_campaign=sindresorhus">
|
||||
<img src="https://sindresorhus.com/assets/thanks/retool-logo.svg" width="210"/>
|
||||
</a>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://doppler.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=chalk&utm_source=github">
|
||||
<div>
|
||||
<img src="https://dashboard.doppler.com/imgs/logo-long.svg" width="240" alt="Doppler">
|
||||
</div>
|
||||
<b>All your environment variables, in one place</b>
|
||||
<div>
|
||||
<span>Stop struggling with scattered API keys, hacking together home-brewed tools,</span>
|
||||
<br>
|
||||
<span>and avoiding access controls. Keep your team and servers in sync with Doppler.</span>
|
||||
</div>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Highlights
|
||||
|
||||
- Expressive API
|
||||
|
|
|
@ -12,7 +12,7 @@ function isTestPkg (p) {
|
|||
}
|
||||
|
||||
function niceName (n) {
|
||||
return n.replace(/^node-|[.-]js$/g, '').replace(' ', '-').toLowerCase()
|
||||
return n.replace(/^node-|[.-]js$/g, '').replace(/\s+/g, ' ').replace(/ /g, '-').toLowerCase()
|
||||
}
|
||||
|
||||
function readDeps (test, excluded) { return function (cb) {
|
||||
|
@ -45,7 +45,7 @@ function readDeps (test, excluded) { return function (cb) {
|
|||
})
|
||||
}}
|
||||
|
||||
var name = package.name || basename
|
||||
var name = niceName(package.name || basename)
|
||||
var spec
|
||||
try {
|
||||
spec = npa(name)
|
||||
|
@ -61,7 +61,7 @@ if (scope) {
|
|||
name = scope + '/' + name
|
||||
}
|
||||
}
|
||||
exports.name = yes ? name : prompt('package name', niceName(name), function (data) {
|
||||
exports.name = yes ? name : prompt('package name', name, function (data) {
|
||||
var its = validateName(data)
|
||||
if (its.validForNewPackages) return data
|
||||
var errors = (its.errors || []).concat(its.warnings || [])
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "init-package-json",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"main": "init-package-json.js",
|
||||
"scripts": {
|
||||
"test": "tap",
|
||||
|
@ -17,19 +17,19 @@
|
|||
"description": "A node module to get your node module started",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.1",
|
||||
"npm-package-arg": "^8.1.0",
|
||||
"npm-package-arg": "^8.1.2",
|
||||
"promzard": "^0.3.0",
|
||||
"read": "~1.0.1",
|
||||
"read-package-json": "^3.0.0",
|
||||
"semver": "^7.3.2",
|
||||
"read-package-json": "^3.0.1",
|
||||
"semver": "^7.3.5",
|
||||
"validate-npm-package-license": "^3.0.4",
|
||||
"validate-npm-package-name": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@npmcli/config": "^1.2.1",
|
||||
"@npmcli/config": "^2.1.0",
|
||||
"mkdirp": "^1.0.4",
|
||||
"rimraf": "^3.0.2",
|
||||
"tap": "^14.10.8"
|
||||
"tap": "^14.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Definitions by: Cameron Hunter <https://github.com/cameronhunter>
|
||||
// Modified by: Angus Croll <https://github.com/angus-c>
|
||||
type Operation = "add" | "replace" | "remove";
|
||||
|
||||
type JSONPatchPathConverter<OUTPUT> = (
|
||||
arrayPath: Array<string | number>
|
||||
) => OUTPUT;
|
||||
|
||||
export function diff(
|
||||
a: object | Array<any>,
|
||||
b: object | Array<any>,
|
||||
): Array<{ op: Operation; path: Array<string | number>; value: any }>;
|
||||
|
||||
export function diff<PATH>(
|
||||
a: object | Array<any>,
|
||||
b: object | Array<any>,
|
||||
jsonPatchPathConverter: JSONPatchPathConverter<PATH>
|
||||
): Array<{ op: Operation; path: PATH; value: any }>;
|
||||
|
||||
export const jsonPatchPathConverter: JSONPatchPathConverter<string>;
|
|
@ -0,0 +1,64 @@
|
|||
import diffObj = require('./index');
|
||||
|
||||
const {diff, jsonPatchPathConverter} = diffObj;
|
||||
const obj1 = {a: 2, b: 3};
|
||||
const obj2 = {a: 2, c: 1};
|
||||
const arr1 = [1, 'bee'];
|
||||
const arr2 = [2, 'bee'];
|
||||
|
||||
|
||||
//OK
|
||||
diff(obj1, obj2);
|
||||
diff(arr1, arr2);
|
||||
diff(obj1, arr1);
|
||||
diff(obj2, arr2);
|
||||
diff(/yes/, arr1);
|
||||
diff(new Date(), arr2);
|
||||
|
||||
|
||||
diff(obj1, obj2, jsonPatchPathConverter);
|
||||
diff(arr1, arr2, jsonPatchPathConverter);
|
||||
diff(obj1, arr1, jsonPatchPathConverter);
|
||||
diff(obj2, arr2, jsonPatchPathConverter);
|
||||
|
||||
// not OK
|
||||
// @ts-expect-error
|
||||
diff(obj1);
|
||||
// @ts-expect-error
|
||||
diff(arr2);
|
||||
// @ts-expect-error
|
||||
diff('a');
|
||||
// @ts-expect-error
|
||||
diff(true);
|
||||
|
||||
// @ts-expect-error
|
||||
diff(obj1, 1);
|
||||
// @ts-expect-error
|
||||
diff(3, arr2);
|
||||
// @ts-expect-error
|
||||
diff(obj1, 'a');
|
||||
// @ts-expect-error
|
||||
diff('b', arr2);
|
||||
|
||||
// @ts-expect-error
|
||||
diff('a', jsonPatchPathConverter);
|
||||
// @ts-expect-error
|
||||
diff(true, jsonPatchPathConverter);
|
||||
|
||||
// @ts-expect-error
|
||||
diff(obj1, 1, jsonPatchPathConverter);
|
||||
// @ts-expect-error
|
||||
diff(3, arr2, jsonPatchPathConverter);
|
||||
// @ts-expect-error
|
||||
diff(obj1, 'a', jsonPatchPathConverter);
|
||||
// @ts-expect-error
|
||||
diff('b', arr2, jsonPatchPathConverter);
|
||||
|
||||
// @ts-expect-error
|
||||
diff(obj1, obj2, 'a');
|
||||
// @ts-expect-error
|
||||
diff(arr1, arr2, 1);
|
||||
// @ts-expect-error
|
||||
diff(obj1, arr1, 'bee');
|
||||
// @ts-expect-error
|
||||
diff(obj2, arr2, 'nope');
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "libnpmaccess",
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.2",
|
||||
"description": "programmatic library for `npm access` commands",
|
||||
"author": "Kat Marchán <kzm@sykosomatic.org>",
|
||||
"license": "ISC",
|
||||
|
@ -14,7 +14,7 @@
|
|||
"devDependencies": {
|
||||
"nock": "^12.0.1",
|
||||
"standard": "^14.3.0",
|
||||
"tap": "^14.10.6"
|
||||
"tap": "^14.11.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -25,8 +25,8 @@
|
|||
"dependencies": {
|
||||
"aproba": "^2.0.0",
|
||||
"minipass": "^3.1.1",
|
||||
"npm-package-arg": "^8.0.0",
|
||||
"npm-registry-fetch": "^9.0.0"
|
||||
"npm-package-arg": "^8.1.2",
|
||||
"npm-registry-fetch": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
## v1.0.0
|
||||
|
||||
- Initial implementation, moves the code that used to live in the **npm cli**,
|
||||
ref: https://github.com/npm/cli/blob/release/v7.10.0/lib/exec.js into this
|
||||
separate module, providing a programmatic API to the **npm exec** functionality.
|
|
@ -0,0 +1,15 @@
|
|||
The ISC License
|
||||
|
||||
Copyright (c) GitHub Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,48 @@
|
|||
# libnpmexec
|
||||
|
||||
[](https://npm.im/libnpmexec)
|
||||
[](https://npm.im/libnpmexec)
|
||||
[](https://github.com/npm/libnpmexec/actions?query=workflow%3Anode-ci)
|
||||
[](https://coveralls.io/github/npm/libnpmexec?branch=main)
|
||||
|
||||
The `npm exec` (`npx`) Programmatic API
|
||||
|
||||
## Install
|
||||
|
||||
`npm install libnpmexec`
|
||||
|
||||
## Usage:
|
||||
|
||||
```js
|
||||
const libexec = require('libnpmexec')
|
||||
await libexec({
|
||||
args: ['yosay', 'Bom dia!'],
|
||||
cache: '~/.npm',
|
||||
yes: true,
|
||||
})
|
||||
```
|
||||
|
||||
## API:
|
||||
|
||||
### `libexec(opts)`
|
||||
|
||||
- `opts`:
|
||||
- `args`: List of pkgs to execute **Array<String>**, defaults to `[]`
|
||||
- `call`: An alternative command to run when using `packages` option **String**, defaults to empty string.
|
||||
- `cache`: The path location to where the npm cache folder is placed **String**
|
||||
- `color`: Output should use color? **Boolean**, defaults to `false`
|
||||
- `localBin`: Location to the `node_modules/.bin` folder of the local project **String**, defaults to empty string.
|
||||
- `locationMsg`: Overrides "at location" message when entering interactive mode **String**
|
||||
- `log`: Sets an optional logger **Object**, defaults to `proc-log` module usage.
|
||||
- `globalBin`: Location to the global space bin folder, same as: `$(npm bin -g)` **String**, defaults to empty string.
|
||||
- `output`: A function to print output to **Function**
|
||||
- `packages`: A list of packages to be used (possibly fetch from the registry) **Array<String>**, defaults to `[]`
|
||||
- `path`: Location to where to read local project info (`package.json`) **String**, defaults to `.`
|
||||
- `runPath`: Location to where to execute the script **String**, defaults to `.`
|
||||
- `scriptShell`: Default shell to be used **String**
|
||||
- `yes`: Should skip download confirmation prompt when fetching missing packages from the registry? **Boolean**
|
||||
- `registry`, `cache`, and more options that are forwarded to [@npmcli/arborist](https://github.com/npm/arborist/) and [pacote](https://github.com/npm/pacote/#options) **Object**
|
||||
|
||||
## LICENSE
|
||||
|
||||
[ISC](./LICENSE)
|
|
@ -0,0 +1,19 @@
|
|||
const crypto = require('crypto')
|
||||
|
||||
const { resolve } = require('path')
|
||||
|
||||
const cacheInstallDir = ({ cache, packages }) => {
|
||||
if (!cache)
|
||||
throw new Error('Must provide a valid cache path')
|
||||
|
||||
// only packages not found in ${prefix}/node_modules
|
||||
return resolve(cache, '_npx', getHash(packages))
|
||||
}
|
||||
|
||||
const getHash = (packages) =>
|
||||
crypto.createHash('sha512')
|
||||
.update(packages.sort((a, b) => a.localeCompare(b)).join('\n'))
|
||||
.digest('hex')
|
||||
.slice(0, 16)
|
||||
|
||||
module.exports = cacheInstallDir
|
|
@ -0,0 +1,20 @@
|
|||
const getBinFromManifest = (mani) => {
|
||||
// if we have a bin matching (unscoped portion of) packagename, use that
|
||||
// otherwise if there's 1 bin or all bin value is the same (alias), use
|
||||
// that, otherwise fail
|
||||
const bin = mani.bin || {}
|
||||
if (new Set(Object.values(bin)).size === 1)
|
||||
return Object.keys(bin)[0]
|
||||
|
||||
// XXX probably a util to parse this better?
|
||||
const name = mani.name.replace(/^@[^/]+\//, '')
|
||||
if (bin[name])
|
||||
return name
|
||||
|
||||
// XXX need better error message
|
||||
throw Object.assign(new Error('could not determine executable to run'), {
|
||||
pkgid: mani._id,
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = getBinFromManifest
|
|
@ -0,0 +1,185 @@
|
|||
const { delimiter, resolve } = require('path')
|
||||
const { promisify } = require('util')
|
||||
const read = promisify(require('read'))
|
||||
const stat = promisify(require('fs').stat)
|
||||
|
||||
const Arborist = require('@npmcli/arborist')
|
||||
const ciDetect = require('@npmcli/ci-detect')
|
||||
const logger = require('proc-log')
|
||||
const mkdirp = require('mkdirp-infer-owner')
|
||||
const npa = require('npm-package-arg')
|
||||
const pacote = require('pacote')
|
||||
const readPackageJson = require('read-package-json-fast')
|
||||
|
||||
const cacheInstallDir = require('./cache-install-dir.js')
|
||||
const getBinFromManifest = require('./get-bin-from-manifest.js')
|
||||
const manifestMissing = require('./manifest-missing.js')
|
||||
const noTTY = require('./no-tty.js')
|
||||
const runScript = require('./run-script.js')
|
||||
|
||||
const fileExists = (file) => stat(file)
|
||||
.then((stat) => stat.isFile())
|
||||
.catch(() => false)
|
||||
|
||||
/* istanbul ignore next */
|
||||
const PATH = (
|
||||
process.env.PATH || process.env.Path || process.env.path
|
||||
).split(delimiter)
|
||||
|
||||
const exec = async (opts) => {
|
||||
const {
|
||||
args = [],
|
||||
call = '',
|
||||
color = false,
|
||||
localBin = '',
|
||||
locationMsg = undefined,
|
||||
globalBin = '',
|
||||
output,
|
||||
packages: _packages = [],
|
||||
path = '.',
|
||||
runPath = '.',
|
||||
scriptShell = undefined,
|
||||
yes = undefined,
|
||||
...flatOptions
|
||||
} = opts
|
||||
const log = flatOptions.log || logger
|
||||
|
||||
// dereferences values because we manipulate it later
|
||||
const packages = [..._packages]
|
||||
const pathArr = [...PATH]
|
||||
const _run = () => runScript({
|
||||
args,
|
||||
call,
|
||||
color,
|
||||
flatOptions,
|
||||
locationMsg,
|
||||
log,
|
||||
output,
|
||||
path,
|
||||
pathArr,
|
||||
runPath,
|
||||
scriptShell,
|
||||
})
|
||||
|
||||
// nothing to maybe install, skip the arborist dance
|
||||
if (!call && !args.length && !packages.length)
|
||||
return await _run()
|
||||
|
||||
const needPackageCommandSwap = args.length && !packages.length
|
||||
// if there's an argument and no package has been explicitly asked for
|
||||
// check the local and global bin paths for a binary named the same as
|
||||
// the argument and run it if it exists, otherwise fall through to
|
||||
// the behavior of treating the single argument as a package name
|
||||
if (needPackageCommandSwap) {
|
||||
let binExists = false
|
||||
if (await fileExists(`${localBin}/${args[0]}`)) {
|
||||
pathArr.unshift(localBin)
|
||||
binExists = true
|
||||
} else if (await fileExists(`${globalBin}/${args[0]}`)) {
|
||||
pathArr.unshift(globalBin)
|
||||
binExists = true
|
||||
}
|
||||
|
||||
if (binExists)
|
||||
return await _run()
|
||||
|
||||
packages.push(args[0])
|
||||
}
|
||||
|
||||
// If we do `npm exec foo`, and have a `foo` locally, then we'll
|
||||
// always use that, so we don't really need to fetch the manifest.
|
||||
// So: run npa on each packages entry, and if it is a name with a
|
||||
// rawSpec==='', then try to readPackageJson at
|
||||
// node_modules/${name}/package.json, and only pacote fetch if
|
||||
// that fails.
|
||||
const manis = await Promise.all(packages.map(async p => {
|
||||
const spec = npa(p, path)
|
||||
if (spec.type === 'tag' && spec.rawSpec === '') {
|
||||
// fall through to the pacote.manifest() approach
|
||||
try {
|
||||
const pj = resolve(path, 'node_modules', spec.name, 'package.json')
|
||||
return await readPackageJson(pj)
|
||||
} catch (er) {}
|
||||
}
|
||||
// Force preferOnline to true so we are making sure to pull in the latest
|
||||
// This is especially useful if the user didn't give us a version, and
|
||||
// they expect to be running @latest
|
||||
return await pacote.manifest(p, {
|
||||
...flatOptions,
|
||||
preferOnline: true,
|
||||
})
|
||||
}))
|
||||
|
||||
if (needPackageCommandSwap)
|
||||
args[0] = getBinFromManifest(manis[0])
|
||||
|
||||
// figure out whether we need to install stuff, or if local is fine
|
||||
const localArb = new Arborist({
|
||||
...flatOptions,
|
||||
path,
|
||||
})
|
||||
const tree = await localArb.loadActual()
|
||||
|
||||
// do we have all the packages in manifest list?
|
||||
const needInstall =
|
||||
manis.some(manifest => manifestMissing({ tree, manifest }))
|
||||
|
||||
if (needInstall) {
|
||||
const { cache } = flatOptions
|
||||
const installDir = cacheInstallDir({ cache, packages })
|
||||
await mkdirp(installDir)
|
||||
const arb = new Arborist({
|
||||
...flatOptions,
|
||||
path: installDir,
|
||||
})
|
||||
const tree = await arb.loadActual()
|
||||
|
||||
// at this point, we have to ensure that we get the exact same
|
||||
// version, because it's something that has only ever been installed
|
||||
// by npm exec in the cache install directory
|
||||
const add = manis.filter(mani => manifestMissing({
|
||||
tree,
|
||||
manifest: {
|
||||
...mani,
|
||||
_from: `${mani.name}@${mani.version}`,
|
||||
},
|
||||
}))
|
||||
.map(mani => mani._from)
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
|
||||
// no need to install if already present
|
||||
if (add.length) {
|
||||
if (!yes) {
|
||||
// set -n to always say no
|
||||
if (yes === false)
|
||||
throw new Error('canceled')
|
||||
|
||||
if (noTTY() || ciDetect()) {
|
||||
log.warn('exec', `The following package${
|
||||
add.length === 1 ? ' was' : 's were'
|
||||
} not found and will be installed: ${
|
||||
add.map((pkg) => pkg.replace(/@$/, '')).join(', ')
|
||||
}`)
|
||||
} else {
|
||||
const addList = add.map(a => ` ${a.replace(/@$/, '')}`)
|
||||
.join('\n') + '\n'
|
||||
const prompt = `Need to install the following packages:\n${
|
||||
addList
|
||||
}Ok to proceed? `
|
||||
const confirm = await read({ prompt, default: 'y' })
|
||||
if (confirm.trim().toLowerCase().charAt(0) !== 'y')
|
||||
throw new Error('canceled')
|
||||
}
|
||||
}
|
||||
await arb.reify({
|
||||
...flatOptions,
|
||||
add,
|
||||
})
|
||||
}
|
||||
pathArr.unshift(resolve(installDir, 'node_modules/.bin'))
|
||||
}
|
||||
|
||||
return await _run()
|
||||
}
|
||||
|
||||
module.exports = exec
|
|
@ -0,0 +1,17 @@
|
|||
const manifestMissing = ({ tree, manifest }) => {
|
||||
// if the tree doesn't have a child by that name/version, return true
|
||||
// true means we need to install it
|
||||
const child = tree.children.get(manifest.name)
|
||||
// if no child, we have to load it
|
||||
if (!child)
|
||||
return true
|
||||
|
||||
// if no version/tag specified, allow whatever's there
|
||||
if (manifest._from === `${manifest.name}@`)
|
||||
return false
|
||||
|
||||
// otherwise the version has to match what we WOULD get
|
||||
return child.version !== manifest.version
|
||||
}
|
||||
|
||||
module.exports = manifestMissing
|
|
@ -0,0 +1 @@
|
|||
module.exports = () => !process.stdin.isTTY
|
|
@ -0,0 +1,86 @@
|
|||
const { delimiter } = require('path')
|
||||
|
||||
const chalk = require('chalk')
|
||||
const ciDetect = require('@npmcli/ci-detect')
|
||||
const runScript = require('@npmcli/run-script')
|
||||
const readPackageJson = require('read-package-json-fast')
|
||||
const noTTY = require('./no-tty.js')
|
||||
|
||||
const nocolor = {
|
||||
reset: s => s,
|
||||
bold: s => s,
|
||||
dim: s => s,
|
||||
}
|
||||
|
||||
const run = async ({
|
||||
args,
|
||||
call,
|
||||
color,
|
||||
flatOptions,
|
||||
locationMsg,
|
||||
log,
|
||||
output = () => {},
|
||||
path,
|
||||
pathArr,
|
||||
runPath,
|
||||
scriptShell,
|
||||
}) => {
|
||||
// turn list of args into command string
|
||||
const script = call || args.shift() || scriptShell
|
||||
const colorize = color ? chalk : nocolor
|
||||
|
||||
// do the fakey runScript dance
|
||||
// still should work if no package.json in cwd
|
||||
const realPkg = await readPackageJson(`${path}/package.json`)
|
||||
.catch(() => ({}))
|
||||
const pkg = {
|
||||
...realPkg,
|
||||
scripts: {
|
||||
...(realPkg.scripts || {}),
|
||||
npx: script,
|
||||
},
|
||||
}
|
||||
|
||||
if (log && log.disableProgress)
|
||||
log.disableProgress()
|
||||
|
||||
try {
|
||||
if (script === scriptShell) {
|
||||
const isTTY = !noTTY()
|
||||
|
||||
if (isTTY) {
|
||||
if (ciDetect())
|
||||
return log.warn('exec', 'Interactive mode disabled in CI environment')
|
||||
|
||||
locationMsg = locationMsg || ` at location:\n${colorize.dim(runPath)}`
|
||||
|
||||
output(`${
|
||||
colorize.reset('\nEntering npm script environment')
|
||||
}${
|
||||
colorize.reset(locationMsg)
|
||||
}${
|
||||
colorize.bold('\nType \'exit\' or ^D when finished\n')
|
||||
}`)
|
||||
}
|
||||
}
|
||||
return await runScript({
|
||||
...flatOptions,
|
||||
pkg,
|
||||
banner: false,
|
||||
// we always run in cwd, not --prefix
|
||||
path: runPath,
|
||||
stdioString: true,
|
||||
event: 'npx',
|
||||
args,
|
||||
env: {
|
||||
PATH: pathArr.join(delimiter),
|
||||
},
|
||||
stdio: 'inherit',
|
||||
})
|
||||
} finally {
|
||||
if (log && log.enableProgress)
|
||||
log.enableProgress()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = run
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"name": "libnpmexec",
|
||||
"version": "1.0.1",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"description": "npm exec (npx) programmatic API",
|
||||
"repository": "https://github.com/npm/libnpmexec",
|
||||
"keywords": [
|
||||
"npm",
|
||||
"npmcli",
|
||||
"libnpm",
|
||||
"cli",
|
||||
"workspaces",
|
||||
"libnpmexec"
|
||||
],
|
||||
"author": "GitHub Inc.",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Ruy Adorno",
|
||||
"url": "https://ruyadorno.com",
|
||||
"twitter": "ruyadorno"
|
||||
}
|
||||
],
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"lint": "eslint lib/*.js",
|
||||
"pretest": "npm run lint",
|
||||
"test": "tap test/*.js",
|
||||
"snap": "tap test/*.js",
|
||||
"preversion": "npm test",
|
||||
"postversion": "npm publish",
|
||||
"prepublishOnly": "git push origin --follow-tags"
|
||||
},
|
||||
"tap": {
|
||||
"check-coverage": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"bin-links": "^2.2.1",
|
||||
"eslint": "^7.24.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"tap": "^15.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@npmcli/arborist": "^2.3.0",
|
||||
"@npmcli/ci-detect": "^1.3.0",
|
||||
"@npmcli/run-script": "^1.8.4",
|
||||
"chalk": "^4.1.0",
|
||||
"mkdirp-infer-owner": "^2.0.0",
|
||||
"npm-package-arg": "^8.1.2",
|
||||
"pacote": "^11.3.1",
|
||||
"proc-log": "^1.0.0",
|
||||
"read": "^1.0.7",
|
||||
"read-package-json-fast": "^2.0.2"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "libnpmhook",
|
||||
"version": "6.0.1",
|
||||
"version": "6.0.2",
|
||||
"description": "programmatic API for managing npm registry hooks",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
|
@ -28,7 +28,7 @@
|
|||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"aproba": "^2.0.0",
|
||||
"npm-registry-fetch": "^9.0.0"
|
||||
"npm-registry-fetch": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nock": "^9.6.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "libnpmorg",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.2",
|
||||
"description": "Programmatic api for `npm org` commands",
|
||||
"author": "Kat Marchán <kzm@sykosomatic.org>",
|
||||
"keywords": [
|
||||
|
@ -40,7 +40,7 @@
|
|||
"homepage": "https://npmjs.com/package/libnpmorg",
|
||||
"dependencies": {
|
||||
"aproba": "^2.0.0",
|
||||
"npm-registry-fetch": "^9.0.0"
|
||||
"npm-registry-fetch": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "libnpmpublish",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.1",
|
||||
"description": "Programmatic API for the bits behind npm publish and unpublish",
|
||||
"author": "npm Inc. <support@npmjs.com>",
|
||||
"contributors": [
|
||||
|
@ -44,11 +44,11 @@
|
|||
"bugs": "https://github.com/npm/libnpmpublish/issues",
|
||||
"homepage": "https://npmjs.com/package/libnpmpublish",
|
||||
"dependencies": {
|
||||
"normalize-package-data": "^3.0.0",
|
||||
"npm-package-arg": "^8.1.0",
|
||||
"npm-registry-fetch": "^9.0.0",
|
||||
"normalize-package-data": "^3.0.2",
|
||||
"npm-package-arg": "^8.1.2",
|
||||
"npm-registry-fetch": "^10.0.0",
|
||||
"semver": "^7.1.3",
|
||||
"ssri": "^8.0.0"
|
||||
"ssri": "^8.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "libnpmsearch",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Programmatic API for searching in npm and compatible registries.",
|
||||
"author": "Kat Marchán <kzm@sykosomatic.org>",
|
||||
"files": [
|
||||
|
@ -36,7 +36,7 @@
|
|||
"bugs": "https://github.com/npm/libnpmsearch/issues",
|
||||
"homepage": "https://npmjs.com/package/libnpmsearch",
|
||||
"dependencies": {
|
||||
"npm-registry-fetch": "^9.0.0"
|
||||
"npm-registry-fetch": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "libnpmteam",
|
||||
"description": "npm Team management APIs",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"author": "Kat Marchán <kzm@zkat.tech>",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
|
@ -27,7 +27,7 @@
|
|||
"homepage": "https://npmjs.com/package/libnpmteam",
|
||||
"dependencies": {
|
||||
"aproba": "^2.0.0",
|
||||
"npm-registry-fetch": "^9.0.0"
|
||||
"npm-registry-fetch": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
|
@ -135,9 +135,11 @@ class BundleWalker extends EE {
|
|||
}
|
||||
|
||||
childDep (dep) {
|
||||
if (this.node_modules.indexOf(dep) !== -1 && !this.seen.has(dep)) {
|
||||
this.seen.add(dep)
|
||||
this.child(dep)
|
||||
if (this.node_modules.indexOf(dep) !== -1) {
|
||||
if (!this.seen.has(dep)) {
|
||||
this.seen.add(dep)
|
||||
this.child(dep)
|
||||
}
|
||||
} else if (this.parent) {
|
||||
this.parent.childDep(dep)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "npm-bundled",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"description": "list things in node_modules that are bundledDependencies, or transitive dependencies thereof",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "npm-profile",
|
||||
"version": "5.0.2",
|
||||
"version": "5.0.3",
|
||||
"description": "Library for updating an npmjs.com profile",
|
||||
"keywords": [],
|
||||
"author": "Rebecca Turner <me@re-becca.org> (http://re-becca.org/)",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"npm-registry-fetch": "^9.0.0"
|
||||
"npm-registry-fetch": "^10.0.0"
|
||||
},
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
|
|
@ -309,13 +309,6 @@ previously-generated integrity hash for the saved request information, so
|
|||
`EINTEGRITY` errors can happen if [`opts.cache`](#opts-cache) is used, even if
|
||||
`opts.integrity` is not passed in.
|
||||
|
||||
##### <a name='opts-isFromCI'></a> `opts.isFromCI`
|
||||
|
||||
* Type: Boolean
|
||||
* Default: Based on environment variables
|
||||
|
||||
This is used to populate the `npm-in-ci` request header sent to the registry.
|
||||
|
||||
##### <a name="opts-key"></a> `opts.key`
|
||||
|
||||
* Type: String
|
||||
|
@ -425,6 +418,19 @@ This is a one-time password from a two-factor authenticator. It is required for
|
|||
certain registry interactions when two-factor auth is enabled for a user
|
||||
account.
|
||||
|
||||
##### <a name="opts-otpPrompt"></a> `opts.otpPrompt`
|
||||
|
||||
* Type: Function
|
||||
* Default: null
|
||||
|
||||
This is a method which will be called to provide an OTP if the server
|
||||
responds with a 401 response indicating that a one-time-password is
|
||||
required.
|
||||
|
||||
It may return a promise, which must resolve to the OTP value to be used.
|
||||
If the method fails to provide an OTP value, then the fetch will fail with
|
||||
the auth error that indicated an OTP was needed.
|
||||
|
||||
##### <a name="opts-password"></a> `opts.password`
|
||||
|
||||
* Alias: `_password`
|
||||
|
|
|
@ -1,55 +1,111 @@
|
|||
'use strict'
|
||||
const npa = require('npm-package-arg')
|
||||
|
||||
const defaultOpts = require('./default-opts.js')
|
||||
const url = require('url')
|
||||
// Find the longest registry key that is used for some kind of auth
|
||||
// in the options.
|
||||
const regKeyFromURI = (uri, opts) => {
|
||||
const parsed = new URL(uri)
|
||||
// try to find a config key indicating we have auth for this registry
|
||||
// can be one of :_authToken, :_auth, or :_password and :username
|
||||
// We walk up the "path" until we're left with just //<host>[:<port>],
|
||||
// stopping when we reach '//'.
|
||||
let regKey = `//${parsed.host}${parsed.pathname}`
|
||||
while (regKey.length > '//'.length) {
|
||||
// got some auth for this URI
|
||||
if (hasAuth(regKey, opts))
|
||||
return regKey
|
||||
|
||||
// can be either //host/some/path/:_auth or //host/some/path:_auth
|
||||
// walk up by removing EITHER what's after the slash OR the slash itself
|
||||
regKey = regKey.replace(/([^/]+|\/)$/, '')
|
||||
}
|
||||
}
|
||||
|
||||
const hasAuth = (regKey, opts) => (
|
||||
opts[`${regKey}:_authToken`] ||
|
||||
opts[`${regKey}:_auth`] ||
|
||||
opts[`${regKey}:username`] && opts[`${regKey}:_password`]
|
||||
)
|
||||
|
||||
const sameHost = (a, b) => {
|
||||
const parsedA = new URL(a)
|
||||
const parsedB = new URL(b)
|
||||
return parsedA.host === parsedB.host
|
||||
}
|
||||
|
||||
const getRegistry = opts => {
|
||||
const { spec } = opts
|
||||
const { scope: specScope, subSpec } = spec ? npa(spec) : {}
|
||||
const subSpecScope = subSpec && subSpec.scope
|
||||
const scope = subSpec ? subSpecScope : specScope
|
||||
const scopeReg = scope && opts[`${scope}:registry`]
|
||||
return scopeReg || opts.registry
|
||||
}
|
||||
|
||||
const getAuth = (uri, opts = {}) => {
|
||||
const { forceAuth } = opts
|
||||
if (!uri)
|
||||
throw new Error('URI is required')
|
||||
const regKey = regKeyFromURI(uri, forceAuth || opts)
|
||||
|
||||
// we are only allowed to use what's in forceAuth if specified
|
||||
if (forceAuth && !regKey) {
|
||||
return new Auth({
|
||||
scopeAuthKey: null,
|
||||
token: forceAuth._authToken,
|
||||
username: forceAuth.username,
|
||||
password: forceAuth._password || forceAuth.password,
|
||||
auth: forceAuth._auth || forceAuth.auth,
|
||||
})
|
||||
}
|
||||
|
||||
// no auth for this URI, but might have it for the registry
|
||||
if (!regKey) {
|
||||
const registry = getRegistry(opts)
|
||||
if (registry && uri !== registry && sameHost(uri, registry))
|
||||
return getAuth(registry, opts)
|
||||
else if (registry !== opts.registry) {
|
||||
// If making a tarball request to a different base URI than the
|
||||
// registry where we logged in, but the same auth SHOULD be sent
|
||||
// to that artifact host, then we track where it was coming in from,
|
||||
// and warn the user if we get a 4xx error on it.
|
||||
const scopeAuthKey = regKeyFromURI(registry, opts)
|
||||
return new Auth({ scopeAuthKey })
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
[`${regKey}:_authToken`]: token,
|
||||
[`${regKey}:username`]: username,
|
||||
[`${regKey}:_password`]: password,
|
||||
[`${regKey}:_auth`]: auth,
|
||||
} = opts
|
||||
|
||||
return new Auth({
|
||||
scopeAuthKey: null,
|
||||
token,
|
||||
auth,
|
||||
username,
|
||||
password,
|
||||
})
|
||||
}
|
||||
|
||||
class Auth {
|
||||
constructor ({ token, auth, username, password, scopeAuthKey }) {
|
||||
this.scopeAuthKey = scopeAuthKey
|
||||
this.token = null
|
||||
this.auth = null
|
||||
this.isBasicAuth = false
|
||||
if (token)
|
||||
this.token = token
|
||||
else if (auth)
|
||||
this.auth = auth
|
||||
else if (username && password) {
|
||||
const p = Buffer.from(password, 'base64').toString('utf8')
|
||||
this.auth = Buffer.from(`${username}:${p}`, 'utf8').toString('base64')
|
||||
this.isBasicAuth = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = getAuth
|
||||
function getAuth (registry, opts_ = {}) {
|
||||
if (!registry)
|
||||
throw new Error('registry is required')
|
||||
const opts = opts_.forceAuth ? opts_.forceAuth : { ...defaultOpts, ...opts_ }
|
||||
const AUTH = {}
|
||||
const regKey = registry && registryKey(registry)
|
||||
const doKey = (key, alias) => addKey(opts, AUTH, regKey, key, alias)
|
||||
doKey('token')
|
||||
doKey('_authToken', 'token')
|
||||
doKey('username')
|
||||
doKey('password')
|
||||
doKey('_password', 'password')
|
||||
doKey('email')
|
||||
doKey('_auth')
|
||||
doKey('otp')
|
||||
doKey('always-auth', 'alwaysAuth')
|
||||
if (AUTH.password)
|
||||
AUTH.password = Buffer.from(AUTH.password, 'base64').toString('utf8')
|
||||
|
||||
if (AUTH._auth && !(AUTH.username && AUTH.password)) {
|
||||
let auth = Buffer.from(AUTH._auth, 'base64').toString()
|
||||
auth = auth.split(':')
|
||||
AUTH.username = auth.shift()
|
||||
AUTH.password = auth.join(':')
|
||||
}
|
||||
AUTH.alwaysAuth = AUTH.alwaysAuth === 'false' ? false : !!AUTH.alwaysAuth
|
||||
return AUTH
|
||||
}
|
||||
|
||||
function addKey (opts, obj, scope, key, objKey) {
|
||||
if (opts[key])
|
||||
obj[objKey || key] = opts[key]
|
||||
|
||||
if (scope && opts[`${scope}:${key}`])
|
||||
obj[objKey || key] = opts[`${scope}:${key}`]
|
||||
}
|
||||
|
||||
// Called a nerf dart in the main codebase. Used as a "safe"
|
||||
// key when fetching registry info from config.
|
||||
function registryKey (registry) {
|
||||
const parsed = new url.URL(registry)
|
||||
const formatted = url.format({
|
||||
protocol: parsed.protocol,
|
||||
host: parsed.host,
|
||||
pathname: parsed.pathname,
|
||||
slashes: true,
|
||||
})
|
||||
return url.format(new url.URL('.', formatted)).replace(/^[^:]+:/, '')
|
||||
}
|
||||
|
|
|
@ -5,15 +5,25 @@ const LRU = require('lru-cache')
|
|||
const { Response } = require('minipass-fetch')
|
||||
const defaultOpts = require('./default-opts.js')
|
||||
|
||||
module.exports = checkResponse
|
||||
function checkResponse (method, res, registry, startTime, opts_ = {}) {
|
||||
const opts = { ...defaultOpts, ...opts_ }
|
||||
const checkResponse = async ({ method, uri, res, registry, startTime, auth, opts }) => {
|
||||
opts = { ...defaultOpts, ...opts }
|
||||
if (res.headers.has('npm-notice') && !res.headers.has('x-local-cache'))
|
||||
opts.log.notice('', res.headers.get('npm-notice'))
|
||||
|
||||
checkWarnings(res, registry, opts)
|
||||
if (res.status >= 400) {
|
||||
logRequest(method, res, startTime, opts)
|
||||
if (auth && auth.scopeAuthKey && !auth.token && !auth.auth) {
|
||||
// we didn't have auth for THIS request, but we do have auth for
|
||||
// requests to the registry indicated by the spec's scope value.
|
||||
// Warn the user.
|
||||
opts.log.warn('registry', `No auth for URI, but auth present for scoped registry.
|
||||
|
||||
URI: ${uri}
|
||||
Scoped Registry Key: ${auth.scopeAuthKey}
|
||||
|
||||
More info here: https://github.com/npm/cli/wiki/No-auth-for-URI,-but-auth-present-for-scoped-registry`)
|
||||
}
|
||||
return checkErrors(method, res, startTime, opts)
|
||||
} else {
|
||||
res.body.on('end', () => logRequest(method, res, startTime, opts))
|
||||
|
@ -24,6 +34,7 @@ function checkResponse (method, res, registry, startTime, opts_ = {}) {
|
|||
return res
|
||||
}
|
||||
}
|
||||
module.exports = checkResponse
|
||||
|
||||
function logRequest (method, res, startTime, opts) {
|
||||
const elapsedTime = Date.now() - startTime
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
const pkg = require('./package.json')
|
||||
const ciDetect = require('@npmcli/ci-detect')
|
||||
module.exports = {
|
||||
isFromCI: ciDetect(),
|
||||
log: require('./silentlog.js'),
|
||||
maxSockets: 12,
|
||||
method: 'GET',
|
||||
|
|
|
@ -22,6 +22,7 @@ function packageName (href) {
|
|||
class HttpErrorBase extends Error {
|
||||
constructor (method, res, body, spec) {
|
||||
super()
|
||||
this.name = this.constructor.name
|
||||
this.headers = res.headers.raw()
|
||||
this.statusCode = res.status
|
||||
this.code = `E${res.status}`
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const { HttpErrorAuthOTP } = require('./errors.js')
|
||||
const checkResponse = require('./check-response.js')
|
||||
const getAuth = require('./auth.js')
|
||||
const fetch = require('make-fetch-happen')
|
||||
|
@ -27,26 +28,32 @@ function regFetch (uri, /* istanbul ignore next */ opts_ = {}) {
|
|||
...defaultOpts,
|
||||
...opts_,
|
||||
}
|
||||
const registry = opts.registry = (
|
||||
(opts.spec && pickRegistry(opts.spec, opts)) ||
|
||||
opts.registry ||
|
||||
/* istanbul ignore next */
|
||||
'https://registry.npmjs.org/'
|
||||
)
|
||||
|
||||
if (!urlIsValid(uri)) {
|
||||
// if we did not get a fully qualified URI, then we look at the registry
|
||||
// config or relevant scope to resolve it.
|
||||
const uriValid = urlIsValid(uri)
|
||||
let registry = opts.registry || defaultOpts.registry
|
||||
if (!uriValid) {
|
||||
registry = opts.registry = (
|
||||
(opts.spec && pickRegistry(opts.spec, opts)) ||
|
||||
opts.registry ||
|
||||
registry
|
||||
)
|
||||
uri = `${
|
||||
registry.trim().replace(/\/?$/g, '')
|
||||
}/${
|
||||
uri.trim().replace(/^\//, '')
|
||||
}`
|
||||
// asserts that this is now valid
|
||||
new url.URL(uri)
|
||||
}
|
||||
|
||||
const method = opts.method || 'GET'
|
||||
|
||||
// through that takes into account the scope, the prefix of `uri`, etc
|
||||
const startTime = Date.now()
|
||||
const headers = getHeaders(registry, uri, opts)
|
||||
const auth = getAuth(uri, opts)
|
||||
const headers = getHeaders(uri, auth, opts)
|
||||
let body = opts.body
|
||||
const bodyIsStream = Minipass.isStream(body)
|
||||
const bodyIsPromise = body &&
|
||||
|
@ -92,34 +99,57 @@ function regFetch (uri, /* istanbul ignore next */ opts_ = {}) {
|
|||
opts.preferOnline = true
|
||||
}
|
||||
|
||||
const doFetch = (body) => fetch(uri, {
|
||||
agent: opts.agent,
|
||||
algorithms: opts.algorithms,
|
||||
body,
|
||||
cache: getCacheMode(opts),
|
||||
cacheManager: opts.cache,
|
||||
ca: opts.ca,
|
||||
cert: opts.cert,
|
||||
headers,
|
||||
integrity: opts.integrity,
|
||||
key: opts.key,
|
||||
localAddress: opts.localAddress,
|
||||
maxSockets: opts.maxSockets,
|
||||
memoize: opts.memoize,
|
||||
method: method,
|
||||
noProxy: opts.noProxy,
|
||||
proxy: opts.httpsProxy || opts.proxy,
|
||||
retry: opts.retry ? opts.retry : {
|
||||
retries: opts.fetchRetries,
|
||||
factor: opts.fetchRetryFactor,
|
||||
minTimeout: opts.fetchRetryMintimeout,
|
||||
maxTimeout: opts.fetchRetryMaxtimeout,
|
||||
},
|
||||
strictSSL: opts.strictSSL,
|
||||
timeout: opts.timeout || 30 * 1000,
|
||||
}).then(res => checkResponse(
|
||||
method, res, registry, startTime, opts
|
||||
))
|
||||
const doFetch = async body => {
|
||||
const p = fetch(uri, {
|
||||
agent: opts.agent,
|
||||
algorithms: opts.algorithms,
|
||||
body,
|
||||
cache: getCacheMode(opts),
|
||||
cacheManager: opts.cache,
|
||||
ca: opts.ca,
|
||||
cert: opts.cert,
|
||||
headers,
|
||||
integrity: opts.integrity,
|
||||
key: opts.key,
|
||||
localAddress: opts.localAddress,
|
||||
maxSockets: opts.maxSockets,
|
||||
memoize: opts.memoize,
|
||||
method: method,
|
||||
noProxy: opts.noProxy,
|
||||
proxy: opts.httpsProxy || opts.proxy,
|
||||
retry: opts.retry ? opts.retry : {
|
||||
retries: opts.fetchRetries,
|
||||
factor: opts.fetchRetryFactor,
|
||||
minTimeout: opts.fetchRetryMintimeout,
|
||||
maxTimeout: opts.fetchRetryMaxtimeout,
|
||||
},
|
||||
strictSSL: opts.strictSSL,
|
||||
timeout: opts.timeout || 30 * 1000,
|
||||
}).then(res => checkResponse({
|
||||
method,
|
||||
uri,
|
||||
res,
|
||||
registry,
|
||||
startTime,
|
||||
auth,
|
||||
opts,
|
||||
}))
|
||||
|
||||
if (typeof opts.otpPrompt === 'function') {
|
||||
return p.catch(async er => {
|
||||
if (er instanceof HttpErrorAuthOTP) {
|
||||
// if otp fails to complete, we fail with that failure
|
||||
const otp = await opts.otpPrompt()
|
||||
// if no otp provided, throw the original HTTP error
|
||||
if (!otp)
|
||||
throw er
|
||||
return regFetch(uri, { ...opts, otp })
|
||||
}
|
||||
throw er
|
||||
})
|
||||
} else
|
||||
return p
|
||||
}
|
||||
|
||||
return Promise.resolve(body).then(doFetch)
|
||||
}
|
||||
|
@ -151,7 +181,7 @@ function pickRegistry (spec, opts = {}) {
|
|||
registry = opts[opts.scope.replace(/^@?/, '@') + ':registry']
|
||||
|
||||
if (!registry)
|
||||
registry = opts.registry || 'https://registry.npmjs.org/'
|
||||
registry = opts.registry || defaultOpts.registry
|
||||
|
||||
return registry
|
||||
}
|
||||
|
@ -163,9 +193,8 @@ function getCacheMode (opts) {
|
|||
: 'default'
|
||||
}
|
||||
|
||||
function getHeaders (registry, uri, opts) {
|
||||
function getHeaders (uri, auth, opts) {
|
||||
const headers = Object.assign({
|
||||
'npm-in-ci': !!opts.isFromCI,
|
||||
'user-agent': opts.userAgent,
|
||||
}, opts.headers || {})
|
||||
|
||||
|
@ -178,25 +207,15 @@ function getHeaders (registry, uri, opts) {
|
|||
if (opts.npmCommand)
|
||||
headers['npm-command'] = opts.npmCommand
|
||||
|
||||
const auth = getAuth(registry, opts)
|
||||
// If a tarball is hosted on a different place than the manifest, only send
|
||||
// credentials on `alwaysAuth`
|
||||
const shouldAuth = (
|
||||
auth.alwaysAuth ||
|
||||
new url.URL(uri).host === new url.URL(registry).host
|
||||
)
|
||||
if (shouldAuth && auth.token)
|
||||
if (auth.token)
|
||||
headers.authorization = `Bearer ${auth.token}`
|
||||
else if (shouldAuth && auth.username && auth.password) {
|
||||
const encoded = Buffer.from(
|
||||
`${auth.username}:${auth.password}`, 'utf8'
|
||||
).toString('base64')
|
||||
headers.authorization = `Basic ${encoded}`
|
||||
} else if (shouldAuth && auth._auth)
|
||||
headers.authorization = `Basic ${auth._auth}`
|
||||
else if (auth.auth)
|
||||
headers.authorization = `Basic ${auth.auth}`
|
||||
|
||||
if (shouldAuth && auth.otp)
|
||||
headers['npm-otp'] = auth.otp
|
||||
if (opts.otp)
|
||||
headers['npm-otp'] = opts.otp
|
||||
|
||||
return headers
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "npm-registry-fetch",
|
||||
"version": "9.0.0",
|
||||
"version": "10.1.1",
|
||||
"description": "Fetch-based http client for use with npm registry APIs",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
|
@ -29,7 +29,6 @@
|
|||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@npmcli/ci-detect": "^1.0.0",
|
||||
"lru-cache": "^6.0.0",
|
||||
"make-fetch-happen": "^8.0.9",
|
||||
"minipass": "^3.1.3",
|
||||
|
@ -51,7 +50,7 @@
|
|||
"require-inject": "^1.4.4",
|
||||
"rimraf": "^2.6.2",
|
||||
"ssri": "^8.0.0",
|
||||
"tap": "^14.10.7"
|
||||
"tap": "^15.0.4"
|
||||
},
|
||||
"tap": {
|
||||
"check-coverage": true,
|
||||
|
|
|
@ -40,6 +40,7 @@ const _istream = Symbol('_istream')
|
|||
const _assertType = Symbol('_assertType')
|
||||
const _tarballFromCache = Symbol('_tarballFromCache')
|
||||
const _tarballFromResolved = Symbol.for('pacote.Fetcher._tarballFromResolved')
|
||||
const _cacheFetches = Symbol.for('pacote.Fetcher._cacheFetches')
|
||||
|
||||
class FetcherBase {
|
||||
constructor (spec, opts) {
|
||||
|
@ -166,25 +167,19 @@ class FetcherBase {
|
|||
}
|
||||
|
||||
// private, should be overridden.
|
||||
// Note that they should *not* calculate or check integrity, but *just*
|
||||
// return the raw tarball data stream.
|
||||
// Note that they should *not* calculate or check integrity or cache,
|
||||
// but *just* return the raw tarball data stream.
|
||||
[_tarballFromResolved] () {
|
||||
throw this.notImplementedError
|
||||
}
|
||||
|
||||
// public, should not be overridden
|
||||
tarball () {
|
||||
return this.tarballStream(stream => new Promise((res, rej) => {
|
||||
const buf = []
|
||||
stream.on('error', er => rej(er))
|
||||
stream.on('end', () => {
|
||||
const data = Buffer.concat(buf)
|
||||
data.integrity = this.integrity && String(this.integrity)
|
||||
data.resolved = this.resolved
|
||||
data.from = this.from
|
||||
return res(data)
|
||||
})
|
||||
stream.on('data', d => buf.push(d))
|
||||
return this.tarballStream(stream => stream.concat().then(data => {
|
||||
data.integrity = this.integrity && String(this.integrity)
|
||||
data.resolved = this.resolved
|
||||
data.from = this.from
|
||||
return data
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -194,6 +189,10 @@ class FetcherBase {
|
|||
return cacache.get.stream.byDigest(this.cache, this.integrity, this.opts)
|
||||
}
|
||||
|
||||
get [_cacheFetches] () {
|
||||
return true
|
||||
}
|
||||
|
||||
[_istream] (stream) {
|
||||
// everyone will need one of these, either for verifying or calculating
|
||||
// We always set it, because we have might only have a weak legacy hex
|
||||
|
@ -203,7 +202,31 @@ class FetcherBase {
|
|||
// gets to the point of re-setting the integrity.
|
||||
const istream = ssri.integrityStream(this.opts)
|
||||
istream.on('integrity', i => this.integrity = i)
|
||||
return stream.on('error', er => istream.emit('error', er)).pipe(istream)
|
||||
stream.on('error', er => istream.emit('error', er))
|
||||
|
||||
// if not caching this, just pipe through to the istream and return it
|
||||
if (!this.opts.cache || !this[_cacheFetches])
|
||||
return stream.pipe(istream)
|
||||
|
||||
// we have to return a stream that gets ALL the data, and proxies errors,
|
||||
// but then pipe from the original tarball stream into the cache as well.
|
||||
// To do this without losing any data, and since the cacache put stream
|
||||
// is not a passthrough, we have to pipe from the original stream into
|
||||
// the cache AFTER we pipe into the istream. Since the cache stream
|
||||
// has an asynchronous flush to write its contents to disk, we need to
|
||||
// defer the istream end until the cache stream ends.
|
||||
stream.pipe(istream, { end: false })
|
||||
const cstream = cacache.put.stream(
|
||||
this.opts.cache,
|
||||
`pacote:tarball:${this.from}`,
|
||||
this.opts
|
||||
)
|
||||
stream.pipe(cstream)
|
||||
// defer istream end until after cstream
|
||||
// cache write errors should not crash the fetch, this is best-effort.
|
||||
cstream.promise().catch(() => {}).then(() => istream.end())
|
||||
|
||||
return istream
|
||||
}
|
||||
|
||||
pickIntegrityAlgorithm () {
|
||||
|
@ -232,7 +255,9 @@ class FetcherBase {
|
|||
// An ENOENT trying to read a tgz file, for example, is Right Out.
|
||||
isRetriableError (er) {
|
||||
// TODO: check error class, once those are rolled out to our deps
|
||||
return this.isDataCorruptionError(er) || er.code === 'ENOENT'
|
||||
return this.isDataCorruptionError(er) ||
|
||||
er.code === 'ENOENT' ||
|
||||
er.code === 'EISDIR'
|
||||
}
|
||||
|
||||
// Mostly internal, but has some uses
|
||||
|
|
|
@ -3,6 +3,7 @@ const RemoteFetcher = require('./remote.js')
|
|||
const _tarballFromResolved = Symbol.for('pacote.Fetcher._tarballFromResolved')
|
||||
const pacoteVersion = require('../package.json').version
|
||||
const npa = require('npm-package-arg')
|
||||
const rpj = require('read-package-json-fast')
|
||||
const pickManifest = require('npm-pick-manifest')
|
||||
const ssri = require('ssri')
|
||||
const Minipass = require('minipass')
|
||||
|
@ -156,7 +157,8 @@ class RegistryFetcher extends Fetcher {
|
|||
}
|
||||
if (this.integrity)
|
||||
mani._integrity = String(this.integrity)
|
||||
return this.package = mani
|
||||
this.package = rpj.normalize(mani)
|
||||
return this.package
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ const Minipass = require('minipass')
|
|||
// The default registry URL is a string of great magic.
|
||||
const magic = /^https?:\/\/registry\.npmjs\.org\//
|
||||
|
||||
const _cacheFetches = Symbol.for('pacote.Fetcher._cacheFetches')
|
||||
const _headers = Symbol('_headers')
|
||||
class RemoteFetcher extends Fetcher {
|
||||
constructor (spec, opts) {
|
||||
|
@ -21,6 +22,12 @@ class RemoteFetcher extends Fetcher {
|
|||
this.pkgid = opts.pkgid ? opts.pkgid : `remote:${nameat}${this.resolved}`
|
||||
}
|
||||
|
||||
// Don't need to cache tarball fetches in pacote, because make-fetch-happen
|
||||
// will write into cacache anyway.
|
||||
get [_cacheFetches] () {
|
||||
return false
|
||||
}
|
||||
|
||||
[_tarballFromResolved] () {
|
||||
const stream = new Minipass()
|
||||
const fetchOpts = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "pacote",
|
||||
"version": "11.3.1",
|
||||
"version": "11.3.3",
|
||||
"description": "JavaScript package downloader",
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (https://izs.me)",
|
||||
"bin": {
|
||||
|
@ -17,15 +17,12 @@
|
|||
},
|
||||
"tap": {
|
||||
"timeout": 300,
|
||||
"check-coverage": true,
|
||||
"coverage-map": "map.js",
|
||||
"esm": false
|
||||
"coverage-map": "map.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mutate-fs": "^2.1.1",
|
||||
"npm-registry-mock": "^1.3.1",
|
||||
"require-inject": "^1.4.4",
|
||||
"tap": "^14.11.0"
|
||||
"tap": "^15.0.4"
|
||||
},
|
||||
"files": [
|
||||
"lib/**/*.js"
|
||||
|
@ -49,7 +46,7 @@
|
|||
"npm-package-arg": "^8.0.1",
|
||||
"npm-packlist": "^2.1.4",
|
||||
"npm-pick-manifest": "^6.0.0",
|
||||
"npm-registry-fetch": "^9.0.0",
|
||||
"npm-registry-fetch": "^10.0.0",
|
||||
"promise-retry": "^2.0.1",
|
||||
"read-package-json-fast": "^2.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
The ISC License
|
||||
|
||||
Copyright (c) GitHub, Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,33 @@
|
|||
# proc-log
|
||||
|
||||
Emits 'log' events on the process object which a log output listener can
|
||||
consume and print to the terminal.
|
||||
|
||||
This is used by various modules within the npm CLI stack in order to send
|
||||
log events that [`npmlog`](http://npm.im/npmlog) can consume and print.
|
||||
|
||||
## API
|
||||
|
||||
* `log.error(...args)` calls `process.emit('log', 'error', ...args)`
|
||||
The highest log level. For printing extremely serious errors that
|
||||
indicate something went wrong.
|
||||
* `log.warn(...args)` calls `process.emit('log', 'warn', ...args)`
|
||||
A fairly high log level. Things that the user needs to be aware of, but
|
||||
which won't necessarily cause improper functioning of the system.
|
||||
* `log.notice(...args)` calls `process.emit('log', 'notice', ...args)`
|
||||
Notices which are important, but not necessarily dangerous or a cause for
|
||||
excess concern.
|
||||
* `log.info(...args)` calls `process.emit('log', 'info', ...args)`
|
||||
Informative messages that may benefit the user, but aren't particularly
|
||||
important.
|
||||
* `log.verbose(...args)` calls `process.emit('log', 'verbose', ...args)`
|
||||
Noisy output that is more detail that most users will care about.
|
||||
* `log.silly(...args)` calls `process.emit('log', 'silly', ...args)`
|
||||
Extremely noisy excessive logging messages that are typically only useful
|
||||
for debugging.
|
||||
* `log.http(...args)` calls `process.emit('log', 'http', ...args)`
|
||||
Information about HTTP requests made and/or completed.
|
||||
* `log.pause(...args)` calls `process.emit('log', 'pause')` Used to tell
|
||||
the consumer to stop printing messages.
|
||||
* `log.resume(...args)` calls `process.emit('log', 'resume', ...args)`
|
||||
Used to tell the consumer that it is ok to print messages again.
|
|
@ -0,0 +1,22 @@
|
|||
// emits 'log' events on the process
|
||||
const LEVELS = [
|
||||
'notice',
|
||||
'error',
|
||||
'warn',
|
||||
'info',
|
||||
'verbose',
|
||||
'http',
|
||||
'silly',
|
||||
'pause',
|
||||
'resume',
|
||||
]
|
||||
|
||||
const log = level => (...args) => process.emit('log', level, ...args)
|
||||
|
||||
const logger = {}
|
||||
for (const level of LEVELS)
|
||||
logger[level] = log(level)
|
||||
|
||||
logger.LEVELS = LEVELS
|
||||
|
||||
module.exports = logger
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "proc-log",
|
||||
"version": "1.0.0",
|
||||
"files": [
|
||||
"index.js"
|
||||
],
|
||||
"description": "just emit 'log' events on the process object",
|
||||
"repository": "https://github.com/npm/proc-log",
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (https://izs.me)",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"test": "tap",
|
||||
"snap": "tap",
|
||||
"posttest": "eslint index.js test/*.js",
|
||||
"postsnap": "eslint index.js test/*.js --fix",
|
||||
"preversion": "npm test",
|
||||
"postversion": "npm publish",
|
||||
"prepublishOnly": "git push origin --follow-tags"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.9.0",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"tap": "^15.0.2"
|
||||
}
|
||||
}
|
|
@ -409,7 +409,7 @@ Note: When using 4a please specify type: 4, and when using 5h please specify typ
|
|||
| --- | :---: | :---: | :---: | :---: | :---: |
|
||||
| SOCKS v4 | ✅ | ❌ | ✅ | ❌ | ❌ |
|
||||
| SOCKS v4a | ✅ | ❌ | ✅ | ❌ | ✅ |
|
||||
| SOCKS v5 (includes 5hh) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| SOCKS v5 (includes v5h) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
### new SocksClient(options)
|
||||
|
||||
|
|
|
@ -690,7 +690,7 @@ class SocksClient extends events_1.EventEmitter {
|
|||
if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.connect) {
|
||||
this.setState(constants_1.SocksClientState.Established);
|
||||
this.removeInternalSocketHandlers();
|
||||
this.emit('established', { socket: this.socket });
|
||||
this.emit('established', { remoteHost, socket: this.socket });
|
||||
}
|
||||
else if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.bind) {
|
||||
/* If using BIND, the Socks client is now in BoundWaitingForConnection state.
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "socks",
|
||||
"private": false,
|
||||
"version": "2.6.0",
|
||||
"version": "2.6.1",
|
||||
"description": "Fully featured SOCKS proxy client supporting SOCKSv4, SOCKSv4a, and SOCKSv5. Includes Bind and Associate functionality.",
|
||||
"main": "build/index.js",
|
||||
"typings": "typings/index.d.ts",
|
||||
|
@ -34,8 +34,8 @@
|
|||
"readmeFilename": "README.md",
|
||||
"devDependencies": {
|
||||
"@types/ip": "1.1.0",
|
||||
"@types/mocha": "^8.2.1",
|
||||
"@types/node": "^14.14.35",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/node": "^14.14.41",
|
||||
"coveralls": "3.1.0",
|
||||
"mocha": "^8.3.2",
|
||||
"nyc": "15.1.0",
|
||||
|
@ -44,7 +44,7 @@
|
|||
"ts-node": "^9.1.1",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-config-airbnb": "^5.11.2",
|
||||
"typescript": "^4.2.3"
|
||||
"typescript": "^4.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"ip": "^1.1.5",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "7.10.0",
|
||||
"version": "7.11.2",
|
||||
"name": "npm",
|
||||
"description": "a package manager for JavaScript",
|
||||
"keywords": [
|
||||
|
@ -42,10 +42,10 @@
|
|||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@npmcli/arborist": "^2.3.0",
|
||||
"@npmcli/arborist": "^2.4.1",
|
||||
"@npmcli/ci-detect": "^1.2.0",
|
||||
"@npmcli/config": "^2.1.0",
|
||||
"@npmcli/run-script": "^1.8.4",
|
||||
"@npmcli/config": "^2.2.0",
|
||||
"@npmcli/run-script": "^1.8.5",
|
||||
"abbrev": "~1.1.1",
|
||||
"ansicolors": "~0.3.2",
|
||||
"ansistyles": "~0.1.3",
|
||||
|
@ -61,19 +61,20 @@
|
|||
"graceful-fs": "^4.2.6",
|
||||
"hosted-git-info": "^4.0.2",
|
||||
"ini": "^2.0.0",
|
||||
"init-package-json": "^2.0.2",
|
||||
"init-package-json": "^2.0.3",
|
||||
"is-cidr": "^4.0.2",
|
||||
"json-parse-even-better-errors": "^2.3.1",
|
||||
"leven": "^3.1.0",
|
||||
"libnpmaccess": "^4.0.1",
|
||||
"libnpmaccess": "^4.0.2",
|
||||
"libnpmdiff": "^2.0.4",
|
||||
"libnpmexec": "^1.0.1",
|
||||
"libnpmfund": "^1.0.2",
|
||||
"libnpmhook": "^6.0.1",
|
||||
"libnpmorg": "^2.0.1",
|
||||
"libnpmhook": "^6.0.2",
|
||||
"libnpmorg": "^2.0.2",
|
||||
"libnpmpack": "^2.0.1",
|
||||
"libnpmpublish": "^4.0.0",
|
||||
"libnpmsearch": "^3.1.0",
|
||||
"libnpmteam": "^2.0.2",
|
||||
"libnpmpublish": "^4.0.1",
|
||||
"libnpmsearch": "^3.1.1",
|
||||
"libnpmteam": "^2.0.3",
|
||||
"libnpmversion": "^1.2.0",
|
||||
"make-fetch-happen": "^8.0.14",
|
||||
"minipass": "^3.1.3",
|
||||
|
@ -86,12 +87,12 @@
|
|||
"npm-audit-report": "^2.1.4",
|
||||
"npm-package-arg": "^8.1.2",
|
||||
"npm-pick-manifest": "^6.1.1",
|
||||
"npm-profile": "^5.0.2",
|
||||
"npm-registry-fetch": "^9.0.0",
|
||||
"npm-profile": "^5.0.3",
|
||||
"npm-registry-fetch": "^10.1.1",
|
||||
"npm-user-validate": "^1.0.1",
|
||||
"npmlog": "~4.1.2",
|
||||
"opener": "^1.5.2",
|
||||
"pacote": "^11.3.1",
|
||||
"pacote": "^11.3.3",
|
||||
"parse-conflict-json": "^1.1.1",
|
||||
"qrcode-terminal": "^0.12.0",
|
||||
"read": "~1.0.7",
|
||||
|
@ -135,6 +136,7 @@
|
|||
"leven",
|
||||
"libnpmaccess",
|
||||
"libnpmdiff",
|
||||
"libnpmexec",
|
||||
"libnpmfund",
|
||||
"libnpmhook",
|
||||
"libnpmorg",
|
||||
|
@ -188,7 +190,7 @@
|
|||
"jsdom": "^16.5.2",
|
||||
"licensee": "^8.1.0",
|
||||
"marked-man": "^0.7.0",
|
||||
"tap": "^15.0.2",
|
||||
"tap": "^15.0.6",
|
||||
"yaml": "^1.10.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue