From dc8d1a74a665d9ffadbaa18034d127378b13b305 Mon Sep 17 00:00:00 2001 From: Ruy Adorno Date: Fri, 18 Dec 2020 15:39:05 -0500 Subject: [PATCH] deps: upgrade npm to 7.3.0 PR-URL: https://github.com/nodejs/node/pull/36572 Reviewed-By: Myles Borins Reviewed-By: Antoine du Hamel Reviewed-By: Luigi Pinca --- deps/npm/.eslintrc.json | 7 + deps/npm/AUTHORS | 2 + deps/npm/CHANGELOG.md | 74 +- deps/npm/Makefile | 4 +- deps/npm/README.md | 2 + deps/npm/docs/content/commands/npm-config.md | 39 +- .../docs/content/commands/npm-deprecate.md | 15 +- .../docs/content/commands/npm-run-script.md | 24 +- deps/npm/docs/content/commands/npm.md | 17 +- deps/npm/docs/content/using-npm/config.md | 17 - deps/npm/docs/content/using-npm/scripts.md | 2 +- deps/npm/docs/output/commands/npm-config.html | 35 +- .../docs/output/commands/npm-deprecate.html | 11 +- deps/npm/docs/output/commands/npm-ls.html | 2 +- .../docs/output/commands/npm-run-script.html | 22 +- deps/npm/docs/output/commands/npm.html | 20 +- deps/npm/docs/output/using-npm/config.html | 17 +- deps/npm/docs/output/using-npm/scripts.html | 2 +- deps/npm/lib/adduser.js | 1 + deps/npm/lib/config.js | 80 +- deps/npm/lib/deprecate.js | 5 +- deps/npm/lib/doctor.js | 10 +- deps/npm/lib/get.js | 2 +- deps/npm/lib/init.js | 3 +- deps/npm/lib/npm.js | 6 +- deps/npm/lib/outdated.js | 24 +- deps/npm/lib/profile.js | 515 +++--- deps/npm/lib/publish.js | 16 +- deps/npm/lib/repo.js | 3 +- deps/npm/lib/set.js | 2 +- deps/npm/lib/token.js | 19 +- deps/npm/lib/unpublish.js | 4 +- deps/npm/lib/utils/config.js | 4 - deps/npm/lib/utils/error-handler.js | 4 - deps/npm/lib/utils/error-message.js | 12 +- deps/npm/lib/utils/file-exists.js | 4 +- deps/npm/lib/utils/flat-options.js | 2 - deps/npm/lib/utils/metrics-launch.js | 43 - deps/npm/lib/utils/metrics.js | 80 - deps/npm/lib/utils/reify-output.js | 2 +- deps/npm/lib/view.js | 13 +- deps/npm/man/man1/npm-config.1 | 39 +- deps/npm/man/man1/npm-deprecate.1 | 17 +- deps/npm/man/man1/npm-ls.1 | 2 +- deps/npm/man/man1/npm-run-script.1 | 24 +- deps/npm/man/man1/npm.1 | 25 +- deps/npm/man/man7/config.7 | 23 - deps/npm/man/man7/scripts.7 | 2 +- .../node_modules/@npmcli/config/lib/index.js | 30 +- .../node_modules/@npmcli/config/package.json | 2 +- deps/npm/package.json | 4 +- .../test-lib-config.js-TAP.test.js | 2 +- .../tap-snapshots/test-lib-ls.js-TAP.test.js | 20 +- .../test-lib-profile.js-TAP.test.js | 90 + .../test-lib-utils-config.js-TAP.test.js | 21 - ...test-lib-utils-flat-options.js-TAP.test.js | 2 - .../test/fixtures/eresolve-explanations.js | 2 +- deps/npm/test/lib/adduser.js | 24 +- deps/npm/test/lib/config.js | 92 +- deps/npm/test/lib/deprecate.js | 4 + deps/npm/test/lib/dist-tag.js | 2 +- deps/npm/test/lib/help-search.js | 3 +- deps/npm/test/lib/ls.js | 24 +- deps/npm/test/lib/npm.js | 9 +- deps/npm/test/lib/owner.js | 2 +- deps/npm/test/lib/profile.js | 1465 +++++++++++++++++ deps/npm/test/lib/publish.js | 77 +- deps/npm/test/lib/utils/error-handler.js | 5 - deps/npm/test/lib/utils/flat-options.js | 5 - deps/npm/test/lib/utils/reify-output.js | 5 +- deps/npm/test/lib/utils/replace-info.js | 2 +- 71 files changed, 2489 insertions(+), 701 deletions(-) delete mode 100644 deps/npm/lib/utils/metrics-launch.js delete mode 100644 deps/npm/lib/utils/metrics.js create mode 100644 deps/npm/tap-snapshots/test-lib-profile.js-TAP.test.js create mode 100644 deps/npm/test/lib/profile.js diff --git a/deps/npm/.eslintrc.json b/deps/npm/.eslintrc.json index 139716eefd85a0..8566613e748a7e 100644 --- a/deps/npm/.eslintrc.json +++ b/deps/npm/.eslintrc.json @@ -67,6 +67,13 @@ "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], "keyword-spacing": ["error", { "before": true, "after": true }], "lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }], + "max-len": ["error", 80, { + "ignoreUrls": true, + "ignoreComments": false, + "ignoreRegExpLiterals": true, + "ignoreStrings": true, + "ignoreTemplateLiterals": true + }], "new-cap": ["error", { "newIsCap": true, "capIsNew": false, "properties": true }], "new-parens": "error", "no-array-constructor": "error", diff --git a/deps/npm/AUTHORS b/deps/npm/AUTHORS index 008d9099561199..0d3cedb68c8695 100644 --- a/deps/npm/AUTHORS +++ b/deps/npm/AUTHORS @@ -739,3 +739,5 @@ kai zhu Alex Woollam Daniel Fischer Yash-Singh1 +Edu93Jer +Tieg Zaharia diff --git a/deps/npm/CHANGELOG.md b/deps/npm/CHANGELOG.md index 923c565ab31fc9..accfdafe6643e4 100644 --- a/deps/npm/CHANGELOG.md +++ b/deps/npm/CHANGELOG.md @@ -1,3 +1,75 @@ +## 7.3.0 (2020-12-18) + +### FEATURES + +* [`a9b8bf263`](https://github.com/npm/cli/commit/a9b8bf2634c627fbb16ca3a6bb2c2f1058c3e586) + [#2362](https://github.com/npm/cli/issues/2362) + Support multiple set/get/deletes in npm config + ([@isaacs](https://github.com/isaacs)) + +### BUG FIXES + +* [`9eef63849`](https://github.com/npm/cli/commit/9eef638499c88689acb00d812c10f0407cb95c08) + Pass full set of options to login helper functions. + This fixes `npm login --no-strict-ssl`, as well as a host of other + options that one might want to set while logging in. + Reported by: [@toddself](https://github.com/toddself) + ([@isaacs](https://github.com/isaacs)) +* [`628a554bc`](https://github.com/npm/cli/commit/628a554bc113e4e115d34778bfe8a77cfad1d933) + [#2358](https://github.com/npm/cli/issues/2358) + fix doctor test to work correctly for node pre-release versions + ([@nlf](https://github.com/nlf)) +* [`be4a0900b`](https://github.com/npm/cli/commit/be4a0900b14b2c6315bf62bed8f5affb648215ae) + [#2360](https://github.com/npm/cli/issues/2360) + raise an error early if publishing without login, registry + ([@isaacs](https://github.com/isaacs)) +* [`44d433105`](https://github.com/npm/cli/commit/44d4331058c53909ada62470b23b2185102b2128) + [#2366](https://github.com/npm/cli/issues/2366) + Include prerelease versions when deprecating + ([@tiegz](https://github.com/tiegz)) +* [`cba3341da`](https://github.com/npm/cli/commit/cba3341dae4c92541049dc976e82e2ba19566e95) + [#2373](https://github.com/npm/cli/issues/2373) + npm profile refactor + ([@ruyadorno](https://github.com/ruyadorno)) +* [`7539504e3`](https://github.com/npm/cli/commit/7539504e3abdec28039a7798e5ccb745b536cb6e) + [#2382](https://github.com/npm/cli/issues/2382) + remove the metrics sender + ([@nlf](https://github.com/nlf)) + +### DOCS + +* [`b98569a8c`](https://github.com/npm/cli/commit/b98569a8ca28dbd611fe84492aee996e2e567b55) + add note about `INIT_CWD` to run-script doc +* [`292929279`](https://github.com/npm/cli/commit/292929279854a06ca60ff737b574cbd6503ec5db) + [#2368](https://github.com/npm/cli/issues/2368) + Revert bug-reporting links to GH. + Re: + ([@tiegz](https://github.com/tiegz)) +* [`f4560626f`](https://github.com/npm/cli/commit/f4560626f09dba4889d752f7f739aa5a5f3da741) + update `ISSUE_TEMPLATE` with modern links + ([@isaacs](https://github.com/isaacs)) +* [`bc1c567ed`](https://github.com/npm/cli/commit/bc1c567ed3d853ed4f01d33a800eb453956de6ef) + update npm command doc feature request links + ([@isaacs](https://github.com/isaacs)) +* [`0ad958fe1`](https://github.com/npm/cli/commit/0ad958fe1cb811699caca235f361c8328baac8c4) + [#2381](https://github.com/npm/cli/issues/2381) + (docs,test): assorted typo fixes + ([@XhmikosR](https://github.com/XhmikosR)) + +### TESTING + +* [`a92d310b7`](https://github.com/npm/cli/commit/a92d310b7e9e4c48b08f52785c2e3a6d52a82ad7) + [#2361](https://github.com/npm/cli/issues/2361) + Add max-len to lint rules + ([@Edu93Jer](https://github.com/Edu93Jer)) + +### DEPENDENCIES + +* [`4fc2f3e05`](https://github.com/npm/cli/commit/4fc2f3e05b600aa64fe5eb6b8b77bc070e5a9403) + [#2300](https://github.com/npm/cli/issues/2300) + `@npmcli/config@1.2.8`: + * Support setting email without username/password + ## 7.2.0 (2020-12-15) ### FEATURES @@ -977,7 +1049,7 @@ * fix package.json dependencies order * [`49b2bf5a7`](https://github.com/npm/cli/commit/49b2bf5a798b49d52166744088a80b8a39ccaeb6) `@npmcli/config@1.1.8` - * fix unkown envs to be passed through + * fix unknown envs to be passed through * fix setting correct globalPrefix on load * [`f9aac351d`](https://github.com/npm/cli/commit/f9aac351dd36a19d14e1f951a2e8e20b41545822) `libnpmversion@1.0.5` diff --git a/deps/npm/Makefile b/deps/npm/Makefile index 83046c59aca9b2..4c96615203b026 100644 --- a/deps/npm/Makefile +++ b/deps/npm/Makefile @@ -88,13 +88,13 @@ prune: @[[ "$(shell git status -s)" != "" ]] && echo "ERR: found unpruned files" && exit 1 || echo "git status is clean" -publish: gitclean ls-ok link test docs-clean docs prune +publish: gitclean ls-ok link test docs prune @git push origin :v$(shell node bin/npm-cli.js --no-timing -v) 2>&1 || true git push origin $(BRANCH) &&\ git push origin --tags &&\ node bin/npm-cli.js publish --tag=$(PUBLISHTAG) -release: gitclean ls-ok docs-clean docs prune +release: gitclean ls-ok docs prune @bash scripts/release.sh .PHONY: all latest install dev link docs clean uninstall test man docs-clean docsclean release ls-ok dev-deps prune diff --git a/deps/npm/README.md b/deps/npm/README.md index 05da169f259619..3366c4b271f739 100644 --- a/deps/npm/README.md +++ b/deps/npm/README.md @@ -150,6 +150,8 @@ you should [read this](https://docs.npmjs.com/misc/developers). When you find issues, please report them: * web: + +* archived web: Be sure to include *all* of the output from the npm command that didn't work diff --git a/deps/npm/docs/content/commands/npm-config.md b/deps/npm/docs/content/commands/npm-config.md index ebff540eedfb98..51caa5a61b6075 100644 --- a/deps/npm/docs/content/commands/npm-config.md +++ b/deps/npm/docs/content/commands/npm-config.md @@ -7,15 +7,15 @@ description: Manage the npm configuration files ### Synopsis ```bash -npm config set [-g|--global] -npm config get -npm config delete -npm config list [-l] [--json] +npm config set = [= ...] +npm config get [ [ ...]] +npm config delete [ ...] +npm config list [--json] npm config edit -npm get -npm set [-g|--global] +npm set = [= ...] +npm get [ [ ...]] -aliases: c +alias: c ``` ### Description @@ -39,20 +39,31 @@ Config supports the following sub-commands: #### set ```bash -npm config set key value +npm config set key=value [key=value...] +npm set key=value [key=value...] ``` -Sets the config key to the value. +Sets each of the config keys to the value provided. -If value is omitted, then it sets it to "true". +If value is omitted, then it sets it to an empty string. + +Note: for backwards compatibility, `npm config set key value` is supported +as an alias for `npm config set key=value`. #### get ```bash -npm config get key +npm config get [key ...] +npm get [key ...] ``` -Echo the config value to stdout. +Echo the config value(s) to stdout. + +If multiple keys are provided, then the values will be prefixed with the +key names. + +If no keys are provided, then this command behaves the same as `npm config +list`. #### list @@ -66,10 +77,10 @@ to show the settings in json format. #### delete ```bash -npm config delete key +npm config delete key [key ...] ``` -Deletes the key from all configuration files. +Deletes the specified keys from all configuration files. #### edit diff --git a/deps/npm/docs/content/commands/npm-deprecate.md b/deps/npm/docs/content/commands/npm-deprecate.md index d0440efe890215..139441856bb068 100644 --- a/deps/npm/docs/content/commands/npm-deprecate.md +++ b/deps/npm/docs/content/commands/npm-deprecate.md @@ -7,7 +7,7 @@ description: Deprecate a version of a package ### Synopsis ```bash -npm deprecate [@] +npm deprecate [@] ``` ### Description @@ -22,8 +22,17 @@ versions, so you can do something like this: npm deprecate my-thing@"< 0.2.3" "critical bug fixed in v0.2.3" ``` -Note that you must be the package owner to deprecate something. See the -`owner` and `adduser` help topics. +SemVer ranges passed to this command are interpreted such that they *do* +include prerelease versions. For example: + +```bash +npm deprecate my-thing@1.x "1.x is no longer supported" +``` + +In this case, a version `my-thing@1.0.0-beta.0` will also be deprecated. + +You must be the package owner to deprecate something. See the `owner` and +`adduser` help topics. To un-deprecate a package, specify an empty string (`""`) for the `message` argument. Note that you must use double quotes with no space between them to diff --git a/deps/npm/docs/content/commands/npm-run-script.md b/deps/npm/docs/content/commands/npm-run-script.md index 05f6380c2b3268..b7ab20a73af473 100644 --- a/deps/npm/docs/content/commands/npm-run-script.md +++ b/deps/npm/docs/content/commands/npm-run-script.md @@ -60,18 +60,18 @@ on Unix-like systems it is the `/bin/sh` command, on Windows it is the `cmd.exe` The actual shell referred to by `/bin/sh` also depends on the system. You can customize the shell with the `script-shell` configuration. -Scripts are run from the root of the module, regardless of what the current -working directory is when `npm run` is called. If you want your script to -use different behavior based on what subdirectory you're in, you can use the -`INIT_CWD` environment variable, which holds the full path you were in when -you ran `npm run`. - -`npm run` sets the `NODE` environment variable to the `node` executable with -which `npm` is executed. Also, if the `--scripts-prepend-node-path` is passed, -the directory within which `node` resides is added to the -`PATH`. If `--scripts-prepend-node-path=auto` is passed (which has been the -default in `npm` v3), this is only performed when that `node` executable is -not found in the `PATH`. +Scripts are run from the root of the package folder, regardless of what the +current working directory is when `npm run` is called. If you want your +script to use different behavior based on what subdirectory you're in, you +can use the `INIT_CWD` environment variable, which holds the full path you +were in when you ran `npm run`. + +`npm run` sets the `NODE` environment variable to the `node` executable +with which `npm` is executed. Also, if the `--scripts-prepend-node-path` is +passed, the directory within which `node` resides is added to the `PATH`. +If `--scripts-prepend-node-path=auto` is passed (which has been the default +in `npm` v3), this is only performed when that `node` executable is not +found in the `PATH`. If you try to run a script without having a `node_modules` directory and it fails, you will be given a warning to run `npm install`, just in case you've forgotten. diff --git a/deps/npm/docs/content/commands/npm.md b/deps/npm/docs/content/commands/npm.md index ee3be00171d02a..d0489187e1bbdd 100644 --- a/deps/npm/docs/content/commands/npm.md +++ b/deps/npm/docs/content/commands/npm.md @@ -143,12 +143,21 @@ the contributing guidelines and check the issues list. When you find issues, please report them: * web: + +* archived web: -Be sure to follow the template and bug reporting guidelines. You can also ask -for help in the [support forum](https://npm.community/c/support) if you're -unsure if it's actually a bug or are having trouble coming up with a detailed -reproduction to report. +Be sure to follow the template and bug reporting guidelines. + +### Feature Requests + +Discuss new feature ideas on our discussion forum: + +* + +Or suggest formal RFC proposals: + +* ### Author diff --git a/deps/npm/docs/content/using-npm/config.md b/deps/npm/docs/content/using-npm/config.md index 71102d2e204ea0..2d9a4f8698a98a 100644 --- a/deps/npm/docs/content/using-npm/config.md +++ b/deps/npm/docs/content/using-npm/config.md @@ -744,13 +744,6 @@ Commit message which is used by `npm version` when creating version commit. Any "%s" in the message will be replaced with the version number. -#### metrics-registry - -* Default: The value of `registry` (which defaults to "https://registry.npmjs.org/") -* Type: String - -The registry you want to send cli metrics to if `send-metrics` is true. - #### node-options * Default: null @@ -1089,16 +1082,6 @@ searches. The age of the cache, in seconds, before another registry request is made if using legacy search endpoint. -#### send-metrics - -* Default: false -* Type: Boolean - -If true, success/failure metrics will be reported to the registry stored in -`metrics-registry`. These requests contain the number of successful and -failing runs of the npm CLI and the time period over which those counts were -gathered. No identifying information is included in these requests. - #### shell * Default: SHELL environment variable, or "bash" on Posix, or "cmd" on diff --git a/deps/npm/docs/content/using-npm/scripts.md b/deps/npm/docs/content/using-npm/scripts.md index cf274e44de3ac7..db7bd56b5123c3 100644 --- a/deps/npm/docs/content/using-npm/scripts.md +++ b/deps/npm/docs/content/using-npm/scripts.md @@ -24,7 +24,7 @@ To create "pre" or "post" scripts for any scripts defined in the `"scripts"` sec ### Life Cycle Scripts -There are some special life cycle scripts that happen only in certain situations. These scripts happen in addtion to the "pre" and "post" script. +There are some special life cycle scripts that happen only in certain situations. These scripts happen in addition to the "pre" and "post" script. * `prepare`, `prepublish`, `prepublishOnly`, `prepack`, `postpack` **prepare** (since `npm@4.0.0`) diff --git a/deps/npm/docs/output/commands/npm-config.html b/deps/npm/docs/output/commands/npm-config.html index cce9963f571e2f..924562822781b1 100644 --- a/deps/npm/docs/output/commands/npm-config.html +++ b/deps/npm/docs/output/commands/npm-config.html @@ -145,15 +145,15 @@

