Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/v7.13.0 #3242

Merged
merged 7 commits into from May 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,21 @@
## v7.13.0 (2021-05-13)

* [`076420c14`](https://github.com/npm/cli/commit/076420c149d097056f687e44e21744b743b86e4e)
[#3231](https://github.com/npm/cli/issues/3231)
feat(publish): add workspace support
([@wraithgar](https://github.com/wraithgar))
* [`370b36a36`](https://github.com/npm/cli/commit/370b36a36ca226840761e4214cbccaf2a1a90e3c)
[#3241](https://github.com/npm/cli/issues/3241)
feat(fund): add workspaces support
([@ruyadorno](https://github.com/ruyadorno))

### DEPENDENCIES

* [`0c18e4f77`](https://github.com/npm/cli/commit/0c18e4f774562fa054fedf323bea25805ebf39b3)
`@npmcli/arborist@2.5.0`
* [`b551c6811`](https://github.com/npm/cli/commit/b551c6811251dbc901f47fea3c137f93e205a9e4)
`libnpmfund@1.1.0`

## v7.12.1 (2021-05-10)

### BUG FIXES
Expand Down
56 changes: 56 additions & 0 deletions docs/content/commands/npm-fund.md
Expand Up @@ -8,6 +8,7 @@ description: Retrieve funding information

```bash
npm fund [<pkg>]
npm fund [-w <workspace-name>]
```

### Description
Expand All @@ -24,6 +25,43 @@ The list will avoid duplicated entries and will stack all packages that
share the same url as a single entry. Thus, the list does not have the same
shape of the output from `npm ls`.

#### Example

### Workspaces support

It's possible to filter the results to only include a single workspace and its
dependencies using the `workspace` config option.

#### Example:

Here's an example running `npm fund` in a project with a configured
workspace `a`:

```bash
$ npm fund
test-workspaces-fund@1.0.0
+-- https://example.com/a
| | `-- a@1.0.0
| `-- https://example.com/maintainer
| `-- foo@1.0.0
+-- https://example.com/npmcli-funding
| `-- @npmcli/test-funding
`-- https://example.com/org
`-- bar@2.0.0
```

And here is an example of the expected result when filtering only by
a specific workspace `a` in the same project:

```bash
$ npm fund -w a
test-workspaces-fund@1.0.0
`-- https://example.com/a
| `-- a@1.0.0
`-- https://example.com/maintainer
`-- foo@2.0.0
```

### Configuration

#### browser
Expand All @@ -48,6 +86,23 @@ Show information in JSON format.
Whether to represent the tree structure using unicode characters.
Set it to `false` in order to use all-ansi output.

#### `workspace`

* Default:
* Type: String (can be set multiple times)

Enable running a command in the context of the configured workspaces of the
current project while filtering by running only the workspaces defined by
this configuration option.

Valid values for the `workspace` config are either:
* Workspace names
* Path to a workspace directory
* Path to a parent workspace directory (will result to selecting all of the
nested workspaces)

This value is not exported to the environment for child processes.

#### which

* Type: Number
Expand All @@ -61,3 +116,4 @@ If there are multiple funding sources, which 1-indexed source URL to open.
* [npm docs](/commands/npm-docs)
* [npm ls](/commands/npm-ls)
* [npm config](/commands/npm-config)
* [npm workspaces](/using-npm/workspaces)
7 changes: 7 additions & 0 deletions docs/content/commands/npm-publish.md
Expand Up @@ -47,6 +47,13 @@ by specifying a different default registry or using a
actually publishing to the registry. Reports the details of what would
have been published.

* `[--workspaces]`: Enables workspace context while publishing. All
workspace packages will be published.

* `[--workspace]`: Enables workspaces context and limits results to only
those specified by this config item. Only the packages in the
workspaces given will be published.

The publish will fail if the package name and version combination already
exists in the specified registry.

Expand Down
16 changes: 11 additions & 5 deletions lib/fund.js
Expand Up @@ -13,15 +13,14 @@ const {

const completion = require('./utils/completion/installed-deep.js')
const openUrl = require('./utils/open-url.js')
const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js')

const getPrintableName = ({ name, version }) => {
const printableVersion = version ? `@${version}` : ''
return `${name}${printableVersion}`
}

const BaseCommand = require('./base-command.js')

class Fund extends BaseCommand {
class Fund extends ArboristWorkspaceCmd {
/* istanbul ignore next - see test/lib/load-all-commands.js */
static get description () {
return 'Retrieve funding information'
Expand All @@ -38,6 +37,7 @@ class Fund extends BaseCommand {
'json',
'browser',
'unicode',
'workspace',
'which',
]
}
Expand Down Expand Up @@ -92,10 +92,16 @@ class Fund extends BaseCommand {
return
}

const fundingInfo = getFundingInfo(tree, {
...this.flatOptions,
log: this.npm.log,
workspaces: this.workspaces,
})

if (this.npm.config.get('json'))
this.npm.output(this.printJSON(getFundingInfo(tree)))
this.npm.output(this.printJSON(fundingInfo))
else
this.npm.output(this.printHuman(getFundingInfo(tree)))
this.npm.output(this.printHuman(fundingInfo))
}

printJSON (fundingInfo) {
Expand Down
76 changes: 50 additions & 26 deletions lib/publish.js
Expand Up @@ -8,13 +8,19 @@ const pacote = require('pacote')
const npa = require('npm-package-arg')
const npmFetch = require('npm-registry-fetch')

const flatten = require('./utils/config/flatten.js')
const otplease = require('./utils/otplease.js')
const { getContents, logTar } = require('./utils/tar.js')
const getWorkspaces = require('./workspaces/get-workspaces.js')

// for historical reasons, publishConfig in package.json can contain ANY config
// keys that npm supports in .npmrc files and elsewhere. We *may* want to
// revisit this at some point, and have a minimal set that's a SemVer-major
// change that ought to get a RFC written on it.
const flatten = require('./utils/config/flatten.js')

// this is the only case in the CLI where we use the old full slow
// 'read-package-json' module, because we want to pull in all the
// defaults and metadata, like git sha's and default scripts and all that.
// this is the only case in the CLI where we want to use the old full slow
// 'read-package-json' module, because we want to pull in all the defaults and
// metadata, like git sha's and default scripts and all that.
const readJson = util.promisify(require('read-package-json'))

const BaseCommand = require('./base-command.js')
Expand All @@ -30,7 +36,7 @@ class Publish extends BaseCommand {

/* istanbul ignore next - see test/lib/load-all-commands.js */
static get params () {
return ['tag', 'access', 'dry-run']
return ['tag', 'access', 'dry-run', 'workspace', 'workspaces']
}

/* istanbul ignore next - see test/lib/load-all-commands.js */
Expand All @@ -44,6 +50,10 @@ class Publish extends BaseCommand {
this.publish(args).then(() => cb()).catch(cb)
}

execWorkspaces (args, filters, cb) {
this.publishWorkspaces(args, filters).then(() => cb()).catch(cb)
}

async publish (args) {
if (args.length === 0)
args = ['.']
Expand All @@ -56,6 +66,7 @@ class Publish extends BaseCommand {
const dryRun = this.npm.config.get('dry-run')
const json = this.npm.config.get('json')
const defaultTag = this.npm.config.get('tag')
const silent = log.level === 'silent'

if (semver.validRange(defaultTag))
throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim())
Expand All @@ -68,7 +79,7 @@ class Publish extends BaseCommand {
let manifest = await this.getManifest(spec, opts)

if (manifest.publishConfig)
Object.assign(opts, this.publishConfigToOpts(manifest.publishConfig))
flatten(manifest.publishConfig, opts)

// only run scripts for directory type publishes
if (spec.type === 'directory') {
Expand All @@ -77,7 +88,7 @@ class Publish extends BaseCommand {
path: spec.fetchSpec,
stdio: 'inherit',
pkg: manifest,
banner: log.level !== 'silent',
banner: !silent,
})
}

Expand All @@ -89,7 +100,7 @@ class Publish extends BaseCommand {
// note that publishConfig might have changed as well!
manifest = await this.getManifest(spec, opts)
if (manifest.publishConfig)
Object.assign(opts, this.publishConfigToOpts(manifest.publishConfig))
flatten(manifest.publishConfig, opts)

// note that logTar calls npmlog.notice(), so if we ARE in silent mode,
// this will do nothing, but we still want it in the debuglog if it fails.
Expand All @@ -114,44 +125,57 @@ class Publish extends BaseCommand {
path: spec.fetchSpec,
stdio: 'inherit',
pkg: manifest,
banner: log.level !== 'silent',
banner: !silent,
})

await runScript({
event: 'postpublish',
path: spec.fetchSpec,
stdio: 'inherit',
pkg: manifest,
banner: log.level !== 'silent',
banner: !silent,
})
}

const silent = log.level === 'silent'
if (!silent && json)
this.npm.output(JSON.stringify(pkgContents, null, 2))
else if (!silent)
this.npm.output(`+ ${pkgContents.id}`)
if (!this.workspaces) {
if (!silent && json)
this.npm.output(JSON.stringify(pkgContents, null, 2))
else if (!silent)
this.npm.output(`+ ${pkgContents.id}`)
}

return pkgContents
}

async publishWorkspaces (args, filters) {
// Suppresses JSON output in publish() so we can handle it here
this.workspaces = true

const results = {}
const json = this.npm.config.get('json')
const silent = log.level === 'silent'
const workspaces =
await getWorkspaces(filters, { path: this.npm.localPrefix })
for (const [name, workspace] of workspaces.entries()) {
const pkgContents = await this.publish([workspace])
// This needs to be in-line w/ the rest of the output that non-JSON
// publish generates
if (!silent && !json)
this.npm.output(`+ ${pkgContents.id}`)
else
results[name] = pkgContents
}

if (!silent && json)
this.npm.output(JSON.stringify(results, null, 2))
}

// if it's a directory, read it from the file system
// otherwise, get the full metadata from whatever it is
getManifest (spec, opts) {
if (spec.type === 'directory')
return readJson(`${spec.fetchSpec}/package.json`)
return pacote.manifest(spec, { ...opts, fullMetadata: true })
}

// for historical reasons, publishConfig in package.json can contain
// ANY config keys that npm supports in .npmrc files and elsewhere.
// We *may* want to revisit this at some point, and have a minimal set
// that's a SemVer-major change that ought to get a RFC written on it.
publishConfigToOpts (publishConfig) {
// create a new object that inherits from the config stack
// then squash the css-case into camelCase opts, like we do
// this is Object.assign()'ed onto the base npm.flatOptions
return flatten(publishConfig, {})
}
}
module.exports = Publish
24 changes: 24 additions & 0 deletions lib/workspaces/arborist-cmd.js
@@ -0,0 +1,24 @@
// This is the base for all commands whose execWorkspaces just gets
// a list of workspace names and passes it on to new Arborist() to
// be able to run a filtered Arborist.reify() at some point.

const BaseCommand = require('../base-command.js')
const getWorkspaces = require('../workspaces/get-workspaces.js')
class ArboristCmd extends BaseCommand {
/* istanbul ignore next - see test/lib/load-all-commands.js */
static get params () {
return [
'workspace',
]
}

execWorkspaces (args, filters, cb) {
getWorkspaces(filters, { path: this.npm.localPrefix })
.then(workspaces => {
this.workspaces = [...workspaces.keys()]
this.exec(args, cb)
})
}
}

module.exports = ArboristCmd
23 changes: 23 additions & 0 deletions node_modules/@npmcli/arborist/lib/arborist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.