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

feat(pack): add workspace support #3033

Merged
merged 1 commit into from Apr 8, 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
23 changes: 19 additions & 4 deletions docs/content/commands/npm-pack.md
Expand Up @@ -10,6 +10,25 @@ description: Create a tarball from a package
npm pack [[<@scope>/]<pkg>...] [--dry-run]
```

### Configuration

#### dry-run

Do everything that pack usually does without actually packing anything.
That is, report on what would have gone into the tarball, but nothing
else.

#### workspaces

Enables workspaces context while creating tarballs. Tarballs for each
workspaces will be generated.

#### workspace

Enables workspaces context and limits results to only those specified by
this config item. Tarballs will only be generated for the packages
named in the workspaces given here.

### Description

For anything that's installable (that is, a package folder, tarball,
Expand All @@ -23,10 +42,6 @@ overwritten the second time.

If no arguments are supplied, then npm packs the current package folder.

The `--dry-run` argument will do everything that pack usually does without
actually packing anything. That is, it reports on what would have gone
into the tarball, but nothing else.

### See Also

* [npm-packlist package](http://npm.im/npm-packlist)
Expand Down
23 changes: 22 additions & 1 deletion lib/pack.js
Expand Up @@ -3,6 +3,7 @@ const log = require('npmlog')
const pacote = require('pacote')
const libpack = require('libnpmpack')
const npa = require('npm-package-arg')
const getWorkspaces = require('./workspaces/get-workspaces.js')

const { getContents, logTar } = require('./utils/tar.js')

Expand All @@ -23,7 +24,7 @@ class Pack extends BaseCommand {

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

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

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

async pack (args) {
if (args.length === 0)
args = ['.']
Expand Down Expand Up @@ -62,5 +67,21 @@ class Pack extends BaseCommand {
this.npm.output(tar.filename.replace(/^@/, '').replace(/\//, '-'))
}
}

async packWorkspaces (args, filters) {
// If they either ask for nothing, or explicitly include '.' in the args,
// we effectively translate that into each workspace requested

const useWorkspaces = args.length === 0 || args.includes('.')

if (!useWorkspaces) {
this.npm.log.warn('Ignoring workspaces for specified package(s)')
return this.pack(args)
}

const workspaces =
await getWorkspaces(filters, { path: this.npm.localPrefix })
return this.pack([...workspaces.values(), ...args.filter(a => a !== '.')])
}
}
module.exports = Pack
2 changes: 1 addition & 1 deletion lib/view.js
Expand Up @@ -151,7 +151,7 @@ class View extends BaseCommand {

const local = /^\.@/.test(pkg) || pkg === '.'
if (!local) {
this.npm.log.warn('Ignoring workspaces for remote package')
this.npm.log.warn('Ignoring workspaces for specified package(s)')
return this.view([pkg, ...args])
}
let wholePackument = false
Expand Down
2 changes: 1 addition & 1 deletion tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js
Expand Up @@ -625,7 +625,7 @@ All commands:
npm pack [[<@scope>/]<pkg>...]

Options:
[--dry-run]
[--dry-run] [-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]

Run "npm help pack" for more info

Expand Down
2 changes: 1 addition & 1 deletion tap-snapshots/test-lib-view.js-TAP.test.js
Expand Up @@ -450,7 +450,7 @@ dist-tags:
`

exports[`test/lib/view.js TAP workspaces remote package name > must match snapshot 1`] = `
Ignoring workspaces for remote package
Ignoring workspaces for specified package(s)
`

exports[`test/lib/view.js TAP workspaces remote package name > must match snapshot 2`] = `
Expand Down
14 changes: 14 additions & 0 deletions test/fixtures/mock-npm.js
Expand Up @@ -2,10 +2,24 @@
// npm.config You still need a separate flatOptions but this is the first step
// to eventually just using npm itself

const mockLog = {
clearProgress: () => {},
disableProgress: () => {},
enableProgress: () => {},
http: () => {},
info: () => {},
levels: [],
notice: () => {},
pause: () => {},
silly: () => {},
verbose: () => {},
warn: () => {},
}
const mockNpm = (base = {}) => {
const config = base.config || {}
const flatOptions = base.flatOptions || {}
return {
log: mockLog,
...base,
flatOptions,
config: {
Expand Down
103 changes: 103 additions & 0 deletions test/lib/pack.js
@@ -1,6 +1,7 @@
const t = require('tap')
const requireInject = require('require-inject')
const mockNpm = require('../fixtures/mock-npm')
const pacote = require('pacote')

const OUTPUT = []
const output = (...msg) => OUTPUT.push(msg)
Expand All @@ -11,6 +12,16 @@ const libnpmpack = async (spec, opts) => {

return ''
}
const mockPacote = {
manifest: (spec) => {
if (spec.type === 'directory')
return pacote.manifest(spec)
return {
name: spec.name || 'test-package',
version: spec.version || '1.0.0-test',
}
},
}

t.afterEach(cb => {
OUTPUT.length = 0
Expand Down Expand Up @@ -152,3 +163,95 @@ t.test('should log pack contents', (t) => {
t.end()
})
})

t.test('workspaces', (t) => {
const testDir = t.testdir({
'package.json': JSON.stringify({
name: 'workspaces-test',
version: '1.0.0',
workspaces: ['workspace-a', 'workspace-b'],
}, null, 2),
'workspace-a': {
'package.json': JSON.stringify({
name: 'workspace-a',
version: '1.0.0',
}),
},
'workspace-b': {
'package.json': JSON.stringify({
name: 'workspace-b',
version: '1.0.0',
}),
},
})
const Pack = requireInject('../../lib/pack.js', {
libnpmpack,
pacote: mockPacote,
npmlog: {
notice: () => {},
showProgress: () => {},
clearProgress: () => {},
},
})
const npm = mockNpm({
localPrefix: testDir,
config: {
unicode: false,
json: false,
'dry-run': false,
},
output,
})
const pack = new Pack(npm)

t.test('all workspaces', (t) => {
pack.execWorkspaces([], [], er => {
if (er)
throw er

t.strictSame(OUTPUT, [
['workspace-a-1.0.0.tgz'],
['workspace-b-1.0.0.tgz'],
])
t.end()
})
})

t.test('all workspaces, `.` first arg', (t) => {
pack.execWorkspaces(['.'], [], er => {
if (er)
throw er

t.strictSame(OUTPUT, [
['workspace-a-1.0.0.tgz'],
['workspace-b-1.0.0.tgz'],
])
t.end()
})
})

t.test('one workspace', (t) => {
pack.execWorkspaces([], ['workspace-a'], er => {
if (er)
throw er

t.strictSame(OUTPUT, [
['workspace-a-1.0.0.tgz'],
])
t.end()
})
})

t.test('specific package', (t) => {
pack.execWorkspaces(['abbrev'], [], er => {
if (er)
throw er

t.strictSame(OUTPUT, [
['abbrev-1.0.0-test.tgz'],
])
t.end()
})
})
t.end()
})