Table of contents

Synopsis

-
npm config set <key> <value> [-g|--global]
-npm config get <key>
-npm config delete <key>
-npm config list [-l] [--json]
+
npm config set <key>=<value> [<key>=<value> ...]
+npm config get [<key> [<key> ...]]
+npm config delete <key> [<key> ...]
+npm config list [--json]
 npm config edit
-npm get <key>
-npm set <key> <value> [-g|--global]
+npm set <key>=<value> [<key>=<value> ...]
+npm get [<key> [<key> ...]]
 
-aliases: c
+alias: c
 

Description

npm gets its config settings from the command line, environment @@ -167,23 +167,30 @@

Description

Sub-commands

Config supports the following sub-commands:

set

-
npm config set key value
+
npm config set key=value [key=value...]
+npm set key=value [key=value...]
 
-

Sets the config key to the value.

-

If value is omitted, then it sets it to “true”.

+

Sets each of the config keys to the value provided.

+

If value is omitted, then it sets it to an empty string.

+

Note: for backwards compatibility, npm config set key value is supported +as an alias for npm config set key=value.

get

-
npm config get key
+
npm config get [key ...]
+npm get [key ...]
 
-

Echo the config value to stdout.

+

Echo the config value(s) to stdout.

+

If multiple keys are provided, then the values will be prefixed with the +key names.

+

If no keys are provided, then this command behaves the same as npm config list.

list

npm config list
 

Show all the config settings. Use -l to also show defaults. Use --json to show the settings in json format.

delete

-
npm config delete key
+
npm config delete key [key ...]
 
-

Deletes the key from all configuration files.

+

Deletes the specified keys from all configuration files.

edit

npm config edit
 
diff --git a/deps/npm/docs/output/commands/npm-deprecate.html b/deps/npm/docs/output/commands/npm-deprecate.html index 923448c921106c..949be43d9be171 100644 --- a/deps/npm/docs/output/commands/npm-deprecate.html +++ b/deps/npm/docs/output/commands/npm-deprecate.html @@ -145,7 +145,7 @@

Table of contents

Synopsis

-
npm deprecate <pkg>[@<version>] <message>
+
npm deprecate <pkg>[@<version range>] <message>
 

Description

This command will update the npm registry entry for a package, providing a @@ -154,8 +154,13 @@

Description

versions, so you can do something like this:

npm deprecate my-thing@"< 0.2.3" "critical bug fixed in v0.2.3"
 
-

Note that you must be the package owner to deprecate something. See the -owner and adduser help topics.

+

SemVer ranges passed to this command are interpreted such that they do +include prerelease versions. For example:

+
npm deprecate my-thing@1.x "1.x is no longer supported"
+
+

In this case, a version my-thing@1.0.0-beta.0 will also be deprecated.

+

You must be the package owner to deprecate something. See the owner and +adduser help topics.

To un-deprecate a package, specify an empty string ("") for the message argument. Note that you must use double quotes with no space between them to format an empty string.

diff --git a/deps/npm/docs/output/commands/npm-ls.html b/deps/npm/docs/output/commands/npm-ls.html index 125bbb61939644..1348f9c4ba6094 100644 --- a/deps/npm/docs/output/commands/npm-ls.html +++ b/deps/npm/docs/output/commands/npm-ls.html @@ -159,7 +159,7 @@

Description

the results to only the paths to the packages named. Note that nested packages will also show the paths to the specified packages. For example, running npm ls promzard in npm’s source tree will show:

-
npm@7.2.0 /path/to/npm
+
npm@7.3.0 /path/to/npm
 └─┬ init-package-json@0.0.4
   └── promzard@0.1.5
 
diff --git a/deps/npm/docs/output/commands/npm-run-script.html b/deps/npm/docs/output/commands/npm-run-script.html index 8f80a761d638c9..57f6e6e2b658d8 100644 --- a/deps/npm/docs/output/commands/npm-run-script.html +++ b/deps/npm/docs/output/commands/npm-run-script.html @@ -181,17 +181,17 @@

Description

on Unix-like systems it is the /bin/sh command, on Windows it is the cmd.exe. The actual shell referred to by /bin/sh also depends on the system. You can customize the shell with the script-shell configuration.

-

Scripts are run from the root of the module, regardless of what the current -working directory is when npm run is called. If you want your script to -use different behavior based on what subdirectory you’re in, you can use the -INIT_CWD environment variable, which holds the full path you were in when -you ran npm run.

-

npm run sets the NODE environment variable to the node executable with -which npm is executed. Also, if the --scripts-prepend-node-path is passed, -the directory within which node resides is added to the -PATH. If --scripts-prepend-node-path=auto is passed (which has been the -default in npm v3), this is only performed when that node executable is -not found in the PATH.

+

Scripts are run from the root of the package folder, regardless of what the +current working directory is when npm run is called. If you want your +script to use different behavior based on what subdirectory you’re in, you +can use the INIT_CWD environment variable, which holds the full path you +were in when you ran npm run.

+

npm run sets the NODE environment variable to the node executable +with which npm is executed. Also, if the --scripts-prepend-node-path is +passed, the directory within which node resides is added to the PATH. +If --scripts-prepend-node-path=auto is passed (which has been the default +in npm v3), this is only performed when that node executable is not +found in the PATH.

If you try to run a script without having a node_modules directory and it fails, you will be given a warning to run npm install, just in case you’ve forgotten.

You can use the --silent flag to prevent showing npm ERR! output on error.

diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html index 03c784976123df..6e9e2a618f154e 100644 --- a/deps/npm/docs/output/commands/npm.html +++ b/deps/npm/docs/output/commands/npm.html @@ -141,14 +141,14 @@

npm

Table of contents

- +

Synopsis

npm <command> [args]
 

Version

-

7.2.0

+

7.3.0

Description

npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency @@ -255,12 +255,20 @@

Bugs

When you find issues, please report them:

-

Be sure to follow the template and bug reporting guidelines. You can also ask -for help in the support forum if you’re -unsure if it’s actually a bug or are having trouble coming up with a detailed -reproduction to report.

+

Be sure to follow the template and bug reporting guidelines.

+

Feature Requests

+

Discuss new feature ideas on our discussion forum:

+ +

Or suggest formal RFC proposals:

+

Author

Isaac Z. Schlueter :: isaacs :: diff --git a/deps/npm/docs/output/using-npm/config.html b/deps/npm/docs/output/using-npm/config.html index e3fbeaae8c89c7..ad9c8c44a1fc0b 100644 --- a/deps/npm/docs/output/using-npm/config.html +++ b/deps/npm/docs/output/using-npm/config.html @@ -141,7 +141,7 @@

config

Table of contents

- +

Description

@@ -770,12 +770,6 @@

message

Commit message which is used by npm version when creating version commit.

Any “%s” in the message will be replaced with the version number.

-

metrics-registry

- -

The registry you want to send cli metrics to if send-metrics is true.

node-options

  • Default: null
  • @@ -1062,15 +1056,6 @@

    searchstaleness

The age of the cache, in seconds, before another registry request is made if using legacy search endpoint.

-

send-metrics

-
    -
  • Default: false
  • -
  • Type: Boolean
  • -
-

If true, success/failure metrics will be reported to the registry stored in -metrics-registry. These requests contain the number of successful and -failing runs of the npm CLI and the time period over which those counts were -gathered. No identifying information is included in these requests.

shell

  • Default: SHELL environment variable, or “bash” on Posix, or “cmd” on diff --git a/deps/npm/docs/output/using-npm/scripts.html b/deps/npm/docs/output/using-npm/scripts.html index 91814fb58fba5b..bb02d9b1f8092c 100644 --- a/deps/npm/docs/output/using-npm/scripts.html +++ b/deps/npm/docs/output/using-npm/scripts.html @@ -157,7 +157,7 @@

    Pre & Post Scripts

    }

Life Cycle Scripts

-

There are some special life cycle scripts that happen only in certain situations. These scripts happen in addtion to the “pre” and “post” script.

+

There are some special life cycle scripts that happen only in certain situations. These scripts happen in addition to the “pre” and “post” script.

  • prepare, prepublish, prepublishOnly, prepack, postpack
