From 744a5f70aaba5ffee2096294dfb0d188d459a96f Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 9 Mar 2021 10:11:19 -0800 Subject: [PATCH 1/8] @npmcli/run-script@1.8.4 --- .../@npmcli/run-script/lib/make-spawn-args.js | 20 +- node_modules/@npmcli/run-script/package.json | 3 +- node_modules/puka/CHANGELOG.md | 31 - node_modules/puka/LICENSE.txt | 18 - node_modules/puka/README.md | 411 --------- node_modules/puka/index.js | 804 ------------------ node_modules/puka/package.json | 38 - package-lock.json | 30 +- package.json | 2 +- 9 files changed, 10 insertions(+), 1347 deletions(-) delete mode 100644 node_modules/puka/CHANGELOG.md delete mode 100644 node_modules/puka/LICENSE.txt delete mode 100644 node_modules/puka/README.md delete mode 100644 node_modules/puka/index.js delete mode 100644 node_modules/puka/package.json diff --git a/node_modules/@npmcli/run-script/lib/make-spawn-args.js b/node_modules/@npmcli/run-script/lib/make-spawn-args.js index 4c38b9401ddf0..8ee24c06daf7b 100644 --- a/node_modules/@npmcli/run-script/lib/make-spawn-args.js +++ b/node_modules/@npmcli/run-script/lib/make-spawn-args.js @@ -3,24 +3,6 @@ const isWindows = require('./is-windows.js') const setPATH = require('./set-path.js') const {resolve} = require('path') const npm_config_node_gyp = require.resolve('node-gyp/bin/node-gyp.js') -const { quoteForShell, ShellString, ShellStringText, ShellStringUnquoted } = require('puka') - -const escapeCmd = cmd => { - const result = [] - const parsed = ShellString.sh([cmd]) - for (const child of parsed.children) { - if (child instanceof ShellStringText) { - const children = child.contents.filter(segment => segment !== null).map(segment => quoteForShell(segment, false, isWindows && 'win32')) - result.push(...children) - } else if (child instanceof ShellStringUnquoted) { - result.push(child.value) - } else { - result.push(isWindows ? '&' : ';') - } - } - - return result.join('') -} const makeSpawnArgs = options => { const { @@ -34,7 +16,7 @@ const makeSpawnArgs = options => { } = options const isCmd = /(?:^|\\)cmd(?:\.exe)?$/i.test(scriptShell) - const args = isCmd ? ['/d', '/s', '/c', escapeCmd(cmd)] : ['-c', cmd] + const args = isCmd ? ['/d', '/s', '/c', cmd] : ['-c', cmd] const spawnOpts = { env: setPATH(path, { diff --git a/node_modules/@npmcli/run-script/package.json b/node_modules/@npmcli/run-script/package.json index 9df5b31178747..7e0e5255de410 100644 --- a/node_modules/@npmcli/run-script/package.json +++ b/node_modules/@npmcli/run-script/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/run-script", - "version": "1.8.3", + "version": "1.8.4", "description": "Run a lifecycle script for a package (descendant of npm-lifecycle)", "author": "Isaac Z. Schlueter (https://izs.me)", "license": "ISC", @@ -32,7 +32,6 @@ "@npmcli/promise-spawn": "^1.3.2", "infer-owner": "^1.0.4", "node-gyp": "^7.1.0", - "puka": "^1.0.1", "read-package-json-fast": "^2.0.1" }, "files": [ diff --git a/node_modules/puka/CHANGELOG.md b/node_modules/puka/CHANGELOG.md deleted file mode 100644 index 781b81295a4a7..0000000000000 --- a/node_modules/puka/CHANGELOG.md +++ /dev/null @@ -1,31 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [1.0.1](https://gitlab.com/rhendric/puka/-/compare/v1.0.0...v1.0.1) - 2020-05-16 - -### Fixed - -- Add more carets to win32 command arguments ([45965ca](https://gitlab.com/rhendric/puka/-/commit/45965ca60fcc518082e0b085d8e81f3f3279ffb4)) - - As previously documented and implemented, Puka assumed that all programs - are batch files for the purpose of multi-escaping commands that appear - in pipelines. However, regardless of whether a command is in a pipeline, - one extra layer of escaping is needed if the command invokes a batch - file, which Puka was not producing. This only applies to the arguments - to the command, not to the batch file path, nor to paths used in - redirects. (The property-based spawn test which was supposed to catch - such oversights missed this one because it was invoking the Node.js - executable directly, not, as recommended in the documentation, a batch - file.) - - Going forward, the caveats described in the documentation continue to - apply: if you are running programs on Windows with Puka, make sure they - are batch files, or you may find arguments are being escaped with too - many carets. As the documentation says, if this causes problems for you, - please open an issue so we can work out the details of what a good - workaround looks like. - -## [1.0.0](https://gitlab.com/rhendric/puka/-/tags/v1.0.0) - 2017-09-29 diff --git a/node_modules/puka/LICENSE.txt b/node_modules/puka/LICENSE.txt deleted file mode 100644 index 0141196a59337..0000000000000 --- a/node_modules/puka/LICENSE.txt +++ /dev/null @@ -1,18 +0,0 @@ -Copyright 2017 Ryan Hendrickson - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/puka/README.md b/node_modules/puka/README.md deleted file mode 100644 index 2670f742b3677..0000000000000 --- a/node_modules/puka/README.md +++ /dev/null @@ -1,411 +0,0 @@ -# Puka - -[![GitLab CI pipeline status](https://gitlab.com/rhendric/puka/badges/master/pipeline.svg)](https://gitlab.com/rhendric/puka/commits/master) [![AppVeyor build status](https://img.shields.io/appveyor/ci/rhendric/puka.svg?label=windows%20tests)](https://ci.appveyor.com/project/rhendric/puka) [![Codecov status](https://img.shields.io/codecov/c/gl/rhendric/puka.svg)](https://codecov.io/gl/rhendric/puka) - -Puka is a cross-platform library for safely passing strings through shells. - -#### Contents - -- [Introduction](#introduction) - - [Why would I use Puka?](#why-would-i-use-puka) - - [How do I use Puka?](#how-do-i-use-puka) - - [What's the catch?](#whats-the-catch) -- [API Documentation](#api-documentation) - - [Basic API](#basic-api) - - [sh](#sh) - - [unquoted](#unquoted) - - [Advanced API](#advanced-api) - - [quoteForShell](#quoteforshell) - - [quoteForCmd](#quoteforcmd) - - [quoteForSh](#quoteforsh) - - [ShellString](#shellstring) - - [Secret API](#secret-api) -- [The sh DSL](#the-sh-dsl) - - [Syntax](#syntax) - - [Semantics](#semantics) - - [Types of placeholders](#types-of-placeholders) - -## Introduction - -### Why would I use Puka? - -When launching a child process from Node, you have a choice between launching -directly from the operating system (as with [child_process.spawn](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options), -if you don't use the `{ shell: true }` option), and running the command through -a shell (as with [child_process.exec](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback)). -Using a shell gives you more power, such as the ability to chain multiple -commands together or use redirection, but you have to construct your command as -a single string instead of using an array of arguments. And doing that can be -buggy (if not dangerous) if you don't take care to quote any arguments -correctly for the shell you're targeting, _and_ the quoting has to be done -differently on Windows and non-Windows shells. - -Puka solves that problem by giving you a simple and platform-agnostic way to -build shell commands with arguments that pass through your shell unaltered and -with no unsafe side effects, **whether you are running on Windows or a -Unix-based OS**. - -### How do I use Puka? - -Puka gives you an `sh` function intended for tagging -[template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), -which quotes (if necessary) any values interpolated into the template. A simple -example: - -```javascript -const { sh } = require('puka'); -const { execSync } = require('child_process'); - -const arg = 'file with spaces.txt'; -execSync(sh`some-command ${arg}`); -``` - -But Puka supports more than this! See [the `sh` DSL documentation](#the-sh-dsl) -for a detailed description of all the features currently supported. - -### What's the catch? - -Here are the ones I know about: - -Puka does _not_ ensure that the actual commands you're running are -cross-platform. If you're running npm programs, you generally won't have a -problem with that, but if you want to run ``sh`cat file` `` on Windows, you'll -need to depend on something like -[cash-cat](https://www.npmjs.com/package/cash-cat). - -I searched for days for a way to quote or escape line breaks in arguments to -`cmd.exe`, but couldn't find one (regular `^`-prepending and quotation marks -don't seem to cut it). If you know of a way that works, please [open an -issue](https://gitlab.com/rhendric/puka/issues/new) to tell me about it! Until -then, any line break characters (`\r` or `\n`) in values being interpolated by -`sh` will cause an error to be thrown on Windows only. - -Also on Windows, you may notice quoting mistakes if you run commands that -involve invoking a native executable (not a batch file ending in `.cmd` or -`.bat`). Unfortunately, batch files require some extra escaping on Windows, and -Puka assumes all programs are batch files because npm creates batch file shims -for programs it installs (and, if you care about cross-platform, you'll be -using npm programs in your commands). If this causes problems for you, please -[open an issue](https://gitlab.com/rhendric/puka/issues/new); if your situation -is specific enough, there may be workarounds or improvements to Puka to be -found. - -## API Documentation - -### Basic API - - - - -#### sh - -A string template tag for safely constructing cross-platform shell commands. - -An `sh` template is not actually treated as a literal string to be -interpolated; instead, it is a tiny DSL designed to make working with shell -strings safe, simple, and straightforward. To get started quickly, see the -examples below. [More detailed documentation][1] is available -further down. - -##### Examples - -```javascript -const title = '"this" & "that"'; -sh`script --title=${title}`; // => "script '--title=\"this\" & \"that\"'" -// Note: these examples show results for non-Windows platforms. -// On Windows, the above would instead be -// 'script ^^^"--title=\\^^^"this\\^^^" ^^^& \\^^^"that\\^^^"^^^"'. - -const names = ['file1', 'file 2']; -sh`rimraf ${names}.txt`; // => "rimraf file1.txt 'file 2.txt'" - -const cmd1 = ['cat', 'file 1.txt', 'file 2.txt']; -const cmd2 = ['use-input', '-abc']; -sh`${cmd1}|${cmd2}`; // => "cat 'file 1.txt' 'file 2.txt'|use-input -abc" -``` - -Returns **[String][2]** a string formatted for the platform Node is currently -running on. - -#### unquoted - -This function permits raw strings to be interpolated into a `sh` template. - -**IMPORTANT**: If you're using Puka due to security concerns, make sure you -don't pass any untrusted content to `unquoted`. This may be obvious, but -stray punctuation in an `unquoted` section can compromise the safety of the -entire shell command. - -##### Parameters - -- `value` any value (it will be treated as a string) - -##### Examples - -```javascript -const both = true; -sh`foo ${unquoted(both ? '&&' : '||')} bar`; // => 'foo && bar' -``` - -### Advanced API - -If these functions make life easier for you, go ahead and use them; they -are just as well supported as the above. But if you aren't certain you -need them, you probably don't. - - -#### quoteForShell - -Quotes a string for injecting into a shell command. - -This function is exposed for some hypothetical case when the `sh` DSL simply -won't do; `sh` is expected to be the more convenient option almost always. -Compare: - -```javascript -console.log('cmd' + args.map(a => ' ' + quoteForShell(a)).join('')); -console.log(sh`cmd ${args}`); // same as above - -console.log('cmd' + args.map(a => ' ' + quoteForShell(a, true)).join('')); -console.log(sh`cmd "${args}"`); // same as above -``` - -Additionally, on Windows, `sh` checks the entire command string for pipes, -which subtly change how arguments need to be quoted. If your commands may -involve pipes, you are strongly encouraged to use `sh` and not try to roll -your own with `quoteForShell`. - -##### Parameters - -- `text` **[String][2]** to be quoted -- `forceQuote` **[Boolean][3]?** whether to always add quotes even if the string - is already safe. Defaults to `false`. -- `platform` **[String][2]?** a value that `process.platform` might take: - `'win32'`, `'linux'`, etc.; determines how the string is to be formatted. - When omitted, effectively the same as `process.platform`. - -Returns **[String][2]** a string that is safe for the current (or specified) -platform. - -#### quoteForCmd - -A Windows-specific version of [quoteForShell][4]. - -##### Parameters - -- `text` **[String][2]** to be quoted -- `forceQuote` **[Boolean][3]?** whether to always add quotes even if the string - is already safe. Defaults to `false`. - -#### quoteForSh - -A Unix-specific version of [quoteForShell][4]. - -##### Parameters - -- `text` **[String][2]** to be quoted -- `forceQuote` **[Boolean][3]?** whether to always add quotes even if the string - is already safe. Defaults to `false`. - -#### ShellString - -A ShellString represents a shell command after it has been interpolated, but -before it has been formatted for a particular platform. ShellStrings are -useful if you want to prepare a command for a different platform than the -current one, for instance. - -To create a ShellString, use `ShellString.sh` the same way you would use -top-level `sh`. - -##### toString - -A method to format a ShellString into a regular String formatted for a -particular platform. - -###### Parameters - -- `platform` **[String][2]?** a value that `process.platform` might take: - `'win32'`, `'linux'`, etc.; determines how the string is to be formatted. - When omitted, effectively the same as `process.platform`. - -Returns **[String][2]** - -##### sh - -`ShellString.sh` is a template tag just like `sh`; the only difference is -that this function returns a ShellString which has not yet been formatted -into a String. - -Returns **[ShellString][5]** - -### Secret API - -Some internals of string formatting have been exposed for the ambitious and -brave souls who want to try to extend Puka to handle more shells or custom -interpolated values. This ‘secret’ API is partially documented in the code -but not here, and the semantic versioning guarantees on this API are bumped -down by one level: in other words, minor version releases of Puka can change -the secret API in backward-incompatible ways, and patch releases can add or -deprecate functionality. - -If it's not even documented in the code, use at your own risk—no semver -guarantees apply. - - -[1]: #the-sh-dsl - -[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String - -[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean - -[4]: #quoteforshell - -[5]: #shellstring - -## The sh DSL - -### Syntax - -An `sh` template comprises words, separated by whitespace. Words can contain: - -- text, which is composed of any characters that are not whitespace, single or - double quotes, or any of the special characters - ``# $ & ( ) ; < > \ ` |``; -- quotations, which are matching single or double quotes surrounding any - characters other than the delimiting quote; and -- placeholders, using the standard JavaScript template syntax (`${}`). - (Placeholders may also appear inside quotations.) - -The special characters ``# $ & ( ) ; < > \ ` |``, if unquoted, form their own -words. - -Redirect operators (`<`, `>`, `>>`, `2>`, etc.) receive their own special -handling, as do semicolons. Other than these two exceptions, no attempt is made -to understand any more sophisticated features of shell syntax. - -Standard JavaScript escape sequences, such as `\t`, are honored in the template -literal, and are treated equivalently to the characters they represent. There -is no further mechanism for escaping within the `sh` DSL itself; in particular, -if you want to put quotes inside quotes, you have to use interpolation, like -this: - -```javascript -sh`echo "${'single = \', double = "'}"` // => "echo 'single = '\\'', double = \"'" -``` - -### Semantics - -Words that do not contain placeholders are emitted mostly verbatim to the -output string. Quotations are formatted in the expected style for the target -platform (single quotes for Unix, double quotes for Windows) regardless of the -quotes used in the template literal—as with JavaScript, single and double quotes -are interchangeable, except for the requirement to pair like with like. Unquoted -semicolons are translated to ampersands on Windows; all other special characters -(as enumerated above), when unquoted, are passed as-is to the output for the -shell to interpret. - -Puka may still quote words not containing the above special characters, if they -contain characters that need quoting on the target platform. For example, on -Windows, the character `%` is used for variable interpolation in `cmd.exe`, and -Puka quotes it on on that platform even if it appears unquoted in the template -literal. Consequently, there is no need to be paranoid about quoting anything -that doesn't look alphanumeric inside a `sh` template literal, for fear of being -burned on a different operating system; anything that matches the definition of -‘text’ above will never need manual quoting. - -#### Types of placeholders - -##### Strings - -If a word contains a string placeholder, then the value of the placeholder is -interpolated into the word and the entire word, if necessary, is quoted. If -the placeholder occurs within quotes, no further quoting is performed: - -```javascript -sh`script --file="${'herp derp'}.txt"`; // => "script --file='herp derp.txt'" -``` - -This behavior can be exploited to force consistent quoting, if desired; but -both of the examples below are safe on all platforms: - -```javascript -const words = ['oneword', 'two words']; -sh`minimal ${words[0]}`; // => "minimal oneword" -sh`minimal ${words[1]}`; // => "minimal 'two words'" -sh`consistent '${words[0]}'`; // => "consistent 'oneword'" -sh`consistent '${words[1]}'`; // => "consistent 'two words'" -``` - -##### Arrays and iterables - -If a word contains a placeholder for an array (or other iterable object), then -the entire word is repeated once for each value in the array, separated by -spaces. If the array is empty, then the word is not emitted at all, and neither -is any leading whitespace. - -```javascript -const files = ['foo', 'bar']; -sh`script ${files}`; // => "script foo bar" -sh`script --file=${files}`; // => "script --file=foo --file=bar" -sh`script --file=${[]}`; // => "script" -``` - -Note that, since special characters are their own words, the pipe operator here -is not repeated: - -```javascript -const cmd = ['script', 'foo', 'bar']; -sh`${cmd}|another-script`; // => "script foo bar|another-script" -``` - -Multiple arrays in the same word generate a Cartesian product: - -```javascript -const names = ['foo', 'bar'], exts = ['log', 'txt']; -// Same word -sh`... ${names}.${exts}`; // => "... foo.log foo.txt bar.log bar.txt" -sh`... "${names} ${exts}"`; // => "... 'foo log' 'foo txt' 'bar log' 'bar txt'" - -// Not the same word (extra space just for emphasis): -sh`... ${names} ${exts}`; // => "... foo bar log txt" -sh`... ${names};${exts}`; // => "... foo bar;log txt" -``` - -Finally, if a placeholder appears in the object of a redirect operator, the -entire redirect is repeated as necessary: - -```javascript -sh`script > ${['foo', 'bar']}.txt`; // => "script > foo.txt > bar.txt" -sh`script > ${[]}.txt`; // => "script" -``` - -##### unquoted - -The `unquoted` function returns a value that will skip being quoted when used -in a placeholder, alone or in an array. - -```javascript -const cmd = 'script < input.txt'; -const fields = ['foo', 'bar']; -sh`${unquoted(cmd)} | json ${fields}`; // => "script < input.txt | json foo bar" -``` - -##### ShellString - -If `ShellString.sh` is used to construct an unformatted ShellString, that value -can be used in a placeholder to insert the contents of the ShellString into the -outer template literal. This is safer than using `unquoted` as in the previous -example, but `unquoted` can be used when all you have is a string from another -(trusted!) source. - -```javascript -const url = 'http://example.com/data.json?x=1&y=2'; -const curl = ShellString.sh`curl -L ${url}`; -const fields = ['foo', 'bar']; -sh`${curl} | json ${fields}`; // => "curl -L 'http://example.com/data.json?x=1&y=2' | json foo bar" -``` - -##### Anything else - -... is treated like a string—namely, a value `x` is equivalent to `'' + x`, if -not in one of the above categories. diff --git a/node_modules/puka/index.js b/node_modules/puka/index.js deleted file mode 100644 index b69e47d7639db..0000000000000 --- a/node_modules/puka/index.js +++ /dev/null @@ -1,804 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -/** - * Key a method on your object with this symbol and you can get special - * formatting for that value! See ShellStringText, ShellStringUnquoted, or - * shellStringSemicolon for examples. - * @ignore - */ -const formatSymbol = Symbol('format'); -/** - * This symbol is for implementing advanced behaviors like the need for extra - * carets in Windows shell strings that use pipes. If present, it's called in - * an earlier phase than formatSymbol, and is passed a mutable context that can - * be read during the format phase to influence formatting. - * @ignore - */ -const preformatSymbol = Symbol('preformat'); - -// When minimum Node version becomes 6, replace calls to sticky with /.../y and -// inline execFrom. -let stickySupported = true; -try { - new RegExp('', 'y'); -} catch (e) { - stickySupported = false; -} -const sticky = stickySupported ? source => new RegExp(source, 'y') : source => new RegExp(`^(?:${source})`); -const execFrom = stickySupported ? (re, haystack, index) => (re.lastIndex = index, re.exec(haystack)) : (re, haystack, index) => re.exec(haystack.substr(index)); - -function quoteForCmd(text, forceQuote) { - let caretDepth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; - // See the below blog post for an explanation of this function and - // quoteForWin32: - // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ - if (!text.length) { - return '""'; - } - if (/[\n\r]/.test(text)) { - throw new Error("Line breaks can't be quoted on Windows"); - } - const caretEscape = /["%]/.test(text); - text = quoteForWin32(text, forceQuote || !caretEscape && /[&()<>^|]/.test(text)); - if (caretEscape) { - // See Win32Context for explanation of what caretDepth is for. - do { - text = text.replace(/[\t "%&()<>^|]/g, '^$&'); - } while (caretDepth--); - } - return text; -} -const quoteForWin32 = (text, forceQuote) => forceQuote || /[\t "]/.test(text) ? `"${text.replace(/\\+(?=$|")/g, '$&$&').replace(/"/g, '\\"')}"` : text; -const cmdMetaChars = /[\t\n\r "%&()<>^|]/; -class Win32Context { - constructor() { - this.currentScope = newScope(null); - this.scopesByObject = new Map(); - this.argDetectState = 0; - this.argSet = new Set(); - } - read(text) { - // When cmd.exe executes a batch file, or pipes to or from one, it spawns a - // second copy of itself to run the inner command. This necessitates - // doubling up on carets so that escaped characters survive both cmd.exe - // invocations. See: - // https://stackoverflow.com/questions/8192318/why-does-delayed-expansion-fail-when-inside-a-piped-block-of-code#8194279 - // https://ss64.com/nt/syntax-redirection.html - // - // Parentheses can create an additional subshell, requiring additional - // escaping... it's a mess. - // - // So here's what we do about it: we read all unquoted text in a shell - // string and put it through this tiny parser that looks for pipes, - // sequence operators (&, &&, ||), redirects, and parentheses. This can't - // be part of the main Puka parsing, because it can be affected by - // `unquoted(...)` values provided at evaluation time. - // - // Then, after associating each thing that needs to be quoted with a scope - // (via `mark()`), and identifying whether or not it's an argument to a - // command, we can determine the depth of caret escaping required in each - // scope and pass it (via `Formatter::quote()`) to `quoteForCmd()`. - // - // See also `ShellStringText`, which holds the logic for the previous - // paragraph. - const length = text.length; - for (let pos = 0, match; pos < length;) { - while (match = execFrom(reUnimportant, text, pos)) { - if (match[2] == null) { - // (not whitespace) - if (match[1] != null) { - // (>&) - this.argDetectState = this.argDetectState === 0 ? ADS_FLAG_INITIAL_REDIRECT : 0; - } else if (this.argDetectState !== ADS_FLAG_ARGS) { - this.argDetectState |= ADS_FLAG_WORD; - } - } else { - // (whitespace) - if ((this.argDetectState & ADS_FLAG_WORD) !== 0) { - this.argDetectState = ADS_FLAG_ARGS & ~this.argDetectState >> 1; - } - } - pos += match[0].length; - } - if (pos >= length) break; - if (match = execFrom(reSeqOp, text, pos)) { - this.seq(); - pos += match[0].length; - } else { - const char = text.charCodeAt(pos); - if (char === CARET) { - pos += 2; - } else if (char === QUOTE) { - // If you were foolish enough to leave a dangling quotation mark in - // an unquoted span... you're likely to have bigger problems than - // incorrect escaping. So we just do the simplest thing of looking for - // the end quote only in this piece of text. - pos += execFrom(reNotQuote, text, pos + 1)[0].length + 2; - } else { - if (char === OPEN_PAREN) { - this.enterScope(); - } else if (char === CLOSE_PAREN) { - this.exitScope(); - } else if (char === PIPE) { - this.pipe(); - } else { - // (char === '<' or '>') - this.argDetectState = this.argDetectState === 0 ? ADS_FLAG_INITIAL_REDIRECT : 0; - } - pos++; - } - } - } - } - enterScope() { - this.currentScope = newScope(this.currentScope); - this.argDetectState = 0; - } - exitScope() { - this.currentScope = this.currentScope.parent || (this.currentScope.parent = newScope(null)); - this.argDetectState = ADS_FLAG_ARGS; - } - seq() { - // | binds tighter than sequence operators, so the latter create new sibling - // scopes for future |s to mutate. - this.currentScope = newScope(this.currentScope.parent); - this.argDetectState = 0; - } - pipe() { - this.currentScope.depthDelta = 1; - this.argDetectState = 0; - } - mark(obj) { - this.scopesByObject.set(obj, this.currentScope); - if (this.argDetectState === ADS_FLAG_ARGS) { - this.argSet.add(obj); - } else { - this.argDetectState |= ADS_FLAG_WORD; - } - } - at(obj) { - const scope = this.scopesByObject.get(obj); - return { - depth: getDepth(scope), - isArgument: this.argSet.has(obj), - isNative: scope.isNative - }; - } -} -// These flags span the Win32Context's argument detection state machine. WORD -// is set when the context is inside a word that is not an argument (meaning it -// is either the first word in the command, or it is the object of a redirect). -// ARGS is set when the context has reached the arguments of a command. -// INITIAL_REDIRECT tracks the edge case when a redirect occurs before the -// first word of the command (if this flag is set, reaching the end of a word -// should take the state machine back to 0 instead of setting ADS_FLAG_ARGS). -const ADS_FLAG_WORD = 0x1; -const ADS_FLAG_ARGS = 0x2; -const ADS_FLAG_INITIAL_REDIRECT = 0x4; -const getDepth = scope => scope === null ? 0 : scope.depth !== -1 ? scope.depth : scope.depth = getDepth(scope.parent) + scope.depthDelta; -const newScope = parent => ({ - parent, - depthDelta: 0, - depth: -1, - isNative: false -}); -const CARET = '^'.charCodeAt(); -const QUOTE = '"'.charCodeAt(); -const OPEN_PAREN = '('.charCodeAt(); -const CLOSE_PAREN = ')'.charCodeAt(); -const PIPE = '|'.charCodeAt(); -const reNotQuote = sticky('[^"]*'); -const reSeqOp = sticky('&&?|\\|\\|'); -const reUnimportant = sticky('(\\d*>&)|[^\\s"$&()<>^|]+|(\\s+)'); - -const quoteForSh = (text, forceQuote) => text.length ? forceQuote || shMetaChars.test(text) ? `'${text.replace(/'/g, "'\\''")}'`.replace(/^(?:'')+(?!$)/, '').replace(/\\'''/g, "\\'") : text : "''"; -const shMetaChars = /[\t\n\r "#$&'()*;<>?\\`|~]/; - -/** - * To get a Formatter, call `Formatter.for`. - * - * To create a new Formatter, pass an object to `Formatter.declare`. - * - * To set the global default Formatter, assign to `Formatter.default`. - * - * @class - * @property {Formatter} default - The Formatter to be used when no platform - * is provided—for example, when creating strings with `sh`. - * @ignore - */ -function Formatter() {} -Object.assign(Formatter, -/** @lends Formatter */ -{ - /** - * Gets a Formatter that has been declared for the provided platform, or - * the base `'sh'` formatter if there is no Formatter specific to this - * platform, or the Formatter for the current platform if no specific platform - * is provided. - */ - for(platform) { - return platform == null ? Formatter.default || (Formatter.default = Formatter.for(process.platform)) : Formatter._registry.get(platform) || Formatter._registry.get('sh'); - }, - /** - * Creates a new Formatter or mutates the properties on an existing - * Formatter. The `platform` key on the provided properties object determines - * when the Formatter is retrieved. - */ - declare(props) { - const platform = props && props.platform || 'sh'; - const existingFormatter = Formatter._registry.get(platform); - const formatter = Object.assign(existingFormatter || new Formatter(), props); - formatter.emptyString === void 0 && (formatter.emptyString = formatter.quote('', true)); - existingFormatter || Formatter._registry.set(formatter.platform, formatter); - }, - _registry: new Map(), - prototype: { - platform: 'sh', - quote: quoteForSh, - metaChars: shMetaChars, - hasExtraMetaChars: false, - statementSeparator: ';', - createContext() { - return defaultContext; - } - } -}); -const defaultContext = { - at() {} -}; -Formatter.declare(); -Formatter.declare({ - platform: 'win32', - quote(text, forceQuote, opts) { - const caretDepth = opts ? (opts.depth || 0) + (opts.isArgument && !opts.isNative ? 1 : 0) : 0; - return quoteForCmd(text, forceQuote, caretDepth); - }, - metaChars: cmdMetaChars, - hasExtraMetaChars: true, - statementSeparator: '&', - createContext(root) { - const context = new this.Context(); - root[preformatSymbol](context); - return context; - }, - Context: Win32Context -}); - -const isObject = any => any === Object(any); -function memoize(f) { - const cache = new WeakMap(); - return arg => { - let result = cache.get(arg); - if (result === void 0) { - result = f(arg); - cache.set(arg, result); - } - return result; - }; -} - -/** - * Represents a contiguous span of text that may or must be quoted. The contents - * may already contain quoted segments, which will always be quoted. If unquoted - * segments also require quoting, the entire span will be quoted together. - * @ignore - */ -class ShellStringText { - constructor(contents, untested) { - this.contents = contents; - this.untested = untested; - } - [formatSymbol](formatter, context) { - const unformattedContents = this.contents; - const length = unformattedContents.length; - const contents = new Array(length); - for (let i = 0; i < length; i++) { - const c = unformattedContents[i]; - contents[i] = isObject(c) && formatSymbol in c ? c[formatSymbol](formatter) : c; - } - for (let unquoted = true, i = 0; i < length; i++) { - const content = contents[i]; - if (content === null) { - unquoted = !unquoted; - } else { - if (unquoted && (formatter.hasExtraMetaChars || this.untested && this.untested.has(i)) && formatter.metaChars.test(content)) { - return formatter.quote(contents.join(''), false, context.at(this)); - } - } - } - const parts = []; - for (let quoted = null, i = 0; i < length; i++) { - const content = contents[i]; - if (content === null) { - quoted = quoted ? (parts.push(formatter.quote(quoted.join(''), true, context.at(this))), null) : []; - } else { - (quoted || parts).push(content); - } - } - const result = parts.join(''); - return result.length ? result : formatter.emptyString; - } - [preformatSymbol](context) { - context.mark(this); - } -} - -/** - * Represents a contiguous span of text that will not be quoted. - * @ignore - */ -class ShellStringUnquoted { - constructor(value) { - this.value = value; - } - [formatSymbol]() { - return this.value; - } - [preformatSymbol](context) { - context.read(this.value); - } -} - -/** - * Represents a semicolon... or an ampersand, on Windows. - * @ignore - */ -const shellStringSemicolon = { - [formatSymbol](formatter) { - return formatter.statementSeparator; - }, - [preformatSymbol](context) { - context.seq(); - } -}; - -const PLACEHOLDER = {}; -const parse = memoize(templateSpans => { - // These are the token types our DSL can recognize. Their values won't escape - // this function. - const TOKEN_TEXT = 0; - const TOKEN_QUOTE = 1; - const TOKEN_SEMI = 2; - const TOKEN_UNQUOTED = 3; - const TOKEN_SPACE = 4; - const TOKEN_REDIRECT = 5; - const result = []; - let placeholderCount = 0; - let prefix = null; - let onlyPrefixOnce = false; - let contents = []; - let quote = 0; - const lastSpan = templateSpans.length - 1; - for (let spanIndex = 0; spanIndex <= lastSpan; spanIndex++) { - const templateSpan = templateSpans[spanIndex]; - const posEnd = templateSpan.length; - let tokenStart = 0; - if (spanIndex) { - placeholderCount++; - contents.push(PLACEHOLDER); - } - // For each span, we first do a recognizing pass in which we use regular - // expressions to identify the positions of tokens in the text, and then - // a second pass that actually splits the text into the minimum number of - // substrings necessary. - const recognized = []; // [type1, index1, type2, index2...] - let firstWordBreak = -1; - let lastWordBreak = -1; - { - let pos = 0, - match; - while (pos < posEnd) { - if (quote) { - if (match = execFrom(quote === CHAR_SQUO ? reQuotation1 : reQuotation2, templateSpan, pos)) { - recognized.push(TOKEN_TEXT, pos); - pos += match[0].length; - } - if (pos < posEnd) { - recognized.push(TOKEN_QUOTE, pos++); - quote = 0; - } - } else { - if (match = execFrom(reRedirectOrSpace, templateSpan, pos)) { - firstWordBreak < 0 && (firstWordBreak = pos); - lastWordBreak = pos; - recognized.push(match[1] ? TOKEN_REDIRECT : TOKEN_SPACE, pos); - pos += match[0].length; - } - if (match = execFrom(reText, templateSpan, pos)) { - const setBreaks = match[1] != null; - setBreaks && firstWordBreak < 0 && (firstWordBreak = pos); - recognized.push(setBreaks ? TOKEN_UNQUOTED : TOKEN_TEXT, pos); - pos += match[0].length; - setBreaks && (lastWordBreak = pos); - } - const char = templateSpan.charCodeAt(pos); - if (char === CHAR_SEMI) { - firstWordBreak < 0 && (firstWordBreak = pos); - recognized.push(TOKEN_SEMI, pos++); - lastWordBreak = pos; - } else if (char === CHAR_SQUO || char === CHAR_DQUO) { - recognized.push(TOKEN_QUOTE, pos++); - quote = char; - } - } - } - } - // Word breaks are only important if they separate words with placeholders, - // so we can ignore the first/last break if this is the first/last span. - spanIndex === 0 && (firstWordBreak = -1); - spanIndex === lastSpan && (lastWordBreak = posEnd); - // Here begins the second pass mentioned above. This loop runs one more - // iteration than there are tokens in recognized, because it handles tokens - // on a one-iteration delay; hence the i <= iEnd instead of i < iEnd. - const iEnd = recognized.length; - for (let i = 0, type = -1; i <= iEnd; i += 2) { - let typeNext = -1, - pos; - if (i === iEnd) { - pos = posEnd; - } else { - typeNext = recognized[i]; - pos = recognized[i + 1]; - // If the next token is space or redirect, but there's another word - // break in this span, then we can handle that token the same way we - // would handle unquoted text because it isn't being attached to a - // placeholder. - typeNext >= TOKEN_SPACE && pos !== lastWordBreak && (typeNext = TOKEN_UNQUOTED); - } - const breakHere = pos === firstWordBreak || pos === lastWordBreak; - if (pos && (breakHere || typeNext !== type)) { - let value = type === TOKEN_QUOTE ? null : type === TOKEN_SEMI ? shellStringSemicolon : templateSpan.substring(tokenStart, pos); - if (type >= TOKEN_SEMI) { - // This branch handles semicolons, unquoted text, spaces, and - // redirects. shellStringSemicolon is already a formatSymbol object; - // the rest need to be wrapped. - type === TOKEN_SEMI || (value = new ShellStringUnquoted(value)); - // We don't need to check placeholderCount here like we do below; - // that's only relevant during the first word break of the span, and - // because this iteration of the loop is processing the token that - // was checked for breaks in the previous iteration, it will have - // already been handled. For the same reason, prefix is guaranteed to - // be null. - if (contents.length) { - result.push(new ShellStringText(contents, null)); - contents = []; - } - // Only spaces and redirects become prefixes, but not if they've been - // rewritten to unquoted above. - if (type >= TOKEN_SPACE) { - prefix = value; - onlyPrefixOnce = type === TOKEN_SPACE; - } else { - result.push(value); - } - } else { - contents.push(value); - } - tokenStart = pos; - } - if (breakHere) { - if (placeholderCount) { - result.push({ - contents, - placeholderCount, - prefix, - onlyPrefixOnce - }); - } else { - // There's no prefix to handle in this branch; a prefix prior to this - // span would mean placeholderCount > 0, and a prefix in this span - // can't be created because spaces and redirects get rewritten to - // unquoted before the last word break. - contents.length && result.push(new ShellStringText(contents, null)); - } - placeholderCount = 0; - prefix = null; - onlyPrefixOnce = false; - contents = []; - } - type = typeNext; - } - } - if (quote) { - throw new SyntaxError(`String is missing a ${String.fromCharCode(quote)} character`); - } - return result; -}); -const CHAR_SEMI = ';'.charCodeAt(); -const CHAR_SQUO = "'".charCodeAt(); -const CHAR_DQUO = '"'.charCodeAt(); -const reQuotation1 = sticky("[^']+"); -const reQuotation2 = sticky('[^"]+'); -const reText = sticky('[^\\s"#$&\'();<>\\\\`|]+|([#$&()\\\\`|]+)'); -const reRedirectOrSpace = sticky('(\\s*\\d*[<>]+\\s*)|\\s+'); - -class BitSet { - constructor() { - this.vector = new Int32Array(1); - } - has(n) { - return (this.vector[n >>> 5] & 1 << n) !== 0; - } - add(n) { - const i = n >>> 5, - requiredLength = i + 1; - let vector = this.vector, - _vector = vector, - length = _vector.length; - if (requiredLength > length) { - while (requiredLength > (length *= 2)); - const oldValues = vector; - vector = new Int32Array(length); - vector.set(oldValues); - this.vector = vector; - } - vector[i] |= 1 << n; - } -} - -function evaluate(template, values) { - values = values.map(toStringishArray); - const children = []; - let valuesStart = 0; - for (let i = 0, iMax = template.length; i < iMax; i++) { - const word = template[i]; - if (formatSymbol in word) { - children.push(word); - continue; - } - const contents = word.contents, - placeholderCount = word.placeholderCount, - prefix = word.prefix, - onlyPrefixOnce = word.onlyPrefixOnce; - const kMax = contents.length; - const valuesEnd = valuesStart + placeholderCount; - const tuples = cartesianProduct(values, valuesStart, valuesEnd); - valuesStart = valuesEnd; - for (let j = 0, jMax = tuples.length; j < jMax; j++) { - const needSpace = j > 0; - const tuple = tuples[j]; - (needSpace || prefix) && children.push(needSpace && (onlyPrefixOnce || !prefix) ? unquotedSpace : prefix); - let interpolatedContents = []; - let untested = null; - let quoting = false; - let tupleIndex = 0; - for (let k = 0; k < kMax; k++) { - const content = contents[k]; - if (content === PLACEHOLDER) { - const value = tuple[tupleIndex++]; - if (quoting) { - interpolatedContents.push(value); - } else { - if (isObject(value) && formatSymbol in value) { - if (interpolatedContents.length) { - children.push(new ShellStringText(interpolatedContents, untested)); - interpolatedContents = []; - untested = null; - } - children.push(value); - } else { - (untested || (untested = new BitSet())).add(interpolatedContents.length); - interpolatedContents.push(value); - } - } - } else { - interpolatedContents.push(content); - content === null && (quoting = !quoting); - } - } - if (interpolatedContents.length) { - children.push(new ShellStringText(interpolatedContents, untested)); - } - } - } - return children; -} -const primToStringish = value => value == null ? '' + value : value; -function toStringishArray(value) { - let array; - switch (true) { - default: - if (isObject(value)) { - if (Array.isArray(value)) { - array = value; - break; - } - if (Symbol.iterator in value) { - array = Array.from(value); - break; - } - } - array = [value]; - } - return array.map(primToStringish); -} -function cartesianProduct(arrs, start, end) { - const size = end - start; - let resultLength = 1; - for (let i = start; i < end; i++) { - resultLength *= arrs[i].length; - } - if (resultLength > 1e6) { - throw new RangeError("Far too many elements to interpolate"); - } - const result = new Array(resultLength); - const indices = new Array(size).fill(0); - for (let i = 0; i < resultLength; i++) { - const value = result[i] = new Array(size); - for (let j = 0; j < size; j++) { - value[j] = arrs[j + start][indices[j]]; - } - for (let j = size - 1; j >= 0; j--) { - if (++indices[j] < arrs[j + start].length) break; - indices[j] = 0; - } - } - return result; -} -const unquotedSpace = new ShellStringUnquoted(' '); - -/** - * A ShellString represents a shell command after it has been interpolated, but - * before it has been formatted for a particular platform. ShellStrings are - * useful if you want to prepare a command for a different platform than the - * current one, for instance. - * - * To create a ShellString, use `ShellString.sh` the same way you would use - * top-level `sh`. - */ -class ShellString { - /** @hideconstructor */ - constructor(children) { - this.children = children; - } - /** - * `ShellString.sh` is a template tag just like `sh`; the only difference is - * that this function returns a ShellString which has not yet been formatted - * into a String. - * @returns {ShellString} - * @function sh - * @static - * @memberof ShellString - */ - static sh(templateSpans) { - for (var _len = arguments.length, values = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - values[_key - 1] = arguments[_key]; - } - return new ShellString(evaluate(parse(templateSpans), values)); - } - /** - * A method to format a ShellString into a regular String formatted for a - * particular platform. - * - * @param {String} [platform] a value that `process.platform` might take: - * `'win32'`, `'linux'`, etc.; determines how the string is to be formatted. - * When omitted, effectively the same as `process.platform`. - * @returns {String} - */ - toString(platform) { - return this[formatSymbol](Formatter.for(platform)); - } - [formatSymbol](formatter) { - let context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : formatter.createContext(this); - return this.children.map(child => child[formatSymbol](formatter, context)).join(''); - } - [preformatSymbol](context) { - const children = this.children; - for (let i = 0, iMax = children.length; i < iMax; i++) { - const child = children[i]; - if (preformatSymbol in child) { - child[preformatSymbol](context); - } - } - } -} - -/** - * A Windows-specific version of {@link quoteForShell}. - * @param {String} text to be quoted - * @param {Boolean} [forceQuote] whether to always add quotes even if the string - * is already safe. Defaults to `false`. - */ - -/** - * A Unix-specific version of {@link quoteForShell}. - * @param {String} text to be quoted - * @param {Boolean} [forceQuote] whether to always add quotes even if the string - * is already safe. Defaults to `false`. - */ - -/** - * Quotes a string for injecting into a shell command. - * - * This function is exposed for some hypothetical case when the `sh` DSL simply - * won't do; `sh` is expected to be the more convenient option almost always. - * Compare: - * - * ```javascript - * console.log('cmd' + args.map(a => ' ' + quoteForShell(a)).join('')); - * console.log(sh`cmd ${args}`); // same as above - * - * console.log('cmd' + args.map(a => ' ' + quoteForShell(a, true)).join('')); - * console.log(sh`cmd "${args}"`); // same as above - * ``` - * - * Additionally, on Windows, `sh` checks the entire command string for pipes, - * which subtly change how arguments need to be quoted. If your commands may - * involve pipes, you are strongly encouraged to use `sh` and not try to roll - * your own with `quoteForShell`. - * - * @param {String} text to be quoted - * @param {Boolean} [forceQuote] whether to always add quotes even if the string - * is already safe. Defaults to `false`. - * @param {String} [platform] a value that `process.platform` might take: - * `'win32'`, `'linux'`, etc.; determines how the string is to be formatted. - * When omitted, effectively the same as `process.platform`. - * - * @returns {String} a string that is safe for the current (or specified) - * platform. - */ -function quoteForShell(text, forceQuote, platform) { - return Formatter.for(platform).quote(text, forceQuote); -} - -/** - * A string template tag for safely constructing cross-platform shell commands. - * - * An `sh` template is not actually treated as a literal string to be - * interpolated; instead, it is a tiny DSL designed to make working with shell - * strings safe, simple, and straightforward. To get started quickly, see the - * examples below. {@link #the-sh-dsl More detailed documentation} is available - * further down. - * - * @name sh - * @example - * const title = '"this" & "that"'; - * sh`script --title=${title}`; // => "script '--title=\"this\" & \"that\"'" - * // Note: these examples show results for non-Windows platforms. - * // On Windows, the above would instead be - * // 'script ^^^"--title=\\^^^"this\\^^^" ^^^& \\^^^"that\\^^^"^^^"'. - * - * const names = ['file1', 'file 2']; - * sh`rimraf ${names}.txt`; // => "rimraf file1.txt 'file 2.txt'" - * - * const cmd1 = ['cat', 'file 1.txt', 'file 2.txt']; - * const cmd2 = ['use-input', '-abc']; - * sh`${cmd1}|${cmd2}`; // => "cat 'file 1.txt' 'file 2.txt'|use-input -abc" - * - * @returns {String} - a string formatted for the platform Node is currently - * running on. - */ -const sh = function () { - return ShellString.sh.apply(ShellString, arguments).toString(); -}; - -/** - * This function permits raw strings to be interpolated into a `sh` template. - * - * **IMPORTANT**: If you're using Puka due to security concerns, make sure you - * don't pass any untrusted content to `unquoted`. This may be obvious, but - * stray punctuation in an `unquoted` section can compromise the safety of the - * entire shell command. - * - * @param value - any value (it will be treated as a string) - * - * @example - * const both = true; - * sh`foo ${unquoted(both ? '&&' : '||')} bar`; // => 'foo && bar' - */ -const unquoted = value => new ShellStringUnquoted(value); - -exports.Formatter = Formatter; -exports.ShellString = ShellString; -exports.ShellStringText = ShellStringText; -exports.ShellStringUnquoted = ShellStringUnquoted; -exports.quoteForCmd = quoteForCmd; -exports.quoteForSh = quoteForSh; -exports.quoteForShell = quoteForShell; -exports.sh = sh; -exports.shellStringSemicolon = shellStringSemicolon; -exports.formatSymbol = formatSymbol; -exports.preformatSymbol = preformatSymbol; -exports.unquoted = unquoted; diff --git a/node_modules/puka/package.json b/node_modules/puka/package.json deleted file mode 100644 index 41798dc2493b8..0000000000000 --- a/node_modules/puka/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "puka", - "version": "1.0.1", - "description": "A cross-platform library for safely passing strings through shells", - "keywords": [ - "args", - "arguments", - "cmd", - "command", - "command-line", - "cross-platform", - "escape", - "escaping", - "exec", - "linux", - "mac", - "macos", - "osx", - "quote", - "quoting", - "sh", - "shell", - "spawn", - "unix", - "win", - "win32", - "windows" - ], - "homepage": "https://gitlab.com/rhendric/puka", - "bugs": "https://gitlab.com/rhendric/puka/issues", - "license": "MIT", - "author": "Ryan Hendrickson ", - "repository": "gitlab:rhendric/puka", - "dependencies": {}, - "engines": { - "node": ">=4" - } -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9ab2710988952..c70233898e5dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -255,7 +255,7 @@ "@npmcli/arborist": "^2.2.6", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^1.2.9", - "@npmcli/run-script": "^1.8.3", + "@npmcli/run-script": "^1.8.4", "abbrev": "~1.1.1", "ansicolors": "~0.3.2", "ansistyles": "~0.1.3", @@ -977,16 +977,15 @@ } }, "node_modules/@npmcli/run-script": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.3.tgz", - "integrity": "sha512-ELPGWAVU/xyU+A+H3pEPj0QOvYwLTX71RArXcClFzeiyJ/b/McsZ+d0QxpznvfFtZzxGN/gz/1cvlqICR4/suQ==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.4.tgz", + "integrity": "sha512-Yd9HXTtF1JGDXZw0+SOn+mWLYS0e7bHBHVC/2C8yqs4wUrs/k8rwBSinD7rfk+3WG/MFGRZKxjyoD34Pch2E/A==", "inBundle": true, "dependencies": { "@npmcli/node-gyp": "^1.0.2", "@npmcli/promise-spawn": "^1.3.2", "infer-owner": "^1.0.4", "node-gyp": "^7.1.0", - "puka": "^1.0.1", "read-package-json-fast": "^2.0.1" } }, @@ -6294,15 +6293,6 @@ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "inBundle": true }, - "node_modules/puka": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/puka/-/puka-1.0.1.tgz", - "integrity": "sha512-ssjRZxBd7BT3dte1RR3VoeT2cT/ODH8x+h0rUF1rMqB0srHYf48stSDWfiYakTp5UBZMxroZhB2+ExLDHm7W3g==", - "inBundle": true, - "engines": { - "node": ">=4" - } - }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -11076,15 +11066,14 @@ } }, "@npmcli/run-script": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.3.tgz", - "integrity": "sha512-ELPGWAVU/xyU+A+H3pEPj0QOvYwLTX71RArXcClFzeiyJ/b/McsZ+d0QxpznvfFtZzxGN/gz/1cvlqICR4/suQ==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.4.tgz", + "integrity": "sha512-Yd9HXTtF1JGDXZw0+SOn+mWLYS0e7bHBHVC/2C8yqs4wUrs/k8rwBSinD7rfk+3WG/MFGRZKxjyoD34Pch2E/A==", "requires": { "@npmcli/node-gyp": "^1.0.2", "@npmcli/promise-spawn": "^1.3.2", "infer-owner": "^1.0.4", "node-gyp": "^7.1.0", - "puka": "^1.0.1", "read-package-json-fast": "^2.0.1" } }, @@ -15044,11 +15033,6 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" }, - "puka": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/puka/-/puka-1.0.1.tgz", - "integrity": "sha512-ssjRZxBd7BT3dte1RR3VoeT2cT/ODH8x+h0rUF1rMqB0srHYf48stSDWfiYakTp5UBZMxroZhB2+ExLDHm7W3g==" - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", diff --git a/package.json b/package.json index f4d34f98abd6c..464f411043eca 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@npmcli/arborist": "^2.2.6", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^1.2.9", - "@npmcli/run-script": "^1.8.3", + "@npmcli/run-script": "^1.8.4", "abbrev": "~1.1.1", "ansicolors": "~0.3.2", "ansistyles": "~0.1.3", From eca38cb6262da1797719ad060dfae1dff3f8852b Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 9 Mar 2021 10:12:36 -0800 Subject: [PATCH 2/8] byte-size@7.0.1 --- node_modules/byte-size/LICENSE | 2 +- node_modules/byte-size/README.hbs | 2 +- node_modules/byte-size/README.md | 2 +- node_modules/byte-size/dist/index.js | 2 +- node_modules/byte-size/package.json | 8 ++++---- package-lock.json | 15 +++++++-------- package.json | 2 +- 7 files changed, 16 insertions(+), 17 deletions(-) diff --git a/node_modules/byte-size/LICENSE b/node_modules/byte-size/LICENSE index d9e1e9d6918a4..5699dfbe51830 100644 --- a/node_modules/byte-size/LICENSE +++ b/node_modules/byte-size/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-20 Lloyd Brookes <75pound@gmail.com> +Copyright (c) 2014-21 Lloyd Brookes <75pound@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/node_modules/byte-size/README.hbs b/node_modules/byte-size/README.hbs index 5b677f2a98870..8d0b32f3e5fdc 100644 --- a/node_modules/byte-size/README.hbs +++ b/node_modules/byte-size/README.hbs @@ -159,6 +159,6 @@ Old browser (adds `window.byteSize`): * * * -© 2014-20 Lloyd Brookes \<75pound@gmail.com\>. +© 2014-21 Lloyd Brookes \<75pound@gmail.com\>. Isomorphic test suite by [test-runner](https://github.com/test-runner-js/test-runner) and [web-runner](https://github.com/test-runner-js/web-runner). Documented by [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown). diff --git a/node_modules/byte-size/README.md b/node_modules/byte-size/README.md index 71f47b1e49b3c..955f707b77007 100644 --- a/node_modules/byte-size/README.md +++ b/node_modules/byte-size/README.md @@ -193,6 +193,6 @@ Old browser (adds `window.byteSize`): * * * -© 2014-20 Lloyd Brookes \<75pound@gmail.com\>. +© 2014-21 Lloyd Brookes \<75pound@gmail.com\>. Isomorphic test suite by [test-runner](https://github.com/test-runner-js/test-runner) and [web-runner](https://github.com/test-runner-js/web-runner). Documented by [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown). diff --git a/node_modules/byte-size/dist/index.js b/node_modules/byte-size/dist/index.js index 78129d8b02d21..dd1debda59abd 100644 --- a/node_modules/byte-size/dist/index.js +++ b/node_modules/byte-size/dist/index.js @@ -1,7 +1,7 @@ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : - (global = global || self, global.byteSize = factory()); + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.byteSize = factory()); }(this, (function () { 'use strict'; /** diff --git a/node_modules/byte-size/package.json b/node_modules/byte-size/package.json index e69b7e5f53ae2..b5f454592da10 100644 --- a/node_modules/byte-size/package.json +++ b/node_modules/byte-size/package.json @@ -8,7 +8,7 @@ "url": "http://repejota.com" } ], - "version": "7.0.0", + "version": "7.0.1", "main": "dist/index.js", "license": "MIT", "engines": { @@ -39,12 +39,12 @@ "dist": "rollup -f umd -n byteSize -o dist/index.js index.mjs" }, "devDependencies": { - "@test-runner/web": "^0.3.4", + "@test-runner/web": "^0.3.5", "coveralls": "^3.1.0", "esm-runner": "^0.3.4", "isomorphic-assert": "^0.1.1", - "jsdoc-to-markdown": "^5.0.3", - "rollup": "^2.10.9", + "jsdoc-to-markdown": "^7.0.0", + "rollup": "^2.40.0", "test-object-model": "^0.6.1" }, "standard": { diff --git a/package-lock.json b/package-lock.json index c70233898e5dd..b77b68362b342 100644 --- a/package-lock.json +++ b/package-lock.json @@ -209,7 +209,6 @@ "promise-retry", "promzard", "psl", - "puka", "punycode", "qs", "read-cmd-shim", @@ -260,7 +259,7 @@ "ansicolors": "~0.3.2", "ansistyles": "~0.1.3", "archy": "~1.0.0", - "byte-size": "^7.0.0", + "byte-size": "^7.0.1", "cacache": "^15.0.5", "chalk": "^4.1.0", "chownr": "^2.0.0", @@ -1574,9 +1573,9 @@ "inBundle": true }, "node_modules/byte-size": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-7.0.0.tgz", - "integrity": "sha512-NNiBxKgxybMBtWdmvx7ZITJi4ZG+CYUgwOSZTfqB1qogkRHrhbQE/R2r5Fh94X+InN5MCYz6SvB/ejHMj/HbsQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz", + "integrity": "sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A==", "inBundle": true, "engines": { "node": ">=10" @@ -11519,9 +11518,9 @@ "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" }, "byte-size": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-7.0.0.tgz", - "integrity": "sha512-NNiBxKgxybMBtWdmvx7ZITJi4ZG+CYUgwOSZTfqB1qogkRHrhbQE/R2r5Fh94X+InN5MCYz6SvB/ejHMj/HbsQ==" + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz", + "integrity": "sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A==" }, "cacache": { "version": "15.0.5", diff --git a/package.json b/package.json index 464f411043eca..4214b3ef4876d 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "ansicolors": "~0.3.2", "ansistyles": "~0.1.3", "archy": "~1.0.0", - "byte-size": "^7.0.0", + "byte-size": "^7.0.1", "cacache": "^15.0.5", "chalk": "^4.1.0", "chownr": "^2.0.0", From 112dc483350039ed5447e62679258425382576f5 Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 9 Mar 2021 10:13:16 -0800 Subject: [PATCH 3/8] @npmcli/arborist@2.2.7 --- .../arborist/lib/arborist/build-ideal-tree.js | 8 +++++--- .../@npmcli/arborist/lib/arborist/reify.js | 20 +++++++++++++++++-- node_modules/@npmcli/arborist/package.json | 2 +- package-lock.json | 14 ++++++------- package.json | 2 +- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js index 17b42f81bb39a..412d6ce8b7119 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js +++ b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js @@ -431,11 +431,13 @@ module.exports = cls => class IdealTreeBuilder extends cls { // ie, doing `foo@bar` we just return foo // but if it's a url or git, we don't know the name until we // fetch it and look in its manifest. - return Promise.all(add.map(rawSpec => - this[_retrieveSpecName](npa(rawSpec)) + return Promise.all(add.map(rawSpec => { + // We do NOT provide the path here, because user-additions need + // to be resolved relative to the CWD the user is in. + return this[_retrieveSpecName](npa(rawSpec)) .then(add => this[_updateFilePath](add)) .then(add => this[_followSymlinkPath](add)) - )).then(add => { + })).then(add => { this[_resolvedAdd] = add // now add is a list of spec objects with names. // find a home for each of them! diff --git a/node_modules/@npmcli/arborist/lib/arborist/reify.js b/node_modules/@npmcli/arborist/lib/arborist/reify.js index c3ea1b43bbc76..803fb9782f07c 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/reify.js +++ b/node_modules/@npmcli/arborist/lib/arborist/reify.js @@ -6,6 +6,7 @@ const rpj = require('read-package-json-fast') const { updateDepSpec } = require('../dep-spec.js') const AuditReport = require('../audit-report.js') const {subset} = require('semver') +const npa = require('npm-package-arg') const {dirname, resolve, relative} = require('path') const {depth: dfwalk} = require('treeverse') @@ -881,11 +882,17 @@ module.exports = cls => class Reifier extends cls { process.emit('time', 'reify:save') + // resolvedAdd is the list of user add requests, but with names added + // to things like git repos and tarball file/urls. However, if the + // user requested 'foo@', and we have a foo@file:../foo, then we should + // end up saving the spec we actually used, not whatever they gave us. if (this[_resolvedAdd]) { const root = this.idealTree const pkg = root.package - for (const req of this[_resolvedAdd]) { - const {name, rawSpec, subSpec} = req + for (const { name } of this[_resolvedAdd]) { + const req = npa(root.edgesOut.get(name).spec, root.realpath) + const {rawSpec, subSpec} = req + const spec = subSpec ? subSpec.rawSpec : rawSpec const child = root.children.get(name) @@ -910,6 +917,15 @@ module.exports = cls => class Reifier extends cls { const save = h.https && h.auth ? `git+${h.https(opt)}` : h.shortcut(opt) updateDepSpec(pkg, name, save) + } else if (req.type === 'directory' || req.type === 'file') { + // save the relative path in package.json + // Normally saveSpec is updated with the proper relative + // path already, but it's possible to specify a full absolute + // path initially, in which case we can end up with the wrong + // thing, so just get the ultimate fetchSpec and relativize it. + const p = req.fetchSpec.replace(/^file:/, '') + const rel = relpath(root.realpath, p) + updateDepSpec(pkg, name, `file:${rel}`) } else updateDepSpec(pkg, name, req.saveSpec) } diff --git a/node_modules/@npmcli/arborist/package.json b/node_modules/@npmcli/arborist/package.json index 268f0e72a92d5..35623f90c44ed 100644 --- a/node_modules/@npmcli/arborist/package.json +++ b/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "2.2.6", + "version": "2.2.7", "description": "Manage node_modules trees", "dependencies": { "@npmcli/installed-package-contents": "^1.0.7", diff --git a/package-lock.json b/package-lock.json index b77b68362b342..07cd28466e61a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -251,7 +251,7 @@ ], "license": "Artistic-2.0", "dependencies": { - "@npmcli/arborist": "^2.2.6", + "@npmcli/arborist": "^2.2.7", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^1.2.9", "@npmcli/run-script": "^1.8.4", @@ -811,9 +811,9 @@ } }, "node_modules/@npmcli/arborist": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.2.6.tgz", - "integrity": "sha512-dLWPjMeUTrlIJG+f4j3cOMZ24vJ1GQmZm7QixXeOnx3XCo7reWqzjDZfQvPE21FPUuz25EGoRw+MSHzc7OlxAA==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.2.7.tgz", + "integrity": "sha512-NulX/tVu45PIXO4DSNpVQkLrN94OjzAUxgLQ7Vsdb1macSmklJwQF0+4jBgC2riMpdWJP+IiFsxg1k9T9RoRRg==", "inBundle": true, "dependencies": { "@npmcli/installed-package-contents": "^1.0.7", @@ -10933,9 +10933,9 @@ "dev": true }, "@npmcli/arborist": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.2.6.tgz", - "integrity": "sha512-dLWPjMeUTrlIJG+f4j3cOMZ24vJ1GQmZm7QixXeOnx3XCo7reWqzjDZfQvPE21FPUuz25EGoRw+MSHzc7OlxAA==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.2.7.tgz", + "integrity": "sha512-NulX/tVu45PIXO4DSNpVQkLrN94OjzAUxgLQ7Vsdb1macSmklJwQF0+4jBgC2riMpdWJP+IiFsxg1k9T9RoRRg==", "requires": { "@npmcli/installed-package-contents": "^1.0.7", "@npmcli/map-workspaces": "^1.0.2", diff --git a/package.json b/package.json index 4214b3ef4876d..7669dfa73bbfa 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@npmcli/arborist": "^2.2.6", + "@npmcli/arborist": "^2.2.7", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^1.2.9", "@npmcli/run-script": "^1.8.4", From dae8b0d8de6d024c567db9782cc12b81e3260a5b Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 9 Mar 2021 10:14:46 -0800 Subject: [PATCH 4/8] libnpmdiff@2.0.4 --- node_modules/libnpmdiff/README.md | 2 +- node_modules/libnpmdiff/index.js | 5 +-- node_modules/libnpmdiff/lib/tarball.js | 33 ++++++++++++++++++ node_modules/libnpmdiff/package.json | 6 ++-- node_modules/pacote/README.md | 12 +++++++ node_modules/pacote/lib/dir.js | 33 ++++-------------- node_modules/pacote/lib/index.js | 11 ++++++ .../pacote/lib/util/tar-create-options.js | 24 +++++++++++++ node_modules/pacote/package.json | 2 +- package-lock.json | 34 +++++++++++-------- package.json | 2 +- 11 files changed, 116 insertions(+), 48 deletions(-) create mode 100644 node_modules/libnpmdiff/lib/tarball.js create mode 100644 node_modules/pacote/lib/util/tar-create-options.js diff --git a/node_modules/libnpmdiff/README.md b/node_modules/libnpmdiff/README.md index d1cf53fc6c5c7..bc260ad15ce12 100644 --- a/node_modules/libnpmdiff/README.md +++ b/node_modules/libnpmdiff/README.md @@ -86,7 +86,7 @@ Fetches the registry tarballs and compare files between a spec `a` and spec `b`. - `diffSrcPrefix `: Prefix to be used in the filenames from `a`. Defaults to `a/`. - `diffDstPrefix `: Prefix to be used in the filenames from `b`. Defaults to `b/`. - `diffText `: Should treat all files as text and try to print diff for binary files. Defaults to `false`. -- ...`cache`, `registry` and other common options accepted by [pacote](https://github.com/npm/pacote#options) +- ...`cache`, `registry`, `where` and other common options accepted by [pacote](https://github.com/npm/pacote#options) Returns a `Promise` that fullfils with a `String` containing the resulting patch diffs. diff --git a/node_modules/libnpmdiff/index.js b/node_modules/libnpmdiff/index.js index 0bfc8734ef639..73dc3ee64e3ce 100644 --- a/node_modules/libnpmdiff/index.js +++ b/node_modules/libnpmdiff/index.js @@ -1,6 +1,7 @@ const pacote = require('pacote') const formatDiff = require('./lib/format-diff.js') +const getTarball = require('./lib/tarball.js') const untar = require('./lib/untar.js') const argsError = () => @@ -25,8 +26,8 @@ const diff = async (specs, opts = {}) => { // fetches tarball using pacote const [a, b] = await Promise.all([ - pacote.tarball(aManifest._resolved, opts), - pacote.tarball(bManifest._resolved, opts), + getTarball(aManifest, opts), + getTarball(bManifest, opts), ]) // read all files diff --git a/node_modules/libnpmdiff/lib/tarball.js b/node_modules/libnpmdiff/lib/tarball.js new file mode 100644 index 0000000000000..0c8fb177a3885 --- /dev/null +++ b/node_modules/libnpmdiff/lib/tarball.js @@ -0,0 +1,33 @@ +const { relative } = require('path') + +const npa = require('npm-package-arg') +const pkgContents = require('@npmcli/installed-package-contents') +const pacote = require('pacote') +const { tarCreateOptions } = pacote.DirFetcher +const tar = require('tar') + +// returns a simplified tarball when reading files from node_modules folder, +// thus avoiding running the prepare scripts and the extra logic from packlist +const nodeModulesTarball = (manifest, opts) => + pkgContents({ path: manifest._resolved, depth: 1 }) + .then(files => + files.map(file => relative(manifest._resolved, file)) + ) + .then(files => + tar.c(tarCreateOptions(manifest), files).concat() + ) + +const tarball = (manifest, opts) => { + const resolved = manifest._resolved + const where = opts.where || process.cwd() + + const fromNodeModules = npa(resolved).type === 'directory' + && /node_modules[\\/](@[^\\/]+\/)?[^\\/]+[\\/]?$/.test(relative(where, resolved)) + + if (fromNodeModules) + return nodeModulesTarball(manifest, opts) + + return pacote.tarball(manifest._resolved, opts) +} + +module.exports = tarball diff --git a/node_modules/libnpmdiff/package.json b/node_modules/libnpmdiff/package.json index fab4293e9374e..aa13954c63010 100644 --- a/node_modules/libnpmdiff/package.json +++ b/node_modules/libnpmdiff/package.json @@ -1,6 +1,6 @@ { "name": "libnpmdiff", - "version": "2.0.3", + "version": "2.0.4", "description": "The registry diff", "repository": "https://github.com/npm/libnpmdiff", "files": [ @@ -55,10 +55,12 @@ }, "dependencies": { "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", "binary-extensions": "^2.2.0", "diff": "^5.0.0", "minimatch": "^3.0.4", - "pacote": "^11.2.3", + "npm-package-arg": "^8.1.1", + "pacote": "^11.3.0", "tar": "^6.1.0" } } diff --git a/node_modules/pacote/README.md b/node_modules/pacote/README.md index 619e0ec44e8f6..2328c0a4a55dc 100644 --- a/node_modules/pacote/README.md +++ b/node_modules/pacote/README.md @@ -168,6 +168,18 @@ resolved, and other properties, as they are determined. times (even just to validate the cache) for a given packument, since it is unlikely to change in the span of a single command. + +### Advanced API + +Each different type of fetcher is exposed for more advanced usage such as +using helper methods from this classes: + +* `DirFetcher` +* `FileFetcher` +* `GitFetcher` +* `RegistryFetcher` +* `RemoteFetcher` + ## Extracted File Modes Files are extracted with a mode matching the following formula: diff --git a/node_modules/pacote/lib/dir.js b/node_modules/pacote/lib/dir.js index 4a89348b9290c..0d3a00d95ae7c 100644 --- a/node_modules/pacote/lib/dir.js +++ b/node_modules/pacote/lib/dir.js @@ -4,11 +4,10 @@ const cacache = require('cacache') const Minipass = require('minipass') const { promisify } = require('util') const readPackageJson = require('read-package-json-fast') -const isPackageBin = require('./util/is-package-bin.js') +const tarCreateOptions = require('./util/tar-create-options.js') const packlist = require('npm-packlist') const tar = require('tar') const _prepareDir = Symbol('_prepareDir') -const _tarcOpts = Symbol('_tarcOpts') const { resolve } = require('path') const runScript = require('@npmcli/run-script') @@ -21,6 +20,11 @@ class DirFetcher extends Fetcher { this.resolved = this.spec.fetchSpec } + // exposes tarCreateOptions as public API + static tarCreateOptions (manifest) { + return tarCreateOptions(manifest) + } + get types () { return ['directory'] } @@ -65,35 +69,12 @@ class DirFetcher extends Fetcher { // pipe to the stream, and proxy errors the chain. this[_prepareDir]() .then(() => packlist({ path: this.resolved })) - .then(files => tar.c(this[_tarcOpts](), files) + .then(files => tar.c(tarCreateOptions(this.package), files) .on('error', er => stream.emit('error', er)).pipe(stream)) .catch(er => stream.emit('error', er)) return stream } - [_tarcOpts] () { - return { - cwd: this.resolved, - prefix: 'package/', - portable: true, - gzip: true, - - // ensure that package bins are always executable - // Note that npm-packlist is already filtering out - // anything that is not a regular file, ignored by - // .npmignore or package.json "files", etc. - filter: (path, stat) => { - if (isPackageBin(this.package, path)) - stat.mode |= 0o111 - return true - }, - - // Provide a specific date in the 1980s for the benefit of zip, - // which is confounded by files dated at the Unix epoch 0. - mtime: new Date('1985-10-26T08:15:00.000Z'), - } - } - manifest () { if (this.package) return Promise.resolve(this.package) diff --git a/node_modules/pacote/lib/index.js b/node_modules/pacote/lib/index.js index 546ba960baa2e..cbcbd7c92d15f 100644 --- a/node_modules/pacote/lib/index.js +++ b/node_modules/pacote/lib/index.js @@ -1,5 +1,16 @@ const { get } = require('./fetcher.js') +const GitFetcher = require('./git.js') +const RegistryFetcher = require('./registry.js') +const FileFetcher = require('./file.js') +const DirFetcher = require('./dir.js') +const RemoteFetcher = require('./remote.js') + module.exports = { + GitFetcher, + RegistryFetcher, + FileFetcher, + DirFetcher, + RemoteFetcher, resolve: (spec, opts) => get(spec, opts).resolve(), extract: (spec, dest, opts) => get(spec, opts).extract(dest), manifest: (spec, opts) => get(spec, opts).manifest(), diff --git a/node_modules/pacote/lib/util/tar-create-options.js b/node_modules/pacote/lib/util/tar-create-options.js new file mode 100644 index 0000000000000..e8abbe175b262 --- /dev/null +++ b/node_modules/pacote/lib/util/tar-create-options.js @@ -0,0 +1,24 @@ +const isPackageBin = require('./is-package-bin.js') + +const tarCreateOptions = manifest => ({ + cwd: manifest._resolved, + prefix: 'package/', + portable: true, + gzip: true, + + // ensure that package bins are always executable + // Note that npm-packlist is already filtering out + // anything that is not a regular file, ignored by + // .npmignore or package.json "files", etc. + filter: (path, stat) => { + if (isPackageBin(manifest, path)) + stat.mode |= 0o111 + return true + }, + + // Provide a specific date in the 1980s for the benefit of zip, + // which is confounded by files dated at the Unix epoch 0. + mtime: new Date('1985-10-26T08:15:00.000Z'), +}) + +module.exports = tarCreateOptions diff --git a/node_modules/pacote/package.json b/node_modules/pacote/package.json index a1668056f9794..dca67f3e8876a 100644 --- a/node_modules/pacote/package.json +++ b/node_modules/pacote/package.json @@ -1,6 +1,6 @@ { "name": "pacote", - "version": "11.2.7", + "version": "11.3.0", "description": "JavaScript package downloader", "author": "Isaac Z. Schlueter (https://izs.me)", "bin": { diff --git a/package-lock.json b/package-lock.json index 07cd28466e61a..c6b42954628c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -275,7 +275,7 @@ "json-parse-even-better-errors": "^2.3.1", "leven": "^3.1.0", "libnpmaccess": "^4.0.1", - "libnpmdiff": "^2.0.3", + "libnpmdiff": "^2.0.4", "libnpmfund": "^1.0.2", "libnpmhook": "^6.0.1", "libnpmorg": "^2.0.1", @@ -4766,16 +4766,18 @@ } }, "node_modules/libnpmdiff": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/libnpmdiff/-/libnpmdiff-2.0.3.tgz", - "integrity": "sha512-BgVvJCjd+EGY3Ifb3+gWkZwMjn6kYMtruT88XXOrJCWyjnG5aRdFv3lKuJx5JdU5ku08G5LlY8tOZdfRn72m7w==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/libnpmdiff/-/libnpmdiff-2.0.4.tgz", + "integrity": "sha512-q3zWePOJLHwsLEUjZw3Kyu/MJMYfl4tWCg78Vl6QGSfm4aXBUSVzMzjJ6jGiyarsT4d+1NH4B1gxfs62/+y9iQ==", "inBundle": true, "dependencies": { "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", "binary-extensions": "^2.2.0", "diff": "^5.0.0", "minimatch": "^3.0.4", - "pacote": "^11.2.3", + "npm-package-arg": "^8.1.1", + "pacote": "^11.3.0", "tar": "^6.1.0" }, "engines": { @@ -5970,9 +5972,9 @@ } }, "node_modules/pacote": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.2.7.tgz", - "integrity": "sha512-ogxPor11v/rnU9ukwLlI2dPx22q9iob1+yZyqSwerKsOvBMhU9e+SJHtxY4y2N0MRH4/5jGsGiRLsZeJWyM4dQ==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.3.0.tgz", + "integrity": "sha512-cygprcGpEVqvDzpuPMkGVXW/ooc2ibpoosuJ4YHcUXozDs9VJP7Vha+41pYppG2MVNis4t1BB8IygIBh7vVr2Q==", "inBundle": true, "dependencies": { "@npmcli/git": "^2.0.1", @@ -13887,15 +13889,17 @@ } }, "libnpmdiff": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/libnpmdiff/-/libnpmdiff-2.0.3.tgz", - "integrity": "sha512-BgVvJCjd+EGY3Ifb3+gWkZwMjn6kYMtruT88XXOrJCWyjnG5aRdFv3lKuJx5JdU5ku08G5LlY8tOZdfRn72m7w==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/libnpmdiff/-/libnpmdiff-2.0.4.tgz", + "integrity": "sha512-q3zWePOJLHwsLEUjZw3Kyu/MJMYfl4tWCg78Vl6QGSfm4aXBUSVzMzjJ6jGiyarsT4d+1NH4B1gxfs62/+y9iQ==", "requires": { "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", "binary-extensions": "^2.2.0", "diff": "^5.0.0", "minimatch": "^3.0.4", - "pacote": "^11.2.3", + "npm-package-arg": "^8.1.1", + "pacote": "^11.3.0", "tar": "^6.1.0" } }, @@ -14790,9 +14794,9 @@ } }, "pacote": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.2.7.tgz", - "integrity": "sha512-ogxPor11v/rnU9ukwLlI2dPx22q9iob1+yZyqSwerKsOvBMhU9e+SJHtxY4y2N0MRH4/5jGsGiRLsZeJWyM4dQ==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.3.0.tgz", + "integrity": "sha512-cygprcGpEVqvDzpuPMkGVXW/ooc2ibpoosuJ4YHcUXozDs9VJP7Vha+41pYppG2MVNis4t1BB8IygIBh7vVr2Q==", "requires": { "@npmcli/git": "^2.0.1", "@npmcli/installed-package-contents": "^1.0.6", diff --git a/package.json b/package.json index 7669dfa73bbfa..7d8451133d2fa 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "json-parse-even-better-errors": "^2.3.1", "leven": "^3.1.0", "libnpmaccess": "^4.0.1", - "libnpmdiff": "^2.0.3", + "libnpmdiff": "^2.0.4", "libnpmfund": "^1.0.2", "libnpmhook": "^6.0.1", "libnpmorg": "^2.0.1", From a42d217a812389f5fe78ebee028cb0fd552121a0 Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 9 Mar 2021 10:15:11 -0800 Subject: [PATCH 5/8] pacote@11.3.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c6b42954628c6..ac7fb1c5c2656 100644 --- a/package-lock.json +++ b/package-lock.json @@ -300,7 +300,7 @@ "npm-user-validate": "^1.0.1", "npmlog": "~4.1.2", "opener": "^1.5.2", - "pacote": "^11.2.7", + "pacote": "^11.3.0", "parse-conflict-json": "^1.1.1", "qrcode-terminal": "^0.12.0", "read": "~1.0.7", diff --git a/package.json b/package.json index 7d8451133d2fa..940cc1922c14a 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "npm-user-validate": "^1.0.1", "npmlog": "~4.1.2", "opener": "^1.5.2", - "pacote": "^11.2.7", + "pacote": "^11.3.0", "parse-conflict-json": "^1.1.1", "qrcode-terminal": "^0.12.0", "read": "~1.0.7", From 3b6710c3cfea51cb126877276cd5eaba2845ae33 Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 9 Mar 2021 10:17:22 -0800 Subject: [PATCH 6/8] update deps --- node_modules/.gitignore | 1 + package-lock.json | 199 ++++++++++++++++++++++------------------ 2 files changed, 110 insertions(+), 90 deletions(-) diff --git a/node_modules/.gitignore b/node_modules/.gitignore index df322e6a05cb4..6ffd61bf29c27 100644 --- a/node_modules/.gitignore +++ b/node_modules/.gitignore @@ -353,6 +353,7 @@ package-lock.json /unist-util-stringify-position /unist-util-visit /unist-util-visit-parents +/universalify /util-promisify /v8-compile-cache /vfile diff --git a/package-lock.json b/package-lock.json index ac7fb1c5c2656..a12e8e5cf1397 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2546,13 +2546,13 @@ } }, "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "dependencies": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", + "estraverse": "^5.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1" }, @@ -2561,12 +2561,21 @@ "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=4.0" + "node": ">=6.0" }, "optionalDependencies": { "source-map": "~0.6.1" } }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/escodegen/node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -3717,12 +3726,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-raw/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, "node_modules/hast-util-to-parse5": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", @@ -4564,36 +4567,36 @@ "inBundle": true }, "node_modules/jsdom": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", - "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.0.tgz", + "integrity": "sha512-QxZH0nmDTnTTVI0YDm4RUlaUPl5dcyn62G5TMDNfMmTW+J1u1v9gCR8WR+WZ6UghAa7nKJjDOFaI00eMMWvJFQ==", "dev": true, "dependencies": { - "abab": "^2.0.3", - "acorn": "^7.1.1", + "abab": "^2.0.5", + "acorn": "^8.0.5", "acorn-globals": "^6.0.0", "cssom": "^0.4.4", - "cssstyle": "^2.2.0", + "cssstyle": "^2.3.0", "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", + "decimal.js": "^10.2.1", "domexception": "^2.0.1", - "escodegen": "^1.14.1", + "escodegen": "^2.0.0", "html-encoding-sniffer": "^2.0.1", "is-potential-custom-element-name": "^1.0.0", "nwsapi": "^2.2.0", - "parse5": "5.1.1", + "parse5": "6.0.1", "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", + "request-promise-native": "^1.0.9", + "saxes": "^5.0.1", "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", + "tough-cookie": "^4.0.0", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.0.0", - "ws": "^7.2.3", + "ws": "^7.4.4", "xml-name-validator": "^3.0.0" }, "engines": { @@ -4608,6 +4611,18 @@ } } }, + "node_modules/jsdom/node_modules/acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -6058,9 +6073,9 @@ } }, "node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, "node_modules/path-exists": { @@ -9591,28 +9606,19 @@ } }, "node_modules/tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", "dev": true, "dependencies": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" }, "engines": { "node": ">=6" } }, - "node_modules/tough-cookie/node_modules/ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/tr46": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", @@ -9938,6 +9944,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -10255,9 +10270,9 @@ } }, "node_modules/ws": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz", - "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==", "dev": true, "engines": { "node": ">=8.3.0" @@ -12243,18 +12258,24 @@ "dev": true }, "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "requires": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", + "estraverse": "^5.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" }, "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -13136,14 +13157,6 @@ "web-namespaces": "^1.0.0", "xtend": "^4.0.0", "zwitch": "^1.0.0" - }, - "dependencies": { - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - } } }, "hast-util-to-parse5": { @@ -13739,37 +13752,45 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdom": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", - "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.0.tgz", + "integrity": "sha512-QxZH0nmDTnTTVI0YDm4RUlaUPl5dcyn62G5TMDNfMmTW+J1u1v9gCR8WR+WZ6UghAa7nKJjDOFaI00eMMWvJFQ==", "dev": true, "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", + "abab": "^2.0.5", + "acorn": "^8.0.5", "acorn-globals": "^6.0.0", "cssom": "^0.4.4", - "cssstyle": "^2.2.0", + "cssstyle": "^2.3.0", "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", + "decimal.js": "^10.2.1", "domexception": "^2.0.1", - "escodegen": "^1.14.1", + "escodegen": "^2.0.0", "html-encoding-sniffer": "^2.0.1", "is-potential-custom-element-name": "^1.0.0", "nwsapi": "^2.2.0", - "parse5": "5.1.1", + "parse5": "6.0.1", "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", + "request-promise-native": "^1.0.9", + "saxes": "^5.0.1", "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", + "tough-cookie": "^4.0.0", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.0.0", - "ws": "^7.2.3", + "ws": "^7.4.4", "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", + "dev": true + } } }, "jsesc": { @@ -14862,9 +14883,9 @@ } }, "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, "path-exists": { @@ -17409,22 +17430,14 @@ } }, "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", "dev": true, "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "dependencies": { - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - } + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" } }, "tr46": { @@ -17662,6 +17675,12 @@ "unist-util-is": "^4.0.0" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -17919,9 +17938,9 @@ } }, "ws": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz", - "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==", "dev": true, "requires": {} }, From eb9dac363db57b983ebf82f9d93a2d684519a59e Mon Sep 17 00:00:00 2001 From: Gar Date: Sun, 28 Feb 2021 19:00:58 -0800 Subject: [PATCH 7/8] fix(npm.output): make output go through npm.output All output that anything wants to make now goes through `npm.output()`. This is an incremental change getting us closer to where we want to be with testing. PR-URL: https://github.com/npm/cli/pull/2795 Credit: @wraithgar Close: #2795 Reviewed-by: @ruyadorno, @isaacs --- lib/access.js | 5 +- lib/adduser.js | 3 +- lib/audit.js | 3 +- lib/bin.js | 3 +- lib/cache.js | 15 +- lib/completion.js | 49 +++-- lib/config.js | 7 +- lib/diff.js | 3 +- lib/dist-tag.js | 7 +- lib/doctor.js | 3 +- lib/exec.js | 3 +- lib/explain.js | 5 +- lib/explore.js | 3 +- lib/fund.js | 7 +- lib/help-search.js | 5 +- lib/help.js | 3 +- lib/hook.js | 39 ++-- lib/init.js | 3 +- lib/ls.js | 3 +- lib/npm.js | 7 + lib/org.js | 25 ++- lib/outdated.js | 7 +- lib/owner.js | 194 +++++++++--------- lib/pack.js | 3 +- lib/ping.js | 3 +- lib/prefix.js | 3 +- lib/profile.js | 37 ++-- lib/publish.js | 5 +- lib/rebuild.js | 3 +- lib/root.js | 3 +- lib/run-script.js | 15 +- lib/search.js | 5 +- lib/star.js | 3 +- lib/stars.js | 3 +- lib/team.js | 41 ++-- lib/token.js | 21 +- lib/unpublish.js | 3 +- lib/utils/audit-error.js | 5 +- lib/utils/npm-usage.js | 5 +- lib/utils/open-url.js | 3 +- lib/utils/output.js | 7 - lib/utils/reify-output.js | 17 +- lib/version.js | 7 +- lib/whoami.js | 3 +- tap-snapshots/test-lib-profile.js-TAP.test.js | 18 +- test/lib/access.js | 16 +- test/lib/adduser.js | 6 +- test/lib/audit.js | 14 +- test/lib/bin.js | 35 ++-- test/lib/cache.js | 11 +- test/lib/completion.js | 6 +- test/lib/config.js | 6 +- test/lib/diff.js | 2 +- test/lib/dist-tag.js | 6 +- test/lib/doctor.js | 12 +- test/lib/exec.js | 2 +- test/lib/explain.js | 6 +- test/lib/explore.js | 6 +- test/lib/fund.js | 6 +- test/lib/help-search.js | 2 +- test/lib/help.js | 10 +- test/lib/hook.js | 8 +- test/lib/init.js | 6 +- test/lib/ls.js | 13 +- test/lib/org.js | 9 +- test/lib/outdated.js | 19 +- test/lib/owner.js | 10 +- test/lib/pack.js | 8 +- test/lib/ping.js | 16 +- test/lib/prefix.js | 12 +- test/lib/profile.js | 11 +- test/lib/publish.js | 21 +- test/lib/rebuild.js | 9 +- test/lib/root.js | 12 +- test/lib/run-script.js | 2 +- test/lib/search.js | 10 +- test/lib/star.js | 11 +- test/lib/stars.js | 11 +- test/lib/team.js | 10 +- test/lib/token.js | 13 +- test/lib/unpublish.js | 6 +- test/lib/utils/audit-error.js | 12 +- test/lib/utils/npm-usage.js | 6 +- test/lib/utils/open-url.js | 7 +- test/lib/utils/output.js | 8 - test/lib/utils/reify-output.js | 92 ++++----- test/lib/version.js | 11 +- test/lib/whoami.js | 12 +- 88 files changed, 541 insertions(+), 585 deletions(-) delete mode 100644 lib/utils/output.js delete mode 100644 test/lib/utils/output.js diff --git a/lib/access.js b/lib/access.js index e11934af43ebc..3bc21119033f5 100644 --- a/lib/access.js +++ b/lib/access.js @@ -3,7 +3,6 @@ const path = require('path') const libaccess = require('libnpmaccess') const readPackageJson = require('read-package-json-fast') -const output = require('./utils/output.js') const otplease = require('./utils/otplease.js') const usageUtil = require('./utils/usage.js') const getIdentity = require('./utils/get-identity.js') @@ -157,7 +156,7 @@ class Access { const pkgs = await libaccess.lsPackages(owner, opts) // TODO - print these out nicely (breaking change) - output(JSON.stringify(pkgs, null, 2)) + this.npm.output(JSON.stringify(pkgs, null, 2)) } get ['ls-collaborators'] () { @@ -169,7 +168,7 @@ class Access { const collabs = await libaccess.lsCollaborators(pkgName, usr, opts) // TODO - print these out nicely (breaking change) - output(JSON.stringify(collabs, null, 2)) + this.npm.output(JSON.stringify(collabs, null, 2)) } async edit () { diff --git a/lib/adduser.js b/lib/adduser.js index dac0f5a46840d..45d602fd2c844 100644 --- a/lib/adduser.js +++ b/lib/adduser.js @@ -1,5 +1,4 @@ const log = require('npmlog') -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') const replaceInfo = require('./utils/replace-info.js') const authTypes = { @@ -49,7 +48,7 @@ class AddUser { scope, }) - output(message) + this.npm.output(message) } getRegistry ({ scope, registry }) { diff --git a/lib/audit.js b/lib/audit.js index dfa01cb2709fa..b8c85605dba43 100644 --- a/lib/audit.js +++ b/lib/audit.js @@ -1,6 +1,5 @@ const Arborist = require('@npmcli/arborist') const auditReport = require('npm-audit-report') -const output = require('./utils/output.js') const reifyFinish = require('./utils/reify-finish.js') const auditError = require('./utils/audit-error.js') const usageUtil = require('./utils/usage.js') @@ -57,7 +56,7 @@ class Audit { reporter, }) process.exitCode = process.exitCode || result.exitCode - output(result.report) + this.npm.output(result.report) } } } diff --git a/lib/bin.js b/lib/bin.js index 11490c41cbcc5..f540cc57c8cd7 100644 --- a/lib/bin.js +++ b/lib/bin.js @@ -1,4 +1,3 @@ -const output = require('./utils/output.js') const envPath = require('./utils/path.js') const usageUtil = require('./utils/usage.js') @@ -18,7 +17,7 @@ class Bin { async bin (args) { const b = this.npm.bin - output(b) + this.npm.output(b) if (this.npm.flatOptions.global && !envPath.includes(b)) console.error('(not in PATH env variable)') } diff --git a/lib/cache.js b/lib/cache.js index 8469559764fb3..3ca99fd2562e3 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -1,7 +1,6 @@ const cacache = require('cacache') const { promisify } = require('util') const log = require('npmlog') -const output = require('./utils/output.js') const pacote = require('pacote') const path = require('path') const rimraf = promisify(require('rimraf')) @@ -116,13 +115,13 @@ with --force.`) ? `~${cache.substr(process.env.HOME.length)}` : cache const stats = await cacache.verify(cache) - output(`Cache verified and compressed (${prefix})`) - output(`Content verified: ${stats.verifiedContent} (${stats.keptSize} bytes)`) - stats.badContentCount && output(`Corrupted content removed: ${stats.badContentCount}`) - stats.reclaimedCount && output(`Content garbage-collected: ${stats.reclaimedCount} (${stats.reclaimedSize} bytes)`) - stats.missingContent && output(`Missing content: ${stats.missingContent}`) - output(`Index entries: ${stats.totalEntries}`) - output(`Finished in ${stats.runTime.total / 1000}s`) + this.npm.output(`Cache verified and compressed (${prefix})`) + this.npm.output(`Content verified: ${stats.verifiedContent} (${stats.keptSize} bytes)`) + stats.badContentCount && this.npm.output(`Corrupted content removed: ${stats.badContentCount}`) + stats.reclaimedCount && this.npm.output(`Content garbage-collected: ${stats.reclaimedCount} (${stats.reclaimedSize} bytes)`) + stats.missingContent && this.npm.output(`Missing content: ${stats.missingContent}`) + this.npm.output(`Index entries: ${stats.totalEntries}`) + this.npm.output(`Finished in ${stats.runTime.total / 1000}s`) } } diff --git a/lib/completion.js b/lib/completion.js index 4c37e6ef354ef..5baf17665800d 100644 --- a/lib/completion.js +++ b/lib/completion.js @@ -39,7 +39,6 @@ const configNames = Object.keys(types) const shorthandNames = Object.keys(shorthands) const allConfs = configNames.concat(shorthandNames) const isWindowsShell = require('./utils/is-windows-shell.js') -const output = require('./utils/output.js') const fileExists = require('./utils/file-exists.js') const usageUtil = require('./utils/usage.js') @@ -131,14 +130,14 @@ class Completion { if (partialWords.slice(0, -1).indexOf('--') === -1) { if (word.charAt(0) === '-') - return wrap(opts, configCompl(opts)) + return this.wrap(opts, configCompl(opts)) if (words[w - 1] && words[w - 1].charAt(0) === '-' && !isFlag(words[w - 1])) { // awaiting a value for a non-bool config. // don't even try to do this for now - return wrap(opts, configValueCompl(opts)) + return this.wrap(opts, configValueCompl(opts)) } } @@ -152,7 +151,7 @@ class Completion { // check if there's a command already. const cmd = parsed.argv.remain[1] if (!cmd) - return wrap(opts, cmdCompl(opts)) + return this.wrap(opts, cmdCompl(opts)) Object.keys(parsed).forEach(k => this.npm.config.set(k, parsed[k])) @@ -162,9 +161,29 @@ class Completion { const impl = this.npm.commands[cmd] if (impl && impl.completion) { const comps = await impl.completion(opts) - return wrap(opts, comps) + return this.wrap(opts, comps) } } + + // The command should respond with an array. Loop over that, + // wrapping quotes around any that have spaces, and writing + // them to stdout. + // If any of the items are arrays, then join them with a space. + // Ie, returning ['a', 'b c', ['d', 'e']] would allow it to expand + // to: 'a', 'b c', or 'd' 'e' + wrap (opts, compls) { + if (!Array.isArray(compls)) + compls = compls ? [compls] : [] + + compls = compls.map(c => + Array.isArray(c) ? c.map(escape).join(' ') : escape(c)) + + if (opts.partialWord) + compls = compls.filter(c => c.startsWith(opts.partialWord)) + + if (compls.length > 0) + this.npm.output(compls.join('\n')) + } } const dumpScript = async () => { @@ -214,26 +233,6 @@ const unescape = w => w.charAt(0) === '\'' ? w.replace(/^'|'$/g, '') const escape = w => !/\s+/.test(w) ? w : '\'' + w + '\'' -// The command should respond with an array. Loop over that, -// wrapping quotes around any that have spaces, and writing -// them to stdout. -// If any of the items are arrays, then join them with a space. -// Ie, returning ['a', 'b c', ['d', 'e']] would allow it to expand -// to: 'a', 'b c', or 'd' 'e' -const wrap = (opts, compls) => { - if (!Array.isArray(compls)) - compls = compls ? [compls] : [] - - compls = compls.map(c => - Array.isArray(c) ? c.map(escape).join(' ') : escape(c)) - - if (opts.partialWord) - compls = compls.filter(c => c.startsWith(opts.partialWord)) - - if (compls.length > 0) - output(compls.join('\n')) -} - // the current word has a dash. Return the config names, // with the same number of dashes as the current word has. const configCompl = opts => { diff --git a/lib/config.js b/lib/config.js index 2805db9b80ec7..7009f46016d23 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,6 +1,5 @@ const { defaults, types } = require('./utils/config.js') const usageUtil = require('./utils/usage.js') -const output = require('./utils/output.js') const mkdirp = require('mkdirp-infer-owner') const { dirname } = require('path') @@ -142,7 +141,7 @@ class Config { const pref = keys.length > 1 ? `${key}=` : '' out.push(pref + this.npm.config.get(key)) } - output(out.join('\n')) + this.npm.output(out.join('\n')) } async del (keys) { @@ -241,7 +240,7 @@ ${defData} ) } - output(msg.join('\n').trim()) + this.npm.output(msg.join('\n').trim()) } async listJson () { @@ -252,7 +251,7 @@ ${defData} publicConf[key] = this.npm.config.get(key) } - output(JSON.stringify(publicConf, null, 2)) + this.npm.output(JSON.stringify(publicConf, null, 2)) } usageError () { diff --git a/lib/diff.js b/lib/diff.js index 859e6f76feeef..ed36a30673c43 100644 --- a/lib/diff.js +++ b/lib/diff.js @@ -9,7 +9,6 @@ const pacote = require('pacote') const pickManifest = require('npm-pick-manifest') const usageUtil = require('./utils/usage.js') -const output = require('./utils/output.js') const readLocalPkg = require('./utils/read-local-package.js') class Diff { @@ -55,7 +54,7 @@ class Diff { diffFiles: args, where: this.where, }) - return output(res) + return this.npm.output(res) } async retrieveSpecs ([a, b]) { diff --git a/lib/dist-tag.js b/lib/dist-tag.js index 171a88c527e5d..4b7e2602043be 100644 --- a/lib/dist-tag.js +++ b/lib/dist-tag.js @@ -3,7 +3,6 @@ const npa = require('npm-package-arg') const regFetch = require('npm-registry-fetch') const semver = require('semver') -const output = require('./utils/output.js') const otplease = require('./utils/otplease.js') const readLocalPkgName = require('./utils/read-local-package.js') const usageUtil = require('./utils/usage.js') @@ -91,7 +90,7 @@ class DistTag { spec, } await otplease(reqOpts, reqOpts => regFetch(url, reqOpts)) - output(`+${t}: ${spec.name}@${version}`) + this.npm.output(`+${t}: ${spec.name}@${version}`) } async remove (spec, tag, opts) { @@ -116,7 +115,7 @@ class DistTag { spec, } await otplease(reqOpts, reqOpts => regFetch(url, reqOpts)) - output(`-${tag}: ${spec.name}@${version}`) + this.npm.output(`-${tag}: ${spec.name}@${version}`) } async list (spec, opts) { @@ -133,7 +132,7 @@ class DistTag { const tags = await this.fetchTags(spec, opts) const msg = Object.keys(tags).map(k => `${k}: ${tags[k]}`).sort().join('\n') - output(msg) + this.npm.output(msg) return tags } catch (err) { log.error('dist-tag ls', "Couldn't get dist-tag data for", spec) diff --git a/lib/doctor.js b/lib/doctor.js index 81860004e344e..63619d0cf5377 100644 --- a/lib/doctor.js +++ b/lib/doctor.js @@ -10,7 +10,6 @@ const semver = require('semver') const { promisify } = require('util') const ansiTrim = require('./utils/ansi-trim.js') const isWindows = require('./utils/is-windows.js') -const output = require('./utils/output.js') const ping = require('./utils/ping.js') const usageUtil = require('./utils/usage.js') const { defaults: { registry: defaultRegistry } } = require('./utils/config.js') @@ -111,7 +110,7 @@ class Doctor { const silent = this.npm.log.levels[this.npm.log.level] > this.npm.log.levels.error if (!silent) { - output(table(outTable, tableOpts)) + this.npm.output(table(outTable, tableOpts)) if (!allOk) console.error('') } diff --git a/lib/exec.js b/lib/exec.js index d1db49128587e..69c3cfe75c2cc 100644 --- a/lib/exec.js +++ b/lib/exec.js @@ -1,4 +1,3 @@ -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') const { promisify } = require('util') const read = promisify(require('read')) @@ -224,7 +223,7 @@ class Exec { if (process.stdin.isTTY) { if (ciDetect()) return this.npm.log.warn('exec', 'Interactive mode disabled in CI environment') - output(`\nEntering npm script environment\nType 'exit' or ^D when finished\n`) + this.npm.output(`\nEntering npm script environment\nType 'exit' or ^D when finished\n`) } } return await runScript({ diff --git a/lib/explain.js b/lib/explain.js index 01541040ef649..f46d3b5072637 100644 --- a/lib/explain.js +++ b/lib/explain.js @@ -1,7 +1,6 @@ const usageUtil = require('./utils/usage.js') const { explainNode } = require('./utils/explain-dep.js') const completion = require('./utils/completion/installed-deep.js') -const output = require('./utils/output.js') const Arborist = require('@npmcli/arborist') const npa = require('npm-package-arg') const semver = require('semver') @@ -59,9 +58,9 @@ class Explain { } if (this.npm.flatOptions.json) - output(JSON.stringify(expls, null, 2)) + this.npm.output(JSON.stringify(expls, null, 2)) else { - output(expls.map(expl => { + this.npm.output(expls.map(expl => { return explainNode(expl, Infinity, this.npm.color) }).join('\n\n')) } diff --git a/lib/explore.js b/lib/explore.js index fdfe6e1bcf7c8..e09e867406e1d 100644 --- a/lib/explore.js +++ b/lib/explore.js @@ -5,7 +5,6 @@ const rpj = require('read-package-json-fast') const runScript = require('@npmcli/run-script') const { join, resolve, relative } = require('path') const completion = require('./utils/completion/installed-shallow.js') -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') class Explore { @@ -54,7 +53,7 @@ class Explore { } if (!args.length) - output(`\nExploring ${path}\nType 'exit' or ^D when finished\n`) + this.npm.output(`\nExploring ${path}\nType 'exit' or ^D when finished\n`) this.npm.log.disableProgress() try { return await runScript({ diff --git a/lib/fund.js b/lib/fund.js index 1e9724266401f..826c3170e7e8a 100644 --- a/lib/fund.js +++ b/lib/fund.js @@ -12,7 +12,6 @@ const { } = require('libnpmfund') const completion = require('./utils/completion/installed-deep.js') -const output = require('./utils/output.js') const openUrl = require('./utils/open-url.js') const usageUtil = require('./utils/usage.js') @@ -85,7 +84,7 @@ class Fund { ? this.printJSON : this.printHuman - output( + this.npm.output( print( getFundingInfo(tree), opts @@ -206,9 +205,9 @@ class Fund { validSources.forEach(({ type, url }, i) => { const typePrefix = type ? `${type} funding` : 'Funding' const msg = `${typePrefix} available at the following URL` - output(`${i + 1}: ${msg}: ${url}`) + this.npm.output(`${i + 1}: ${msg}: ${url}`) }) - output('Run `npm fund [<@scope>/] --which=1`, for example, to open the first funding URL listed in that package') + this.npm.output('Run `npm fund [<@scope>/] --which=1`, for example, to open the first funding URL listed in that package') } else { const noFundingError = new Error(`No valid funding method available for: ${spec}`) noFundingError.code = 'ENOFUND' diff --git a/lib/help-search.js b/lib/help-search.js index ed2bc23b9109d..9648e3b1478d5 100644 --- a/lib/help-search.js +++ b/lib/help-search.js @@ -1,7 +1,6 @@ const fs = require('fs') const path = require('path') const color = require('ansicolors') -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') const npmUsage = require('./utils/npm-usage.js') const { promisify } = require('util') @@ -44,8 +43,8 @@ class HelpSearch { if (!formatted.trim()) npmUsage(this.npm, false) else { - output(formatted) - output(didYouMean(args[0], cmdList)) + this.npm.output(formatted) + this.npm.output(didYouMean(args[0], cmdList)) } } diff --git a/lib/help.js b/lib/help.js index d7897326f3118..ef7e3bfd03214 100644 --- a/lib/help.js +++ b/lib/help.js @@ -4,7 +4,6 @@ const path = require('path') const log = require('npmlog') const openUrl = require('./utils/open-url.js') const glob = require('glob') -const output = require('./utils/output.js') const usage = require('./utils/usage.js') @@ -70,7 +69,7 @@ class Help { this.npm.commands[section].usage) { this.npm.config.set('loglevel', 'silent') log.level = 'silent' - output(this.npm.commands[section].usage) + this.npm.output(this.npm.commands[section].usage) return } diff --git a/lib/hook.js b/lib/hook.js index 312f542d7cff6..a6f04d6532e50 100644 --- a/lib/hook.js +++ b/lib/hook.js @@ -1,5 +1,4 @@ const hookApi = require('libnpmhook') -const output = require('./utils/output.js') const otplease = require('./utils/otplease.js') const relativeDate = require('tiny-relative-date') const Table = require('cli-table3') @@ -44,12 +43,12 @@ class Hook { async add (pkg, uri, secret, opts) { const hook = await hookApi.add(pkg, uri, secret, opts) if (opts.json) - output(JSON.stringify(hook, null, 2)) + this.npm.output(JSON.stringify(hook, null, 2)) else if (opts.parseable) { - output(Object.keys(hook).join('\t')) - output(Object.keys(hook).map(k => hook[k]).join('\t')) + this.npm.output(Object.keys(hook).join('\t')) + this.npm.output(Object.keys(hook).map(k => hook[k]).join('\t')) } else if (!opts.silent && opts.loglevel !== 'silent') { - output(`+ ${this.hookName(hook)} ${ + this.npm.output(`+ ${this.hookName(hook)} ${ opts.unicode ? ' ➜ ' : ' -> ' } ${hook.endpoint}`) } @@ -58,19 +57,19 @@ class Hook { async ls (pkg, opts) { const hooks = await hookApi.ls({ ...opts, package: pkg }) if (opts.json) - output(JSON.stringify(hooks, null, 2)) + this.npm.output(JSON.stringify(hooks, null, 2)) else if (opts.parseable) { - output(Object.keys(hooks[0]).join('\t')) + this.npm.output(Object.keys(hooks[0]).join('\t')) hooks.forEach(hook => { - output(Object.keys(hook).map(k => hook[k]).join('\t')) + this.npm.output(Object.keys(hook).map(k => hook[k]).join('\t')) }) } else if (!hooks.length) - output("You don't have any hooks configured yet.") + this.npm.output("You don't have any hooks configured yet.") else if (!opts.silent && opts.loglevel !== 'silent') { if (hooks.length === 1) - output('You have one hook configured.') + this.npm.output('You have one hook configured.') else - output(`You have ${hooks.length} hooks configured.`) + this.npm.output(`You have ${hooks.length} hooks configured.`) const table = new Table({ head: ['id', 'target', 'endpoint'] }) hooks.forEach((hook) => { @@ -90,19 +89,19 @@ class Hook { } else table.push([{ colSpan: 2, content: 'never triggered' }]) }) - output(table.toString()) + this.npm.output(table.toString()) } } async rm (id, opts) { const hook = await hookApi.rm(id, opts) if (opts.json) - output(JSON.stringify(hook, null, 2)) + this.npm.output(JSON.stringify(hook, null, 2)) else if (opts.parseable) { - output(Object.keys(hook).join('\t')) - output(Object.keys(hook).map(k => hook[k]).join('\t')) + this.npm.output(Object.keys(hook).join('\t')) + this.npm.output(Object.keys(hook).map(k => hook[k]).join('\t')) } else if (!opts.silent && opts.loglevel !== 'silent') { - output(`- ${this.hookName(hook)} ${ + this.npm.output(`- ${this.hookName(hook)} ${ opts.unicode ? ' ✘ ' : ' X ' } ${hook.endpoint}`) } @@ -111,12 +110,12 @@ class Hook { async update (id, uri, secret, opts) { const hook = await hookApi.update(id, uri, secret, opts) if (opts.json) - output(JSON.stringify(hook, null, 2)) + this.npm.output(JSON.stringify(hook, null, 2)) else if (opts.parseable) { - output(Object.keys(hook).join('\t')) - output(Object.keys(hook).map(k => hook[k]).join('\t')) + this.npm.output(Object.keys(hook).join('\t')) + this.npm.output(Object.keys(hook).map(k => hook[k]).join('\t')) } else if (!opts.silent && opts.loglevel !== 'silent') { - output(`+ ${this.hookName(hook)} ${ + this.npm.output(`+ ${this.hookName(hook)} ${ opts.unicode ? ' ➜ ' : ' -> ' } ${hook.endpoint}`) } diff --git a/lib/init.js b/lib/init.js index af97a9614e368..3f9abbcdd354d 100644 --- a/lib/init.js +++ b/lib/init.js @@ -2,7 +2,6 @@ const initJson = require('init-package-json') const npa = require('npm-package-arg') const usageUtil = require('./utils/usage.js') -const output = require('./utils/output.js') class Init { constructor (npm) { @@ -61,7 +60,7 @@ class Init { this.npm.log.disableProgress() const initFile = this.npm.config.get('init-module') if (!this.npm.flatOptions.yes && !this.npm.flatOptions.force) { - output([ + this.npm.output([ 'This utility will walk you through creating a package.json file.', 'It only covers the most common items, and tries to guess sensible defaults.', '', diff --git a/lib/ls.js b/lib/ls.js index 359fe21e6f8cc..b94684401e6f6 100644 --- a/lib/ls.js +++ b/lib/ls.js @@ -9,7 +9,6 @@ const npa = require('npm-package-arg') const usageUtil = require('./utils/usage.js') const completion = require('./utils/completion/installed-deep.js') -const output = require('./utils/output.js') const _depth = Symbol('depth') const _dedupe = Symbol('dedupe') @@ -147,7 +146,7 @@ class LS { const [rootError] = tree.errors.filter(e => e.code === 'EJSONPARSE' && e.path === resolve(path, 'package.json')) - output( + this.npm.output( json ? jsonOutput({ path, problems, result, rootError, seenItems }) : parseable diff --git a/lib/npm.js b/lib/npm.js index 1f8c785e755c4..0534e630606e4 100644 --- a/lib/npm.js +++ b/lib/npm.js @@ -282,6 +282,13 @@ const npm = module.exports = new class extends EventEmitter { } return resolve(this.config.get('tmp'), this[_tmpFolder]) } + + // output to stdout in a progress bar compatible way + output (...msg) { + this.log.clearProgress() + console.log(...msg) + this.log.showProgress() + } }() // now load everything required by the class methods diff --git a/lib/org.js b/lib/org.js index 054e1833dba4b..2a08941a83f3a 100644 --- a/lib/org.js +++ b/lib/org.js @@ -1,6 +1,5 @@ const liborg = require('libnpmorg') const usageUtil = require('./utils/usage.js') -const output = require('./utils/output.js') const otplease = require('./utils/otplease.js') const Table = require('cli-table3') @@ -72,17 +71,17 @@ class Org { return liborg.set(org, user, role, opts).then(memDeets => { if (opts.json) - output(JSON.stringify(memDeets, null, 2)) + this.npm.output(JSON.stringify(memDeets, null, 2)) else if (opts.parseable) { - output(['org', 'orgsize', 'user', 'role'].join('\t')) - output([ + this.npm.output(['org', 'orgsize', 'user', 'role'].join('\t')) + this.npm.output([ memDeets.org.name, memDeets.org.size, memDeets.user, memDeets.role, ].join('\t')) } else if (!opts.silent && opts.loglevel !== 'silent') - output(`Added ${memDeets.user} as ${memDeets.role} to ${memDeets.org.name}. You now have ${memDeets.org.size} member${memDeets.org.size === 1 ? '' : 's'} in this org.`) + this.npm.output(`Added ${memDeets.user} as ${memDeets.role} to ${memDeets.org.name}. You now have ${memDeets.org.size} member${memDeets.org.size === 1 ? '' : 's'} in this org.`) return memDeets }) @@ -102,17 +101,17 @@ class Org { org = org.replace(/^[~@]?/, '') const userCount = Object.keys(roster).length if (opts.json) { - output(JSON.stringify({ + this.npm.output(JSON.stringify({ user, org, userCount, deleted: true, })) } else if (opts.parseable) { - output(['user', 'org', 'userCount', 'deleted'].join('\t')) - output([user, org, userCount, true].join('\t')) + this.npm.output(['user', 'org', 'userCount', 'deleted'].join('\t')) + this.npm.output([user, org, userCount, true].join('\t')) } else if (!opts.silent && opts.loglevel !== 'silent') - output(`Successfully removed ${user} from ${org}. You now have ${userCount} member${userCount === 1 ? '' : 's'} in this org.`) + this.npm.output(`Successfully removed ${user} from ${org}. You now have ${userCount} member${userCount === 1 ? '' : 's'} in this org.`) }) } @@ -129,18 +128,18 @@ class Org { roster = newRoster } if (opts.json) - output(JSON.stringify(roster, null, 2)) + this.npm.output(JSON.stringify(roster, null, 2)) else if (opts.parseable) { - output(['user', 'role'].join('\t')) + this.npm.output(['user', 'role'].join('\t')) Object.keys(roster).forEach(user => { - output([user, roster[user]].join('\t')) + this.npm.output([user, roster[user]].join('\t')) }) } else if (!opts.silent && opts.loglevel !== 'silent') { const table = new Table({ head: ['user', 'role'] }) Object.keys(roster).sort().forEach(user => { table.push([user, roster[user]]) }) - output(table.toString()) + this.npm.output(table.toString()) } }) } diff --git a/lib/outdated.js b/lib/outdated.js index fc6967faf60fe..be5820870411c 100644 --- a/lib/outdated.js +++ b/lib/outdated.js @@ -9,7 +9,6 @@ const pickManifest = require('npm-pick-manifest') const Arborist = require('@npmcli/arborist') -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') const ansiTrim = require('./utils/ansi-trim.js') @@ -75,9 +74,9 @@ class Outdated { // display results if (this.opts.json) - output(this.makeJSON(outdated)) + this.npm.output(this.makeJSON(outdated)) else if (this.opts.parseable) - output(this.makeParseable(outdated)) + this.npm.output(this.makeParseable(outdated)) else { const outList = outdated.map(x => this.makePretty(x)) const outHead = ['Package', @@ -99,7 +98,7 @@ class Outdated { align: ['l', 'r', 'r', 'r', 'l'], stringLength: s => ansiTrim(s).length, } - output(table(outTable, tableOpts)) + this.npm.output(table(outTable, tableOpts)) } } diff --git a/lib/owner.js b/lib/owner.js index 6cb9904880dc2..cd387e94d9eaf 100644 --- a/lib/owner.js +++ b/lib/owner.js @@ -3,7 +3,6 @@ const npa = require('npm-package-arg') const npmFetch = require('npm-registry-fetch') const pacote = require('pacote') -const output = require('./utils/output.js') const otplease = require('./utils/otplease.js') const readLocalPkg = require('./utils/read-local-package.js') const usageUtil = require('./utils/usage.js') @@ -89,9 +88,9 @@ class Owner { const packumentOpts = { ...opts, fullMetadata: true } const { maintainers } = await pacote.packument(spec, packumentOpts) if (!maintainers || !maintainers.length) - output('no admin found') + this.npm.output('no admin found') else - output(maintainers.map(o => `${o.name} <${o.email}>`).join('\n')) + this.npm.output(maintainers.map(o => `${o.name} <${o.email}>`).join('\n')) return maintainers } catch (err) { @@ -114,7 +113,8 @@ class Owner { log.verbose('owner add', '%s to %s', user, pkg) const spec = npa(pkg) - return putOwners(spec, user, opts, validateAddOwner) + return this.putOwners(spec, user, opts, + (newOwner, owners) => this.validateAddOwner(newOwner, owners)) } async rm (user, pkg, opts) { @@ -131,109 +131,111 @@ class Owner { log.verbose('owner rm', '%s from %s', user, pkg) const spec = npa(pkg) - return putOwners(spec, user, opts, validateRmOwner) + return this.putOwners(spec, user, opts, + (rmOwner, owners) => this.validateRmOwner(rmOwner, owners)) } -} -module.exports = Owner -const validateAddOwner = (newOwner, owners) => { - owners = owners || [] - for (const o of owners) { - if (o.name === newOwner.name) { - log.info( - 'owner add', - 'Already a package owner: ' + o.name + ' <' + o.email + '>' - ) - return false - } - } - return [ - ...owners, - newOwner, - ] -} - -const validateRmOwner = (rmOwner, owners) => { - let found = false - const m = owners.filter(function (o) { - var match = (o.name === rmOwner.name) - found = found || match - return !match - }) - - if (!found) { - log.info('owner rm', 'Not a package owner: ' + rmOwner.name) - return false - } + async putOwners (spec, user, opts, validation) { + const uri = `/-/user/org.couchdb.user:${encodeURIComponent(user)}` + let u = '' - if (!m.length) { - throw Object.assign( - new Error( - 'Cannot remove all owners of a package. Add someone else first.' - ), - { code: 'EOWNERRM' } - ) - } + try { + u = await npmFetch.json(uri, opts) + } catch (err) { + log.error('owner mutate', `Error getting user data for ${user}`) + throw err + } - return m -} + if (user && (!u || !u.name || u.error)) { + throw Object.assign( + new Error( + "Couldn't get user data for " + user + ': ' + JSON.stringify(u) + ), + { code: 'EOWNERUSER' } + ) + } -const putOwners = async (spec, user, opts, validation) => { - const uri = `/-/user/org.couchdb.user:${encodeURIComponent(user)}` - let u = '' + // normalize user data + u = { name: u.name, email: u.email } - try { - u = await npmFetch.json(uri, opts) - } catch (err) { - log.error('owner mutate', `Error getting user data for ${user}`) - throw err - } + const data = await pacote.packument(spec, { ...opts, fullMetadata: true }) - if (user && (!u || !u.name || u.error)) { - throw Object.assign( - new Error( - "Couldn't get user data for " + user + ': ' + JSON.stringify(u) - ), - { code: 'EOWNERUSER' } - ) - } + // save the number of maintainers before validation for comparison + const before = data.maintainers ? data.maintainers.length : 0 - // normalize user data - u = { name: u.name, email: u.email } + const m = validation(u, data.maintainers) + if (!m) + return // invalid owners - const data = await pacote.packument(spec, { ...opts, fullMetadata: true }) + const body = { + _id: data._id, + _rev: data._rev, + maintainers: m, + } + const dataPath = `/${spec.escapedName}/-rev/${encodeURIComponent(data._rev)}` + const res = await otplease(opts, opts => { + return npmFetch.json(dataPath, { + ...opts, + method: 'PUT', + body, + spec, + }) + }) - // save the number of maintainers before validation for comparison - const before = data.maintainers ? data.maintainers.length : 0 + if (!res.error) { + if (m.length < before) + this.npm.output(`- ${user} (${spec.name})`) + else + this.npm.output(`+ ${user} (${spec.name})`) + } else { + throw Object.assign( + new Error('Failed to update package: ' + JSON.stringify(res)), + { code: 'EOWNERMUTATE' } + ) + } + return res + } + + validateAddOwner (newOwner, owners) { + owners = owners || [] + for (const o of owners) { + if (o.name === newOwner.name) { + log.info( + 'owner add', + 'Already a package owner: ' + o.name + ' <' + o.email + '>' + ) + return false + } + } + return [ + ...owners, + newOwner, + ] + } + + validateRmOwner (rmOwner, owners) { + let found = false + const m = owners.filter(function (o) { + var match = (o.name === rmOwner.name) + found = found || match + return !match + }) + + if (!found) { + log.info('owner rm', 'Not a package owner: ' + rmOwner.name) + return false + } - const m = validation(u, data.maintainers) - if (!m) - return // invalid owners + if (!m.length) { + throw Object.assign( + new Error( + 'Cannot remove all owners of a package. Add someone else first.' + ), + { code: 'EOWNERRM' } + ) + } - const body = { - _id: data._id, - _rev: data._rev, - maintainers: m, - } - const dataPath = `/${spec.escapedName}/-rev/${encodeURIComponent(data._rev)}` - const res = await otplease(opts, opts => - npmFetch.json(dataPath, { - ...opts, - method: 'PUT', - body, - spec, - })) - - if (!res.error) { - if (m.length < before) - output(`- ${user} (${spec.name})`) - else - output(`+ ${user} (${spec.name})`) - } else { - throw Object.assign( - new Error('Failed to update package: ' + JSON.stringify(res)), - { code: 'EOWNERMUTATE' } - ) + return m } - return res } +module.exports = Owner diff --git a/lib/pack.js b/lib/pack.js index cf1e77f48ee69..7ffe3138ed55f 100644 --- a/lib/pack.js +++ b/lib/pack.js @@ -7,7 +7,6 @@ const npa = require('npm-package-arg') const { getContents, logTar } = require('./utils/tar.js') const writeFile = util.promisify(require('fs').writeFile) -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') @@ -49,7 +48,7 @@ class Pack { for (const tar of tarballs) { logTar(tar, { log, unicode }) - output(tar.filename.replace(/^@/, '').replace(/\//, '-')) + this.npm.output(tar.filename.replace(/^@/, '').replace(/\//, '-')) } } } diff --git a/lib/ping.js b/lib/ping.js index e43f0640f212b..3643fe3b621aa 100644 --- a/lib/ping.js +++ b/lib/ping.js @@ -1,5 +1,4 @@ const log = require('npmlog') -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') const pingUtil = require('./utils/ping.js') @@ -24,7 +23,7 @@ class Ping { const time = Date.now() - start log.notice('PONG', `${time / 1000}ms`) if (this.npm.flatOptions.json) { - output(JSON.stringify({ + this.npm.output(JSON.stringify({ registry: this.npm.flatOptions.registry, time, details, diff --git a/lib/prefix.js b/lib/prefix.js index e46f9c4cdd94a..8ec5ab9efcf78 100644 --- a/lib/prefix.js +++ b/lib/prefix.js @@ -1,4 +1,3 @@ -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') class Prefix { @@ -16,7 +15,7 @@ class Prefix { } async prefix (args) { - return output(this.npm.prefix) + return this.npm.output(this.npm.prefix) } } module.exports = Prefix diff --git a/lib/profile.js b/lib/profile.js index dab99092b0a0f..a0a8606014da4 100644 --- a/lib/profile.js +++ b/lib/profile.js @@ -7,7 +7,6 @@ const qrcodeTerminal = require('qrcode-terminal') const Table = require('cli-table3') const otplease = require('./utils/otplease.js') -const output = require('./utils/output.js') const pulseTillDone = require('./utils/pulse-till-done.js') const readUserInfo = require('./utils/read-user-info.js') const usageUtil = require('./utils/usage.js') @@ -116,7 +115,7 @@ class Profile { delete info.cidr_whitelist if (conf.json) { - output(JSON.stringify(info, null, 2)) + this.npm.output(JSON.stringify(info, null, 2)) return } @@ -145,21 +144,21 @@ class Profile { .filter((arg) => arg.trim() !== '') .map((arg) => cleaned[arg]) .join('\t') - output(values) + this.npm.output(values) } else { if (conf.parseable) { for (const key of Object.keys(info)) { if (key === 'tfa') - output(`${key}\t${cleaned[tfa]}`) + this.npm.output(`${key}\t${cleaned[tfa]}`) else - output(`${key}\t${info[key]}`) + this.npm.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()) + this.npm.output(table.toString()) } } } @@ -215,13 +214,13 @@ class Profile { const result = await otplease(conf, conf => npmProfile.set(newUser, conf)) if (conf.json) - output(JSON.stringify({ [prop]: result[prop] }, null, 2)) + this.npm.output(JSON.stringify({ [prop]: result[prop] }, null, 2)) else if (conf.parseable) - output(prop + '\t' + result[prop]) + this.npm.output(prop + '\t' + result[prop]) else if (result[prop] != null) - output('Set', prop, 'to', result[prop]) + this.npm.output('Set', prop, 'to', result[prop]) else - output('Set', prop) + this.npm.output('Set', prop) } async enable2fa (args) { @@ -327,7 +326,7 @@ class Profile { ) if (challenge.tfa === null) { - output('Two factor authentication mode changed to: ' + mode) + this.npm.output('Two factor authentication mode changed to: ' + mode) return } @@ -344,7 +343,7 @@ class Profile { const secret = otpauth.searchParams.get('secret') const code = await qrcode(challenge.tfa) - output( + this.npm.output( 'Scan into your authenticator app:\n' + code + '\n Or enter code:', secret ) @@ -355,17 +354,17 @@ class Profile { const result = await npmProfile.set({ tfa: [interactiveOTP] }, conf) - output( + this.npm.output( '2FA successfully enabled. Below are your recovery codes, ' + 'please print these out.' ) - output( + this.npm.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) + this.npm.output('\t' + tfaCode) } async disable2fa (args) { @@ -373,7 +372,7 @@ class Profile { const info = await pulseTillDone.withPromise(npmProfile.get(conf)) if (!info.tfa || info.tfa.pending) { - output('Two factor authentication not enabled.') + this.npm.output('Two factor authentication not enabled.') return } @@ -391,11 +390,11 @@ class Profile { }, conf)) if (conf.json) - output(JSON.stringify({ tfa: false }, null, 2)) + this.npm.output(JSON.stringify({ tfa: false }, null, 2)) else if (conf.parseable) - output('tfa\tfalse') + this.npm.output('tfa\tfalse') else - output('Two factor authentication disabled.') + this.npm.output('Two factor authentication disabled.') } } module.exports = Profile diff --git a/lib/publish.js b/lib/publish.js index c8e82c44c5a3c..b0bf922138ce3 100644 --- a/lib/publish.js +++ b/lib/publish.js @@ -9,7 +9,6 @@ const npa = require('npm-package-arg') const npmFetch = require('npm-registry-fetch') const { flatten } = require('./utils/flat-options.js') -const output = require('./utils/output.js') const otplease = require('./utils/otplease.js') const usageUtil = require('./utils/usage.js') const { getContents, logTar } = require('./utils/tar.js') @@ -115,9 +114,9 @@ class Publish { const silent = log.level === 'silent' if (!silent && json) - output(JSON.stringify(pkgContents, null, 2)) + this.npm.output(JSON.stringify(pkgContents, null, 2)) else if (!silent) - output(`+ ${pkgContents.id}`) + this.npm.output(`+ ${pkgContents.id}`) return pkgContents } diff --git a/lib/rebuild.js b/lib/rebuild.js index 1091b01589389..ffbdebc21fa27 100644 --- a/lib/rebuild.js +++ b/lib/rebuild.js @@ -3,7 +3,6 @@ const Arborist = require('@npmcli/arborist') const npa = require('npm-package-arg') const semver = require('semver') const usageUtil = require('./utils/usage.js') -const output = require('./utils/output.js') const completion = require('./utils/completion/installed-deep.js') class Rebuild { @@ -52,7 +51,7 @@ class Rebuild { } else await arb.rebuild() - output('rebuilt dependencies successfully') + this.npm.output('rebuilt dependencies successfully') } isNode (specs, node) { diff --git a/lib/root.js b/lib/root.js index 8e5ac63d7b9b8..7c3fa2bbb3544 100644 --- a/lib/root.js +++ b/lib/root.js @@ -1,4 +1,3 @@ -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') class Root { @@ -16,7 +15,7 @@ class Root { } async root () { - output(this.npm.dir) + this.npm.output(this.npm.dir) } } module.exports = Root diff --git a/lib/run-script.js b/lib/run-script.js index cdfd88f10f7b8..dc822668d0318 100644 --- a/lib/run-script.js +++ b/lib/run-script.js @@ -2,7 +2,6 @@ const runScript = require('@npmcli/run-script') const { isServerPackage } = runScript const readJson = require('read-package-json-fast') const { resolve } = require('path') -const output = require('./utils/output.js') const log = require('npmlog') const usageUtil = require('./utils/usage.js') const didYouMean = require('./utils/did-you-mean.js') @@ -117,13 +116,13 @@ class RunScript { return allScripts if (this.npm.flatOptions.json) { - output(JSON.stringify(scripts, null, 2)) + this.npm.output(JSON.stringify(scripts, null, 2)) return allScripts } if (this.npm.flatOptions.parseable) { for (const [script, cmd] of Object.entries(scripts)) - output(`${script}:${cmd}`) + this.npm.output(`${script}:${cmd}`) return allScripts } @@ -138,18 +137,18 @@ class RunScript { } if (cmds.length) - output(`Lifecycle scripts included in ${name}:`) + this.npm.output(`Lifecycle scripts included in ${name}:`) for (const script of cmds) - output(prefix + script + indent + scripts[script]) + this.npm.output(prefix + script + indent + scripts[script]) if (!cmds.length && runScripts.length) - output(`Scripts available in ${name} via \`npm run-script\`:`) + this.npm.output(`Scripts available in ${name} via \`npm run-script\`:`) else if (runScripts.length) - output('\navailable via `npm run-script`:') + this.npm.output('\navailable via `npm run-script`:') for (const script of runScripts) - output(prefix + script + indent + scripts[script]) + this.npm.output(prefix + script + indent + scripts[script]) return allScripts } diff --git a/lib/search.js b/lib/search.js index e0922b9846cdb..35e3eeb0e552c 100644 --- a/lib/search.js +++ b/lib/search.js @@ -5,7 +5,6 @@ const log = require('npmlog') const formatPackageStream = require('./search/format-package-stream.js') const packageFilter = require('./search/package-filter.js') -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') function prepareIncludes (args) { @@ -83,12 +82,12 @@ class Search { p.on('data', chunk => { if (!anyOutput) anyOutput = true - output(chunk.toString('utf8')) + this.npm.output(chunk.toString('utf8')) }) await p.promise() if (!anyOutput && !opts.json && !opts.parseable) - output('No matches found for ' + (args.map(JSON.stringify).join(' '))) + this.npm.output('No matches found for ' + (args.map(JSON.stringify).join(' '))) log.silly('search', 'search completed') log.clearProgress() diff --git a/lib/star.js b/lib/star.js index b39d23b2c1170..073c93a898eaf 100644 --- a/lib/star.js +++ b/lib/star.js @@ -2,7 +2,6 @@ const fetch = require('npm-registry-fetch') const log = require('npmlog') const npa = require('npm-package-arg') -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') const getIdentity = require('./utils/get-identity') @@ -73,7 +72,7 @@ class Star { body, }) - output(show + ' ' + pkg.name) + this.npm.output(show + ' ' + pkg.name) log.verbose('star', data) return data } diff --git a/lib/stars.js b/lib/stars.js index fe280705b4b5c..e0a6a0003ecc3 100644 --- a/lib/stars.js +++ b/lib/stars.js @@ -1,7 +1,6 @@ const log = require('npmlog') const fetch = require('npm-registry-fetch') -const output = require('./utils/output.js') const getIdentity = require('./utils/get-identity.js') const usageUtil = require('./utils/usage.js') @@ -36,7 +35,7 @@ class Stars { log.warn('stars', 'user has not starred any packages') for (const row of rows) - output(row.value) + this.npm.output(row.value) } } module.exports = Stars diff --git a/lib/team.js b/lib/team.js index 4947739a045c4..3ba2c023dbaef 100644 --- a/lib/team.js +++ b/lib/team.js @@ -1,7 +1,6 @@ const columns = require('cli-columns') const libteam = require('libnpmteam') -const output = require('./utils/output.js') const otplease = require('./utils/otplease.js') const usageUtil = require('./utils/usage.js') @@ -66,82 +65,82 @@ class Team { async create (entity, opts) { await libteam.create(entity, opts) if (opts.json) { - output(JSON.stringify({ + this.npm.output(JSON.stringify({ created: true, team: entity, })) } else if (opts.parseable) - output(`${entity}\tcreated`) + this.npm.output(`${entity}\tcreated`) else if (!opts.silent && opts.loglevel !== 'silent') - output(`+@${entity}`) + this.npm.output(`+@${entity}`) } async destroy (entity, opts) { await libteam.destroy(entity, opts) if (opts.json) { - output(JSON.stringify({ + this.npm.output(JSON.stringify({ deleted: true, team: entity, })) } else if (opts.parseable) - output(`${entity}\tdeleted`) + this.npm.output(`${entity}\tdeleted`) else if (!opts.silent && opts.loglevel !== 'silent') - output(`-@${entity}`) + this.npm.output(`-@${entity}`) } async add (entity, user, opts) { await libteam.add(user, entity, opts) if (opts.json) { - output(JSON.stringify({ + this.npm.output(JSON.stringify({ added: true, team: entity, user, })) } else if (opts.parseable) - output(`${user}\t${entity}\tadded`) + this.npm.output(`${user}\t${entity}\tadded`) else if (!opts.silent && opts.loglevel !== 'silent') - output(`${user} added to @${entity}`) + this.npm.output(`${user} added to @${entity}`) } async rm (entity, user, opts) { await libteam.rm(user, entity, opts) if (opts.json) { - output(JSON.stringify({ + this.npm.output(JSON.stringify({ removed: true, team: entity, user, })) } else if (opts.parseable) - output(`${user}\t${entity}\tremoved`) + this.npm.output(`${user}\t${entity}\tremoved`) else if (!opts.silent && opts.loglevel !== 'silent') - output(`${user} removed from @${entity}`) + this.npm.output(`${user} removed from @${entity}`) } async listUsers (entity, opts) { const users = (await libteam.lsUsers(entity, opts)).sort() if (opts.json) - output(JSON.stringify(users, null, 2)) + this.npm.output(JSON.stringify(users, null, 2)) else if (opts.parseable) - output(users.join('\n')) + this.npm.output(users.join('\n')) else if (!opts.silent && opts.loglevel !== 'silent') { const plural = users.length === 1 ? '' : 's' const more = users.length === 0 ? '' : ':\n' - output(`\n@${entity} has ${users.length} user${plural}${more}`) - output(columns(users, { padding: 1 })) + this.npm.output(`\n@${entity} has ${users.length} user${plural}${more}`) + this.npm.output(columns(users, { padding: 1 })) } } async listTeams (entity, opts) { const teams = (await libteam.lsTeams(entity, opts)).sort() if (opts.json) - output(JSON.stringify(teams, null, 2)) + this.npm.output(JSON.stringify(teams, null, 2)) else if (opts.parseable) - output(teams.join('\n')) + this.npm.output(teams.join('\n')) else if (!opts.silent && opts.loglevel !== 'silent') { const plural = teams.length === 1 ? '' : 's' const more = teams.length === 0 ? '' : ':\n' - output(`\n@${entity} has ${teams.length} team${plural}${more}`) - output(columns(teams.map(t => `@${t}`), { padding: 1 })) + this.npm.output(`\n@${entity} has ${teams.length} team${plural}${more}`) + this.npm.output(columns(teams.map(t => `@${t}`), { padding: 1 })) } } } diff --git a/lib/token.js b/lib/token.js index ad6d5c6fcb82c..ad634c0b00772 100644 --- a/lib/token.js +++ b/lib/token.js @@ -5,7 +5,6 @@ const log = require('npmlog') const profile = require('npm-profile') const otplease = require('./utils/otplease.js') -const output = require('./utils/output.js') const pulseTillDone = require('./utils/pulse-till-done.js') const readUserInfo = require('./utils/read-user-info.js') const usageUtil = require('./utils/usage.js') @@ -64,12 +63,12 @@ class Token { log.info('token', 'getting list') const tokens = await pulseTillDone.withPromise(profile.listTokens(conf)) if (conf.json) { - output(JSON.stringify(tokens, null, 2)) + this.npm.output(JSON.stringify(tokens, null, 2)) return } else if (conf.parseable) { - output(['key', 'token', 'created', 'readonly', 'CIDR whitelist'].join('\t')) + this.npm.output(['key', 'token', 'created', 'readonly', 'CIDR whitelist'].join('\t')) tokens.forEach((token) => { - output([ + this.npm.output([ token.key, token.token, token.created, @@ -95,7 +94,7 @@ class Token { token.cidr_whitelist ? token.cidr_whitelist.join(', ') : '', ]) }) - output(table.toString()) + this.npm.output(table.toString()) } async rm (args) { @@ -127,11 +126,11 @@ class Token { }) })) if (conf.json) - output(JSON.stringify(toRemove)) + this.npm.output(JSON.stringify(toRemove)) else if (conf.parseable) - output(toRemove.join('\t')) + this.npm.output(toRemove.join('\t')) else - output('Removed ' + toRemove.length + ' token' + (toRemove.length !== 1 ? 's' : '')) + this.npm.output('Removed ' + toRemove.length + ' token' + (toRemove.length !== 1 ? 's' : '')) } async create (args) { @@ -149,14 +148,14 @@ class Token { delete result.key delete result.updated if (conf.json) - output(JSON.stringify(result)) + this.npm.output(JSON.stringify(result)) else if (conf.parseable) - Object.keys(result).forEach((k) => output(k + '\t' + result[k])) + Object.keys(result).forEach((k) => this.npm.output(k + '\t' + result[k])) else { const table = new Table() for (const k of Object.keys(result)) table.push({ [ansistyles.bright(k)]: String(result[k]) }) - output(table.toString()) + this.npm.output(table.toString()) } }) } diff --git a/lib/unpublish.js b/lib/unpublish.js index 34751da4a5909..acba6ea521191 100644 --- a/lib/unpublish.js +++ b/lib/unpublish.js @@ -8,7 +8,6 @@ const libunpub = require('libnpmpublish').unpublish const readJson = util.promisify(require('read-package-json')) const usageUtil = require('./utils/usage.js') -const output = require('./utils/output.js') const otplease = require('./utils/otplease.js') const getIdentity = require('./utils/get-identity.js') @@ -107,7 +106,7 @@ class Unpublish { } if (!silent && loglevel !== 'silent') - output(`- ${pkgName}${pkgVersion}`) + this.npm.output(`- ${pkgName}${pkgVersion}`) } } module.exports = Unpublish diff --git a/lib/utils/audit-error.js b/lib/utils/audit-error.js index ae0749ff6f0be..c58c1d16e6885 100644 --- a/lib/utils/audit-error.js +++ b/lib/utils/audit-error.js @@ -3,7 +3,6 @@ // prints a JSON version of the error if it's --json // returns 'true' if there was an error, false otherwise -const output = require('./output.js') const auditError = (npm, report) => { if (!report || !report.error) return false @@ -18,7 +17,7 @@ const auditError = (npm, report) => { const { body: errBody } = error const body = Buffer.isBuffer(errBody) ? errBody.toString() : errBody if (npm.flatOptions.json) { - output(JSON.stringify({ + npm.output(JSON.stringify({ message: error.message, method: error.method, uri: error.uri, @@ -27,7 +26,7 @@ const auditError = (npm, report) => { body, }, null, 2)) } else - output(body) + npm.output(body) throw 'audit endpoint returned an error' } diff --git a/lib/utils/npm-usage.js b/lib/utils/npm-usage.js index 220f8037f164d..b77bca7bec1a8 100644 --- a/lib/utils/npm-usage.js +++ b/lib/utils/npm-usage.js @@ -1,6 +1,5 @@ const didYouMean = require('./did-you-mean.js') const { dirname } = require('path') -const output = require('./output.js') const { cmdList } = require('./cmd-list') module.exports = (npm, valid = true) => { @@ -8,7 +7,7 @@ module.exports = (npm, valid = true) => { const usesBrowser = npm.config.get('viewer') === 'browser' ? ' (in a browser)' : '' npm.log.level = 'silent' - output(` + npm.output(` Usage: npm npm install install all the dependencies in your project @@ -34,7 +33,7 @@ npm@${npm.version} ${dirname(dirname(__dirname))} `) if (npm.argv.length >= 1) - output(didYouMean(npm.argv[0], cmdList)) + npm.output(didYouMean(npm.argv[0], cmdList)) if (!valid) process.exitCode = 1 diff --git a/lib/utils/open-url.js b/lib/utils/open-url.js index 1fe456bd050be..41fac33ec66e9 100644 --- a/lib/utils/open-url.js +++ b/lib/utils/open-url.js @@ -1,4 +1,3 @@ -const output = require('./output.js') const opener = require('opener') const { URL } = require('url') @@ -16,7 +15,7 @@ const open = async (npm, url, errMsg) => { }, null, 2) : `${errMsg}:\n ${url}\n` - output(alternateMsg) + npm.output(alternateMsg) } if (browser === false) { diff --git a/lib/utils/output.js b/lib/utils/output.js deleted file mode 100644 index 2d1549859ac0d..0000000000000 --- a/lib/utils/output.js +++ /dev/null @@ -1,7 +0,0 @@ -const log = require('npmlog') -// output to stdout in a progress bar compatible way -module.exports = (...msg) => { - log.clearProgress() - console.log(...msg) - log.showProgress() -} diff --git a/lib/utils/reify-output.js b/lib/utils/reify-output.js index 216f0e902e90a..ddad32121e8b4 100644 --- a/lib/utils/reify-output.js +++ b/lib/utils/reify-output.js @@ -10,7 +10,6 @@ // run `npm audit fix` to fix them, or `npm audit` for details const log = require('npmlog') -const output = require('./output.js') const { depth } = require('treeverse') const ms = require('ms') const auditReport = require('npm-audit-report') @@ -72,10 +71,10 @@ const reifyOutput = (npm, arb) => { summary.audit = npm.command === 'audit' ? auditReport : auditReport.toJSON().metadata } - output(JSON.stringify(summary, 0, 2)) + npm.output(JSON.stringify(summary, 0, 2)) } else { packagesChangedMessage(npm, summary) - packagesFundingMessage(summary) + packagesFundingMessage(npm, summary) printAuditReport(npm, auditReport) } } @@ -98,7 +97,7 @@ const printAuditReport = (npm, report) => { auditLevel, }) process.exitCode = process.exitCode || res.exitCode - output('\n' + res.report) + npm.output('\n' + res.report) } const packagesChangedMessage = (npm, { added, removed, changed, audited }) => { @@ -136,18 +135,18 @@ const packagesChangedMessage = (npm, { added, removed, changed, audited }) => { msg.push(`audited ${audited} package${audited === 1 ? '' : 's'}`) msg.push(` in ${ms(Date.now() - npm.started)}`) - output(msg.join('')) + npm.output(msg.join('')) } -const packagesFundingMessage = ({ funding }) => { +const packagesFundingMessage = (npm, { funding }) => { if (!funding) return - output('') + npm.output('') const pkg = funding === 1 ? 'package' : 'packages' const is = funding === 1 ? 'is' : 'are' - output(`${funding} ${pkg} ${is} looking for funding`) - output(' run `npm fund` for details') + npm.output(`${funding} ${pkg} ${is} looking for funding`) + npm.output(' run `npm fund` for details') } module.exports = reifyOutput diff --git a/lib/version.js b/lib/version.js index 1ba834f5d711b..a7c0c1955d536 100644 --- a/lib/version.js +++ b/lib/version.js @@ -1,5 +1,4 @@ const libversion = require('libnpmversion') -const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') class Version { @@ -56,7 +55,7 @@ class Version { ...this.npm.flatOptions, path: this.npm.prefix, }) - return output(`${prefix}${version}`) + return this.npm.output(`${prefix}${version}`) } async list () { @@ -78,9 +77,9 @@ class Version { results[key] = version if (this.npm.flatOptions.json) - output(JSON.stringify(results, null, 2)) + this.npm.output(JSON.stringify(results, null, 2)) else - output(results) + this.npm.output(results) } } module.exports = Version diff --git a/lib/whoami.js b/lib/whoami.js index 39184ed9c581c..7ce877104c185 100644 --- a/lib/whoami.js +++ b/lib/whoami.js @@ -1,4 +1,3 @@ -const output = require('./utils/output.js') const getIdentity = require('./utils/get-identity.js') const usageUtil = require('./utils/usage.js') @@ -23,7 +22,7 @@ class Whoami { async whoami (args) { const opts = this.npm.flatOptions const username = await getIdentity(this.npm, opts) - output(opts.json ? JSON.stringify(username) : username) + this.npm.output(opts.json ? JSON.stringify(username) : username) } } module.exports = Whoami diff --git a/tap-snapshots/test-lib-profile.js-TAP.test.js b/tap-snapshots/test-lib-profile.js-TAP.test.js index bb838ad92c97d..58975515162f6 100644 --- a/tap-snapshots/test-lib-profile.js-TAP.test.js +++ b/tap-snapshots/test-lib-profile.js-TAP.test.js @@ -9,7 +9,11 @@ exports[`test/lib/profile.js TAP enable-2fa from token and set otp, retries on p 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 +1234 +2FA 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`] = ` @@ -29,7 +33,17 @@ 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 +tfa 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 +fullname Foo Bar +homepage https://github.com +freenode foobar +twitter https://twitter.com/npmjs +github https://github.com/npm ` exports[`test/lib/profile.js TAP profile get no args default output > should output table with contents 1`] = ` diff --git a/test/lib/access.js b/test/lib/access.js index 3a732ad0aac37..8134c1d9116d2 100644 --- a/test/lib/access.js +++ b/test/lib/access.js @@ -3,6 +3,10 @@ const requireInject = require('require-inject') const Access = require('../../lib/access.js') +const npm = { + output: () => null, +} + test('completion', t => { const access = new Access({ flatOptions: {} }) const testComp = (argv, expect) => { @@ -457,9 +461,8 @@ test('npm access ls-packages with no team', (t) => { }, }, '../../lib/utils/get-identity.js': () => Promise.resolve('foo'), - '../../lib/utils/output.js': () => null, }) - const access = new Access({}) + const access = new Access(npm) access.exec([ 'ls-packages', ], (err) => { @@ -477,9 +480,8 @@ test('access ls-packages on team', (t) => { return {} }, }, - '../../lib/utils/output.js': () => null, }) - const access = new Access({}) + const access = new Access(npm) access.exec([ 'ls-packages', 'myorg:myteam', @@ -503,9 +505,8 @@ test('access ls-collaborators on current', (t) => { return {} }, }, - '../../lib/utils/output.js': () => null, }) - const access = new Access({ prefix }) + const access = new Access({ prefix, ...npm }) access.exec([ 'ls-collaborators', ], (err) => { @@ -523,9 +524,8 @@ test('access ls-collaborators on spec', (t) => { return {} }, }, - '../../lib/utils/output.js': () => null, }) - const access = new Access({}) + const access = new Access(npm) access.exec([ 'ls-collaborators', 'yargs', diff --git a/test/lib/adduser.js b/test/lib/adduser.js index 32fd97c1bd46d..106cd429e37a6 100644 --- a/test/lib/adduser.js +++ b/test/lib/adduser.js @@ -61,6 +61,9 @@ const npm = { }, setCredentialsByURI, }, + output: msg => { + result = msg + }, } const AddUser = requireInject('../../lib/adduser.js', { @@ -70,9 +73,6 @@ const AddUser = requireInject('../../lib/adduser.js', { registryOutput = msg }, }, - '../../lib/utils/output.js': msg => { - result = msg - }, '../../lib/auth/legacy.js': authDummy, }) diff --git a/test/lib/audit.js b/test/lib/audit.js index 6fd9c8a2c9b8f..d291ef87948c9 100644 --- a/test/lib/audit.js +++ b/test/lib/audit.js @@ -14,6 +14,9 @@ t.test('should audit using Arborist', t => { flatOptions: { json: false, }, + output: () => { + OUTPUT_CALLED = true + }, } const Audit = requireInject('../../lib/audit.js', { 'npm-audit-report': () => { @@ -37,9 +40,6 @@ t.test('should audit using Arborist', t => { REIFY_FINISH_CALLED = true }, - '../../lib/utils/output.js': () => { - OUTPUT_CALLED = true - }, }) const audit = new Audit(npm) @@ -70,6 +70,7 @@ t.test('should audit - json', t => { flatOptions: { json: true, }, + output: () => {}, } const Audit = requireInject('../../lib/audit.js', { @@ -83,7 +84,6 @@ t.test('should audit - json', t => { } }, '../../lib/utils/reify-output.js': () => {}, - '../../lib/utils/output.js': () => {}, }) const audit = new Audit(npm) @@ -107,6 +107,9 @@ t.test('report endpoint error', t => { log: { warn: (...warning) => LOGS.push(warning), }, + output: (...msg) => { + OUTPUT.push(msg) + }, } const Audit = requireInject('../../lib/audit.js', { 'npm-audit-report': () => { @@ -130,9 +133,6 @@ t.test('report endpoint error', t => { } }, '../../lib/utils/reify-output.js': () => {}, - '../../lib/utils/output.js': (...msg) => { - OUTPUT.push(msg) - }, }) const audit = new Audit(npm) diff --git a/test/lib/bin.js b/test/lib/bin.js index e96eb91af9708..512fa8d0267b9 100644 --- a/test/lib/bin.js +++ b/test/lib/bin.js @@ -5,13 +5,16 @@ test('bin', (t) => { t.plan(3) const dir = '/bin/dir' - const Bin = requireInject('../../lib/bin.js', { - '../../lib/utils/output.js': (output) => { + const Bin = require('../../lib/bin.js') + + const npm = { + bin: dir, + flatOptions: { global: false }, + output: (output) => { t.equal(output, dir, 'prints the correct directory') }, - }) - - const bin = new Bin({ bin: dir, flatOptions: { global: false } }) + } + const bin = new Bin(npm) bin.exec([], (err) => { t.ifError(err, 'npm bin') @@ -33,12 +36,16 @@ test('bin -g', (t) => { const Bin = requireInject('../../lib/bin.js', { '../../lib/utils/path.js': [dir], - '../../lib/utils/output.js': (output) => { - t.equal(output, dir, 'prints the correct directory') - }, }) - const bin = new Bin({ bin: dir, flatOptions: { global: true } }) + const npm = { + bin: dir, + flatOptions: { global: true }, + output: (output) => { + t.equal(output, dir, 'prints the correct directory') + }, + } + const bin = new Bin(npm) bin.exec([], (err) => { t.ifError(err, 'npm bin') @@ -60,11 +67,15 @@ test('bin -g (not in path)', (t) => { const Bin = requireInject('../../lib/bin.js', { '../../lib/utils/path.js': ['/not/my/dir'], - '../../lib/utils/output.js': (output) => { + }) + const npm = { + bin: dir, + flatOptions: { global: true }, + output: (output) => { t.equal(output, dir, 'prints the correct directory') }, - }) - const bin = new Bin({ bin: dir, flatOptions: { global: true } }) + } + const bin = new Bin(npm) bin.exec([], (err) => { t.ifError(err, 'npm bin') diff --git a/test/lib/cache.js b/test/lib/cache.js index 67499f37e9f30..5c2588f34ecba 100644 --- a/test/lib/cache.js +++ b/test/lib/cache.js @@ -8,9 +8,14 @@ const flatOptions = { force: false, } +let outputOutput = [] + const npm = { flatOptions, cache: '/fake/path', + output: (msg) => { + outputOutput.push(msg) + }, } let rimrafPath = '' @@ -41,11 +46,6 @@ const pacote = { }, } -let outputOutput = [] -const output = (msg) => { - outputOutput.push(msg) -} - const cacacheVerifyStats = { keptSize: 100, verifiedContent: 1, @@ -63,7 +63,6 @@ const Cache = requireInject('../../lib/cache.js', { npmlog, pacote, rimraf, - '../../lib/utils/output.js': output, '../../lib/utils/usage.js': usageUtil, }) diff --git a/test/lib/completion.js b/test/lib/completion.js index 89e8134ebb303..708f138251d19 100644 --- a/test/lib/completion.js +++ b/test/lib/completion.js @@ -46,6 +46,9 @@ const npm = { }, }, }, + output: (line) => { + output.push(line) + }, } const cmdList = { @@ -80,9 +83,6 @@ const Completion = requireInject('../../lib/completion.js', { '../../lib/utils/config.js': config, '../../lib/utils/deref-command.js': deref, '../../lib/utils/is-windows-shell.js': false, - '../../lib/utils/output.js': (line) => { - output.push(line) - }, }) const completion = new Completion(npm) diff --git a/test/lib/config.js b/test/lib/config.js index c2420aefb4a00..48934ba4e74d2 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -62,15 +62,15 @@ const npm = { return true }, }, + output: msg => { + result = msg + }, } const usageUtil = () => 'usage instructions' const mocks = { '../../lib/utils/config.js': { defaults, types }, - '../../lib/utils/output.js': msg => { - result = msg - }, '../../lib/utils/usage.js': usageUtil, } diff --git a/test/lib/diff.js b/test/lib/diff.js index 2af3bd69bc2e5..9f58505dca6ad 100644 --- a/test/lib/diff.js +++ b/test/lib/diff.js @@ -23,12 +23,12 @@ const npm = { get prefix () { return this.flatOptions.prefix }, + output: noop, } const mocks = { npmlog: { info: noop, verbose: noop }, libnpmdiff: (...args) => libnpmdiff(...args), 'npm-registry-fetch': async () => ({}), - '../../lib/utils/output.js': noop, '../../lib/utils/read-local-package.js': async () => rlp(), '../../lib/utils/usage.js': () => 'usage instructions', } diff --git a/test/lib/dist-tag.js b/test/lib/dist-tag.js index b761fb103cda8..a3c05bb2b3a15 100644 --- a/test/lib/dist-tag.js +++ b/test/lib/dist-tag.js @@ -58,9 +58,6 @@ const DistTag = requireInject('../../lib/dist-tag.js', { get 'npm-registry-fetch' () { return npmRegistryFetchMock }, - '../../lib/utils/output.js': msg => { - result = msg - }, }) const distTag = new DistTag({ @@ -70,6 +67,9 @@ const distTag = new DistTag({ return _flatOptions[key] }, }, + output: msg => { + result = msg + }, }) test('ls in current package', (t) => { diff --git a/test/lib/doctor.js b/test/lib/doctor.js index eaa7ad72df8a5..56bbdf974491d 100644 --- a/test/lib/doctor.js +++ b/test/lib/doctor.js @@ -104,6 +104,9 @@ const npm = { }, }, version: '7.1.0', + output: (data) => { + output.push(data) + }, } let latestNpm = npm.version @@ -123,9 +126,6 @@ const cacache = { const Doctor = requireInject('../../lib/doctor.js', { '../../lib/utils/is-windows.js': false, '../../lib/utils/ping.js': ping, - '../../lib/utils/output.js': (data) => { - output.push(data) - }, cacache, pacote, 'make-fetch-happen': fetch, @@ -285,9 +285,6 @@ test('node versions', t => { const WinDoctor = requireInject('../../lib/doctor.js', { '../../lib/utils/is-windows.js': true, '../../lib/utils/ping.js': ping, - '../../lib/utils/output.js': (data) => { - output.push(data) - }, cacache, pacote, 'make-fetch-happen': fetch, @@ -566,9 +563,6 @@ test('node versions', t => { const Doctor = requireInject('../../lib/doctor.js', { '../../lib/utils/is-windows.js': false, '../../lib/utils/ping.js': ping, - '../../lib/utils/output.js': (data) => { - output.push(data) - }, cacache, pacote, 'make-fetch-happen': fetch, diff --git a/test/lib/exec.js b/test/lib/exec.js index 4dc7f31cc31f4..eb9fef6a61da2 100644 --- a/test/lib/exec.js +++ b/test/lib/exec.js @@ -55,6 +55,7 @@ const npm = { LOG_WARN.push(args) }, }, + output, } const RUN_SCRIPTS = [] @@ -93,7 +94,6 @@ const mocks = { pacote, read, 'mkdirp-infer-owner': mkdirp, - '../../lib/utils/output.js': output, } const Exec = requireInject('../../lib/exec.js', mocks) const exec = new Exec(npm) diff --git a/test/lib/explain.js b/test/lib/explain.js index 22bfb8639ecff..2e82928d89d19 100644 --- a/test/lib/explain.js +++ b/test/lib/explain.js @@ -4,15 +4,15 @@ const npm = { prefix: null, color: true, flatOptions: {}, + output: (...args) => { + OUTPUT.push(args) + }, } const { resolve } = require('path') const OUTPUT = [] const Explain = requireInject('../../lib/explain.js', { - '../../lib/utils/output.js': (...args) => { - OUTPUT.push(args) - }, // keep the snapshots pared down a bit, since this has its own tests. '../../lib/utils/explain-dep.js': { diff --git a/test/lib/explore.js b/test/lib/explore.js index 6f1f3bb47f240..af16444ca8f71 100644 --- a/test/lib/explore.js +++ b/test/lib/explore.js @@ -55,9 +55,6 @@ const getExplore = (windows) => { }, 'read-package-json-fast': mockRPJ, '@npmcli/run-script': mockRunScript, - '../../lib/utils/output.js': out => { - output.push(out) - }, }) const npm = { dir: windows ? 'c:\\npm\\dir' : '/npm/dir', @@ -69,6 +66,9 @@ const getExplore = (windows) => { flatOptions: { shell: 'shell-command', }, + output: out => { + output.push(out) + }, } return new Explore(npm) } diff --git a/test/lib/fund.js b/test/lib/fund.js index 831d76f151bb7..2ae604a653632 100644 --- a/test/lib/fund.js +++ b/test/lib/fund.js @@ -202,9 +202,6 @@ const openUrl = async (npm, url, msg) => { } const Fund = requireInject('../../lib/fund.js', { '../../lib/utils/open-url.js': openUrl, - '../../lib/utils/output.js': msg => { - result += msg + '\n' - }, pacote: { manifest: (arg) => arg.name === 'ntl' ? Promise.resolve({ @@ -218,6 +215,9 @@ const fund = new Fund({ get prefix () { return _flatOptions.prefix }, + output: msg => { + result += msg + '\n' + }, }) test('fund with no package containing funding', t => { diff --git a/test/lib/help-search.js b/test/lib/help-search.js index 8b1ecd46eb774..6228f5ca97b3b 100644 --- a/test/lib/help-search.js +++ b/test/lib/help-search.js @@ -21,6 +21,7 @@ const npm = { return cb(npmHelpErr) }, }, + output, } let npmUsageArg = null @@ -45,7 +46,6 @@ const glob = (p, cb) => const HelpSearch = requireInject('../../lib/help-search.js', { '../../lib/utils/npm-usage.js': npmUsage, - '../../lib/utils/output.js': output, glob, }) const helpSearch = new HelpSearch(npm) diff --git a/test/lib/help.js b/test/lib/help.js index addbe4dcc1f9b..ae2f7e99dafa8 100644 --- a/test/lib/help.js +++ b/test/lib/help.js @@ -14,6 +14,7 @@ const npmConfig = { } let helpSearchArgs = null +const OUTPUT = [] const npm = { config: { get: (key) => npmConfig[key], @@ -34,11 +35,9 @@ const npm = { }, }, deref: (cmd) => {}, -} - -const OUTPUT = [] -const output = (msg) => { - OUTPUT.push(msg) + output: msg => { + OUTPUT.push(msg) + }, } const globDefaults = [ @@ -74,7 +73,6 @@ const openUrl = async (npm, url, msg) => { const Help = requireInject('../../lib/help.js', { '../../lib/utils/npm-usage.js': npmUsage, '../../lib/utils/open-url.js': openUrl, - '../../lib/utils/output.js': output, child_process: { spawn, }, diff --git a/test/lib/hook.js b/test/lib/hook.js index 923f86e81ddf3..3e012d94ee74f 100644 --- a/test/lib/hook.js +++ b/test/lib/hook.js @@ -1,6 +1,7 @@ const { test } = require('tap') const requireInject = require('require-inject') +const output = [] const npm = { flatOptions: { json: false, @@ -9,6 +10,9 @@ const npm = { loglevel: 'info', unicode: false, }, + output: (msg) => { + output.push(msg) + }, } const pkgTypes = { @@ -51,12 +55,8 @@ const libnpmhook = { }, } -const output = [] const Hook = requireInject('../../lib/hook.js', { '../../lib/utils/otplease.js': async (opts, fn) => fn(opts), - '../../lib/utils/output.js': (msg) => { - output.push(msg) - }, libnpmhook, }) const hook = new Hook(npm) diff --git a/test/lib/init.js b/test/lib/init.js index db5411ba76bf8..8b9f32e156e3d 100644 --- a/test/lib/init.js +++ b/test/lib/init.js @@ -14,13 +14,13 @@ const npm = { config: { set () {} }, flatOptions: {}, log: npmLog, + output: (...msg) => { + result += msg.join('\n') + }, } const mocks = { 'init-package-json': (dir, initFile, config, cb) => cb(null, 'data'), '../../lib/utils/usage.js': () => 'usage instructions', - '../../lib/utils/output.js': (...msg) => { - result += msg.join('\n') - }, } const Init = requireInject('../../lib/init.js', mocks) const init = new Init(npm) diff --git a/test/lib/ls.js b/test/lib/ls.js index bd81776d5f3df..bcbd3413563dd 100644 --- a/test/lib/ls.js +++ b/test/lib/ls.js @@ -1,8 +1,6 @@ -const { resolve } = require('path') - const t = require('tap') -const requireInject = require('require-inject') +const { resolve } = require('path') const { utimesSync } = require('fs') const touchHiddenPackageLock = prefix => { const later = new Date(Date.now() + 10000) @@ -106,11 +104,7 @@ const _flatOptions = { }, production: false, } -const LS = requireInject('../../lib/ls.js', { - '../../lib/utils/output.js': msg => { - result = msg - }, -}) +const LS = require('../../lib/ls.js') const ls = new LS({ flatOptions: _flatOptions, limit: { @@ -127,6 +121,9 @@ const ls = new LS({ return _flatOptions[key] }, }, + output: msg => { + result = msg + }, }) const redactCwd = res => diff --git a/test/lib/org.js b/test/lib/org.js index d21df85d64312..4ffbf0af411a6 100644 --- a/test/lib/org.js +++ b/test/lib/org.js @@ -2,6 +2,7 @@ const { test } = require('tap') const requireInject = require('require-inject') const ansiTrim = require('../../lib/utils/ansi-trim.js') +const output = [] const npm = { flatOptions: { json: false, @@ -9,10 +10,11 @@ const npm = { silent: false, loglevel: 'info', }, + output: (msg) => { + output.push(msg) + }, } -const output = [] - let orgSize = 1 let orgSetArgs = null let orgRmArgs = null @@ -41,9 +43,6 @@ const libnpmorg = { const Org = requireInject('../../lib/org.js', { '../../lib/utils/otplease.js': async (opts, fn) => fn(opts), - '../../lib/utils/output.js': (msg) => { - output.push(msg) - }, libnpmorg, }) const org = new Org(npm) diff --git a/test/lib/outdated.js b/test/lib/outdated.js index aa8a1bcb6b3a5..02952971b69f9 100644 --- a/test/lib/outdated.js +++ b/test/lib/outdated.js @@ -68,17 +68,8 @@ const packument = spec => { } let logs -const cleanLogs = (done) => { - logs = '' - const fn = (...args) => { - logs += '\n' - args.map(el => { - logs += el - return logs - }) - } - console.log = fn - done() +const output = (msg) => { + logs = `${logs}\n${msg}` } const globalDir = t.testdir({ @@ -102,10 +93,14 @@ const outdated = (dir, opts) => { prefix: dir, globalDir: `${globalDir}/node_modules`, flatOptions: opts, + output, }) } -t.beforeEach(cleanLogs) +t.beforeEach((done) => { + logs = '' + done() +}) const redactCwd = (path) => { const normalizePath = p => p diff --git a/test/lib/owner.js b/test/lib/owner.js index 4f8f430886b7e..14753689f9384 100644 --- a/test/lib/owner.js +++ b/test/lib/owner.js @@ -6,7 +6,12 @@ let readLocalPkgResponse = null const noop = () => null -const npm = { flatOptions: {} } +const npm = { + flatOptions: {}, + output: (msg) => { + result = result ? `${result}\n${msg}` : msg + }, +} const npmFetch = { json: noop } const npmlog = { error: noop, info: noop, verbose: noop } const pacote = { packument: noop } @@ -15,9 +20,6 @@ const mocks = { npmlog, 'npm-registry-fetch': npmFetch, pacote, - '../../lib/utils/output.js': (...msg) => { - result += msg.join('\n') - }, '../../lib/utils/otplease.js': async (opts, fn) => fn({ otp: '123456', opts }), '../../lib/utils/read-local-package.js': async () => readLocalPkgResponse, '../../lib/utils/usage.js': () => 'usage instructions', diff --git a/test/lib/pack.js b/test/lib/pack.js index 73a19baa3ef73..ce319be76ba3f 100644 --- a/test/lib/pack.js +++ b/test/lib/pack.js @@ -18,7 +18,6 @@ t.afterEach(cb => { t.test('should pack current directory with no arguments', (t) => { const Pack = requireInject('../../lib/pack.js', { - '../../lib/utils/output.js': output, libnpmpack, npmlog: { notice: () => {}, @@ -32,6 +31,7 @@ t.test('should pack current directory with no arguments', (t) => { json: false, dryRun: false, }, + output, }) pack.exec([], er => { @@ -53,7 +53,6 @@ t.test('should pack given directory', (t) => { }) const Pack = requireInject('../../lib/pack.js', { - '../../lib/utils/output.js': output, libnpmpack, npmlog: { notice: () => {}, @@ -67,6 +66,7 @@ t.test('should pack given directory', (t) => { json: true, dryRun: true, }, + output, }) pack.exec([testDir], er => { @@ -88,7 +88,6 @@ t.test('should pack given directory for scoped package', (t) => { }) const Pack = requireInject('../../lib/pack.js', { - '../../lib/utils/output.js': output, libnpmpack, npmlog: { notice: () => {}, @@ -102,6 +101,7 @@ t.test('should pack given directory for scoped package', (t) => { json: true, dryRun: true, }, + output, }) return pack.exec([testDir], er => { @@ -116,7 +116,6 @@ t.test('should pack given directory for scoped package', (t) => { t.test('should log pack contents', (t) => { const Pack = requireInject('../../lib/pack.js', { - '../../lib/utils/output.js': output, '../../lib/utils/tar.js': { ...require('../../lib/utils/tar.js'), logTar: () => { @@ -136,6 +135,7 @@ t.test('should log pack contents', (t) => { json: false, dryRun: false, }, + output, }) pack.exec([], er => { diff --git a/test/lib/ping.js b/test/lib/ping.js index cf47530749b33..f3563036f71fd 100644 --- a/test/lib/ping.js +++ b/test/lib/ping.js @@ -81,12 +81,6 @@ test('pings and returns json', (t) => { t.equal(spec, flatOptions, 'passes flatOptions') return details }, - '../../lib/utils/output.js': function (spec) { - const parsed = JSON.parse(spec) - t.equal(parsed.registry, flatOptions.registry, 'returns the correct registry url') - t.match(parsed.details, details, 'prints returned details') - t.type(parsed.time, 'number', 'returns time as a number') - }, npmlog: { notice: (type, spec) => { ++noticeCalls @@ -100,7 +94,15 @@ test('pings and returns json', (t) => { }, }, }) - const ping = new Ping({ flatOptions }) + const ping = new Ping({ + flatOptions, + output: function (spec) { + const parsed = JSON.parse(spec) + t.equal(parsed.registry, flatOptions.registry, 'returns the correct registry url') + t.match(parsed.details, details, 'prints returned details') + t.type(parsed.time, 'number', 'returns time as a number') + }, + }) ping.exec([], (err) => { t.equal(noticeCalls, 2, 'should have logged 2 lines') diff --git a/test/lib/prefix.js b/test/lib/prefix.js index dfb50f174f5db..5eb7ddb934d93 100644 --- a/test/lib/prefix.js +++ b/test/lib/prefix.js @@ -1,16 +1,16 @@ -const { test } = require('tap') -const requireInject = require('require-inject') +const t = require('tap') -test('prefix', (t) => { +t.test('prefix', (t) => { t.plan(3) const dir = '/prefix/dir' - const Prefix = requireInject('../../lib/prefix.js', { - '../../lib/utils/output.js': (output) => { + const Prefix = require('../../lib/prefix.js') + const prefix = new Prefix({ + prefix: dir, + output: (output) => { t.equal(output, dir, 'prints the correct directory') }, }) - const prefix = new Prefix({ prefix: dir }) prefix.exec([], (err) => { t.ifError(err, 'npm prefix') diff --git a/test/lib/profile.js b/test/lib/profile.js index 743ba2d6872e1..d1be93b0cbb62 100644 --- a/test/lib/profile.js +++ b/test/lib/profile.js @@ -8,7 +8,13 @@ const flatOptions = { parseable: false, registry: 'https://registry.npmjs.org/', } -const npm = { config: {}, flatOptions: { ...flatOptions }} +const npm = { + config: {}, + flatOptions: { ...flatOptions }, + output: (...msg) => { + result = result ? `${result}\n${msg.join('\n')}` : msg.join('\n') + }, +} const mocks = { ansistyles: { bright: a => a }, npmlog: { @@ -32,9 +38,6 @@ const mocks = { .join('\n') } }, - '../../lib/utils/output.js': (...msg) => { - result += msg.join('\n') - }, '../../lib/utils/pulse-till-done.js': { withPromise: async a => a, }, diff --git a/test/lib/publish.js b/test/lib/publish.js index 0e857fafddfe2..6337a1fcf0b2a 100644 --- a/test/lib/publish.js +++ b/test/lib/publish.js @@ -134,9 +134,6 @@ t.test('if loglevel=info and json, should not output package contents', (t) => { log.level = 'info' const Publish = requireInject('../../lib/publish.js', { - '../../lib/utils/output.js': () => { - t.pass('output is called') - }, '../../lib/utils/tar.js': { getContents: () => ({ id: 'someid', @@ -162,6 +159,9 @@ t.test('if loglevel=info and json, should not output package contents', (t) => { return { token: 'some.registry.token' } }, }, + output: () => { + t.pass('output is called') + }, }) publish.exec([testDir], (er) => { @@ -184,9 +184,6 @@ t.test('if loglevel=silent and dry-run, should not output package contents or pu log.level = 'silent' const Publish = requireInject('../../lib/publish.js', { - '../../lib/utils/output.js': () => { - throw new Error('should not output in dry run mode') - }, '../../lib/utils/tar.js': { getContents: () => ({ id: 'someid', @@ -211,6 +208,9 @@ t.test('if loglevel=silent and dry-run, should not output package contents or pu throw new Error('should not call getCredentialsByURI in dry run') }, }, + output: () => { + throw new Error('should not output in dry run mode') + }, }) publish.exec([testDir], (er) => { @@ -241,9 +241,6 @@ t.test('if loglevel=info and dry-run, should not publish, should log package con t.pass('logTar is called') }, }, - '../../lib/utils/output.js': () => { - t.pass('output fn is called') - }, libnpmpublish: { publish: () => { throw new Error('should not call libnpmpublish in dry run') @@ -258,7 +255,11 @@ t.test('if loglevel=info and dry-run, should not publish, should log package con ...config, getCredentialsByURI: () => { throw new Error('should not call getCredentialsByURI in dry run') - }}, + }, + }, + output: () => { + t.pass('output fn is called') + }, }) publish.exec([testDir], (er) => { diff --git a/test/lib/rebuild.js b/test/lib/rebuild.js index ee081c087f07f..1eb45e0d1d7bd 100644 --- a/test/lib/rebuild.js +++ b/test/lib/rebuild.js @@ -1,7 +1,6 @@ const fs = require('fs') const { resolve } = require('path') const t = require('tap') -const requireInject = require('require-inject') let result = '' @@ -11,15 +10,11 @@ const npm = { global: false, }, prefix: '', -} -const mocks = { - '../../lib/utils/output.js': (...msg) => { + output: (...msg) => { result += msg.join('\n') }, - '../../lib/utils/usage.js': () => 'usage instructions', } - -const Rebuild = requireInject('../../lib/rebuild.js', mocks) +const Rebuild = require('../../lib/rebuild.js') const rebuild = new Rebuild(npm) t.afterEach(cb => { diff --git a/test/lib/root.js b/test/lib/root.js index e8ccc1106d772..4a1aefa02dcea 100644 --- a/test/lib/root.js +++ b/test/lib/root.js @@ -1,16 +1,16 @@ -const { test } = require('tap') -const requireInject = require('require-inject') +const t = require('tap') -test('root', (t) => { +t.test('root', (t) => { t.plan(3) const dir = '/root/dir' - const Root = requireInject('../../lib/root.js', { - '../../lib/utils/output.js': (output) => { + const Root = require('../../lib/root.js') + const root = new Root({ + dir, + output: (output) => { t.equal(output, dir, 'prints the correct directory') }, }) - const root = new Root({ dir }) root.exec([], (err) => { t.ifError(err, 'npm root') diff --git a/test/lib/run-script.js b/test/lib/run-script.js index 43592d3243466..0566daf2341f4 100644 --- a/test/lib/run-script.js +++ b/test/lib/run-script.js @@ -18,6 +18,7 @@ const npm = { npm.config.settings[k] = v }, }, + output: (...msg) => output.push(msg), } const output = [] @@ -40,7 +41,6 @@ const getRS = windows => { }), npmlog, '../../lib/utils/is-windows-shell.js': windows, - '../../lib/utils/output.js': (...msg) => output.push(msg), }) return new RunScript(npm) } diff --git a/test/lib/search.js b/test/lib/search.js index 59c59f3b96e27..ea7ec4ca7c19e 100644 --- a/test/lib/search.js +++ b/test/lib/search.js @@ -12,7 +12,12 @@ const flatOptions = { opts: '', }, } -const npm = { flatOptions: { ...flatOptions } } +const npm = { + flatOptions: { ...flatOptions }, + output: (...msg) => { + result += msg.join('\n') + }, +} const npmlog = { silly () {}, clearProgress () {}, @@ -23,9 +28,6 @@ const libnpmsearch = { const mocks = { npmlog, libnpmsearch, - '../../lib/utils/output.js': (...msg) => { - result += msg.join('\n') - }, '../../lib/utils/usage.js': () => 'usage instructions', // '../../lib/search/format-package-stream.js': a => a, } diff --git a/test/lib/star.js b/test/lib/star.js index 64efd9ef8c7ed..774fabe3924c4 100644 --- a/test/lib/star.js +++ b/test/lib/star.js @@ -4,15 +4,18 @@ const t = require('tap') let result = '' const noop = () => null -const npm = { config: { get () {} }, flatOptions: { unicode: false } } +const npm = { + config: { get () {} }, + flatOptions: { unicode: false }, + output: (...msg) => { + result += msg.join('\n') + }, +} const npmFetch = { json: noop } const npmlog = { error: noop, info: noop, verbose: noop } const mocks = { npmlog, 'npm-registry-fetch': npmFetch, - '../../lib/utils/output.js': (...msg) => { - result += msg.join('\n') - }, '../../lib/utils/get-identity.js': async () => 'foo', '../../lib/utils/usage.js': () => 'usage instructions', } diff --git a/test/lib/stars.js b/test/lib/stars.js index 383b5adf42677..b242a883ad90f 100644 --- a/test/lib/stars.js +++ b/test/lib/stars.js @@ -4,15 +4,18 @@ const t = require('tap') let result = '' const noop = () => null -const npm = { config: { get () {} }, flatOptions: {} } +const npm = { + config: { get () {} }, + flatOptions: {}, + output: (...msg) => { + result = [result, ...msg].join('\n') + }, +} const npmFetch = { json: noop } const npmlog = { warn: noop } const mocks = { npmlog, 'npm-registry-fetch': npmFetch, - '../../lib/utils/output.js': (...msg) => { - result = [result, ...msg].join('\n') - }, '../../lib/utils/get-identity.js': async () => 'foo', '../../lib/utils/usage.js': () => 'usage instructions', } diff --git a/test/lib/team.js b/test/lib/team.js index a264597258d5a..458f8ee6700ce 100644 --- a/test/lib/team.js +++ b/test/lib/team.js @@ -10,13 +10,15 @@ const libnpmteam = { async lsUsers () {}, async rm () {}, } -const npm = { flatOptions: {} } +const npm = { + flatOptions: {}, + output: (...msg) => { + result += msg.join('\n') + }, +} const mocks = { libnpmteam, 'cli-columns': a => a.join(' '), - '../../lib/utils/output.js': (...msg) => { - result += msg.join('\n') - }, '../../lib/utils/otplease.js': async (opts, fn) => fn(opts), '../../lib/utils/usage.js': () => 'usage instructions', } diff --git a/test/lib/token.js b/test/lib/token.js index 412d2746befd4..3ca44ab5c90c6 100644 --- a/test/lib/token.js +++ b/test/lib/token.js @@ -7,9 +7,11 @@ const mocks = { log: {}, readUserInfo: {}, } +const npm = { + output: (...args) => mocks.output(...args), +} const Token = requireInject('../../lib/token.js', { - '../../lib/utils/output.js': (...args) => mocks.output(...args), '../../lib/utils/otplease.js': (opts, fn) => { return Promise.resolve().then(() => fn(opts)) }, @@ -17,11 +19,14 @@ const Token = requireInject('../../lib/token.js', { 'npm-profile': mocks.profile, npmlog: mocks.log, }) -const token = new Token({}) + +const token = new Token(npm) const tokenWithMocks = (mockRequests) => { for (const mod in mockRequests) { - if (mod !== 'npm') { + if (mod === 'npm') + mockRequests.npm = { ...npm, ...mockRequests.npm } + else { if (typeof mockRequests[mod] === 'function') mocks[mod] = mockRequests[mod] else { @@ -44,7 +49,7 @@ const tokenWithMocks = (mockRequests) => { } } - const token = new Token(mockRequests.npm || {}) + const token = new Token(mockRequests.npm || npm) return [token, reset] } diff --git a/test/lib/unpublish.js b/test/lib/unpublish.js index 80a879cb6e6df..b1255b94a8fe4 100644 --- a/test/lib/unpublish.js +++ b/test/lib/unpublish.js @@ -10,6 +10,9 @@ const npm = { silent: false, loglevel: 'silly', }, + output: (...msg) => { + result += msg.join('\n') + }, } const mocks = { npmlog: { silly () {}, verbose () {} }, @@ -18,9 +21,6 @@ const mocks = { 'npm-package-arg': noop, 'npm-registry-fetch': { json: noop }, 'read-package-json': cb => cb(), - '../../lib/utils/output.js': (...msg) => { - result += msg.join('\n') - }, '../../lib/utils/otplease.js': async (opts, fn) => fn(opts), '../../lib/utils/usage.js': () => 'usage instructions', '../../lib/utils/get-identity.js': async () => 'foo', diff --git a/test/lib/utils/audit-error.js b/test/lib/utils/audit-error.js index ea7c84373e9f3..d236f4f9e9eaf 100644 --- a/test/lib/utils/audit-error.js +++ b/test/lib/utils/audit-error.js @@ -1,20 +1,18 @@ const t = require('tap') -const requireInject = require('require-inject') const LOGS = [] +const OUTPUT = [] +const output = (...msg) => OUTPUT.push(msg) +const auditError = require('../../../lib/utils/audit-error.js') + const npm = { command: null, flatOptions: {}, log: { warn: (...msg) => LOGS.push(msg), }, + output, } -const OUTPUT = [] -const output = (...msg) => OUTPUT.push(msg) -const auditError = requireInject('../../../lib/utils/audit-error.js', { - '../../../lib/utils/output.js': output, -}) - t.afterEach(cb => { npm.flatOptions = {} OUTPUT.length = 0 diff --git a/test/lib/utils/npm-usage.js b/test/lib/utils/npm-usage.js index dbbde947ce5ed..fbc453811ec2f 100644 --- a/test/lib/utils/npm-usage.js +++ b/test/lib/utils/npm-usage.js @@ -3,10 +3,10 @@ const t = require('tap') const OUTPUT = [] const output = (...msg) => OUTPUT.push(msg) const requireInject = require('require-inject') -const usage = requireInject('../../../lib/utils/npm-usage.js', { - '../../../lib/utils/output.js': output, -}) +const usage = require('../../../lib/utils/npm-usage.js') + const npm = requireInject('../../../lib/npm.js') +npm.output = output t.test('usage', t => { t.afterEach((cb) => { diff --git a/test/lib/utils/open-url.js b/test/lib/utils/open-url.js index e8ab8f15a14a8..781b70d3e8d31 100644 --- a/test/lib/utils/open-url.js +++ b/test/lib/utils/open-url.js @@ -1,6 +1,8 @@ const { test } = require('tap') const requireInject = require('require-inject') +const OUTPUT = [] +const output = (...args) => OUTPUT.push(args) const npm = { _config: { json: false, @@ -12,11 +14,9 @@ const npm = { npm._config[k] = v }, }, + output, } -const OUTPUT = [] -const output = (...args) => OUTPUT.push(args) - let openerUrl = null let openerOpts = null let openerResult = null @@ -27,7 +27,6 @@ const opener = (url, opts, cb) => { } const openUrl = requireInject('../../../lib/utils/open-url.js', { - '../../../lib/utils/output.js': output, opener, }) diff --git a/test/lib/utils/output.js b/test/lib/utils/output.js deleted file mode 100644 index 72871187d889a..0000000000000 --- a/test/lib/utils/output.js +++ /dev/null @@ -1,8 +0,0 @@ -const t = require('tap') -const logs = [] -console.log = (...msg) => logs.push(msg) -const output = require('../../../lib/utils/output.js') -output('hello', 'world') -output('hello') -output('world') -t.strictSame(logs, [['hello', 'world'], ['hello'], ['world']]) diff --git a/test/lib/utils/reify-output.js b/test/lib/utils/reify-output.js index e41eabcb896e9..e4f58b4668e05 100644 --- a/test/lib/utils/reify-output.js +++ b/test/lib/utils/reify-output.js @@ -1,5 +1,4 @@ const t = require('tap') -const requireInject = require('require-inject') const log = require('npmlog') log.level = 'warn' @@ -13,22 +12,13 @@ const npm = { started: Date.now(), flatOptions: settings, } -const getReifyOutput = tester => - requireInject( - '../../../lib/utils/reify-output.js', - { - '../../../lib/utils/output.js': tester, - } - ) - +const reifyOutput = require('../../../lib/utils/reify-output.js') t.test('missing info', (t) => { t.plan(1) - const reifyOutput = getReifyOutput( - out => t.doesNotHave( - out, - 'looking for funding', - 'should not print fund message if missing info' - ) + npm.output = out => t.doesNotHave( + out, + 'looking for funding', + 'should not print fund message if missing info' ) reifyOutput(npm, { @@ -43,12 +33,10 @@ t.test('missing info', (t) => { t.test('even more missing info', t => { t.plan(1) - const reifyOutput = getReifyOutput( - out => t.doesNotHave( - out, - 'looking for funding', - 'should not print fund message if missing info' - ) + npm.output = out => t.doesNotHave( + out, + 'looking for funding', + 'should not print fund message if missing info' ) reifyOutput(npm, { @@ -60,17 +48,15 @@ t.test('even more missing info', t => { t.test('single package', (t) => { t.plan(1) - const reifyOutput = getReifyOutput( - out => { - if (out.endsWith('looking for funding')) { - t.match( - out, - '1 package is looking for funding', - 'should print single package message' - ) - } + npm.output = out => { + if (out.endsWith('looking for funding')) { + t.match( + out, + '1 package is looking for funding', + 'should print single package message' + ) } - ) + } reifyOutput(npm, { // a report with an error is the same as no report at all, if @@ -110,12 +96,10 @@ t.test('no message when funding config is false', (t) => { settings.fund = true }) settings.fund = false - const reifyOutput = getReifyOutput( - out => { - if (out.endsWith('looking for funding')) - t.fail('should not print funding info', { actual: out }) - } - ) + npm.output = out => { + if (out.endsWith('looking for funding')) + t.fail('should not print funding info', { actual: out }) + } reifyOutput(npm, { actualTree: { @@ -147,17 +131,15 @@ t.test('no message when funding config is false', (t) => { t.test('print appropriate message for many packages', (t) => { t.plan(1) - const reifyOutput = getReifyOutput( - out => { - if (out.endsWith('looking for funding')) { - t.match( - out, - '3 packages are looking for funding', - 'should print single package message' - ) - } + npm.output = out => { + if (out.endsWith('looking for funding')) { + t.match( + out, + '3 packages are looking for funding', + 'should print single package message' + ) } - ) + } reifyOutput(npm, { actualTree: { @@ -206,9 +188,9 @@ t.test('print appropriate message for many packages', (t) => { }) t.test('no output when silent', t => { - const reifyOutput = getReifyOutput(out => { + npm.output = out => { t.fail('should not get output when silent', { actual: out }) - }) + } t.teardown(() => log.level = 'warn') log.level = 'silent' reifyOutput(npm, { @@ -235,9 +217,9 @@ t.test('no output when silent', t => { t.test('packages changed message', t => { const output = [] - const reifyOutput = getReifyOutput(out => { + npm.output = out => { output.push(out) - }) + } // return a test function that builds up the mock and snapshots output const testCase = (t, added, removed, changed, audited, json, command) => { @@ -311,9 +293,7 @@ t.test('packages changed message', t => { t.test('added packages should be looked up within returned tree', t => { t.test('has added pkg in inventory', t => { t.plan(1) - const reifyOutput = getReifyOutput( - out => t.matchSnapshot(out) - ) + npm.output = out => t.matchSnapshot(out) reifyOutput(npm, { actualTree: { @@ -332,9 +312,7 @@ t.test('added packages should be looked up within returned tree', t => { t.test('missing added pkg in inventory', t => { t.plan(1) - const reifyOutput = getReifyOutput( - out => t.matchSnapshot(out) - ) + npm.output = out => t.matchSnapshot(out) reifyOutput(npm, { actualTree: { diff --git a/test/lib/version.js b/test/lib/version.js index e0e07f5172efe..a8fcd831fb5c3 100644 --- a/test/lib/version.js +++ b/test/lib/version.js @@ -11,14 +11,13 @@ const npm = { }, prefix: '', version: '1.0.0', -} -const mocks = { - libnpmversion: noop, - '../../lib/utils/output.js': (...msg) => { + output: (...msg) => { for (const m of msg) result.push(m) }, - '../../lib/utils/usage.js': () => 'usage instructions', +} +const mocks = { + libnpmversion: noop, } const Version = requireInject('../../lib/version.js', mocks) @@ -65,7 +64,7 @@ t.test('too many args', t => { version.exec(['foo', 'bar'], err => { t.match( err, - 'usage instructions', + 'npm version', 'should throw usage instructions error' ) diff --git a/test/lib/whoami.js b/test/lib/whoami.js index 3d9618ffa7150..1a1ecd25742e1 100644 --- a/test/lib/whoami.js +++ b/test/lib/whoami.js @@ -5,11 +5,13 @@ test('whoami', (t) => { t.plan(3) const Whoami = requireInject('../../lib/whoami.js', { '../../lib/utils/get-identity.js': () => Promise.resolve('foo'), - '../../lib/utils/output.js': (output) => { + }) + const whoami = new Whoami({ + flatOptions: {}, + output: (output) => { t.equal(output, 'foo', 'should output the username') }, }) - const whoami = new Whoami({ flatOptions: {} }) whoami.exec([], (err) => { t.ifError(err, 'npm whoami') @@ -21,11 +23,13 @@ test('whoami json', (t) => { t.plan(3) const Whoami = requireInject('../../lib/whoami.js', { '../../lib/utils/get-identity.js': () => Promise.resolve('foo'), - '../../lib/utils/output.js': (output) => { + }) + const whoami = new Whoami({ + flatOptions: { json: true }, + output: (output) => { t.equal(output, '"foo"', 'should output the username as json') }, }) - const whoami = new Whoami({ flatOptions: { json: true } }) whoami.exec([], (err) => { t.ifError(err, 'npm whoami') From 06cc79c23fc1bdb77f33d8c7d373f6ca58308542 Mon Sep 17 00:00:00 2001 From: Gar Date: Wed, 3 Mar 2021 20:26:33 -0800 Subject: [PATCH 8/8] fix(usage): clean up usage declarations Small refactor of commands to allow usage to be more programmatically generated, leading us in the direction of more tighly coupling each command to the params it accepts. PR-URL: https://github.com/npm/cli/pull/2821 Credit: @wraithgar Close: #2821 Reviewed-by: @isaacs --- lib/access.js | 46 +- lib/adduser.js | 16 +- lib/audit.js | 21 +- lib/base-command.js | 38 ++ lib/bin.js | 13 +- lib/bugs.js | 13 +- lib/cache.js | 30 +- lib/ci.js | 12 +- lib/completion.js | 13 +- lib/config.js | 33 +- lib/dedupe.js | 11 +- lib/deprecate.js | 23 +- lib/diff.js | 27 +- lib/dist-tag.js | 23 +- lib/doctor.js | 12 +- lib/edit.js | 13 +- lib/exec.js | 42 +- lib/explain.js | 13 +- lib/explore.js | 13 +- lib/find-dupes.js | 12 +- lib/fund.js | 18 +- lib/get.js | 16 +- lib/help-search.js | 14 +- lib/help.js | 13 +- lib/hook.js | 22 +- lib/init.js | 22 +- lib/install-ci-test.js | 17 +- lib/install-test.js | 21 +- lib/install.js | 38 +- lib/link.js | 20 +- lib/ll.js | 13 +- lib/logout.js | 16 +- lib/ls.js | 16 +- lib/org.js | 23 +- lib/outdated.js | 15 +- lib/owner.js | 39 +- lib/pack.js | 13 +- lib/ping.js | 13 +- lib/prefix.js | 13 +- lib/profile.js | 25 +- lib/prune.js | 15 +- lib/publish.js | 19 +- lib/rebuild.js | 13 +- lib/repo.js | 13 +- lib/restart.js | 5 +- lib/root.js | 14 +- lib/run-script.js | 16 +- lib/search.js | 16 +- lib/set-script.js | 13 +- lib/set.js | 17 +- lib/shrinkwrap.js | 13 +- lib/star.js | 18 +- lib/stars.js | 13 +- lib/start.js | 5 +- lib/stop.js | 5 +- lib/team.js | 27 +- lib/test.js | 5 +- lib/token.js | 28 +- lib/uninstall.js | 17 +- lib/unpublish.js | 14 +- lib/unstar.js | 5 + lib/update.js | 16 +- lib/utils/lifecycle-cmd.js | 15 +- lib/version.js | 21 +- lib/view.js | 17 +- lib/whoami.js | 22 +- .../test-lib-dist-tag.js-TAP.test.js | 25 + tap-snapshots/test-lib-publish.js-TAP.test.js | 6 +- .../test-lib-utils-npm-usage.js-TAP.test.js | 472 ++++++++++++++---- test/lib/access.js | 2 +- test/lib/adduser.js | 4 + test/lib/bin.js | 3 +- test/lib/bugs.js | 5 + test/lib/cache.js | 2 +- test/lib/config.js | 13 +- test/lib/deprecate.js | 4 +- test/lib/explore.js | 2 +- test/lib/load-all-commands.js | 4 +- test/lib/npm.js | 2 +- test/lib/owner.js | 36 +- test/lib/restart.js | 1 - test/lib/start.js | 1 - test/lib/stop.js | 1 - test/lib/utils/lifecycle-cmd.js | 7 +- 84 files changed, 1023 insertions(+), 760 deletions(-) create mode 100644 lib/base-command.js diff --git a/lib/access.js b/lib/access.js index 3bc21119033f5..0df36beeac15f 100644 --- a/lib/access.js +++ b/lib/access.js @@ -4,8 +4,8 @@ const libaccess = require('libnpmaccess') const readPackageJson = require('read-package-json-fast') const otplease = require('./utils/otplease.js') -const usageUtil = require('./utils/usage.js') const getIdentity = require('./utils/get-identity.js') +const BaseCommand = require('./base-command.js') const subcommands = [ 'public', @@ -19,24 +19,23 @@ const subcommands = [ '2fa-not-required', ] -class Access { - constructor (npm) { - this.npm = npm +class Access extends BaseCommand { + static get name () { + return 'access' } - get usage () { - return usageUtil( - 'access', - 'npm access public []\n' + - 'npm access restricted []\n' + - 'npm access grant []\n' + - 'npm access revoke []\n' + - 'npm access 2fa-required []\n' + - 'npm access 2fa-not-required []\n' + - 'npm access ls-packages [||]\n' + - 'npm access ls-collaborators [ []]\n' + - 'npm access edit []' - ) + static get usage () { + return [ + 'public []', + 'restricted []', + 'grant []', + 'revoke []', + '2fa-required []', + '2fa-not-required []', + 'ls-packages [||]', + 'ls-collaborators [ []]', + 'edit []', + ] } async completion (opts) { @@ -66,12 +65,7 @@ class Access { } exec (args, cb) { - this.access(args) - .then(x => cb(null, x)) - .catch(err => err.code === 'EUSAGE' - ? cb(err.message) - : cb(err) - ) + this.access(args).then(() => cb()).catch(cb) } async access ([cmd, ...args]) { @@ -202,12 +196,6 @@ class Access { return name } } - - usageError (msg) { - return Object.assign(new Error(`\nUsage: ${msg}\n\n` + this.usage), { - code: 'EUSAGE', - }) - } } module.exports = Access diff --git a/lib/adduser.js b/lib/adduser.js index 45d602fd2c844..da318a1f3feb8 100644 --- a/lib/adduser.js +++ b/lib/adduser.js @@ -1,6 +1,6 @@ const log = require('npmlog') -const usageUtil = require('./utils/usage.js') const replaceInfo = require('./utils/replace-info.js') +const BaseCommand = require('./base-command.js') const authTypes = { legacy: require('./auth/legacy.js'), oauth: require('./auth/oauth.js'), @@ -8,17 +8,13 @@ const authTypes = { sso: require('./auth/sso.js'), } -class AddUser { - constructor (npm) { - this.npm = npm +class AddUser extends BaseCommand { + static get name () { + return 'adduser' } - /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'adduser', - 'npm adduser [--registry=url] [--scope=@orgname] [--always-auth]' - ) + static get usage () { + return ['[--registry=url] [--scope=@orgname] [--always-auth]'] } exec (args, cb) { diff --git a/lib/audit.js b/lib/audit.js index b8c85605dba43..6e64987b612ae 100644 --- a/lib/audit.js +++ b/lib/audit.js @@ -2,21 +2,20 @@ const Arborist = require('@npmcli/arborist') const auditReport = require('npm-audit-report') const reifyFinish = require('./utils/reify-finish.js') const auditError = require('./utils/audit-error.js') -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Audit { - constructor (npm) { - this.npm = npm +class Audit extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'audit' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'audit', - 'npm audit [--json] [--production]' + - '\nnpm audit fix ' + - '[--force|--package-lock-only|--dry-run|--production|--only=(dev|prod)]' - ) + static get usage () { + return [ + '[--json] [--production]', + 'fix [--force|--package-lock-only|--dry-run|--production|--only=(dev|prod)]', + ] } async completion (opts) { diff --git a/lib/base-command.js b/lib/base-command.js new file mode 100644 index 0000000000000..c31e4a4d7f1b2 --- /dev/null +++ b/lib/base-command.js @@ -0,0 +1,38 @@ +// Base class for npm.commands[cmd] +const usageUtil = require('./utils/usage.js') + +class BaseCommand { + constructor (npm) { + this.npm = npm + } + + get usage () { + let usage = `npm ${this.constructor.name}\n\n` + if (this.constructor.description) + usage = `${usage}${this.constructor.description}\n\n` + + usage = `${usage}Usage:\n` + if (!this.constructor.usage) + usage = `${usage}npm ${this.constructor.name}` + else + usage = `${usage}${this.constructor.usage.map(u => `npm ${this.constructor.name} ${u}`).join('\n')}` + + // Mostly this just appends aliases, this could be more clear + usage = usageUtil(this.constructor.name, usage) + usage = `${usage}\n\nRun "npm ${this.constructor.name} help" for more info` + return usage + } + + usageError (msg) { + if (!msg) { + return Object.assign(new Error(`\nUsage: ${this.usage}`), { + code: 'EUSAGE', + }) + } + + return Object.assign(new Error(`\nUsage: ${msg}\n\n${this.usage}`), { + code: 'EUSAGE', + }) + } +} +module.exports = BaseCommand diff --git a/lib/bin.js b/lib/bin.js index f540cc57c8cd7..1450fb539bffa 100644 --- a/lib/bin.js +++ b/lib/bin.js @@ -1,14 +1,13 @@ const envPath = require('./utils/path.js') -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Bin { - constructor (npm) { - this.npm = npm +class Bin extends BaseCommand { + static get name () { + return 'bin' } - /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('bin', 'npm bin [-g]') + static get usage () { + return ['[-g]'] } exec (args, cb) { diff --git a/lib/bugs.js b/lib/bugs.js index fb0d7c92770c7..1814dd7bc461e 100644 --- a/lib/bugs.js +++ b/lib/bugs.js @@ -1,17 +1,16 @@ const log = require('npmlog') const pacote = require('pacote') const openUrl = require('./utils/open-url.js') -const usageUtil = require('./utils/usage.js') const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js') +const BaseCommand = require('./base-command.js') -class Bugs { - constructor (npm) { - this.npm = npm +class Bugs extends BaseCommand { + static get name () { + return 'bugs' } - /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('bugs', 'npm bugs []') + static get usage () { + return ['[]'] } exec (args, cb) { diff --git a/lib/cache.js b/lib/cache.js index 3ca99fd2562e3..80a5c68ebc0e9 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -4,23 +4,25 @@ const log = require('npmlog') const pacote = require('pacote') const path = require('path') const rimraf = promisify(require('rimraf')) +const BaseCommand = require('./base-command.js') -const usageUtil = require('./utils/usage.js') -class Cache { - constructor (npm) { - this.npm = npm +class Cache extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'cache' } - get usage () { - return usageUtil('cache', - 'npm cache add ' + - '\nnpm cache add ' + - '\nnpm cache add ' + - '\nnpm cache add ' + - '\nnpm cache add @' + - '\nnpm cache clean' + - '\nnpm cache verify' - ) + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [ + 'add ', + 'add ', + 'add ', + 'add ', + 'add @', + 'clean', + 'verify', + ] } async completion (opts) { diff --git a/lib/ci.js b/lib/ci.js index 03a91a60463f2..3ea19937616e6 100644 --- a/lib/ci.js +++ b/lib/ci.js @@ -7,7 +7,6 @@ const fs = require('fs') const readdir = util.promisify(fs.readdir) const log = require('npmlog') -const usageUtil = require('./utils/usage.js') const removeNodeModules = async where => { const rimrafOpts = { glob: false } @@ -18,15 +17,12 @@ const removeNodeModules = async where => { await Promise.all(entries.map(f => rimraf(`${path}/${f}`, rimrafOpts))) process.emit('timeEnd', 'npm-ci:rm') } +const BaseCommand = require('./base-command.js') -class CI { - constructor (npm) { - this.npm = npm - } - +class CI extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('ci', 'npm ci') + static get name () { + return 'ci' } exec (args, cb) { diff --git a/lib/completion.js b/lib/completion.js index 5baf17665800d..3ee68cdacaf95 100644 --- a/lib/completion.js +++ b/lib/completion.js @@ -41,17 +41,18 @@ const allConfs = configNames.concat(shorthandNames) const isWindowsShell = require('./utils/is-windows-shell.js') const fileExists = require('./utils/file-exists.js') -const usageUtil = require('./utils/usage.js') const { promisify } = require('util') +const BaseCommand = require('./base-command.js') -class Completion { - constructor (npm) { - this.npm = npm +class Completion extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'completion' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('completion', 'source <(npm completion)') + static get description () { + return 'npm command completion script. save to ~/.bashrc or ~/.zshrc' } // completion for the completion command diff --git a/lib/config.js b/lib/config.js index 7009f46016d23..c29253e430a33 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,5 +1,4 @@ const { defaults, types } = require('./utils/config.js') -const usageUtil = require('./utils/usage.js') const mkdirp = require('mkdirp-infer-owner') const { dirname } = require('path') @@ -28,22 +27,22 @@ const keyValues = args => { const publicVar = k => !/^(\/\/[^:]+:)?_/.test(k) -class Config { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Config extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'config' } - get usage () { - return usageUtil( - 'config', - 'npm config set = [= ...]' + - '\nnpm config get [ [ ...]]' + - '\nnpm config delete [ ...]' + - '\nnpm config list [--json]' + - '\nnpm config edit' + - '\nnpm set = [= ...]' + - '\nnpm get [ [ ...]]' - ) + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [ + 'set = [= ...]', + 'get [ [ ...]]', + 'delete [ ...]', + 'list [--json]', + 'edit', + ] } async completion (opts) { @@ -253,10 +252,6 @@ ${defData} } this.npm.output(JSON.stringify(publicConf, null, 2)) } - - usageError () { - return Object.assign(new Error(this.usage), { code: 'EUSAGE' }) - } } module.exports = Config diff --git a/lib/dedupe.js b/lib/dedupe.js index 59978895effb2..50a56211fc847 100644 --- a/lib/dedupe.js +++ b/lib/dedupe.js @@ -1,16 +1,13 @@ // dedupe duplicated packages, or find them in the tree const Arborist = require('@npmcli/arborist') -const usageUtil = require('./utils/usage.js') const reifyFinish = require('./utils/reify-finish.js') -class Dedupe { - constructor (npm) { - this.npm = npm - } +const BaseCommand = require('./base-command.js') +class Dedupe extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('dedupe', 'npm dedupe') + static get name () { + return 'dedupe' } exec (args, cb) { diff --git a/lib/deprecate.js b/lib/deprecate.js index 48f27ab6c35e8..a0c67f805d2f9 100644 --- a/lib/deprecate.js +++ b/lib/deprecate.js @@ -4,18 +4,17 @@ const npa = require('npm-package-arg') const semver = require('semver') const getIdentity = require('./utils/get-identity.js') const libaccess = require('libnpmaccess') -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Deprecate { - constructor (npm) { - this.npm = npm +class Deprecate extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'deprecate' } - get usage () { - return usageUtil( - 'deprecate', - 'npm deprecate [@] ' - ) + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return ['[@] '] } async completion (opts) { @@ -71,12 +70,6 @@ class Deprecate { ignoreBody: true, })) } - - usageError () { - return Object.assign(new Error(`\nUsage: ${this.usage}`), { - code: 'EUSAGE', - }) - } } module.exports = Deprecate diff --git a/lib/diff.js b/lib/diff.js index ed36a30673c43..0e322ec643849 100644 --- a/lib/diff.js +++ b/lib/diff.js @@ -8,23 +8,24 @@ const npmlog = require('npmlog') const pacote = require('pacote') const pickManifest = require('npm-pick-manifest') -const usageUtil = require('./utils/usage.js') const readLocalPkg = require('./utils/read-local-package.js') +const BaseCommand = require('./base-command.js') -class Diff { - constructor (npm) { - this.npm = npm +class Diff extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'diff' } - get usage () { - return usageUtil( - 'diff', - 'npm diff [...]' + - '\nnpm diff --diff= [...]' + - '\nnpm diff --diff= [--diff=] [...]' + - '\nnpm diff --diff= [--diff=] [...]' + - '\nnpm diff [--diff-ignore-all-space] [--diff-name-only] [...] [...]' - ) + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [ + '[...]', + '--diff= [...]', + '--diff= [--diff=] [...]', + '--diff= [--diff=] [...]', + '[--diff-ignore-all-space] [--diff-name-only] [...] [...]', + ] } get where () { diff --git a/lib/dist-tag.js b/lib/dist-tag.js index 4b7e2602043be..cdc95ac6f0cd7 100644 --- a/lib/dist-tag.js +++ b/lib/dist-tag.js @@ -5,20 +5,21 @@ const semver = require('semver') const otplease = require('./utils/otplease.js') const readLocalPkgName = require('./utils/read-local-package.js') -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class DistTag { - constructor (npm) { - this.npm = npm +class DistTag extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'dist-tag' } - get usage () { - return usageUtil( - 'dist-tag', - 'npm dist-tag add @ []' + - '\nnpm dist-tag rm ' + - '\nnpm dist-tag ls []' - ) + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [ + 'add @ []', + 'rm ', + 'ls []', + ] } async completion (opts) { diff --git a/lib/doctor.js b/lib/doctor.js index 63619d0cf5377..fbe44714140aa 100644 --- a/lib/doctor.js +++ b/lib/doctor.js @@ -11,7 +11,6 @@ const { promisify } = require('util') const ansiTrim = require('./utils/ansi-trim.js') const isWindows = require('./utils/is-windows.js') const ping = require('./utils/ping.js') -const usageUtil = require('./utils/usage.js') const { defaults: { registry: defaultRegistry } } = require('./utils/config.js') const lstat = promisify(fs.lstat) const readdir = promisify(fs.readdir) @@ -31,14 +30,11 @@ const maskLabel = mask => { return label.join(', ') } -class Doctor { - constructor (npm) { - this.npm = npm - } - +const BaseCommand = require('./base-command.js') +class Doctor extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('doctor', 'npm doctor') + static get name () { + return 'doctor' } exec (args, cb) { diff --git a/lib/edit.js b/lib/edit.js index a7dbb38205b02..1dbe8e4c103ad 100644 --- a/lib/edit.js +++ b/lib/edit.js @@ -4,18 +4,19 @@ const { resolve } = require('path') const fs = require('graceful-fs') const { spawn } = require('child_process') -const usageUtil = require('./utils/usage.js') const splitPackageNames = require('./utils/split-package-names.js') const completion = require('./utils/completion/installed-shallow.js') +const BaseCommand = require('./base-command.js') -class Edit { - constructor (npm) { - this.npm = npm +class Edit extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'edit' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('edit', 'npm edit [/...]') + static get usage () { + return ['[/...]'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/exec.js b/lib/exec.js index 69c3cfe75c2cc..b2443b17accd2 100644 --- a/lib/exec.js +++ b/lib/exec.js @@ -1,4 +1,3 @@ -const usageUtil = require('./utils/usage.js') const { promisify } = require('util') const read = promisify(require('read')) const mkdirp = require('mkdirp-infer-owner') @@ -12,6 +11,7 @@ const pacote = require('pacote') const npa = require('npm-package-arg') const fileExists = require('./utils/file-exists.js') const PATH = require('./utils/path.js') +const BaseCommand = require('./base-command.js') // it's like this: // @@ -38,31 +38,25 @@ const PATH = require('./utils/path.js') // runScript({ pkg, event: 'npx', ... }) // process.env.npm_lifecycle_event = 'npx' -class Exec { - constructor (npm) { - this.npm = npm +class Exec extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'exec' } - get usage () { - return usageUtil('exec', - 'Run a command from a local or remote npm package.\n\n' + - - 'npm exec -- [@] [args...]\n' + - 'npm exec --package=[@] -- [args...]\n' + - 'npm exec -c \' [args...]\'\n' + - 'npm exec --package=foo -c \' [args...]\'\n' + - '\n' + - 'npx [@] [args...]\n' + - 'npx -p [@] [args...]\n' + - 'npx -c \' [args...]\'\n' + - 'npx -p [@] -c \' [args...]\'' + - '\n' + - 'Run without --call or positional args to open interactive subshell\n', - - '\n--package= (may be specified multiple times)\n' + - '-p is a shorthand for --package only when using npx executable\n' + - '-c --call= (may not be mixed with positional arguments)' - ) + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Run a command from a local or remote npm package.' + } + + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [ + '-- [@] [args...]', + '--package=[@] -- [args...]', + '-c \' [args...]\'', + '--package=foo -c \' [args...]\'', + ] } exec (args, cb) { diff --git a/lib/explain.js b/lib/explain.js index f46d3b5072637..6af7611867786 100644 --- a/lib/explain.js +++ b/lib/explain.js @@ -1,4 +1,3 @@ -const usageUtil = require('./utils/usage.js') const { explainNode } = require('./utils/explain-dep.js') const completion = require('./utils/completion/installed-deep.js') const Arborist = require('@npmcli/arborist') @@ -6,15 +5,17 @@ const npa = require('npm-package-arg') const semver = require('semver') const { relative, resolve } = require('path') const validName = require('validate-npm-package-name') +const BaseCommand = require('./base-command.js') -class Explain { - constructor (npm) { - this.npm = npm +class Explain extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'explain' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('explain', 'npm explain ') + static get usage () { + return [''] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/explore.js b/lib/explore.js index e09e867406e1d..34f6d10793c7e 100644 --- a/lib/explore.js +++ b/lib/explore.js @@ -5,16 +5,17 @@ const rpj = require('read-package-json-fast') const runScript = require('@npmcli/run-script') const { join, resolve, relative } = require('path') const completion = require('./utils/completion/installed-shallow.js') -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Explore { - constructor (npm) { - this.npm = npm +class Explore extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'explore' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('explore', 'npm explore [ -- ]') + static get usage () { + return [' [ -- ]'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/find-dupes.js b/lib/find-dupes.js index 5061be9cc381a..ecb945f47bb41 100644 --- a/lib/find-dupes.js +++ b/lib/find-dupes.js @@ -1,14 +1,10 @@ // dedupe duplicated packages, or find them in the tree -const usageUtil = require('./utils/usage.js') - -class FindDupes { - constructor (npm) { - this.npm = npm - } +const BaseCommand = require('./base-command.js') +class FindDupes extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('find-dupes', 'npm find-dupes') + static get name () { + return 'find-dupes' } exec (args, cb) { diff --git a/lib/fund.js b/lib/fund.js index 826c3170e7e8a..a723c62d2c3c5 100644 --- a/lib/fund.js +++ b/lib/fund.js @@ -13,25 +13,23 @@ const { const completion = require('./utils/completion/installed-deep.js') const openUrl = require('./utils/open-url.js') -const usageUtil = require('./utils/usage.js') const getPrintableName = ({ name, version }) => { const printableVersion = version ? `@${version}` : '' return `${name}${printableVersion}` } -class Fund { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') + +class Fund extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'fund' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'fund', - 'npm fund', - 'npm fund [--json] [--browser] [--unicode] [[<@scope>/] [--which=]' - ) + static get usage () { + return ['[--json] [--browser] [--unicode] [[<@scope>/] [--which=]'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/get.js b/lib/get.js index a5b2f5514473d..a5d58accc8307 100644 --- a/lib/get.js +++ b/lib/get.js @@ -1,16 +1,14 @@ -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Get { - constructor (npm) { - this.npm = npm +class Get extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'get' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'get', - 'npm get [ ...] (See `npm config`)' - ) + static get usage () { + return ['[ ...] (See `npm config`)'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/help-search.js b/lib/help-search.js index 9648e3b1478d5..4e727c3e72954 100644 --- a/lib/help-search.js +++ b/lib/help-search.js @@ -1,21 +1,23 @@ const fs = require('fs') const path = require('path') const color = require('ansicolors') -const usageUtil = require('./utils/usage.js') const npmUsage = require('./utils/npm-usage.js') const { promisify } = require('util') const glob = promisify(require('glob')) const readFile = promisify(fs.readFile) const didYouMean = require('./utils/did-you-mean.js') const { cmdList } = require('./utils/cmd-list.js') +const BaseCommand = require('./base-command.js') -class HelpSearch { - constructor (npm) { - this.npm = npm +class HelpSearch extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'help-search' } - get usage () { - return usageUtil('help-search', 'npm help-search ') + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [''] } exec (args, cb) { diff --git a/lib/help.js b/lib/help.js index ef7e3bfd03214..93abf878ba26f 100644 --- a/lib/help.js +++ b/lib/help.js @@ -5,16 +5,17 @@ const log = require('npmlog') const openUrl = require('./utils/open-url.js') const glob = require('glob') -const usage = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Help { - constructor (npm) { - this.npm = npm +class Help extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'help' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usage('help', 'npm help []') + static get usage () { + return [' []'] } async completion (opts) { diff --git a/lib/hook.js b/lib/hook.js index a6f04d6532e50..6cda3504f43d7 100644 --- a/lib/hook.js +++ b/lib/hook.js @@ -2,20 +2,20 @@ const hookApi = require('libnpmhook') const otplease = require('./utils/otplease.js') const relativeDate = require('tiny-relative-date') const Table = require('cli-table3') -const usageUtil = require('./utils/usage.js') -class Hook { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Hook extends BaseCommand { + static get name () { + return 'hook' } - get usage () { - return usageUtil('hook', [ - 'npm hook add [--type=]', - 'npm hook ls [pkg]', - 'npm hook rm ', - 'npm hook update ', - ].join('\n')) + static get usage () { + return [ + 'add [--type=]', + 'ls [pkg]', + 'rm ', + 'update ', + ] } exec (args, cb) { diff --git a/lib/init.js b/lib/init.js index 3f9abbcdd354d..42b02dfdc6a77 100644 --- a/lib/init.js +++ b/lib/init.js @@ -1,21 +1,21 @@ const initJson = require('init-package-json') const npa = require('npm-package-arg') -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Init { - constructor (npm) { - this.npm = npm +class Init extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'init' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'init', - '\nnpm init [--force|-f|--yes|-y|--scope]' + - '\nnpm init <@scope> (same as `npx <@scope>/create`)' + - '\nnpm init [<@scope>/] (same as `npx [<@scope>/]create-`)' - ) + static get usage () { + return [ + '[--force|-f|--yes|-y|--scope]', + '<@scope> (same as `npx <@scope>/create`)', + '[<@scope>/] (same as `npx [<@scope>/]create-`)', + ] } exec (args, cb) { diff --git a/lib/install-ci-test.js b/lib/install-ci-test.js index d1740999d4b67..c52b5c9e8073f 100644 --- a/lib/install-ci-test.js +++ b/lib/install-ci-test.js @@ -1,19 +1,12 @@ // npm install-ci-test // Runs `npm ci` and then runs `npm test` -const usageUtil = require('./utils/usage.js') +const CI = require('./ci.js') -class InstallCITest { - constructor (npm) { - this.npm = npm - } - - get usage () { - return usageUtil( - 'install-ci-test', - 'npm install-ci-test [args]' + - '\nSame args as `npm ci`' - ) +class InstallCITest extends CI { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'install-ci-test' } exec (args, cb) { diff --git a/lib/install-test.js b/lib/install-test.js index 487f8da00b6d3..76c6f367dd3c8 100644 --- a/lib/install-test.js +++ b/lib/install-test.js @@ -1,23 +1,12 @@ // npm install-test // Runs `npm install` and then runs `npm test` -const usageUtil = require('./utils/usage.js') +const Install = require('./install.js') -class InstallTest { - constructor (npm) { - this.npm = npm - } - - get usage () { - return usageUtil( - 'install-test', - 'npm install-test [args]' + - '\nSame args as `npm install`' - ) - } - - async completion (opts) { - return this.npm.commands.install.completion(opts) +class InstallTest extends Install { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'install-test' } exec (args, cb) { diff --git a/lib/install.js b/lib/install.js index d7fd384d5bd6f..8df63a219ef74 100644 --- a/lib/install.js +++ b/lib/install.js @@ -3,35 +3,33 @@ const fs = require('fs') const util = require('util') const readdir = util.promisify(fs.readdir) -const usageUtil = require('./utils/usage.js') const reifyFinish = require('./utils/reify-finish.js') const log = require('npmlog') const { resolve, join } = require('path') const Arborist = require('@npmcli/arborist') const runScript = require('@npmcli/run-script') -class Install { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Install extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'install' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'install', - 'npm install (with no args, in package dir)' + - '\nnpm install [<@scope>/]' + - '\nnpm install [<@scope>/]@' + - '\nnpm install [<@scope>/]@' + - '\nnpm install [<@scope>/]@' + - '\nnpm install @npm:' + - '\nnpm install ' + - '\nnpm install ' + - '\nnpm install ' + - '\nnpm install ' + - '\nnpm install /', - '[--save-prod|--save-dev|--save-optional|--save-peer] [--save-exact] [--no-save]' - ) + static get usage () { + return [ + '[<@scope>/]', + '[<@scope>/]@', + '[<@scope>/]@', + '[<@scope>/]@', + '@npm:', + '', + '', + '', + '', + '/ [--save-prod|--save-dev|--save-optional|--save-peer] [--save-exact] [--no-save]', + ] } async completion (opts) { diff --git a/lib/link.js b/lib/link.js index 6d5e207105825..66f83d9f5b0a7 100644 --- a/lib/link.js +++ b/lib/link.js @@ -8,21 +8,21 @@ const npa = require('npm-package-arg') const rpj = require('read-package-json-fast') const semver = require('semver') -const usageUtil = require('./utils/usage.js') const reifyFinish = require('./utils/reify-finish.js') -class Link { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Link extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'link' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'link', - 'npm link (in package dir)' + - '\nnpm link [<@scope>/][@]' - ) + static get usage () { + return [ + '(in package dir)', + '[<@scope>/][@]', + ] } async completion (opts) { diff --git a/lib/ll.js b/lib/ll.js index 7915f5d27c011..3e3428a7ff5eb 100644 --- a/lib/ll.js +++ b/lib/ll.js @@ -1,13 +1,14 @@ const LS = require('./ls.js') -const usageUtil = require('./utils/usage.js') class LL extends LS { /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'll', - 'npm ll [[<@scope>/] ...]' - ) + static get name () { + return 'll' + } + + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return ['[[<@scope>/] ...]'] } exec (args, cb) { diff --git a/lib/logout.js b/lib/logout.js index 9fb1eab21a152..b3f64f671d326 100644 --- a/lib/logout.js +++ b/lib/logout.js @@ -1,19 +1,17 @@ const log = require('npmlog') const getAuth = require('npm-registry-fetch/auth.js') const npmFetch = require('npm-registry-fetch') -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Logout { - constructor (npm) { - this.npm = npm +class Logout extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'logout' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'logout', - 'npm logout [--registry=] [--scope=<@scope>]' - ) + static get usage () { + return ['[--registry=] [--scope=<@scope>]'] } exec (args, cb) { diff --git a/lib/ls.js b/lib/ls.js index b94684401e6f6..9ff2761c2f928 100644 --- a/lib/ls.js +++ b/lib/ls.js @@ -7,7 +7,6 @@ const Arborist = require('@npmcli/arborist') const { breadth } = require('treeverse') const npa = require('npm-package-arg') -const usageUtil = require('./utils/usage.js') const completion = require('./utils/completion/installed-deep.js') const _depth = Symbol('depth') @@ -21,18 +20,17 @@ const _parent = Symbol('parent') const _problems = Symbol('problems') const _required = Symbol('required') const _type = Symbol('type') +const BaseCommand = require('./base-command.js') -class LS { - constructor (npm) { - this.npm = npm +class LS extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'ls' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'ls', - 'npm ls [[<@scope>/] ...]' - ) + static get usage () { + return ['npm ls [[<@scope>/] ...]'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/org.js b/lib/org.js index 2a08941a83f3a..b9f84b060f8a8 100644 --- a/lib/org.js +++ b/lib/org.js @@ -1,20 +1,21 @@ const liborg = require('libnpmorg') -const usageUtil = require('./utils/usage.js') const otplease = require('./utils/otplease.js') const Table = require('cli-table3') +const BaseCommand = require('./base-command.js') -class Org { - constructor (npm) { - this.npm = npm +class Org extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'org' } - get usage () { - return usageUtil( - 'org', - 'npm org set orgname username [developer | admin | owner]\n' + - 'npm org rm orgname username\n' + - 'npm org ls orgname []' - ) + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [ + 'set orgname username [developer | admin | owner]', + 'rm orgname username', + 'ls orgname []', + ] } async completion (opts) { diff --git a/lib/outdated.js b/lib/outdated.js index be5820870411c..7225577ea42d4 100644 --- a/lib/outdated.js +++ b/lib/outdated.js @@ -9,19 +9,18 @@ const pickManifest = require('npm-pick-manifest') const Arborist = require('@npmcli/arborist') -const usageUtil = require('./utils/usage.js') const ansiTrim = require('./utils/ansi-trim.js') +const BaseCommand = require('./base-command.js') -class Outdated { - constructor (npm) { - this.npm = npm +class Outdated extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'outdated' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('outdated', - 'npm outdated [[<@scope>/] ...]' - ) + static get usage () { + return ['[[<@scope>/] ...]'] } exec (args, cb) { diff --git a/lib/owner.js b/lib/owner.js index cd387e94d9eaf..b62f125ac3bb6 100644 --- a/lib/owner.js +++ b/lib/owner.js @@ -5,24 +5,21 @@ const pacote = require('pacote') const otplease = require('./utils/otplease.js') const readLocalPkg = require('./utils/read-local-package.js') -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Owner { - constructor (npm) { - this.npm = npm +class Owner extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'owner' } - get usage () { - return usageUtil( - 'owner', - 'npm owner add [<@scope>/]' + - '\nnpm owner rm [<@scope>/]' + - '\nnpm owner ls [<@scope>/]' - ) - } - - get usageError () { - return Object.assign(new Error(this.usage), { code: 'EUSAGE' }) + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [ + 'add [<@scope>/]', + 'rm [<@scope>/]', + 'ls [<@scope>/]', + ] } async completion (opts) { @@ -69,7 +66,7 @@ class Owner { case 'remove': return this.rm(args[0], args[1], opts) default: - throw this.usageError + throw this.usageError() } } @@ -77,7 +74,7 @@ class Owner { if (!pkg) { const pkgName = await readLocalPkg(this.npm) if (!pkgName) - throw this.usageError + throw this.usageError() pkg = pkgName } @@ -101,12 +98,12 @@ class Owner { async add (user, pkg, opts) { if (!user) - throw this.usageError + throw this.usageError() if (!pkg) { const pkgName = await readLocalPkg(this.npm) if (!pkgName) - throw this.usageError + throw this.usageError() pkg = pkgName } @@ -119,12 +116,12 @@ class Owner { async rm (user, pkg, opts) { if (!user) - throw this.usageError + throw this.usageError() if (!pkg) { const pkgName = await readLocalPkg(this.npm) if (!pkgName) - throw this.usageError + throw this.usageError() pkg = pkgName } diff --git a/lib/pack.js b/lib/pack.js index 7ffe3138ed55f..326fcc0cd1441 100644 --- a/lib/pack.js +++ b/lib/pack.js @@ -8,16 +8,17 @@ const { getContents, logTar } = require('./utils/tar.js') const writeFile = util.promisify(require('fs').writeFile) -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Pack { - constructor (npm) { - this.npm = npm +class Pack extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'pack' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('pack', 'npm pack [[<@scope>/]...] [--dry-run]') + static get usage () { + return ['[[<@scope>/]...] [--dry-run]'] } exec (args, cb) { diff --git a/lib/ping.js b/lib/ping.js index 3643fe3b621aa..e60b1f1debd80 100644 --- a/lib/ping.js +++ b/lib/ping.js @@ -1,15 +1,16 @@ const log = require('npmlog') -const usageUtil = require('./utils/usage.js') const pingUtil = require('./utils/ping.js') +const BaseCommand = require('./base-command.js') -class Ping { - constructor (npm) { - this.npm = npm +class Ping extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'ping' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('ping', 'npm ping\nping registry') + static get description () { + return 'ping registry' } exec (args, cb) { diff --git a/lib/prefix.js b/lib/prefix.js index 8ec5ab9efcf78..5ade87f6429f6 100644 --- a/lib/prefix.js +++ b/lib/prefix.js @@ -1,13 +1,14 @@ -const usageUtil = require('./utils/usage.js') +const BaseCommand = require('./base-command.js') -class Prefix { - constructor (npm) { - this.npm = npm +class Prefix extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'prefix' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('prefix', 'npm prefix [-g]') + static get usage () { + return ['[-g]'] } exec (args, cb) { diff --git a/lib/profile.js b/lib/profile.js index a0a8606014da4..1c0df49888540 100644 --- a/lib/profile.js +++ b/lib/profile.js @@ -9,7 +9,6 @@ const Table = require('cli-table3') const otplease = require('./utils/otplease.js') const pulseTillDone = require('./utils/pulse-till-done.js') const readUserInfo = require('./utils/read-user-info.js') -const usageUtil = require('./utils/usage.js') const qrcode = url => new Promise((resolve) => qrcodeTerminal.generate(url, resolve)) @@ -37,19 +36,21 @@ const writableProfileKeys = [ 'github', ] -class Profile { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Profile extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'profile' } - get usage () { - return usageUtil( - 'profile', - 'npm profile enable-2fa [auth-only|auth-and-writes]\n', - 'npm profile disable-2fa\n', - 'npm profile get []\n', - 'npm profile set ' - ) + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [ + 'enable-2fa [auth-only|auth-and-writes]', + 'disable-2fa', + 'get []', + 'set ', + ] } async completion (opts) { diff --git a/lib/prune.js b/lib/prune.js index b839301d5194c..c2cddb1a22b33 100644 --- a/lib/prune.js +++ b/lib/prune.js @@ -1,18 +1,17 @@ // prune extraneous packages const Arborist = require('@npmcli/arborist') -const usageUtil = require('./utils/usage.js') const reifyFinish = require('./utils/reify-finish.js') -class Prune { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Prune extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'prune' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('prune', - 'npm prune [[<@scope>/]...] [--production]' - ) + static get usage () { + return ['[[<@scope>/]...] [--production]'] } exec (args, cb) { diff --git a/lib/publish.js b/lib/publish.js index b0bf922138ce3..f8e0eafe11886 100644 --- a/lib/publish.js +++ b/lib/publish.js @@ -10,7 +10,6 @@ const npmFetch = require('npm-registry-fetch') const { flatten } = require('./utils/flat-options.js') const otplease = require('./utils/otplease.js') -const usageUtil = require('./utils/usage.js') const { getContents, logTar } = require('./utils/tar.js') // this is the only case in the CLI where we use the old full slow @@ -18,16 +17,18 @@ const { getContents, logTar } = require('./utils/tar.js') // defaults and metadata, like git sha's and default scripts and all that. const readJson = util.promisify(require('read-package-json')) -class Publish { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Publish extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'publish' } - get usage () { - return usageUtil('publish', - 'npm publish [] [--tag ] [--access ] [--dry-run]' + - '\n\nPublishes \'.\' if no argument supplied' + - '\nSets tag `latest` if no --tag specified') + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return [ + '[] [--tag ] [--access ] [--dry-run]', + ] } exec (args, cb) { diff --git a/lib/rebuild.js b/lib/rebuild.js index ffbdebc21fa27..74f5ae5f6eba5 100644 --- a/lib/rebuild.js +++ b/lib/rebuild.js @@ -2,17 +2,18 @@ const { resolve } = require('path') const Arborist = require('@npmcli/arborist') const npa = require('npm-package-arg') const semver = require('semver') -const usageUtil = require('./utils/usage.js') const completion = require('./utils/completion/installed-deep.js') -class Rebuild { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Rebuild extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'rebuild' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('rebuild', 'npm rebuild [[<@scope>/][@] ...]') + static get usage () { + return ['[[<@scope>/][@] ...]'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/repo.js b/lib/repo.js index f0be99d4d69e9..aa07e07a819f7 100644 --- a/lib/repo.js +++ b/lib/repo.js @@ -4,16 +4,17 @@ const { URL } = require('url') const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js') const openUrl = require('./utils/open-url.js') -const usageUtil = require('./utils/usage.js') -class Repo { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Repo extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'repo' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('repo', 'npm repo [ [ ...]]') + static get usage () { + return ['[ [ ...]]'] } exec (args, cb) { diff --git a/lib/restart.js b/lib/restart.js index d5a7789ca92c0..1f3eb5af94f82 100644 --- a/lib/restart.js +++ b/lib/restart.js @@ -2,8 +2,9 @@ const LifecycleCmd = require('./utils/lifecycle-cmd.js') // This ends up calling run-script(['restart', ...args]) class Restart extends LifecycleCmd { - constructor (npm) { - super(npm, 'restart') + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'restart' } } module.exports = Restart diff --git a/lib/root.js b/lib/root.js index 7c3fa2bbb3544..1fe82c6fad773 100644 --- a/lib/root.js +++ b/lib/root.js @@ -1,13 +1,13 @@ -const usageUtil = require('./utils/usage.js') - -class Root { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Root extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'root' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('root', 'npm root [-g]') + static get usage () { + return ['[-g]'] } exec (args, cb) { diff --git a/lib/run-script.js b/lib/run-script.js index dc822668d0318..3ea85b79ffd18 100644 --- a/lib/run-script.js +++ b/lib/run-script.js @@ -3,7 +3,6 @@ const { isServerPackage } = runScript const readJson = require('read-package-json-fast') const { resolve } = require('path') const log = require('npmlog') -const usageUtil = require('./utils/usage.js') const didYouMean = require('./utils/did-you-mean.js') const isWindowsShell = require('./utils/is-windows-shell.js') @@ -18,17 +17,16 @@ const cmdList = [ 'version', ].reduce((l, p) => l.concat(['pre' + p, p, 'post' + p]), []) -class RunScript { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class RunScript extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'run-script' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'run-script', - 'npm run-script [-- ]' - ) + static get usage () { + return [' [-- ]'] } async completion (opts) { diff --git a/lib/search.js b/lib/search.js index 35e3eeb0e552c..c24000156f67a 100644 --- a/lib/search.js +++ b/lib/search.js @@ -5,7 +5,6 @@ const log = require('npmlog') const formatPackageStream = require('./search/format-package-stream.js') const packageFilter = require('./search/package-filter.js') -const usageUtil = require('./utils/usage.js') function prepareIncludes (args) { return args @@ -25,17 +24,16 @@ function prepareExcludes (searchexclude) { .filter(s => s) } -class Search { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Search extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'search' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil( - 'search', - 'npm search [-l|--long] [--json] [--parseable] [--no-description] [search terms ...]' - ) + static get usage () { + return ['[-l|--long] [--json] [--parseable] [--no-description] [search terms ...]'] } exec (args, cb) { diff --git a/lib/set-script.js b/lib/set-script.js index 25545898e1640..6241981323c4a 100644 --- a/lib/set-script.js +++ b/lib/set-script.js @@ -1,17 +1,18 @@ const log = require('npmlog') -const usageUtil = require('./utils/usage.js') const fs = require('fs') const parseJSON = require('json-parse-even-better-errors') const rpj = require('read-package-json-fast') -class SetScript { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class SetScript extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'set-script' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('set-script', 'npm set-script [