diff --git a/deps/npm/lib/adduser.js b/deps/npm/lib/adduser.js index 4b9fdf1f5f1664..b6c3321745667c 100644 --- a/deps/npm/lib/adduser.js +++ b/deps/npm/lib/adduser.js @@ -59,6 +59,7 @@ const adduser = async (args) => { log.notice('', `Log in on ${replaceInfo(registry)}`) const { message, newCreds } = await auth({ + ...npm.flatOptions, creds, registry, scope, diff --git a/deps/npm/lib/config.js b/deps/npm/lib/config.js index 561c31e0b3ec54..b32cf3359d33b4 100644 --- a/deps/npm/lib/config.js +++ b/deps/npm/lib/config.js @@ -15,13 +15,13 @@ const ini = require('ini') const usage = usageUtil( 'config', - 'npm config set ' + - '\nnpm config get []' + - '\nnpm config delete ' + + 'npm config set = [= ...]' + + '\nnpm config get [ [ ...]]' + + '\nnpm config delete [ ...]' + '\nnpm config list [--json]' + '\nnpm config edit' + - '\nnpm set ' + - '\nnpm get []' + '\nnpm set = [= ...]' + + '\nnpm get [ [ ...]]' ) const cmd = (args, cb) => config(args).then(() => cb()).catch(cb) @@ -63,20 +63,20 @@ const completion = (opts, cb) => { const UsageError = () => Object.assign(new Error(usage), { code: 'EUSAGE' }) -const config = async ([action, key, val]) => { +const config = async ([action, ...args]) => { npm.log.disableProgress() try { switch (action) { case 'set': - await set(key, val) + await set(args) break case 'get': - await get(key) + await get(args) break case 'delete': case 'rm': case 'del': - await del(key) + await del(args) break case 'list': case 'ls': @@ -93,46 +93,58 @@ const config = async ([action, key, val]) => { } } -const set = async (key, val) => { - if (key === undefined) - throw UsageError() - - if (val === undefined) { - if (key.indexOf('=') !== -1) { - const k = key.split('=') - key = k.shift() - val = k.join('=') - } else - val = '' +// take an array of `[key, value, k2=v2, k3, v3, ...]` and turn into +// { key: value, k2: v2, k3: v3 } +const keyValues = args => { + const kv = {} + for (let i = 0; i < args.length; i++) { + const arg = args[i].split('=') + const key = arg.shift() + const val = arg.length ? arg.join('=') + : i < args.length - 1 ? args[++i] + : '' + kv[key.trim()] = val.trim() } + return kv +} + +const set = async (args) => { + if (!args.length) + throw UsageError() - key = key.trim() - val = val.trim() - npm.log.info('config', 'set %j %j', key, val) const where = npm.flatOptions.global ? 'global' : 'user' - npm.config.set(key, val, where) - if (!npm.config.validate(where)) - npm.log.warn('config', 'omitting invalid config values') + for (const [key, val] of Object.entries(keyValues(args))) { + npm.log.info('config', 'set %j %j', key, val) + npm.config.set(key, val || '', where) + if (!npm.config.validate(where)) + npm.log.warn('config', 'omitting invalid config values') + } await npm.config.save(where) } -const get = async key => { - if (!key) +const get = async keys => { + if (!keys.length) return list() - if (!publicVar(key)) - throw `The ${key} option is protected, and cannot be retrieved in this way` + const out = [] + for (const key of keys) { + if (!publicVar(key)) + throw `The ${key} option is protected, and cannot be retrieved in this way` - output(npm.config.get(key)) + const pref = keys.length > 1 ? `${key}=` : '' + out.push(pref + npm.config.get(key)) + } + output(out.join('\n')) } -const del = async key => { - if (!key) +const del = async keys => { + if (!keys.length) throw UsageError() const where = npm.flatOptions.global ? 'global' : 'user' - npm.config.delete(key, where) + for (const key of keys) + npm.config.delete(key, where) await npm.config.save(where) } diff --git a/deps/npm/lib/deprecate.js b/deps/npm/lib/deprecate.js index fbaad54b7fde3d..e049986452b793 100644 --- a/deps/npm/lib/deprecate.js +++ b/deps/npm/lib/deprecate.js @@ -25,7 +25,8 @@ const completion = (opts, cb) => { return libaccess.lsPackages(username, npm.flatOptions).then((packages) => { return Object.keys(packages) .filter((name) => packages[name] === 'write' && - (opts.conf.argv.remain.length === 0 || name.startsWith(opts.conf.argv.remain[0])) + (opts.conf.argv.remain.length === 0 || + name.startsWith(opts.conf.argv.remain[0])) ) }) }).then((list) => cb(null, list), (err) => cb(err)) @@ -57,7 +58,7 @@ const deprecate = async ([pkg, msg]) => { }) Object.keys(packument.versions) - .filter(v => semver.satisfies(v, spec)) + .filter(v => semver.satisfies(v, spec, { includePrerelease: true })) .forEach(v => { packument.versions[v].deprecated = msg }) diff --git a/deps/npm/lib/doctor.js b/deps/npm/lib/doctor.js index 0b29936bb4d1f8..f42b19fa686b7e 100644 --- a/deps/npm/lib/doctor.js +++ b/deps/npm/lib/doctor.js @@ -59,7 +59,8 @@ const getLatestNodejsVersion = async () => { if (lts && semver.gt(version, maxLTS)) maxLTS = version - if (semver.satisfies(version, currentRange) && semver.gt(version, maxCurrent)) + if (semver.satisfies(version, currentRange) && + semver.gt(version, maxCurrent)) maxCurrent = version } const recommended = semver.gt(maxCurrent, maxLTS) ? maxCurrent : maxLTS @@ -175,7 +176,12 @@ const verifyCachedFiles = async () => { tracker.info('verifyCachedFiles', 'Verifying the npm cache') try { const stats = await cacache.verify(npm.flatOptions.cache) - const { badContentCount, reclaimedCount, missingContent, reclaimedSize } = stats + const { + badContentCount, + reclaimedCount, + missingContent, + reclaimedSize, + } = stats if (badContentCount || reclaimedCount || missingContent) { if (badContentCount) tracker.warn('verifyCachedFiles', `Corrupted content removed: ${badContentCount}`) diff --git a/deps/npm/lib/get.js b/deps/npm/lib/get.js index bccdc72121f4b1..ab2141e35721ae 100644 --- a/deps/npm/lib/get.js +++ b/deps/npm/lib/get.js @@ -3,7 +3,7 @@ const usageUtil = require('./utils/usage.js') const usage = usageUtil( 'get', - 'npm get (See `npm config`)' + 'npm get [ ...] (See `npm config`)' ) const completion = npm.commands.config.completion diff --git a/deps/npm/lib/init.js b/deps/npm/lib/init.js index 6e4213c28ff6f6..60ea52e1674911 100644 --- a/deps/npm/lib/init.js +++ b/deps/npm/lib/init.js @@ -41,8 +41,9 @@ const init = async args => { } } npm.config.set('package', []) + const newArgs = [packageName, ...args.slice(1)] return new Promise((res, rej) => { - npm.commands.exec([packageName, ...args.slice(1)], er => er ? rej(er) : res()) + npm.commands.exec(newArgs, er => er ? rej(er) : res()) }) } diff --git a/deps/npm/lib/npm.js b/deps/npm/lib/npm.js index 4430b80539d031..7a15a29b2fed99 100644 --- a/deps/npm/lib/npm.js +++ b/deps/npm/lib/npm.js @@ -13,7 +13,8 @@ require('graceful-fs').gracefulify(require('fs')) const procLogListener = require('./utils/proc-log-listener.js') -const hasOwnProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key) +const hasOwnProperty = (obj, key) => + Object.prototype.hasOwnProperty.call(obj, key) // the first time `npm.commands.xyz` is loaded, it gets added // to the cmds object, so we don't have to load it again. @@ -206,8 +207,6 @@ const npm = module.exports = new class extends EventEmitter { this.projectScope = this.config.get('scope') || getProjectScope(this.prefix) - - startMetrics() } get flatOptions () { @@ -296,7 +295,6 @@ const npm = module.exports = new class extends EventEmitter { const log = require('npmlog') const { promisify } = require('util') -const startMetrics = require('./utils/metrics.js').start const which = promisify(require('which')) diff --git a/deps/npm/lib/outdated.js b/deps/npm/lib/outdated.js index f0ec7a72f40ee9..b5f0f3b5b29287 100644 --- a/deps/npm/lib/outdated.js +++ b/deps/npm/lib/outdated.js @@ -167,7 +167,7 @@ async function outdated_ (tree, deps, opts) { } } catch (err) { // silently catch and ignore ETARGET, E403 & - // E404 errors, deps are just skipped + // E404 errors, deps are just skipped { if (!( err.code === 'ETARGET' || err.code === 'E403' || @@ -234,7 +234,16 @@ function makePretty (dep, opts) { // :::: function makeParseable (list, opts) { return list.map(dep => { - const { name, current, wanted, latest, path, dependent, type, homepage } = dep + const { + name, + current, + wanted, + latest, + path, + dependent, + type, + homepage, + } = dep const out = [ path, name + '@' + wanted, @@ -252,7 +261,16 @@ function makeParseable (list, opts) { function makeJSON (list, opts) { const out = {} list.forEach(dep => { - const { name, current, wanted, latest, path, type, dependent, homepage } = dep + const { + name, + current, + wanted, + latest, + path, + type, + dependent, + homepage, + } = dep out[name] = { current, wanted, diff --git a/deps/npm/lib/profile.js b/deps/npm/lib/profile.js index 50d7ddd7d22e6d..24f026ce85ec48 100644 --- a/deps/npm/lib/profile.js +++ b/deps/npm/lib/profile.js @@ -1,35 +1,36 @@ -const ansistyles = require('ansistyles') const inspect = require('util').inspect +const { URL } = require('url') +const ansistyles = require('ansistyles') const log = require('npmlog') +const npmProfile = require('npm-profile') +const qrcodeTerminal = require('qrcode-terminal') +const Table = require('cli-table3') + const npm = require('./npm.js') const otplease = require('./utils/otplease.js') const output = require('./utils/output.js') -const profile = require('npm-profile') const pulseTillDone = require('./utils/pulse-till-done.js') -const qrcodeTerminal = require('qrcode-terminal') const readUserInfo = require('./utils/read-user-info.js') -const Table = require('cli-table3') -const { URL } = require('url') +const usageUtil = require('./utils/usage.js') -module.exports = profileCmd - -profileCmd.usage = - 'npm profile enable-2fa [auth-only|auth-and-writes]\n' + - 'npm profile disable-2fa\n' + - 'npm profile get []\n' + +const usage = usageUtil( + 'npm profile enable-2fa [auth-only|auth-and-writes]\n', + 'npm profile disable-2fa\n', + 'npm profile get []\n', 'npm profile set ' +) -profileCmd.subcommands = ['enable-2fa', 'disable-2fa', 'get', 'set'] - -profileCmd.completion = function (opts, cb) { +const completion = (opts, cb) => { var argv = opts.conf.argv.remain + const subcommands = ['enable-2fa', 'disable-2fa', 'get', 'set'] + + if (!argv[2]) + return cb(null, subcommands) + switch (argv[2]) { case 'enable-2fa': case 'enable-tfa': - if (argv.length === 3) - return cb(null, ['auth-and-writes', 'auth-only']) - else - return cb(null, []) + return cb(null, ['auth-and-writes', 'auth-only']) case 'disable-2fa': case 'disable-tfa': @@ -41,35 +42,33 @@ profileCmd.completion = function (opts, cb) { } } -function withCb (prom, cb) { - prom.then((value) => cb(null, value), cb) -} +const cmd = (args, cb) => profile(args).then(() => cb()).catch(cb) -function profileCmd (args, cb) { +const profile = async (args) => { if (args.length === 0) - return cb(new Error(profileCmd.usage)) + throw new Error(usage) + log.gauge.show('profile') - switch (args[0]) { + + const [subcmd, ...opts] = args + + switch (subcmd) { case 'enable-2fa': case 'enable-tfa': case 'enable2fa': case 'enabletfa': - withCb(enable2fa(args.slice(1)), cb) - break + return enable2fa(opts) case 'disable-2fa': case 'disable-tfa': case 'disable2fa': case 'disabletfa': - withCb(disable2fa(), cb) - break + return disable2fa() case 'get': - withCb(get(args.slice(1)), cb) - break + return get(opts) case 'set': - withCb(set(args.slice(1)), cb) - break + return set(opts) default: - cb(new Error('Unknown profile command: ' + args[0])) + throw new Error('Unknown profile command: ' + subcmd) } } @@ -86,52 +85,62 @@ const knownProfileKeys = [ 'updated', ] -function get (args) { +const get = async args => { const tfa = 'two-factor auth' - const conf = npm.flatOptions - return pulseTillDone.withPromise(profile.get(conf)).then((info) => { - if (!info.cidr_whitelist) - delete info.cidr_whitelist - if (conf.json) { - output(JSON.stringify(info, null, 2)) - return - } - const cleaned = {} - knownProfileKeys.forEach((k) => { - cleaned[k] = info[k] || '' - }) - Object.keys(info).filter((k) => !(k in cleaned)).forEach((k) => { - cleaned[k] = info[k] || '' - }) - delete cleaned.tfa - delete cleaned.email_verified - cleaned.email += info.email_verified ? ' (verified)' : '(unverified)' - if (info.tfa && !info.tfa.pending) - cleaned[tfa] = info.tfa.mode - else - cleaned[tfa] = 'disabled' - - if (args.length) { - const values = args // comma or space separated ↓ - .join(',').split(/,/).map((arg) => arg.trim()).filter((arg) => arg !== '') - .map((arg) => cleaned[arg]) - .join('\t') - output(values) - } else { - if (conf.parseable) { - Object.keys(info).forEach((key) => { - if (key === 'tfa') - output(`${key}\t${cleaned[tfa]}`) - else - output(`${key}\t${info[key]}`) - }) - } else { - const table = new Table() - Object.keys(cleaned).forEach((k) => table.push({ [ansistyles.bright(k)]: cleaned[k] })) - output(table.toString()) + const conf = { ...npm.flatOptions } + + const info = await pulseTillDone.withPromise(npmProfile.get(conf)) + + if (!info.cidr_whitelist) + delete info.cidr_whitelist + + if (conf.json) { + output(JSON.stringify(info, null, 2)) + return + } + + // clean up and format key/values for output + const cleaned = {} + for (const key of knownProfileKeys) + cleaned[key] = info[key] || '' + + const unknownProfileKeys = Object.keys(info).filter((k) => !(k in cleaned)) + for (const key of unknownProfileKeys) + cleaned[key] = info[key] || '' + + delete cleaned.tfa + delete cleaned.email_verified + cleaned.email += info.email_verified ? ' (verified)' : '(unverified)' + + if (info.tfa && !info.tfa.pending) + cleaned[tfa] = info.tfa.mode + else + cleaned[tfa] = 'disabled' + + if (args.length) { + const values = args // comma or space separated + .join(',') + .split(/,/) + .filter((arg) => arg.trim() !== '') + .map((arg) => cleaned[arg]) + .join('\t') + output(values) + } else { + if (conf.parseable) { + for (const key of Object.keys(info)) { + if (key === 'tfa') + output(`${key}\t${cleaned[tfa]}`) + else + output(`${key}\t${info[key]}`) } + } else { + const table = new Table() + for (const key of Object.keys(cleaned)) + table.push({ [ansistyles.bright(key)]: cleaned[key] }) + + output(table.toString()) } - }) + } } const writableProfileKeys = [ @@ -144,83 +153,87 @@ const writableProfileKeys = [ 'github', ] -function set (args) { - const conf = npm.flatOptions +const set = async (args) => { + const conf = { ...npm.flatOptions } const prop = (args[0] || '').toLowerCase().trim() + let value = args.length > 1 ? args.slice(1).join(' ') : null + + const readPasswords = async () => { + const newpassword = await readUserInfo.password('New password: ') + const confirmedpassword = await readUserInfo.password(' Again: ') + + if (newpassword !== confirmedpassword) { + log.warn('profile', 'Passwords do not match, please try again.') + return readPasswords() + } + + return newpassword + } + if (prop !== 'password' && value === null) - return Promise.reject(Error('npm profile set ')) + throw new Error('npm profile set ') if (prop === 'password' && value !== null) { - return Promise.reject(Error( + throw new Error( 'npm profile set password\n' + - 'Do not include your current or new passwords on the command line.')) + 'Do not include your current or new passwords on the command line.') } - if (writableProfileKeys.indexOf(prop) === -1) - return Promise.reject(Error(`"${prop}" is not a property we can set. Valid properties are: ` + writableProfileKeys.join(', '))) - - return Promise.resolve().then(() => { - if (prop === 'password') { - return readUserInfo.password('Current password: ').then((current) => { - return readPasswords().then((newpassword) => { - value = { old: current, new: newpassword } - }) - }) - } else if (prop === 'email') { - return readUserInfo.password('Password: ').then((current) => { - return { password: current, email: value } - }) - } - function readPasswords () { - return readUserInfo.password('New password: ').then((password1) => { - return readUserInfo.password(' Again: ').then((password2) => { - if (password1 !== password2) { - log.warn('profile', 'Passwords do not match, please try again.') - return readPasswords() - } - return password1 - }) - }) - } - }).then(() => { - // FIXME: Work around to not clear everything other than what we're setting - return pulseTillDone.withPromise(profile.get(conf).then((user) => { - const newUser = {} - writableProfileKeys.forEach((k) => { - newUser[k] = user[k] - }) - newUser[prop] = value - return otplease(conf, conf => profile.set(newUser, conf)) - .then((result) => { - if (conf.json) - output(JSON.stringify({ [prop]: result[prop] }, null, 2)) - else if (conf.parseable) - output(prop + '\t' + result[prop]) - else if (result[prop] != null) - output('Set', prop, 'to', result[prop]) - else - output('Set', prop) - }) - })) - }) + + if (writableProfileKeys.indexOf(prop) === -1) { + throw new Error(`"${prop}" is not a property we can set. ` + + `Valid properties are: ` + writableProfileKeys.join(', ')) + } + + if (prop === 'password') { + const current = await readUserInfo.password('Current password: ') + const newpassword = await readPasswords() + + value = { old: current, new: newpassword } + } + + // FIXME: Work around to not clear everything other than what we're setting + const user = await pulseTillDone.withPromise(npmProfile.get(conf)) + const newUser = {} + + for (const key of writableProfileKeys) + newUser[key] = user[key] + + newUser[prop] = value + + const result = await otplease(conf, conf => npmProfile.set(newUser, conf)) + + if (conf.json) + output(JSON.stringify({ [prop]: result[prop] }, null, 2)) + else if (conf.parseable) + output(prop + '\t' + result[prop]) + else if (result[prop] != null) + output('Set', prop, 'to', result[prop]) + else + output('Set', prop) } -function enable2fa (args) { +const enable2fa = async (args) => { if (args.length > 1) - return Promise.reject(new Error('npm profile enable-2fa [auth-and-writes|auth-only]')) + throw new Error('npm profile enable-2fa [auth-and-writes|auth-only]') const mode = args[0] || 'auth-and-writes' if (mode !== 'auth-only' && mode !== 'auth-and-writes') { - return Promise.reject(new Error(`Invalid two-factor authentication mode "${mode}".\n` + + throw new Error( + `Invalid two-factor authentication mode "${mode}".\n` + 'Valid modes are:\n' + ' auth-only - Require two-factor authentication only when logging in\n' + - ' auth-and-writes - Require two-factor authentication when logging in AND when publishing')) + ' auth-and-writes - Require two-factor authentication when logging in ' + + 'AND when publishing' + ) } - const conf = npm.flatOptions + + const conf = { ...npm.flatOptions } if (conf.json || conf.parseable) { - return Promise.reject(new Error( + throw new Error( 'Enabling two-factor authentication is an interactive operation and ' + - (conf.json ? 'JSON' : 'parseable') + ' output mode is not available')) + (conf.json ? 'JSON' : 'parseable') + ' output mode is not available' + ) } const info = { @@ -229,119 +242,153 @@ function enable2fa (args) { }, } - return Promise.resolve().then(() => { - // if they're using legacy auth currently then we have to update them to a - // bearer token before continuing. - const auth = getAuth(conf) - if (auth.basic) { - log.info('profile', 'Updating authentication to bearer token') - return profile.createToken( - auth.basic.password, false, [], conf - ).then((result) => { - if (!result.token) { - throw new Error(`Your registry ${conf.registry} does not seem to ` + - 'support bearer tokens. Bearer tokens are required for ' + - 'two-factor authentication') - } - npm.config.setCredentialsByURI(conf.registry, { token: result.token }) - return npm.config.save('user') - }) + // if they're using legacy auth currently then we have to + // update them to a bearer token before continuing. + const auth = getAuth(conf) + + if (!auth.basic && !auth.token) { + throw new Error( + 'You need to be logged in to registry ' + + `${conf.registry} in order to enable 2fa` + ) + } + + if (auth.basic) { + log.info('profile', 'Updating authentication to bearer token') + const result = await npmProfile.createToken( + auth.basic.password, false, [], conf + ) + + if (!result.token) { + throw new Error( + `Your registry ${conf.registry} does not seem to ` + + 'support bearer tokens. Bearer tokens are required for ' + + 'two-factor authentication' + ) } - }).then(() => { - log.notice('profile', 'Enabling two factor authentication for ' + mode) - return readUserInfo.password() - }).then((password) => { - info.tfa.password = password - log.info('profile', 'Determine if tfa is pending') - return pulseTillDone.withPromise(profile.get(conf)).then((info) => { - if (!info.tfa) - return - if (info.tfa.pending) { - log.info('profile', 'Resetting two-factor authentication') - return pulseTillDone.withPromise(profile.set({ tfa: { password, mode: 'disable' } }, conf)) - } else { - if (conf.auth.otp) - return - return readUserInfo.otp('Enter one-time password from your authenticator app: ').then((otp) => { - conf.auth.otp = otp - }) - } - }) - }).then(() => { - log.info('profile', 'Setting two-factor authentication to ' + mode) - return pulseTillDone.withPromise(profile.set(info, conf)) - }).then((challenge) => { - if (challenge.tfa === null) { - output('Two factor authentication mode changed to: ' + mode) - return + + npm.config.setCredentialsByURI(conf.registry, { token: result.token }) + await npm.config.save('user') + } + + log.notice('profile', 'Enabling two factor authentication for ' + mode) + const password = await readUserInfo.password() + info.tfa.password = password + + log.info('profile', 'Determine if tfa is pending') + const userInfo = await pulseTillDone.withPromise(npmProfile.get(conf)) + + if (userInfo && userInfo.tfa && userInfo.tfa.pending) { + log.info('profile', 'Resetting two-factor authentication') + await pulseTillDone.withPromise( + npmProfile.set({ tfa: { password, mode: 'disable' } }, conf) + ) + } else if (userInfo && userInfo.tfa) { + if (conf.otp) + conf.otp = conf.otp + else { + const otp = await readUserInfo.otp( + 'Enter one-time password from your authenticator app: ' + ) + conf.otp = otp } - if (typeof challenge.tfa !== 'string' || !/^otpauth:[/][/]/.test(challenge.tfa)) - throw new Error('Unknown error enabling two-factor authentication. Expected otpauth URL, got: ' + inspect(challenge.tfa)) - - const otpauth = new URL(challenge.tfa) - const secret = otpauth.searchParams.get('secret') - return qrcode(challenge.tfa).then((code) => { - output('Scan into your authenticator app:\n' + code + '\n Or enter code:', secret) - }).then((code) => { - return readUserInfo.otp('And an OTP code from your authenticator: ') - }).then((otp1) => { - log.info('profile', 'Finalizing two-factor authentication') - return profile.set({ tfa: [otp1] }, conf) - }).then((result) => { - output('2FA successfully enabled. Below are your recovery codes, please print these out.') - output('You will need these to recover access to your account if you lose your authentication device.') - result.tfa.forEach((c) => output('\t' + c)) - }) - }) + } + + log.info('profile', 'Setting two-factor authentication to ' + mode) + const challenge = await pulseTillDone.withPromise(npmProfile.set(info, conf)) + + if (challenge.tfa === null) { + output('Two factor authentication mode changed to: ' + mode) + return + } + + const badResponse = typeof challenge.tfa !== 'string' + || !/^otpauth:[/][/]/.test(challenge.tfa) + if (badResponse) { + throw new Error( + 'Unknown error enabling two-factor authentication. Expected otpauth URL' + + ', got: ' + inspect(challenge.tfa) + ) + } + + const otpauth = new URL(challenge.tfa) + const secret = otpauth.searchParams.get('secret') + const code = await qrcode(challenge.tfa) + + output( + 'Scan into your authenticator app:\n' + code + '\n Or enter code:', secret + ) + + const interactiveOTP = + await readUserInfo.otp('And an OTP code from your authenticator: ') + + log.info('profile', 'Finalizing two-factor authentication') + + const result = await npmProfile.set({ tfa: [interactiveOTP] }, conf) + + output( + '2FA successfully enabled. Below are your recovery codes, ' + + 'please print these out.' + ) + output( + 'You will need these to recover access to your account ' + + 'if you lose your authentication device.' + ) + + for (const tfaCode of result.tfa) + output('\t' + tfaCode) } -function getAuth (conf) { +const getAuth = conf => { const creds = npm.config.getCredentialsByURI(conf.registry) - let auth + const auth = {} + if (creds.token) - auth = { token: creds.token } + auth.token = creds.token else if (creds.username) - auth = { basic: { username: creds.username, password: creds.password } } + auth.basic = { username: creds.username, password: creds.password } else if (creds.auth) { const basic = Buffer.from(creds.auth, 'base64').toString().split(':', 2) - auth = { basic: { username: basic[0], password: basic[1] } } - } else - auth = {} + auth.basic = { username: basic[0], password: basic[1] } + } if (conf.otp) auth.otp = conf.otp + return auth } -function disable2fa (args) { - let conf = npm.flatOptions - return pulseTillDone.withPromise(profile.get(conf)).then((info) => { - if (!info.tfa || info.tfa.pending) { - output('Two factor authentication not enabled.') - return - } - return readUserInfo.password().then((password) => { - return Promise.resolve().then(() => { - if (conf.otp) - return - return readUserInfo.otp('Enter one-time password from your authenticator: ').then((otp) => { - conf = { ...conf, otp } - }) - }).then(() => { - log.info('profile', 'disabling tfa') - return pulseTillDone.withPromise(profile.set({ tfa: { password: password, mode: 'disable' } }, conf)).then(() => { - if (conf.json) - output(JSON.stringify({ tfa: false }, null, 2)) - else if (conf.parseable) - output('tfa\tfalse') - else - output('Two factor authentication disabled.') - }) - }) - }) - }) -} +const disable2fa = async args => { + const conf = { ...npm.flatOptions } + const info = await pulseTillDone.withPromise(npmProfile.get(conf)) + + if (!info.tfa || info.tfa.pending) { + output('Two factor authentication not enabled.') + return + } + + const password = await readUserInfo.password() -function qrcode (url) { - return new Promise((resolve) => qrcodeTerminal.generate(url, resolve)) + if (!conf.otp) { + const msg = 'Enter one-time password from your authenticator app: ' + conf.otp = await readUserInfo.otp(msg) + } + + log.info('profile', 'disabling tfa') + + await pulseTillDone.withPromise(npmProfile.set({ + tfa: { password: password, mode: 'disable' }, + }, conf)) + + if (conf.json) + output(JSON.stringify({ tfa: false }, null, 2)) + else if (conf.parseable) + output('tfa\tfalse') + else + output('Two factor authentication disabled.') } + +const qrcode = url => + new Promise((resolve) => qrcodeTerminal.generate(url, resolve)) + +module.exports = Object.assign(cmd, { usage, completion }) diff --git a/deps/npm/lib/publish.js b/deps/npm/lib/publish.js index da07a703878dd7..0a4123303e6be5 100644 --- a/deps/npm/lib/publish.js +++ b/deps/npm/lib/publish.js @@ -35,7 +35,21 @@ const publish = async args => { log.verbose('publish', args) const opts = { ...npm.flatOptions } - const { json, defaultTag } = opts + const { json, defaultTag, registry } = opts + + if (!registry) { + throw Object.assign(new Error('No registry specified.'), { + code: 'ENOREGISTRY', + }) + } + + const creds = npm.config.getCredentialsByURI(registry) + if (!creds.token && !creds.username) { + throw Object.assign(new Error('This command requires you to be logged in.'), { + code: 'ENEEDAUTH', + }) + } + if (semver.validRange(defaultTag)) throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim()) diff --git a/deps/npm/lib/repo.js b/deps/npm/lib/repo.js index 7bd3bd154f7398..2dc3bcb1b846f6 100644 --- a/deps/npm/lib/repo.js +++ b/deps/npm/lib/repo.js @@ -36,7 +36,8 @@ const getRepo = async pkg => { } const info = hostedFromMani(mani) - const url = info ? info.browse(mani.repository.directory) : unknownHostedUrl(rurl) + const url = info ? + info.browse(mani.repository.directory) : unknownHostedUrl(rurl) if (!url) { throw Object.assign(new Error('no repository: could not get url'), { diff --git a/deps/npm/lib/set.js b/deps/npm/lib/set.js index 62860e53ffeb15..fd607629328ff8 100644 --- a/deps/npm/lib/set.js +++ b/deps/npm/lib/set.js @@ -1,7 +1,7 @@ module.exports = set -set.usage = 'npm set (See `npm config`)' +set.usage = 'npm set = [= ...] (See `npm config`)' var npm = require('./npm.js') diff --git a/deps/npm/lib/token.js b/deps/npm/lib/token.js index 8809e4412f8576..98bbd30433cdd6 100644 --- a/deps/npm/lib/token.js +++ b/deps/npm/lib/token.js @@ -74,16 +74,19 @@ function token (args, cb) { function generateTokenIds (tokens, minLength) { const byId = {} - tokens.forEach((token) => { + for (const token of tokens) { token.id = token.key for (let ii = minLength; ii < token.key.length; ++ii) { - if (!tokens.some((ot) => ot !== token && ot.key.slice(0, ii) === token.key.slice(0, ii))) { + const match = tokens.some(ot => + ot !== token && + ot.key.slice(0, ii) === token.key.slice(0, ii)) + if (!match) { token.id = token.key.slice(0, ii) break } } byId[token.id] = token - }) + } return byId } @@ -136,7 +139,8 @@ function list (args) { return } generateTokenIds(tokens, 6) - const idWidth = tokens.reduce((acc, token) => Math.max(acc, token.id.length), 0) + const idWidth = tokens.reduce((acc, token) => + Math.max(acc, token.id.length), 0) const table = new Table({ head: ['id', 'token', 'created', 'readonly', 'CIDR whitelist'], colWidths: [Math.max(idWidth, 2) + 2, 9, 12, 10], @@ -170,8 +174,8 @@ function rm (args) { else if (matches.length > 1) throw new Error(`Token ID "${id}" was ambiguous, a new token may have been created since you last ran \`npm token list\`.`) else { - const tokenMatches = tokens.filter((token) => id.indexOf(token.token) === 0) - if (tokenMatches.length === 0) + const tokenMatches = tokens.some(t => id.indexOf(t.token) === 0) + if (!tokenMatches) throw new Error(`Unknown token id or value "${id}".`) toRemove.push(id) @@ -212,7 +216,8 @@ function create (args) { Object.keys(result).forEach((k) => output(k + '\t' + result[k])) else { const table = new Table() - Object.keys(result).forEach((k) => table.push({ [ansistyles.bright(k)]: String(result[k]) })) + for (const k of Object.keys(result)) + table.push({ [ansistyles.bright(k)]: String(result[k]) }) output(table.toString()) } }) diff --git a/deps/npm/lib/unpublish.js b/deps/npm/lib/unpublish.js index 1d7601b60cd84a..75993af9437d0d 100644 --- a/deps/npm/lib/unpublish.js +++ b/deps/npm/lib/unpublish.js @@ -93,8 +93,8 @@ async function unpublish (args) { const { name, version, publishConfig } = manifest const pkgJsonSpec = npa.resolve(name, version) - - res = await otplease(opts, opts => libunpub(pkgJsonSpec, { ...opts, publishConfig })) + const optsWithPub = { ...opts, publishConfig } + res = await otplease(opts, opts => libunpub(pkgJsonSpec, optsWithPub)) pkgName = name pkgVersion = version ? `@${version}` : '' } else { diff --git a/deps/npm/lib/utils/config.js b/deps/npm/lib/utils/config.js index 6abb502e205724..2b6b96e5c81900 100644 --- a/deps/npm/lib/utils/config.js +++ b/deps/npm/lib/utils/config.js @@ -120,7 +120,6 @@ const defaults = { long: false, maxsockets: 50, message: '%s', - 'metrics-registry': null, 'node-options': null, 'node-version': process.version, noproxy: null, @@ -159,7 +158,6 @@ const defaults = { searchlimit: 20, searchopts: '', searchstaleness: 15 * 60, - 'send-metrics': false, shell, shrinkwrap: true, 'sign-git-commit': false, @@ -273,7 +271,6 @@ const types = { long: Boolean, maxsockets: Number, message: String, - 'metrics-registry': [null, String], 'node-options': [null, String], 'node-version': [null, semver], noproxy: [null, String, Array], @@ -312,7 +309,6 @@ const types = { searchlimit: Number, searchopts: String, searchstaleness: Number, - 'send-metrics': Boolean, shell: String, shrinkwrap: Boolean, 'sign-git-commit': Boolean, diff --git a/deps/npm/lib/utils/error-handler.js b/deps/npm/lib/utils/error-handler.js index cb339523dbc671..f9685c91d72125 100644 --- a/deps/npm/lib/utils/error-handler.js +++ b/deps/npm/lib/utils/error-handler.js @@ -7,7 +7,6 @@ let wroteLogFile = false let exitCode = 0 const errorMessage = require('./error-message.js') const replaceInfo = require('./replace-info.js') -const stopMetrics = require('./metrics.js').stop const cacheFile = require('./cache-file.js') @@ -43,9 +42,6 @@ process.on('exit', code => { } } - // kill any outstanding stats reporter if it hasn't finished yet - stopMetrics() - if (code) itWorked = false if (itWorked) diff --git a/deps/npm/lib/utils/error-message.js b/deps/npm/lib/utils/error-message.js index 695b497ab64b73..ac5a935dc87707 100644 --- a/deps/npm/lib/utils/error-message.js +++ b/deps/npm/lib/utils/error-message.js @@ -193,10 +193,14 @@ module.exports = (er) => { else { detail.push(['404', 'This package name is not valid, because', '']) - const errorsArray = (valResult.errors || []).concat(valResult.warnings || []) - errorsArray.forEach(function (item, idx) { - detail.push(['404', ' ' + (idx + 1) + '. ' + item]) - }) + const errorsArray = [ + ...(valResult.errors || []), + ...(valResult.warnings || []), + ] + errorsArray.forEach((item, idx) => detail.push([ + '404', + ' ' + (idx + 1) + '. ' + item, + ])) } detail.push(['404', '\nNote that you can also install from a']) diff --git a/deps/npm/lib/utils/file-exists.js b/deps/npm/lib/utils/file-exists.js index 3149c0ae52fd8d..605472536aab01 100644 --- a/deps/npm/lib/utils/file-exists.js +++ b/deps/npm/lib/utils/file-exists.js @@ -3,6 +3,8 @@ const util = require('util') const stat = util.promisify(fs.stat) -const fileExists = (file) => stat(file).then((stat) => stat.isFile()).catch(() => false) +const fileExists = (file) => stat(file) + .then((stat) => stat.isFile()) + .catch(() => false) module.exports = fileExists diff --git a/deps/npm/lib/utils/flat-options.js b/deps/npm/lib/utils/flat-options.js index 71edca0718c783..828481930369c1 100644 --- a/deps/npm/lib/utils/flat-options.js +++ b/deps/npm/lib/utils/flat-options.js @@ -50,8 +50,6 @@ const flatten = obj => ({ cache: join(obj.cache, '_cacache'), global: obj.global, - metricsRegistry: obj['metrics-registry'] || obj.registry, - sendMetrics: obj['send-metrics'], registry: obj.registry, scope: obj.scope, access: obj.access, diff --git a/deps/npm/lib/utils/metrics-launch.js b/deps/npm/lib/utils/metrics-launch.js deleted file mode 100644 index 4a3dcde1ab9236..00000000000000 --- a/deps/npm/lib/utils/metrics-launch.js +++ /dev/null @@ -1,43 +0,0 @@ -/* eslint-disable camelcase */ -module.exports = launchSendMetrics -var fs = require('graceful-fs') -var child_process = require('child_process') - -if (require.main === module) - main() - -function launchSendMetrics () { - var path = require('path') - var npm = require('../npm.js') - try { - if (!npm.config.get('send-metrics')) - return - var cliMetrics = path.join(npm.config.get('cache'), 'anonymous-cli-metrics.json') - var targetRegistry = npm.config.get('metrics-registry') - fs.statSync(cliMetrics) - return runInBackground(__filename, [cliMetrics, targetRegistry]) - } catch (ex) { - // if the metrics file doesn't exist, don't run - } -} - -function runInBackground (js, args, opts) { - if (!args) - args = [] - args.unshift(js) - if (!opts) - opts = {} - opts.stdio = 'ignore' - opts.detached = true - var child = child_process.spawn(process.execPath, args, opts) - child.unref() - return child -} - -function main () { - var sendMetrics = require('./metrics.js').send - var metricsFile = process.argv[2] - var metricsRegistry = process.argv[3] - - sendMetrics(metricsFile, metricsRegistry) -} diff --git a/deps/npm/lib/utils/metrics.js b/deps/npm/lib/utils/metrics.js deleted file mode 100644 index 432f8dc703477b..00000000000000 --- a/deps/npm/lib/utils/metrics.js +++ /dev/null @@ -1,80 +0,0 @@ -exports.start = startMetrics -exports.stop = stopMetrics -exports.save = saveMetrics -exports.send = sendMetrics - -const fs = require('fs') -const path = require('path') -const npm = require('../npm.js') -const regFetch = require('npm-registry-fetch') -const { v4: uuidv4 } = require('uuid') -const cacheFile = require('./cache-file.js') - -let inMetrics = false - -function startMetrics () { - if (inMetrics) - return - // loaded on demand to avoid any recursive deps when `./metrics-launch` requires us. - var metricsLaunch = require('./metrics-launch.js') - npm.metricsProcess = metricsLaunch() -} - -function stopMetrics () { - if (inMetrics) - return - if (npm.metricsProcess) - npm.metricsProcess.kill('SIGKILL') -} - -function saveMetrics (itWorked) { - if (inMetrics) - return - // If the metrics reporter hasn't managed to PUT yet then kill it so that it doesn't - // step on our updating the anonymous-cli-metrics json - stopMetrics() - var metricsFile = path.join(npm.config.get('cache'), 'anonymous-cli-metrics.json') - var metrics - try { - metrics = JSON.parse(fs.readFileSync(metricsFile)) - metrics.metrics.to = new Date().toISOString() - if (itWorked) - ++metrics.metrics.successfulInstalls - else - ++metrics.metrics.failedInstalls - } catch (ex) { - metrics = { - metricId: uuidv4(), - metrics: { - from: new Date().toISOString(), - to: new Date().toISOString(), - successfulInstalls: itWorked ? 1 : 0, - failedInstalls: itWorked ? 0 : 1, - }, - } - } - try { - cacheFile.write(metricsFile, JSON.stringify(metrics)) - } catch (ex) { - // we couldn't write and/or chown the error metrics file, oh well. - } -} - -function sendMetrics (metricsFile, metricsRegistry) { - inMetrics = true - var cliMetrics = JSON.parse(fs.readFileSync(metricsFile)) - regFetch( - `/-/npm/anon-metrics/v1/${encodeURIComponent(cliMetrics.metricId)}`, - // NOTE: skip npmConfig() to prevent auth - { - registry: metricsRegistry, - method: 'PUT', - body: cliMetrics.metrics, - retry: false, - } - ).then(() => { - fs.unlinkSync(metricsFile) - }, err => { - cacheFile.write(path.join(path.dirname(metricsFile), 'last-send-metrics-error.txt'), err.stack) - }) -} diff --git a/deps/npm/lib/utils/reify-output.js b/deps/npm/lib/utils/reify-output.js index ed573348796c17..4abaadc2ec7aa8 100644 --- a/deps/npm/lib/utils/reify-output.js +++ b/deps/npm/lib/utils/reify-output.js @@ -1,7 +1,7 @@ // pass in an arborist object, and it'll output the data about what // was done, what was audited, etc. // -// added 351 packages, removed 132 packages, and audited 13388 packages in 19.157s +// added ## packages, removed ## packages, and audited ## packages in 19.157s // // 1 package is looking for funding // run `npm fund` for details diff --git a/deps/npm/lib/view.js b/deps/npm/lib/view.js index 42ba4b1c1b68e2..bc9a13d85dd9d0 100644 --- a/deps/npm/lib/view.js +++ b/deps/npm/lib/view.js @@ -196,7 +196,8 @@ const prettyView = async (packument, manifest, opts) => { name: color.yellow(manifest._npmUser.name), email: color.cyan(manifest._npmUser.email), }), - modified: packument.time ? color.yellow(relativeDate(packument.time[packument.version])) : undefined, + modified: !packument.time ? undefined + : color.yellow(relativeDate(packument.time[packument.version])), maintainers: (packument.maintainers || []).map((u) => unparsePerson({ name: color.yellow(u.name), email: color.cyan(u.email), @@ -387,8 +388,14 @@ async function printData (data, name, opts) { if (includeVersions || includeFields || typeof d !== 'string') { if (opts.json) msgJson[msgJson.length - 1][f] = d - else - d = inspect(d, { showHidden: false, depth: 5, colors: npm.color, maxArrayLength: null }) + else { + d = inspect(d, { + showHidden: false, + depth: 5, + colors: npm.color, + maxArrayLength: null, + }) + } } else if (typeof d === 'string' && opts.json) d = JSON.stringify(d) diff --git a/deps/npm/man/man1/npm-config.1 b/deps/npm/man/man1/npm-config.1 index 46e4342644699b..eb2d8bfead011e 100644 --- a/deps/npm/man/man1/npm-config.1 +++ b/deps/npm/man/man1/npm-config.1 @@ -5,15 +5,15 @@ .P .RS 2 .nf -npm config set [\-g|\-\-global] -npm config get -npm config delete -npm config list [\-l] [\-\-json] +npm config set = [= \.\.\.] +npm config get [ [ \.\.\.]] +npm config delete [ \.\.\.] +npm config list [\-\-json] npm config edit -npm get -npm set [\-g|\-\-global] +npm set = [= \.\.\.] +npm get [ [ \.\.\.]] -aliases: c +alias: c .fi .RE .SS Description @@ -36,22 +36,33 @@ Config supports the following sub\-commands: .P .RS 2 .nf -npm config set key value +npm config set key=value [key=value\.\.\.] +npm set key=value [key=value\.\.\.] .fi .RE .P -Sets the config key to the value\. +Sets each of the config keys to the value provided\. .P -If value is omitted, then it sets it to "true"\. +If value is omitted, then it sets it to an empty string\. +.P +Note: for backwards compatibility, \fBnpm config set key value\fP is supported +as an alias for \fBnpm config set key=value\fP\|\. .SS get .P .RS 2 .nf -npm config get key +npm config get [key \.\.\.] +npm get [key \.\.\.] .fi .RE .P -Echo the config value to stdout\. +Echo the config value(s) to stdout\. +.P +If multiple keys are provided, then the values will be prefixed with the +key names\. +.P +If no keys are provided, then this command behaves the same as \fBnpm config +list\fP\|\. .SS list .P .RS 2 @@ -66,11 +77,11 @@ to show the settings in json format\. .P .RS 2 .nf -npm config delete key +npm config delete key [key \.\.\.] .fi .RE .P -Deletes the key from all configuration files\. +Deletes the specified keys from all configuration files\. .SS edit .P .RS 2 diff --git a/deps/npm/man/man1/npm-deprecate.1 b/deps/npm/man/man1/npm-deprecate.1 index 6be05b311c3740..303e7edfec1688 100644 --- a/deps/npm/man/man1/npm-deprecate.1 +++ b/deps/npm/man/man1/npm-deprecate.1 @@ -5,7 +5,7 @@ .P .RS 2 .nf -npm deprecate [@] +npm deprecate [@] .fi .RE .SS Description @@ -22,8 +22,19 @@ npm deprecate my\-thing@"< 0\.2\.3" "critical bug fixed in v0\.2\.3" .fi .RE .P -Note that you must be the package owner to deprecate something\. See the -\fBowner\fP and \fBadduser\fP help topics\. +SemVer ranges passed to this command are interpreted such that they \fIdo\fR +include prerelease versions\. For example: +.P +.RS 2 +.nf +npm deprecate my\-thing@1\.x "1\.x is no longer supported" +.fi +.RE +.P +In this case, a version \fBmy\-thing@1\.0\.0\-beta\.0\fP will also be deprecated\. +.P +You must be the package owner to deprecate something\. See the \fBowner\fP and +\fBadduser\fP help topics\. .P To un\-deprecate a package, specify an empty string (\fB""\fP) for the \fBmessage\fP argument\. Note that you must use double quotes with no space between them to diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1 index 9db81f72900f42..f0ac3f7b6cbddb 100644 --- a/deps/npm/man/man1/npm-ls.1 +++ b/deps/npm/man/man1/npm-ls.1 @@ -26,7 +26,7 @@ example, running \fBnpm ls promzard\fP in npm's source tree will show: .P .RS 2 .nf -npm@7\.2\.0 /path/to/npm +npm@7\.3\.0 /path/to/npm └─┬ init\-package\-json@0\.0\.4 └── promzard@0\.1\.5 .fi diff --git a/deps/npm/man/man1/npm-run-script.1 b/deps/npm/man/man1/npm-run-script.1 index 2fb476eed78965..308e4498a8d175 100644 --- a/deps/npm/man/man1/npm-run-script.1 +++ b/deps/npm/man/man1/npm-run-script.1 @@ -64,18 +64,18 @@ on Unix\-like systems it is the \fB/bin/sh\fP command, on Windows it is the \fBc The actual shell referred to by \fB/bin/sh\fP also depends on the system\. You can customize the shell with the \fBscript\-shell\fP configuration\. .P -Scripts are run from the root of the module, regardless of what the current -working directory is when \fBnpm run\fP is called\. If you want your script to -use different behavior based on what subdirectory you're in, you can use the -\fBINIT_CWD\fP environment variable, which holds the full path you were in when -you ran \fBnpm run\fP\|\. -.P -\fBnpm run\fP sets the \fBNODE\fP environment variable to the \fBnode\fP executable with -which \fBnpm\fP is executed\. Also, if the \fB\-\-scripts\-prepend\-node\-path\fP is passed, -the directory within which \fBnode\fP resides is added to the -\fBPATH\fP\|\. If \fB\-\-scripts\-prepend\-node\-path=auto\fP is passed (which has been the -default in \fBnpm\fP v3), this is only performed when that \fBnode\fP executable is -not found in the \fBPATH\fP\|\. +Scripts are run from the root of the package folder, regardless of what the +current working directory is when \fBnpm run\fP is called\. If you want your +script to use different behavior based on what subdirectory you're in, you +can use the \fBINIT_CWD\fP environment variable, which holds the full path you +were in when you ran \fBnpm run\fP\|\. +.P +\fBnpm run\fP sets the \fBNODE\fP environment variable to the \fBnode\fP executable +with which \fBnpm\fP is executed\. Also, if the \fB\-\-scripts\-prepend\-node\-path\fP is +passed, the directory within which \fBnode\fP resides is added to the \fBPATH\fP\|\. +If \fB\-\-scripts\-prepend\-node\-path=auto\fP is passed (which has been the default +in \fBnpm\fP v3), this is only performed when that \fBnode\fP executable is not +found in the \fBPATH\fP\|\. .P If you try to run a script without having a \fBnode_modules\fP directory and it fails, you will be given a warning to run \fBnpm install\fP, just in case you've forgotten\. diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1 index e18720c1ed51de..68e6ace0478cf9 100644 --- a/deps/npm/man/man1/npm.1 +++ b/deps/npm/man/man1/npm.1 @@ -10,7 +10,7 @@ npm [args] .RE .SS Version .P -7\.2\.0 +7\.3\.0 .SS Description .P npm is the package manager for the Node JavaScript platform\. It puts @@ -155,14 +155,29 @@ When you find issues, please report them: .RS 0 .IP \(bu 2 web: +https://github\.com/npm/npm/issues +.IP \(bu 2 +archived web: https://npm\.community/c/bugs .RE .P -Be sure to follow the template and bug reporting guidelines\. You can also ask -for help in the support forum \fIhttps://npm\.community/c/support\fR if you're -unsure if it's actually a bug or are having trouble coming up with a detailed -reproduction to report\. +Be sure to follow the template and bug reporting guidelines\. +.SS Feature Requests +.P +Discuss new feature ideas on our discussion forum: +.RS 0 +.IP \(bu 2 +https://github\.com/npm/feedback + +.RE +.P +Or suggest formal RFC proposals: +.RS 0 +.IP \(bu 2 +https://github\.com/npm/rfcs + +.RE .SS Author .P Isaac Z\. Schlueter \fIhttp://blog\.izs\.me/\fR :: diff --git a/deps/npm/man/man7/config.7 b/deps/npm/man/man7/config.7 index ebf2314e288d9e..a23a78b24722cf 100644 --- a/deps/npm/man/man7/config.7 +++ b/deps/npm/man/man7/config.7 @@ -1005,16 +1005,6 @@ Type: String Commit message which is used by \fBnpm version\fP when creating version commit\. .P Any "%s" in the message will be replaced with the version number\. -.SS metrics\-registry -.RS 0 -.IP \(bu 2 -Default: The value of \fBregistry\fP (which defaults to "https://registry\.npmjs\.org/") -.IP \(bu 2 -Type: String - -.RE -.P -The registry you want to send cli metrics to if \fBsend\-metrics\fP is true\. .SS node\-options .RS 0 .IP \(bu 2 @@ -1462,19 +1452,6 @@ Type: Number .P The age of the cache, in seconds, before another registry request is made if using legacy search endpoint\. -.SS send\-metrics -.RS 0 -.IP \(bu 2 -Default: false -.IP \(bu 2 -Type: Boolean - -.RE -.P -If true, success/failure metrics will be reported to the registry stored in -\fBmetrics\-registry\fP\|\. These requests contain the number of successful and -failing runs of the npm CLI and the time period over which those counts were -gathered\. No identifying information is included in these requests\. .SS shell .RS 0 .IP \(bu 2 diff --git a/deps/npm/man/man7/scripts.7 b/deps/npm/man/man7/scripts.7 index d2ef4476abd00f..b062f4ea1cda4d 100644 --- a/deps/npm/man/man7/scripts.7 +++ b/deps/npm/man/man7/scripts.7 @@ -21,7 +21,7 @@ To create "pre" or "post" scripts for any scripts defined in the \fB"scripts"\fP .RE .SS Life Cycle Scripts .P -There are some special life cycle scripts that happen only in certain situations\. These scripts happen in addtion to the "pre" and "post" script\. +There are some special life cycle scripts that happen only in certain situations\. These scripts happen in addition to the "pre" and "post" script\. .RS 0 .IP \(bu 2 \fBprepare\fP, \fBprepublish\fP, \fBprepublishOnly\fP, \fBprepack\fP, \fBpostpack\fP diff --git a/deps/npm/node_modules/@npmcli/config/lib/index.js b/deps/npm/node_modules/@npmcli/config/lib/index.js index a80b976a000814..e7fac96c1c8c8f 100644 --- a/deps/npm/node_modules/@npmcli/config/lib/index.js +++ b/deps/npm/node_modules/@npmcli/config/lib/index.js @@ -178,6 +178,11 @@ 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.data.get(where).data[key] = val // this is now dirty, the next call to this.valid will have to check it @@ -512,6 +517,9 @@ class Config { if (where === 'user') { const reg = this.get('registry') const creds = this.getCredentialsByURI(reg) + // we ignore this error because the failed set already removed + // anything that might be a security hazard, and it won't be + // saved back to the .npmrc file, so we're good. try { this.setCredentialsByURI(reg, creds) } catch (_) {} } @@ -576,18 +584,22 @@ class Config { this.delete(`${nerfed}:email`, 'user') this.delete(`${nerfed}:always-auth`, 'user') } else if (username || password || email) { - if (!username) - throw new Error('must include username') - if (!password) - throw new Error('must include password') + 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') this.delete(`${nerfed}:_authToken`, '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') + 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') diff --git a/deps/npm/node_modules/@npmcli/config/package.json b/deps/npm/node_modules/@npmcli/config/package.json index 26581f385c386a..a7050c73a03ef4 100644 --- a/deps/npm/node_modules/@npmcli/config/package.json +++ b/deps/npm/node_modules/@npmcli/config/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/config", - "version": "1.2.7", + "version": "1.2.8", "files": [ "lib" ], diff --git a/deps/npm/package.json b/deps/npm/package.json index 8e2917c5edca2a..47c108bbe7a86e 100644 --- a/deps/npm/package.json +++ b/deps/npm/package.json @@ -1,5 +1,5 @@ { - "version": "7.2.0", + "version": "7.3.0", "name": "npm", "description": "a package manager for JavaScript", "keywords": [ @@ -44,7 +44,7 @@ "dependencies": { "@npmcli/arborist": "^2.0.2", "@npmcli/ci-detect": "^1.2.0", - "@npmcli/config": "^1.2.7", + "@npmcli/config": "^1.2.8", "@npmcli/run-script": "^1.8.1", "abbrev": "~1.1.1", "ansicolors": "~0.3.2", diff --git a/deps/npm/tap-snapshots/test-lib-config.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-config.js-TAP.test.js index 1794dc4fadfb88..d62969145d1d98 100644 --- a/deps/npm/tap-snapshots/test-lib-config.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-config.js-TAP.test.js @@ -129,7 +129,7 @@ long = false ; Run \`npm config ls -l\` to show all defaults. ` -exports[`test/lib/config.js TAP config list overrides > should list overriden configs 1`] = ` +exports[`test/lib/config.js TAP config list overrides > should list overridden configs 1`] = ` ; "cli" config from command line options editor = "vi" diff --git a/deps/npm/tap-snapshots/test-lib-ls.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-ls.js-TAP.test.js index cded6fa5a6333f..120a65baffae93 100644 --- a/deps/npm/tap-snapshots/test-lib-ls.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-ls.js-TAP.test.js @@ -187,7 +187,7 @@ exports[`test/lib/ls.js TAP ls --parseable --production > should output tree con {CWD}/ls-ls-parseable--production/node_modules/prod-dep/node_modules/bar ` -exports[`test/lib/ls.js TAP ls --parseable cycle deps > should print tree output ommiting deduped ref 1`] = ` +exports[`test/lib/ls.js TAP ls --parseable cycle deps > should print tree output omitting deduped ref 1`] = ` {CWD}/ls-ls-parseable-cycle-deps {CWD}/ls-ls-parseable-cycle-deps/node_modules/a {CWD}/ls-ls-parseable-cycle-deps/node_modules/b @@ -287,11 +287,11 @@ exports[`test/lib/ls.js TAP ls --parseable using aliases > should output tree co {CWD}/ls-ls-parseable-using-aliases/node_modules/a ` -exports[`test/lib/ls.js TAP ls --parseable with filter arg > should output parseable contaning only occurences of filtered by package 1`] = ` +exports[`test/lib/ls.js TAP ls --parseable with filter arg > should output parseable contaning only occurrences of filtered by package 1`] = ` {CWD}/ls-ls-parseable-with-filter-arg/node_modules/lorem ` -exports[`test/lib/ls.js TAP ls --parseable with filter arg nested dep > should output parseable contaning only occurences of filtered package 1`] = ` +exports[`test/lib/ls.js TAP ls --parseable with filter arg nested dep > should output parseable contaning only occurrences of filtered package 1`] = ` {CWD}/ls-ls-parseable-with-filter-arg-nested-dep/node_modules/bar ` @@ -299,7 +299,7 @@ exports[`test/lib/ls.js TAP ls --parseable with missing filter arg > should outp ` -exports[`test/lib/ls.js TAP ls --parseable with multiple filter args > should output parseable contaning only occurences of multiple filtered packages and their ancestors 1`] = ` +exports[`test/lib/ls.js TAP ls --parseable with multiple filter args > should output parseable contaning only occurrences of multiple filtered packages and their ancestors 1`] = ` {CWD}/ls-ls-parseable-with-multiple-filter-args/node_modules/lorem {CWD}/ls-ls-parseable-with-multiple-filter-args/node_modules/bar ` @@ -319,8 +319,8 @@ npm-broken-resolved-field-test@1.0.0 {CWD}/ls-ls-broken-resolved-field ` -exports[`test/lib/ls.js TAP ls coloured output > should output tree containing color info 1`] = ` -test-npm-ls@1.0.0 {CWD}/ls-ls-coloured-output +exports[`test/lib/ls.js TAP ls colored output > should output tree containing color info 1`] = ` +test-npm-ls@1.0.0 {CWD}/ls-ls-colored-output +-- foo@1.0.0 invalid | \`-- bar@1.0.0 +-- UNMET DEPENDENCY ipsum@^1.0.0 @@ -550,19 +550,19 @@ dedupe-entries@1.0.0 {CWD}/ls-ls-with-args-and-different-order-of-items ` -exports[`test/lib/ls.js TAP ls with dot filter arg > should output tree contaning only occurences of filtered by package and coloured output 1`] = ` +exports[`test/lib/ls.js TAP ls with dot filter arg > should output tree contaning only occurrences of filtered by package and colored output 1`] = ` test-npm-ls@1.0.0 {CWD}/ls-ls-with-dot-filter-arg \`-- (empty) ` -exports[`test/lib/ls.js TAP ls with filter arg > should output tree contaning only occurences of filtered by package and coloured output 1`] = ` +exports[`test/lib/ls.js TAP ls with filter arg > should output tree contaning only occurrences of filtered by package and colored output 1`] = ` test-npm-ls@1.0.0 {CWD}/ls-ls-with-filter-arg \`-- lorem@1.0.0  ` -exports[`test/lib/ls.js TAP ls with filter arg nested dep > should output tree contaning only occurences of filtered package and its ancestors 1`] = ` +exports[`test/lib/ls.js TAP ls with filter arg nested dep > should output tree contaning only occurrences of filtered package and its ancestors 1`] = ` test-npm-ls@1.0.0 {CWD}/ls-ls-with-filter-arg-nested-dep \`-- foo@1.0.0 \`-- bar@1.0.0 @@ -575,7 +575,7 @@ test-npm-ls@1.0.0 {CWD}/ls-ls-with-missing-filter-arg ` -exports[`test/lib/ls.js TAP ls with multiple filter args > should output tree contaning only occurences of multiple filtered packages and their ancestors 1`] = ` +exports[`test/lib/ls.js TAP ls with multiple filter args > should output tree contaning only occurrences of multiple filtered packages and their ancestors 1`] = ` test-npm-ls@1.0.0 {CWD}/ls-ls-with-multiple-filter-args +-- foo@1.0.0 | \`-- bar@1.0.0 diff --git a/deps/npm/tap-snapshots/test-lib-profile.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-profile.js-TAP.test.js new file mode 100644 index 00000000000000..bb838ad92c97dc --- /dev/null +++ b/deps/npm/tap-snapshots/test-lib-profile.js-TAP.test.js @@ -0,0 +1,90 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/lib/profile.js TAP enable-2fa from token and set otp, retries on pending and verifies with qrcode > should output 2fa enablement success msgs 1`] = ` +Scan into your authenticator app: +qrcode + Or enter code: +12342FA successfully enabled. Below are your recovery codes, please print these out.You will need these to recover access to your account if you lose your authentication device. 123456 789101 +` + +exports[`test/lib/profile.js TAP profile get --parseable > should output parseable result value 1`] = ` +foo +` + +exports[`test/lib/profile.js TAP profile get multiple args --parseable > should output parseable profile value results 1`] = ` +foo foo@github.com (verified) https://github.com/npm +` + +exports[`test/lib/profile.js TAP profile get multiple args comma separated > should output all keys 1`] = ` +foo foo@github.com (verified) https://github.com/npm +` + +exports[`test/lib/profile.js TAP profile get multiple args default output > should output all keys 1`] = ` +foo foo@github.com (verified) https://github.com/npm +` + +exports[`test/lib/profile.js TAP profile get no args --parseable > should output all profile info as parseable result 1`] = ` +tfa auth-and-writesname fooemail foo@github.comemail_verified truecreated 2015-02-26T01:26:37.384Zupdated 2020-08-12T16:19:35.326Zfullname Foo Barhomepage https://github.comfreenode foobartwitter https://twitter.com/npmjsgithub https://github.com/npm +` + +exports[`test/lib/profile.js TAP profile get no args default output > should output table with contents 1`] = ` +name: foo +email: foo@github.com (verified) +two-factor auth: auth-and-writes +fullname: Foo Bar +homepage: https://github.com +freenode: foobar +twitter: https://twitter.com/npmjs +github: https://github.com/npm +created: 2015-02-26T01:26:37.384Z +updated: 2020-08-12T16:19:35.326Z +` + +exports[`test/lib/profile.js TAP profile get no args no tfa enabled > should output expected profile values 1`] = ` +name: foo +email: foo@github.com (verified) +two-factor auth: disabled +fullname: Foo Bar +homepage: https://github.com +freenode: foobar +twitter: https://twitter.com/npmjs +github: https://github.com/npm +created: 2015-02-26T01:26:37.384Z +updated: 2020-08-12T16:19:35.326Z +` + +exports[`test/lib/profile.js TAP profile get no args profile has cidr_whitelist item > should output table with contents 1`] = ` +name: foo +email: foo@github.com (verified) +two-factor auth: auth-and-writes +fullname: Foo Bar +homepage: https://github.com +freenode: foobar +twitter: https://twitter.com/npmjs +github: https://github.com/npm +created: 2015-02-26T01:26:37.384Z +updated: 2020-08-12T16:19:35.326Z +cidr_whitelist: 192.168.1.1 +` + +exports[`test/lib/profile.js TAP profile get no args unverified email > should output table with contents 1`] = ` +name: foo +email: foo@github.com(unverified) +two-factor auth: auth-and-writes +fullname: Foo Bar +homepage: https://github.com +freenode: foobar +twitter: https://twitter.com/npmjs +github: https://github.com/npm +created: 2015-02-26T01:26:37.384Z +updated: 2020-08-12T16:19:35.326Z +` + +exports[`test/lib/profile.js TAP profile set writable key --parseable > should output parseable set key success msg 1`] = ` +fullname Lorem Ipsum +` diff --git a/deps/npm/tap-snapshots/test-lib-utils-config.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-config.js-TAP.test.js index 3bdf905d869e6a..d14c9e95035135 100644 --- a/deps/npm/tap-snapshots/test-lib-utils-config.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-utils-config.js-TAP.test.js @@ -82,7 +82,6 @@ Object { "long": false, "maxsockets": 50, "message": "%s", - "metrics-registry": null, "node-options": null, "node-version": "v14.8.0", "noproxy": null, @@ -120,7 +119,6 @@ Object { "searchlimit": 20, "searchopts": "", "searchstaleness": 900, - "send-metrics": false, "shell": "cmd.exe", "shrinkwrap": true, "sign-git-commit": false, @@ -423,10 +421,6 @@ Object { "long": "{Boolean TYPE}", "maxsockets": "{Number TYPE}", "message": "{String TYPE}", - "metrics-registry": Array [ - null, - "{String TYPE}", - ], "node-options": Array [ null, "{String TYPE}", @@ -509,7 +503,6 @@ Object { "searchlimit": "{Number TYPE}", "searchopts": "{String TYPE}", "searchstaleness": "{Number TYPE}", - "send-metrics": "{Boolean TYPE}", "shell": "{String TYPE}", "shrinkwrap": "{Boolean TYPE}", "sign-git-commit": "{Boolean TYPE}", @@ -616,7 +609,6 @@ Object { "long": false, "maxsockets": 50, "message": "%s", - "metrics-registry": null, "node-options": null, "node-version": "v14.8.0", "noproxy": null, @@ -654,7 +646,6 @@ Object { "searchlimit": 20, "searchopts": "", "searchstaleness": 900, - "send-metrics": false, "shell": "cmd.exe", "shrinkwrap": true, "sign-git-commit": false, @@ -957,10 +948,6 @@ Object { "long": "{Boolean TYPE}", "maxsockets": "{Number TYPE}", "message": "{String TYPE}", - "metrics-registry": Array [ - null, - "{String TYPE}", - ], "node-options": Array [ null, "{String TYPE}", @@ -1043,7 +1030,6 @@ Object { "searchlimit": "{Number TYPE}", "searchopts": "{String TYPE}", "searchstaleness": "{Number TYPE}", - "send-metrics": "{Boolean TYPE}", "shell": "{String TYPE}", "shrinkwrap": "{Boolean TYPE}", "sign-git-commit": "{Boolean TYPE}", @@ -1150,7 +1136,6 @@ Object { "long": false, "maxsockets": 50, "message": "%s", - "metrics-registry": null, "node-options": null, "node-version": "v14.8.0", "noproxy": null, @@ -1188,7 +1173,6 @@ Object { "searchlimit": 20, "searchopts": "", "searchstaleness": 900, - "send-metrics": false, "shell": "/usr/local/bin/bash", "shrinkwrap": true, "sign-git-commit": false, @@ -1493,10 +1477,6 @@ Object { "long": "{Boolean TYPE}", "maxsockets": "{Number TYPE}", "message": "{String TYPE}", - "metrics-registry": Array [ - null, - "{String TYPE}", - ], "node-options": Array [ null, "{String TYPE}", @@ -1579,7 +1559,6 @@ Object { "searchlimit": "{Number TYPE}", "searchopts": "{String TYPE}", "searchstaleness": "{Number TYPE}", - "send-metrics": "{Boolean TYPE}", "shell": "{String TYPE}", "shrinkwrap": "{Boolean TYPE}", "sign-git-commit": "{Boolean TYPE}", diff --git a/deps/npm/tap-snapshots/test-lib-utils-flat-options.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-flat-options.js-TAP.test.js index ae85195d7b78d1..36f76712410b62 100644 --- a/deps/npm/tap-snapshots/test-lib-utils-flat-options.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-utils-flat-options.js-TAP.test.js @@ -54,7 +54,6 @@ Object { "log": Object {}, "long": undefined, "message": "message", - "metricsRegistry": "metrics-registry", "nodeBin": "/path/to/some/node", "nodeVersion": "1.2.3", "noProxy": "noproxy", @@ -100,7 +99,6 @@ Object { }, "staleness": "searchstaleness", }, - "sendMetrics": "send-metrics", "shell": undefined, "signGitCommit": "sign-git-commit", "signGitTag": "sign-git-tag", diff --git a/deps/npm/test/fixtures/eresolve-explanations.js b/deps/npm/test/fixtures/eresolve-explanations.js index b59671d200f216..c5c338e61c8c0f 100644 --- a/deps/npm/test/fixtures/eresolve-explanations.js +++ b/deps/npm/test/fixtures/eresolve-explanations.js @@ -1,4 +1,4 @@ -// some real-world examples of ERESOLVE error explaination objects, +// some real-world examples of ERESOLVE error explanation objects, // copied from arborist or generated there. module.exports = { cycleNested: { diff --git a/deps/npm/test/lib/adduser.js b/deps/npm/test/lib/adduser.js index 4e6a56fc199d60..36f59e0857902f 100644 --- a/deps/npm/test/lib/adduser.js +++ b/deps/npm/test/lib/adduser.js @@ -9,21 +9,27 @@ const _flatOptions = { authType: 'legacy', registry: 'https://registry.npmjs.org/', scope: '', + fromFlatOptions: true, } let failSave = false let deletedConfig = {} let registryOutput = '' let setConfig = {} -const authDummy = () => Promise.resolve({ - message: 'success', - newCreds: { - username: 'u', - password: 'p', - email: 'u@npmjs.org', - alwaysAuth: false, - }, -}) +const authDummy = (options) => { + if (!options.fromFlatOptions) + throw new Error('did not pass full flatOptions to auth function') + + return Promise.resolve({ + message: 'success', + newCreds: { + username: 'u', + password: 'p', + email: 'u@npmjs.org', + alwaysAuth: false, + }, + }) +} const deleteMock = (key, where) => { deletedConfig = { diff --git a/deps/npm/test/lib/config.js b/deps/npm/test/lib/config.js index 74cd530c68270e..5d2f54249cfe3b 100644 --- a/deps/npm/test/lib/config.js +++ b/deps/npm/test/lib/config.js @@ -122,7 +122,7 @@ t.test('config list overrides', t => { config(['list'], (err) => { t.ifError(err, 'npm config list') - t.matchSnapshot(result, 'should list overriden configs') + t.matchSnapshot(result, 'should list overridden configs') }) }) @@ -212,6 +212,33 @@ t.test('config delete key', t => { }) }) +t.test('config delete multiple key', t => { + t.plan(6) + + const expect = [ + 'foo', + 'bar', + ] + + npm.config.delete = (key, where) => { + t.equal(key, expect.shift(), 'should delete expected keyword') + t.equal(where, 'user', 'should delete key from user config by default') + } + + npm.config.save = where => { + t.equal(where, 'user', 'should save user config post-delete') + } + + config(['delete', 'foo', 'bar'], (err) => { + t.ifError(err, 'npm config delete keys') + }) + + t.teardown(() => { + delete npm.config.delete + delete npm.config.save + }) +}) + t.test('config delete key --global', t => { t.plan(4) @@ -293,12 +320,43 @@ t.test('config set key=val', t => { }) }) +t.test('config set multiple keys', t => { + t.plan(11) + + const expect = [ + ['foo', 'bar'], + ['bar', 'baz'], + ['asdf', ''], + ] + const args = ['foo', 'bar', 'bar=baz', 'asdf'] + + npm.config.set = (key, val, where) => { + const [expectKey, expectVal] = expect.shift() + t.equal(key, expectKey, 'should set expected key to user config') + t.equal(val, expectVal, 'should set expected value to user config') + t.equal(where, 'user', 'should set key/val in user config by default') + } + + npm.config.save = where => { + t.equal(where, 'user', 'should save user config') + } + + config(['set', ...args], (err) => { + t.ifError(err, 'npm config set key') + }) + + t.teardown(() => { + delete npm.config.set + delete npm.config.save + }) +}) + t.test('config set key to empty value', t => { t.plan(5) npm.config.set = (key, val, where) => { t.equal(key, 'foo', 'should set expected key to user config') - t.equal(val, '', 'should set empty value to user config') + t.equal(val, '', 'should set "" to user config') t.equal(where, 'user', 'should set key/val in user config by default') } @@ -403,6 +461,36 @@ t.test('config get key', t => { }) }) +t.test('config get multiple keys', t => { + t.plan(4) + + const expect = [ + 'foo', + 'bar', + ] + + const npmConfigGet = npm.config.get + npm.config.get = (key) => { + t.equal(key, expect.shift(), 'should use expected key') + return 'asdf' + } + + npm.config.save = where => { + throw new Error('should not save') + } + + config(['get', 'foo', 'bar'], (err) => { + t.ifError(err, 'npm config get multiple keys') + t.equal(result, 'foo=asdf\nbar=asdf') + }) + + t.teardown(() => { + result = '' + npm.config.get = npmConfigGet + delete npm.config.save + }) +}) + t.test('config get private key', t => { config(['get', '//private-reg.npmjs.org/:_authThoken'], (err) => { t.match( diff --git a/deps/npm/test/lib/deprecate.js b/deps/npm/test/lib/deprecate.js index 3908254ed0d635..229cb9137a42c5 100644 --- a/deps/npm/test/lib/deprecate.js +++ b/deps/npm/test/lib/deprecate.js @@ -13,6 +13,7 @@ npmFetch.json = async (uri, opts) => { versions: { '1.0.0': {}, '1.0.1': {}, + '1.0.1-pre': {}, }, } } @@ -126,6 +127,9 @@ test('deprecates all versions when no range is specified', t => { '1.0.1': { deprecated: 'this version is deprecated', }, + '1.0.1-pre': { + deprecated: 'this version is deprecated', + }, }, }) diff --git a/deps/npm/test/lib/dist-tag.js b/deps/npm/test/lib/dist-tag.js index e9dde48062dc27..8b1106fa39e5d2 100644 --- a/deps/npm/test/lib/dist-tag.js +++ b/deps/npm/test/lib/dist-tag.js @@ -323,6 +323,6 @@ test('completion', t => { }, }, }, (err) => { - t.notOk(err, 'should ignore any unkown name') + t.notOk(err, 'should ignore any unknown name') }) }) diff --git a/deps/npm/test/lib/help-search.js b/deps/npm/test/lib/help-search.js index 5ecf5db0614ac6..f74e2f1efeb9ce 100644 --- a/deps/npm/test/lib/help-search.js +++ b/deps/npm/test/lib/help-search.js @@ -40,7 +40,8 @@ const globDir = { 'npm-more-useless.md': 'exec exec', 'npm-extra-useless.md': 'exec\nexec\nexec', } -const glob = (p, cb) => cb(null, Object.keys(globDir).map((file) => join(globRoot, file))) +const glob = (p, cb) => + cb(null, Object.keys(globDir).map((file) => join(globRoot, file))) const helpSearch = requireInject('../../lib/help-search.js', { '../../lib/npm.js': npm, diff --git a/deps/npm/test/lib/ls.js b/deps/npm/test/lib/ls.js index 256ebf3534302e..7bbfc5f772b9f6 100644 --- a/deps/npm/test/lib/ls.js +++ b/deps/npm/test/lib/ls.js @@ -209,7 +209,7 @@ t.test('ls', (t) => { }) ls(['lorem'], (err) => { t.ifError(err, 'npm ls') - t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurences of filtered by package and coloured output') + t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered by package and colored output') _flatOptions.color = false t.end() }) @@ -231,7 +231,7 @@ t.test('ls', (t) => { }) ls(['.'], (err) => { t.ifError(err, 'should not throw on missing dep above current level') - t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurences of filtered by package and coloured output') + t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered by package and colored output') _flatOptions.all = true _flatOptions.depth = Infinity t.end() @@ -252,7 +252,7 @@ t.test('ls', (t) => { }) ls(['bar'], (err) => { t.ifError(err, 'npm ls') - t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurences of filtered package and its ancestors') + t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered package and its ancestors') t.end() }) }) @@ -280,7 +280,7 @@ t.test('ls', (t) => { }) ls(['bar@*', 'lorem@1.0.0'], (err) => { t.ifError(err, 'npm ls') - t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurences of multiple filtered packages and their ancestors') + t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of multiple filtered packages and their ancestors') t.end() }) }) @@ -443,7 +443,7 @@ t.test('ls', (t) => { }) }) - t.test('coloured output', (t) => { + t.test('colored output', (t) => { _flatOptions.color = true prefix = t.testdir({ 'package.json': JSON.stringify({ @@ -1588,7 +1588,7 @@ t.test('ls --parseable', (t) => { }) ls(['lorem'], (err) => { t.ifError(err, 'npm ls') - t.matchSnapshot(redactCwd(result), 'should output parseable contaning only occurences of filtered by package') + t.matchSnapshot(redactCwd(result), 'should output parseable contaning only occurrences of filtered by package') t.end() }) }) @@ -1607,7 +1607,7 @@ t.test('ls --parseable', (t) => { }) ls(['bar'], (err) => { t.ifError(err, 'npm ls') - t.matchSnapshot(redactCwd(result), 'should output parseable contaning only occurences of filtered package') + t.matchSnapshot(redactCwd(result), 'should output parseable contaning only occurrences of filtered package') t.end() }) }) @@ -1635,7 +1635,7 @@ t.test('ls --parseable', (t) => { }) ls(['bar@*', 'lorem@1.0.0'], (err) => { t.ifError(err, 'npm ls') - t.matchSnapshot(redactCwd(result), 'should output parseable contaning only occurences of multiple filtered packages and their ancestors') + t.matchSnapshot(redactCwd(result), 'should output parseable contaning only occurrences of multiple filtered packages and their ancestors') t.end() }) }) @@ -2158,7 +2158,7 @@ t.test('ls --parseable', (t) => { }, }) ls([], () => { - t.matchSnapshot(redactCwd(result), 'should print tree output ommiting deduped ref') + t.matchSnapshot(redactCwd(result), 'should print tree output omitting deduped ref') t.end() }) }) @@ -2482,7 +2482,7 @@ t.test('ls --json', (t) => { }, }, }, - 'should output json contaning only occurences of filtered by package' + 'should output json contaning only occurrences of filtered by package' ) t.equal( process.exitCode, @@ -2523,7 +2523,7 @@ t.test('ls --json', (t) => { }, }, }, - 'should output json contaning only occurences of filtered by package' + 'should output json contaning only occurrences of filtered by package' ) t.end() }) @@ -2571,7 +2571,7 @@ t.test('ls --json', (t) => { }, }, }, - 'should output json contaning only occurences of multiple filtered packages and their ancestors' + 'should output json contaning only occurrences of multiple filtered packages and their ancestors' ) t.end() }) diff --git a/deps/npm/test/lib/npm.js b/deps/npm/test/lib/npm.js index 2c71d229a7be33..8494af6bb80982 100644 --- a/deps/npm/test/lib/npm.js +++ b/deps/npm/test/lib/npm.js @@ -202,8 +202,6 @@ t.test('npm.load', t => { t.equal(npm.bin, npm.globalBin, 'bin is global bin after prefix setter') t.notEqual(npm.bin, npm.localBin, 'bin is not local bin after prefix setter') - t.equal(npm.config.get('metrics-registry'), npm.config.get('registry')) - beWindows() t.equal(npm.bin, npm.globalBin, 'bin is global bin in windows mode') t.equal(npm.dir, npm.globalDir, 'dir is global dir in windows mode') @@ -261,7 +259,6 @@ t.test('npm.load', t => { process.argv = [ node, process.argv[1], - '--metrics-registry', 'http://example.com', '--prefix', dir, '--userconfig', `${dir}/.npmrc`, '--usage', @@ -292,7 +289,6 @@ t.test('npm.load', t => { throw er t.equal(npm.config.get('scope'), '@foo', 'added the @ sign to scope') - t.equal(npm.config.get('metrics-registry'), 'http://example.com') t.match(logs.filter(l => l[0] !== 'timing' || !/^config:/.test(l[1])), [ [ 'verbose', @@ -342,7 +338,7 @@ t.test('npm.load', t => { /Completed in [0-9]+ms/, ], ]) - t.same(consoleLogs, [['@foo']]) + t.same(consoleLogs, [['scope=@foo\n\u2010not-a-dash=undefined']]) }) // need this here or node 10 will improperly end the promise ahead of time @@ -396,7 +392,6 @@ t.test('set process.title', t => { argv: [ process.execPath, process.argv[1], - '--metrics-registry', 'http://example.com', '--usage', '--scope=foo', 'ls', @@ -415,7 +410,6 @@ t.test('set process.title', t => { argv: [ process.execPath, process.argv[1], - '--metrics-registry', 'http://example.com', '--usage', '--scope=foo', 'token', @@ -436,7 +430,6 @@ t.test('set process.title', t => { argv: [ process.execPath, process.argv[1], - '--metrics-registry', 'http://example.com', '--usage', '--scope=foo', 'token', diff --git a/deps/npm/test/lib/owner.js b/deps/npm/test/lib/owner.js index e217533f0de241..c5f9d646c24b8a 100644 --- a/deps/npm/test/lib/owner.js +++ b/deps/npm/test/lib/owner.js @@ -119,7 +119,7 @@ t.test('owner ls fails to retrieve packument', t => { t.match( err, /ERR/, - 'should throw unkown error' + 'should throw unknown error' ) }) }) diff --git a/deps/npm/test/lib/profile.js b/deps/npm/test/lib/profile.js new file mode 100644 index 00000000000000..48a558cacec646 --- /dev/null +++ b/deps/npm/test/lib/profile.js @@ -0,0 +1,1465 @@ +const t = require('tap') +const requireInject = require('require-inject') + +let result = '' +const flatOptions = { + otp: '', + json: false, + parseable: false, + registry: 'https://registry.npmjs.org/', +} +const npm = { config: {}, flatOptions: { ...flatOptions }} +const mocks = { + ansistyles: { bright: a => a }, + npmlog: { + gauge: { show () {} }, + info () {}, + notice () {}, + warn () {}, + }, + 'npm-profile': { + async get () {}, + async set () {}, + async createToken () {}, + }, + 'qrcode-terminal': { generate: (url, cb) => cb() }, + 'cli-table3': class extends Array { + toString () { + return this + .filter(Boolean) + .map(i => [...Object.entries(i)] + .map(i => i.join(': '))) + .join('\n') + } + }, + '../../lib/npm.js': npm, + '../../lib/utils/output.js': (...msg) => { + result += msg.join('\n') + }, + '../../lib/utils/pulse-till-done.js': { + withPromise: async a => a, + }, + '../../lib/utils/otplease.js': async (opts, fn) => fn(opts), + '../../lib/utils/usage.js': () => 'usage instructions', + '../../lib/utils/read-user-info.js': { + async password () {}, + async otp () {}, + }, +} +const userProfile = { + tfa: { pending: false, mode: 'auth-and-writes' }, + name: 'foo', + email: 'foo@github.com', + email_verified: true, + created: '2015-02-26T01:26:37.384Z', + updated: '2020-08-12T16:19:35.326Z', + cidr_whitelist: null, + fullname: 'Foo Bar', + homepage: 'https://github.com', + freenode: 'foobar', + twitter: 'https://twitter.com/npmjs', + github: 'https://github.com/npm', +} + +t.afterEach(cb => { + result = '' + npm.config = {} + npm.flatOptions = { ...flatOptions } + cb() +}) + +const profile = requireInject('../../lib/profile.js', mocks) + +t.test('no args', t => { + profile([], err => { + t.match( + err, + /usage instructions/, + 'should throw usage instructions' + ) + t.end() + }) +}) + +t.test('profile get no args', t => { + const npmProfile = { + async get () { + return userProfile + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + t.test('default output', t => { + profile(['get'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output table with contents' + ) + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + profile(['get'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + userProfile, + 'should output json profile result' + ) + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + profile(['get'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output all profile info as parseable result' + ) + t.end() + }) + }) + + t.test('no tfa enabled', t => { + const npmProfile = { + async get () { + return { + ...userProfile, + tfa: null, + } + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + profile(['get'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output expected profile values' + ) + t.end() + }) + }) + + t.test('unverified email', t => { + const npmProfile = { + async get () { + return { + ...userProfile, + email_verified: false, + } + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + profile(['get'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output table with contents' + ) + t.end() + }) + }) + + t.test('profile has cidr_whitelist item', t => { + const npmProfile = { + async get () { + return { + ...userProfile, + cidr_whitelist: ['192.168.1.1'], + } + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + profile(['get'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output table with contents' + ) + t.end() + }) + }) + + t.end() +}) + +t.test('profile get ', t => { + const npmProfile = { + async get () { + return userProfile + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + t.test('default output', t => { + profile(['get', 'name'], err => { + if (err) + throw err + + t.equal( + result, + 'foo', + 'should output value result' + ) + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + profile(['get', 'name'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + userProfile, + 'should output json profile result ignoring args filter' + ) + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + profile(['get', 'name'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output parseable result value' + ) + t.end() + }) + }) + + t.end() +}) + +t.test('profile get multiple args', t => { + const npmProfile = { + async get () { + return userProfile + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + t.test('default output', t => { + profile(['get', 'name', 'email', 'github'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output all keys' + ) + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + profile(['get', 'name', 'email', 'github'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + userProfile, + 'should output json profile result and ignore args' + ) + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + profile(['get', 'name', 'email', 'github'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output parseable profile value results' + ) + t.end() + }) + }) + + t.test('comma separated', t => { + profile(['get', 'name,email,github'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output all keys' + ) + t.end() + }) + }) + + t.end() +}) + +t.test('profile set ', t => { + const npmProfile = t => ({ + async get () { + return userProfile + }, + async set (newUser, conf) { + t.match( + newUser, + { + fullname: 'Lorem Ipsum', + }, + 'should set new value to key' + ) + return { + ...userProfile, + ...newUser, + } + }, + }) + + t.test('no key', t => { + profile(['set'], err => { + t.match( + err, + /npm profile set /, + 'should throw proper usage message' + ) + t.end() + }) + }) + + t.test('no value', t => { + profile(['set', 'email'], err => { + t.match( + err, + /npm profile set /, + 'should throw proper usage message' + ) + t.end() + }) + }) + + t.test('set password', t => { + profile(['set', 'password', '1234'], err => { + t.match( + err, + /Do not include your current or new passwords on the command line./, + 'should throw an error refusing to set password from args' + ) + t.end() + }) + }) + + t.test('unwritable key', t => { + profile(['set', 'name', 'foo'], err => { + t.match( + err, + /"name" is not a property we can set./, + 'should throw the unwritable key error' + ) + t.end() + }) + }) + + t.test('writable key', t => { + t.test('default output', t => { + t.plan(2) + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile(t), + }) + + profile(['set', 'fullname', 'Lorem Ipsum'], err => { + if (err) + throw err + + t.equal( + result, + 'Set\nfullname\nto\nLorem Ipsum', + 'should output set key success msg' + ) + }) + }) + + t.test('--json', t => { + t.plan(2) + + npm.flatOptions.json = true + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile(t), + }) + + profile(['set', 'fullname', 'Lorem Ipsum'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + { + fullname: 'Lorem Ipsum', + }, + 'should output json set key success msg' + ) + }) + }) + + t.test('--parseable', t => { + t.plan(2) + + npm.flatOptions.parseable = true + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile(t), + }) + + profile(['set', 'fullname', 'Lorem Ipsum'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output parseable set key success msg' + ) + }) + }) + + t.end() + }) + + t.test('write new email', t => { + t.plan(3) + + const npmProfile = { + async get () { + return userProfile + }, + async set (newUser, conf) { + t.match( + newUser, + { + email: 'foo@npmjs.com', + }, + 'should set new value to email' + ) + t.match( + conf, + npm.flatOptions, + 'should forward flatOptions config' + ) + return { + ...userProfile, + ...newUser, + } + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + profile(['set', 'email', 'foo@npmjs.com'], err => { + if (err) + throw err + + t.equal( + result, + 'Set\nemail\nto\nfoo@npmjs.com', + 'should output set key success msg' + ) + }) + }) + + t.test('change password', t => { + t.plan(6) + + const npmProfile = { + async get () { + return userProfile + }, + async set (newUser, conf) { + t.match( + newUser, + { + password: { + old: 'currentpassword1234', + new: 'newpassword1234', + }, + }, + 'should set new password' + ) + t.match( + conf, + npm.flatOptions, + 'should forward flatOptions config' + ) + return { + ...userProfile, + } + }, + } + + const readUserInfo = { + async password (label) { + if (label === 'Current password: ') + t.ok('should interactively ask for password confirmation') + else if (label === 'New password: ') + t.ok('should interactively ask for new password') + else if (label === ' Again: ') + t.ok('should interactively ask for new password confirmation') + else + throw new Error('Unexpected label: ' + label) + + return label === 'Current password: ' + ? 'currentpassword1234' + : 'newpassword1234' + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + '../../lib/utils/read-user-info.js': readUserInfo, + }) + + profile(['set', 'password'], err => { + if (err) + throw err + + t.equal( + result, + 'Set\npassword', + 'should output set password success msg' + ) + t.end() + }) + }) + + t.test('password confirmation mismatch', t => { + t.plan(3) + let passwordPromptCount = 0 + + const npmProfile = { + async get () { + return userProfile + }, + async set (newUser, conf) { + return { + ...userProfile, + } + }, + } + + const readUserInfo = { + async password (label) { + passwordPromptCount++ + + switch (label) { + case 'Current password: ': + return 'currentpassword1234' + case 'New password: ': + return passwordPromptCount < 3 + ? 'password-that-will-not-be-confirmed' + : 'newpassword' + case ' Again: ': + return 'newpassword' + default: + return 'password1234' + } + }, + } + + const npmlog = { + gauge: { + show () {}, + }, + warn (title, msg) { + t.equal(title, 'profile', 'should use expected profile') + t.equal( + msg, + 'Passwords do not match, please try again.', + 'should log password mismatch message' + ) + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + npmlog, + 'npm-profile': npmProfile, + '../../lib/utils/read-user-info.js': readUserInfo, + }) + + profile(['set', 'password'], err => { + if (err) + throw err + + t.equal( + result, + 'Set\npassword', + 'should output set password success msg' + ) + t.end() + }) + }) + + t.end() +}) + +t.test('enable-2fa', t => { + t.test('invalid args', t => { + profile(['enable-2fa', 'foo', 'bar'], err => { + t.match( + err, + /npm profile enable-2fa \[auth-and-writes|auth-only\]/, + 'should throw usage error' + ) + t.end() + }) + }) + + t.test('invalid two factor auth mode', t => { + profile(['enable-2fa', 'foo'], err => { + t.match( + err, + /Invalid two-factor authentication mode "foo"/, + 'should throw invalid auth mode error' + ) + t.end() + }) + }) + + t.test('no support for --json output', t => { + npm.flatOptions.json = true + + profile(['enable-2fa', 'auth-only'], err => { + t.match( + err.message, + 'Enabling two-factor authentication is an interactive ' + + 'operation and JSON output mode is not available', + 'should throw no support msg' + ) + t.end() + }) + }) + + t.test('no support for --parseable output', t => { + npm.flatOptions.parseable = true + + profile(['enable-2fa', 'auth-only'], err => { + t.match( + err.message, + 'Enabling two-factor authentication is an interactive ' + + 'operation and parseable output mode is not available', + 'should throw no support msg' + ) + t.end() + }) + }) + + t.test('no bearer tokens returned by registry', t => { + t.plan(3) + + // mock legacy basic auth style + npm.config.getCredentialsByURI = reg => { + t.equal(reg, flatOptions.registry, 'should use expected registry') + return { auth: Buffer.from('foo:bar').toString('base64') } + } + + const npmProfile = { + async createToken (pass) { + t.match(pass, 'bar', 'should use password for basic auth') + return {} + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + profile(['enable-2fa', 'auth-only'], err => { + t.match( + err.message, + 'Your registry https://registry.npmjs.org/ does ' + + 'not seem to support bearer tokens. Bearer tokens ' + + 'are required for two-factor authentication', + 'should throw no support msg' + ) + }) + }) + + t.test('from basic username/password auth', t => { + // mock legacy basic auth style with user/pass + npm.config.getCredentialsByURI = () => { + return { username: 'foo', password: 'bar' } + } + + const npmProfile = { + async createToken (pass) { + return {} + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + profile(['enable-2fa', 'auth-only'], err => { + t.match( + err.message, + 'Your registry https://registry.npmjs.org/ does ' + + 'not seem to support bearer tokens. Bearer tokens ' + + 'are required for two-factor authentication', + 'should throw no support msg' + ) + t.end() + }) + }) + + t.test('no auth found', t => { + npm.config.getCredentialsByURI = () => ({}) + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + }) + + profile(['enable-2fa', 'auth-only'], err => { + t.match( + err.message, + 'You need to be logged in to registry ' + + 'https://registry.npmjs.org/ in order to enable 2fa' + ) + t.end() + }) + }) + + t.test('from basic auth, asks for otp', t => { + t.plan(10) + + // mock legacy basic auth style + npm.config = { + getCredentialsByURI (reg) { + t.equal(reg, flatOptions.registry, 'should use expected registry') + return { auth: Buffer.from('foo:bar').toString('base64') } + }, + setCredentialsByURI (registry, { token }) { + t.equal(registry, flatOptions.registry, 'should set expected registry') + t.equal(token, 'token', 'should set expected token') + }, + save (type) { + t.equal(type, 'user', 'should save to user config') + }, + } + + const npmProfile = { + async createToken (pass) { + t.match(pass, 'bar', 'should use password for basic auth') + return { token: 'token' } + }, + async get () { + return userProfile + }, + async set (newProfile, conf) { + t.match( + newProfile, + { + tfa: { + mode: 'auth-only', + }, + }, + 'should set tfa mode' + ) + t.match( + conf, + { + ...npm.flatOptions, + otp: '123456', + }, + 'should forward flatOptions config' + ) + return { + ...userProfile, + tfa: null, + } + }, + } + + const readUserInfo = { + async password () { + t.ok('should interactively ask for password confirmation') + return 'password1234' + }, + async otp (label) { + t.equal( + label, + 'Enter one-time password from your authenticator app: ', + 'should ask for otp confirmation' + ) + return '123456' + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + '../../lib/utils/read-user-info.js': readUserInfo, + }) + + profile(['enable-2fa', 'auth-only'], err => { + if (err) + throw err + + t.equal( + result, + 'Two factor authentication mode changed to: auth-only', + 'should output success msg' + ) + }) + }) + + t.test('from token and set otp, retries on pending and verifies with qrcode', t => { + t.plan(4) + + npm.flatOptions.otp = '1234' + + npm.config = { + getCredentialsByURI () { + return { token: 'token' } + }, + } + + let setCount = 0 + const npmProfile = { + async get () { + return { + ...userProfile, + tfa: { + pending: true, + }, + } + }, + async set (newProfile, conf) { + setCount++ + + // when profile response shows that 2fa is pending the + // first time calling npm-profile.set should reset 2fa + if (setCount === 1) { + t.match( + newProfile, + { + tfa: { + password: 'password1234', + mode: 'disable', + }, + }, + 'should reset 2fa' + ) + } else if (setCount === 2) { + t.match( + newProfile, + { + tfa: { + mode: 'auth-only', + }, + }, + 'should set tfa mode approprietly in follow-up call' + ) + } else if (setCount === 3) { + t.match( + newProfile, + { + tfa: ['123456'], + }, + 'should set tfa as otp code?' + ) + return { + ...userProfile, + tfa: [ + '123456', + '789101', + ], + } + } + + return { + ...userProfile, + tfa: 'otpauth://foo?secret=1234', + } + }, + } + + const readUserInfo = { + async password () { + return 'password1234' + }, + async otp (label) { + return '123456' + }, + } + + const qrcode = { + // eslint-disable-next-line standard/no-callback-literal + generate: (url, cb) => cb('qrcode'), + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + 'qrcode-terminal': qrcode, + '../../lib/utils/read-user-info.js': readUserInfo, + }) + + profile(['enable-2fa', 'auth-only'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output 2fa enablement success msgs' + ) + }) + }) + + t.test('from token and set otp, retrieves invalid otp', t => { + npm.flatOptions.otp = '1234' + + npm.config = { + getCredentialsByURI () { + return { token: 'token' } + }, + } + + const npmProfile = { + async get () { + return { + ...userProfile, + tfa: { + pending: true, + }, + } + }, + async set (newProfile, conf) { + return { + ...userProfile, + tfa: 'http://foo?secret=1234', + } + }, + } + + const readUserInfo = { + async password () { + return 'password1234' + }, + async otp (label) { + return '123456' + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + '../../lib/utils/read-user-info.js': readUserInfo, + }) + + profile(['enable-2fa', 'auth-only'], err => { + t.match( + err, + /Unknown error enabling two-factor authentication./, + 'should throw invalid 2fa auth url error' + ) + t.end() + }) + }) + + t.test('from token auth provides --otp config arg', t => { + npm.flatOptions.otp = '123456' + + npm.config = { + getCredentialsByURI (reg) { + return { token: 'token' } + }, + } + + const npmProfile = { + async get () { + return userProfile + }, + async set (newProfile, conf) { + return { + ...userProfile, + tfa: null, + } + }, + } + + const readUserInfo = { + async password () { + return 'password1234' + }, + async otp () { + throw new Error('should not ask for otp') + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + '../../lib/utils/read-user-info.js': readUserInfo, + }) + + profile(['enable-2fa', 'auth-and-writes'], err => { + if (err) + throw err + + t.equal( + result, + 'Two factor authentication mode changed to: auth-and-writes', + 'should output success msg' + ) + t.end() + }) + }) + + t.test('missing tfa from user profile', t => { + npm.config = { + getCredentialsByURI (reg) { + return { token: 'token' } + }, + } + + const npmProfile = { + async get () { + return { + ...userProfile, + tfa: undefined, + } + }, + async set (newProfile, conf) { + return { + ...userProfile, + tfa: null, + } + }, + } + + const readUserInfo = { + async password () { + return 'password1234' + }, + async otp () { + return '123456' + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + '../../lib/utils/read-user-info.js': readUserInfo, + }) + + profile(['enable-2fa', 'auth-only'], err => { + if (err) + throw err + + t.equal( + result, + 'Two factor authentication mode changed to: auth-only', + 'should output success msg' + ) + t.end() + }) + }) + + t.test('defaults to auth-and-writes permission if no mode specified', t => { + npm.config = { + getCredentialsByURI (reg) { + return { token: 'token' } + }, + } + + const npmProfile = { + async get () { + return { + ...userProfile, + tfa: undefined, + } + }, + async set (newProfile, conf) { + return { + ...userProfile, + tfa: null, + } + }, + } + + const readUserInfo = { + async password () { + return 'password1234' + }, + async otp () { + return '123456' + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + '../../lib/utils/read-user-info.js': readUserInfo, + }) + + profile(['enable-2fa'], err => { + if (err) + throw err + + t.equal( + result, + 'Two factor authentication mode changed to: auth-and-writes', + 'should enable 2fa with auth-and-writes permission' + ) + t.end() + }) + }) + + t.end() +}) + +t.test('disable-2fa', t => { + t.test('no tfa enabled', t => { + const npmProfile = { + async get () { + return { + ...userProfile, + tfa: null, + } + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + }) + + profile(['disable-2fa'], err => { + if (err) + throw err + + t.equal( + result, + 'Two factor authentication not enabled.', + 'should output already disalbed msg' + ) + t.end() + }) + }) + + t.test('requests otp', t => { + const npmProfile = t => ({ + async get () { + return userProfile + }, + async set (newProfile, conf) { + t.deepEqual( + newProfile, + { + tfa: { + password: 'password1234', + mode: 'disable', + }, + }, + 'should send the new info for setting in profile' + ) + t.match( + conf, + { + ...npm.flatOptions, + otp: '1234', + }, + 'should forward flatOptions config' + ) + }, + }) + + const readUserInfo = t => ({ + async password () { + t.ok('should interactively ask for password confirmation') + return 'password1234' + }, + async otp (label) { + t.equal( + label, + 'Enter one-time password from your authenticator app: ', + 'should ask for otp confirmation' + ) + return '1234' + }, + }) + + t.test('default output', t => { + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile(t), + '../../lib/utils/read-user-info.js': readUserInfo(t), + }) + + profile(['disable-2fa'], err => { + if (err) + throw err + + t.equal( + result, + 'Two factor authentication disabled.', + 'should output already disabled msg' + ) + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile(t), + '../../lib/utils/read-user-info.js': readUserInfo(t), + }) + + profile(['disable-2fa'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + { tfa: false }, + 'should output json already disabled msg' + ) + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile(t), + '../../lib/utils/read-user-info.js': readUserInfo(t), + }) + + profile(['disable-2fa'], err => { + if (err) + throw err + + t.equal( + result, + 'tfa\tfalse', + 'should output parseable already disabled msg' + ) + t.end() + }) + }) + + t.end() + }) + + t.test('--otp config already set', t => { + t.plan(3) + + npm.flatOptions.otp = '123456' + + const npmProfile = { + async get () { + return userProfile + }, + async set (newProfile, conf) { + t.deepEqual( + newProfile, + { + tfa: { + password: 'password1234', + mode: 'disable', + }, + }, + 'should send the new info for setting in profile' + ) + t.match( + conf, + { + ...npm.flatOptions, + otp: '123456', + }, + 'should forward flatOptions config' + ) + }, + } + + const readUserInfo = { + async password () { + return 'password1234' + }, + async otp (label) { + throw new Error('should not ask for otp') + }, + } + + const profile = requireInject('../../lib/profile.js', { + ...mocks, + 'npm-profile': npmProfile, + '../../lib/utils/read-user-info.js': readUserInfo, + }) + + profile(['disable-2fa'], err => { + if (err) + throw err + + t.equal( + result, + 'Two factor authentication disabled.', + 'should output already disalbed msg' + ) + }) + }) + + t.end() +}) + +t.test('unknown subcommand', t => { + profile(['asfd'], err => { + t.match( + err, + /Unknown profile command: asfd/, + 'should throw unknown cmd error' + ) + t.end() + }) +}) + +t.test('completion', t => { + const { completion } = profile + + const testComp = ({ t, argv, expect, title }) => { + completion({ conf: { argv: { remain: argv } } }, (err, res) => { + if (err) + throw err + + t.strictSame(res, expect, title) + }) + } + + t.test('npm profile autocomplete', t => { + testComp({ + t, + argv: ['npm', 'profile'], + expect: ['enable-2fa', 'disable-2fa', 'get', 'set'], + title: 'should auto complete with subcommands', + }) + + t.end() + }) + + t.test('npm profile enable autocomplete', t => { + testComp({ + t, + argv: ['npm', 'profile', 'enable-2fa'], + expect: ['auth-and-writes', 'auth-only'], + title: 'should auto complete with auth types', + }) + + t.end() + }) + + t.test('npm profile no autocomplete', t => { + const noAutocompleteCmds = ['disable-2fa', 'disable-tfa', 'get', 'set'] + for (const subcmd of noAutocompleteCmds) { + testComp({ + t, + argv: ['npm', 'profile', subcmd], + expect: [], + title: `${subcmd} should have no autocomplete`, + }) + } + + t.end() + }) + + t.test('npm profile unknown subcommand autocomplete', t => { + completion({ + conf: { + argv: { + remain: ['npm', 'profile', 'asdf'], + }, + }, + }, (err, res) => { + t.match( + err, + /asdf not recognized/, + 'should throw unknown cmd error' + ) + + t.end() + }) + }) + + t.end() +}) diff --git a/deps/npm/test/lib/publish.js b/deps/npm/test/lib/publish.js index 14e2179816394e..c1f353f6fdbc55 100644 --- a/deps/npm/test/lib/publish.js +++ b/deps/npm/test/lib/publish.js @@ -3,7 +3,14 @@ const requireInject = require('require-inject') // mock config const {defaults} = require('../../lib/utils/config.js') -const config = { list: [defaults] } +const credentials = { + token: 'asdfasdf', + alwaysAuth: false, +} +const config = { + list: [defaults], + getCredentialsByURI: () => credentials, +} const fs = require('fs') t.test('should publish with libnpmpublish, respecting publishConfig', (t) => { @@ -23,6 +30,7 @@ t.test('should publish with libnpmpublish, respecting publishConfig', (t) => { flatOptions: { json: true, defaultTag: 'latest', + registry: 'https://registry.npmjs.org', }, config, }, @@ -55,7 +63,7 @@ t.test('should publish with libnpmpublish, respecting publishConfig', (t) => { }, }) - publish([testDir], (er) => { + return publish([testDir], (er) => { if (er) throw er t.pass('got to callback') @@ -77,6 +85,7 @@ t.test('re-loads publishConfig if added during script process', (t) => { flatOptions: { json: true, defaultTag: 'latest', + registry: 'https://registry.npmjs.org/', }, config, }, @@ -108,7 +117,7 @@ t.test('re-loads publishConfig if added during script process', (t) => { }, }) - publish([testDir], (er) => { + return publish([testDir], (er) => { if (er) throw er t.pass('got to callback') @@ -131,6 +140,7 @@ t.test('should not log if silent', (t) => { json: false, defaultTag: 'latest', dryRun: true, + registry: 'https://registry.npmjs.org/', }, config, }, @@ -159,7 +169,7 @@ t.test('should not log if silent', (t) => { }, }) - publish([testDir], (er) => { + return publish([testDir], (er) => { if (er) throw er t.pass('got to callback') @@ -181,6 +191,7 @@ t.test('should log tarball contents', (t) => { json: false, defaultTag: 'latest', dryRun: true, + registry: 'https://registry.npmjs.org/', }, config, }, @@ -206,7 +217,7 @@ t.test('should log tarball contents', (t) => { }, }) - publish([testDir], (er) => { + return publish([testDir], (er) => { if (er) throw er t.pass('got to callback') @@ -220,12 +231,13 @@ t.test('shows usage with wrong set of arguments', (t) => { flatOptions: { json: false, defaultTag: '0.0.13', + registry: 'https://registry.npmjs.org/', }, config, }, }) - publish(['a', 'b', 'c'], (er) => t.matchSnapshot(er, 'should print usage')) + return publish(['a', 'b', 'c'], (er) => t.matchSnapshot(er, 'should print usage')) }) t.test('throws when invalid tag', (t) => { @@ -235,12 +247,13 @@ t.test('throws when invalid tag', (t) => { flatOptions: { json: false, defaultTag: '0.0.13', + registry: 'https://registry.npmjs.org/', }, config, }, }) - publish([], (err) => { + return publish([], (err) => { t.match(err, { message: /Tag name must not be a valid SemVer range: /, }, 'throws when tag name is a valid SemVer range') @@ -274,6 +287,7 @@ t.test('can publish a tarball', t => { flatOptions: { json: true, defaultTag: 'latest', + registry: 'https://registry.npmjs.org/', }, config, }, @@ -298,9 +312,56 @@ t.test('can publish a tarball', t => { }, }) - publish([`${testDir}/package.tgz`], (er) => { + return publish([`${testDir}/package.tgz`], (er) => { if (er) throw er t.pass('got to callback') }) }) + +t.test('throw if no registry', async t => { + t.plan(1) + const publish = requireInject('../../lib/publish.js', { + '../../lib/npm.js': { + flatOptions: { + json: false, + defaultTag: '0.0.13', + registry: null, + }, + config, + }, + }) + + return publish([], (err) => { + t.match(err, { + message: 'No registry specified.', + code: 'ENOREGISTRY', + }, 'throws when registry unset') + }) +}) + +t.test('throw if not logged in', async t => { + t.plan(1) + const publish = requireInject('../../lib/publish.js', { + '../../lib/npm.js': { + flatOptions: { + json: false, + defaultTag: '0.0.13', + registry: 'https://registry.npmjs.org/', + }, + config: { + ...config, + getCredentialsByURI: () => ({ + email: 'me@example.com', + }), + }, + }, + }) + + return publish([], (err) => { + t.match(err, { + message: 'This command requires you to be logged in.', + code: 'ENEEDAUTH', + }, 'throws when not logged in') + }) +}) diff --git a/deps/npm/test/lib/utils/error-handler.js b/deps/npm/test/lib/utils/error-handler.js index 2dc116a4d31711..0b896fee4f1fef 100644 --- a/deps/npm/test/lib/utils/error-handler.js +++ b/deps/npm/test/lib/utils/error-handler.js @@ -78,10 +78,6 @@ const npmlog = { }, } -const metrics = { - stop: () => null, -} - // overrides OS type/release for cross platform snapshots const os = require('os') os.type = () => 'Foo' @@ -124,7 +120,6 @@ const mocks = { summary: [['ERR', err.message]], detail: [['ERR', err.message]], }), - '../../../lib/utils/metrics.js': metrics, '../../../lib/utils/cache-file.js': cacheFile, } diff --git a/deps/npm/test/lib/utils/flat-options.js b/deps/npm/test/lib/utils/flat-options.js index ee7620fa784db7..6f580fabc45114 100644 --- a/deps/npm/test/lib/utils/flat-options.js +++ b/deps/npm/test/lib/utils/flat-options.js @@ -34,8 +34,6 @@ class MockConfig { cache: 'cache', 'node-version': '1.2.3', global: 'global', - 'metrics-registry': 'metrics-registry', - 'send-metrics': 'send-metrics', registry: 'registry', access: 'access', 'always-auth': 'always-auth', @@ -299,15 +297,12 @@ t.test('various default values and falsey fallbacks', t => { const npm = new Mocknpm({ 'script-shell': false, registry: 'http://example.com', - 'metrics-registry': null, searchlimit: 0, 'save-exact': false, 'save-prefix': '>=', }) const opts = flatOptions(npm) t.equal(opts.scriptShell, undefined, 'scriptShell is undefined if falsey') - t.equal(opts.metricsRegistry, 'http://example.com', - 'metricsRegistry defaults to registry') t.equal(opts.search.limit, 20, 'searchLimit defaults to 20') t.equal(opts.savePrefix, '>=', 'save-prefix respected if no save-exact') t.equal(opts.scope, '', 'scope defaults to empty string') diff --git a/deps/npm/test/lib/utils/reify-output.js b/deps/npm/test/lib/utils/reify-output.js index f7fd96ee87d984..f88f072e1ebd10 100644 --- a/deps/npm/test/lib/utils/reify-output.js +++ b/deps/npm/test/lib/utils/reify-output.js @@ -245,7 +245,10 @@ t.test('packages changed message', t => { settings.json = json npmock.command = command const mock = { - actualTree: { inventory: { size: audited, has: () => true }, children: [] }, + actualTree: { + inventory: { size: audited, has: () => true }, + children: [], + }, auditReport: audited ? { toJSON: () => mock.auditReport, vulnerabilities: {}, diff --git a/deps/npm/test/lib/utils/replace-info.js b/deps/npm/test/lib/utils/replace-info.js index f5e1246f1c353b..ea9f06520d0a77 100644 --- a/deps/npm/test/lib/utils/replace-info.js +++ b/deps/npm/test/lib/utils/replace-info.js @@ -46,7 +46,7 @@ t.equal( t.equal( replaceInfo('Something https://user:pass@registry.npmjs.org/ foo bar'), 'Something https://user:***@registry.npmjs.org/ foo bar', - 'should replace single item withing a phrase' + 'should replace single item within a phrase' ) t.deepEqual(