Skip to content

Commit

Permalink
feat: new npm copy command
Browse files Browse the repository at this point in the history
  • Loading branch information
Caleb ツ Everett committed Jan 3, 2022
1 parent cd0f3c8 commit 0f74929
Show file tree
Hide file tree
Showing 9 changed files with 418 additions and 25 deletions.
137 changes: 137 additions & 0 deletions lib/commands/copy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const Arborist = require('@npmcli/arborist')
const getWorkspaces = require('../workspaces/get-workspaces.js')
const { join, relative, dirname } = require('path')
const packlist = require('npm-packlist')
const fs = require('@npmcli/fs')

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

class Copy extends BaseCommand {
static description = 'Copy package to new location'

static name = 'copy'

static params = [
'omit',
'workspace',
'workspaces',
'include-workspace-root',
]

static usage = ['<destination>']

async exec (args) {
await this.copyTo(args, true, new Set([]))
}

// called when --workspace or --workspaces is passed.
async execWorkspaces (args, filters) {
const workspaces = await getWorkspaces(filters, {
path: this.npm.localPrefix,
})

await this.copyTo(
args,
this.includeWorkspaceRoot,
new Set(workspaces.values()))
}

async copyTo (args, includeWorkspaceRoot, workspaces) {
if (args.length !== 1) {
throw this.usageError('Missing required destination argument')
}
const opts = {
...this.npm.flatOptions,
path: this.npm.localPrefix,
log: this.npm.log,
}
const destination = args[0]
const omit = new Set(this.npm.flatOptions.omit)

const tree = await new Arborist(opts).loadActual()

// map of node to location in destination.
const destinations = new Map()

// calculate the root set of packages.
if (includeWorkspaceRoot) {
const to = join(destination, tree.location)
destinations.set(tree, to)
}
for (const edge of tree.edgesOut.values()) {
if (edge.workspace && workspaces.has(edge.to.realpath)) {
const to = join(destination, edge.to.location)
destinations.set(edge.to, to)
}
}

// copy the root set of packages and their dependencies.
const tasks = []
for (const [node, dest] of destinations) {
if (node.isLink && node.target) {
const targetPath = destinations.get(node.target)
if (targetPath == null) {
// This is the first time the link target was seen, it will be the
// only copy in dest, other links to the same target will link to
// this copy.
destinations.set(node.target, dest)
} else {
// The link target is already in the destination
tasks.push(relativeSymlink(targetPath, dest))
}
} else {
if (node.isWorkspace || node.isRoot) {
// workspace and root packages have not been published so they may
// have files that should be excluded.
tasks.push(copyPacklist(node.target.realpath, dest))
} else {
// copy the modules files but not dependencies.
const nm = join(node.realpath, 'node_modules')
tasks.push(fs.cp(node.realpath, dest, {
recursive: true,
errorOnExist: false,
filter: src => src !== nm,
}))
}

// add dependency edges to the queue.
for (const edge of node.edgesOut.values()) {
if (!omit.has(edge.type) && edge.to != null) {
destinations.set(
edge.to,
join(
destinations.get(edge.to.parent) || destination,
relative(edge.to.parent.location, edge.to.location)))
}
}
}
}
await Promise.all(tasks)
}
}
module.exports = Copy

async function copyPacklist (from, to) {
for (const file of await packlist({ path: from })) {
// packlist will include bundled node_modules. ignore it because we're
// already handling copying dependencies.
if (file.startsWith('node_modules/')) {
continue
}

// using recursive copy because packlist doesn't list directories.
// TODO what is npm's preferred recursive copy?
await fs.cp(
join(from, file),
join(to, file),
{ recursive: true, errorOnExist: false })
}
}

async function relativeSymlink (target, path) {
await fs.mkdir(dirname(path), { recursive: true })
await fs.symlink(
'./' + relative(dirname(path), target),
path // link to create
)
}
2 changes: 2 additions & 0 deletions lib/utils/cmd-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const shorthands = {
'clean-install-test': 'cit',
x: 'exec',
why: 'explain',
cp: 'copy',
}

const affordances = {
Expand Down Expand Up @@ -134,6 +135,7 @@ const cmdList = [
'doctor',
'exec',
'explain',
'copy',
]

const plumbing = ['birthday', 'help-search']
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"cli-table3": "^0.6.0",
"columnify": "~1.5.4",
"fastest-levenshtein": "^1.0.12",
"fs-extra": "^10.0.0",
"glob": "^7.2.0",
"graceful-fs": "^4.2.8",
"hosted-git-info": "^4.0.2",
Expand Down
10 changes: 5 additions & 5 deletions tap-snapshots/smoke-tests/index.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ npm help npm more involved overview
All commands:
access, adduser, audit, bin, bugs, cache, ci, completion,
config, dedupe, deprecate, diff, dist-tag, docs, doctor,
edit, exec, explain, explore, find-dupes, fund, get, help,
hook, init, install, install-ci-test, install-test, link,
ll, login, logout, ls, org, outdated, owner, pack, ping,
pkg, prefix, profile, prune, publish, rebuild, repo,
config, copy, dedupe, deprecate, diff, dist-tag, docs,
doctor, edit, exec, explain, explore, find-dupes, fund, get,
help, hook, init, install, install-ci-test, install-test,
link, ll, login, logout, ls, org, outdated, owner, pack,
ping, pkg, prefix, profile, prune, publish, rebuild, repo,
restart, root, run-script, search, set, set-script,
shrinkwrap, star, stars, start, stop, team, test, token,
uninstall, unpublish, unstar, update, version, view, whoami
Expand Down
2 changes: 2 additions & 0 deletions tap-snapshots/test/lib/commands/completion.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Array [
doctor
exec
explain
copy
un
rb
list
Expand All @@ -131,6 +132,7 @@ Array [
clean-install-test
x
why
cp
la
verison
ic
Expand Down
18 changes: 18 additions & 0 deletions tap-snapshots/test/lib/load-all-commands.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,24 @@ alias: c
Run "npm help config" for more info
`

exports[`test/lib/load-all-commands.js TAP load each command copy > must match snapshot 1`] = `
npm copy
Copy package to new location
Usage:
npm copy <destination>
Options:
[--omit <dev|optional|peer> [--omit <dev|optional|peer> ...]]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root]
alias: cp
Run "npm help copy" for more info
`

exports[`test/lib/load-all-commands.js TAP load each command dedupe > must match snapshot 1`] = `
npm dedupe
Expand Down
3 changes: 3 additions & 0 deletions tap-snapshots/test/lib/utils/cmd-list.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Object {
"cit": "install-ci-test",
"clean-install": "ci",
"clean-install-test": "cit",
"cp": "copy",
"create": "init",
"ddp": "dedupe",
"dist-tags": "dist-tag",
Expand Down Expand Up @@ -169,6 +170,7 @@ Object {
"doctor",
"exec",
"explain",
"copy",
],
"plumbing": Array [
"birthday",
Expand All @@ -188,6 +190,7 @@ Object {
"cit": "install-ci-test",
"clean-install": "ci",
"clean-install-test": "cit",
"cp": "copy",
"create": "init",
"ddp": "dedupe",
"i": "install",
Expand Down
56 changes: 36 additions & 20 deletions tap-snapshots/test/lib/utils/npm-usage.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ npm help npm more involved overview
All commands:
access, adduser, audit, bin, bugs, cache, ci, completion,
config, dedupe, deprecate, diff, dist-tag, docs, doctor,
edit, exec, explain, explore, find-dupes, fund, get, help,
hook, init, install, install-ci-test, install-test, link,
ll, login, logout, ls, org, outdated, owner, pack, ping,
pkg, prefix, profile, prune, publish, rebuild, repo,
config, copy, dedupe, deprecate, diff, dist-tag, docs,
doctor, edit, exec, explain, explore, find-dupes, fund, get,
help, hook, init, install, install-ci-test, install-test,
link, ll, login, logout, ls, org, outdated, owner, pack,
ping, pkg, prefix, profile, prune, publish, rebuild, repo,
restart, root, run-script, search, set, set-script,
shrinkwrap, star, stars, start, stop, team, test, token,
uninstall, unpublish, unstar, update, version, view, whoami
Expand Down Expand Up @@ -58,11 +58,11 @@ npm help npm more involved overview
All commands:
access, adduser, audit, bin, bugs, cache, ci, completion,
config, dedupe, deprecate, diff, dist-tag, docs, doctor,
edit, exec, explain, explore, find-dupes, fund, get, help,
hook, init, install, install-ci-test, install-test, link,
ll, login, logout, ls, org, outdated, owner, pack, ping,
pkg, prefix, profile, prune, publish, rebuild, repo,
config, copy, dedupe, deprecate, diff, dist-tag, docs,
doctor, edit, exec, explain, explore, find-dupes, fund, get,
help, hook, init, install, install-ci-test, install-test,
link, ll, login, logout, ls, org, outdated, owner, pack,
ping, pkg, prefix, profile, prune, publish, rebuild, repo,
restart, root, run-script, search, set, set-script,
shrinkwrap, star, stars, start, stop, team, test, token,
uninstall, unpublish, unstar, update, version, view, whoami
Expand Down Expand Up @@ -94,11 +94,11 @@ npm help npm more involved overview
All commands:
access, adduser, audit, bin, bugs, cache, ci, completion,
config, dedupe, deprecate, diff, dist-tag, docs, doctor,
edit, exec, explain, explore, find-dupes, fund, get, help,
hook, init, install, install-ci-test, install-test, link,
ll, login, logout, ls, org, outdated, owner, pack, ping,
pkg, prefix, profile, prune, publish, rebuild, repo,
config, copy, dedupe, deprecate, diff, dist-tag, docs,
doctor, edit, exec, explain, explore, find-dupes, fund, get,
help, hook, init, install, install-ci-test, install-test,
link, ll, login, logout, ls, org, outdated, owner, pack,
ping, pkg, prefix, profile, prune, publish, rebuild, repo,
restart, root, run-script, search, set, set-script,
shrinkwrap, star, stars, start, stop, team, test, token,
uninstall, unpublish, unstar, update, version, view, whoami
Expand Down Expand Up @@ -130,11 +130,11 @@ npm help npm more involved overview (in a browser)
All commands:
access, adduser, audit, bin, bugs, cache, ci, completion,
config, dedupe, deprecate, diff, dist-tag, docs, doctor,
edit, exec, explain, explore, find-dupes, fund, get, help,
hook, init, install, install-ci-test, install-test, link,
ll, login, logout, ls, org, outdated, owner, pack, ping,
pkg, prefix, profile, prune, publish, rebuild, repo,
config, copy, dedupe, deprecate, diff, dist-tag, docs,
doctor, edit, exec, explain, explore, find-dupes, fund, get,
help, hook, init, install, install-ci-test, install-test,
link, ll, login, logout, ls, org, outdated, owner, pack,
ping, pkg, prefix, profile, prune, publish, rebuild, repo,
restart, root, run-script, search, set, set-script,
shrinkwrap, star, stars, start, stop, team, test, token,
uninstall, unpublish, unstar, update, version, view, whoami
Expand Down Expand Up @@ -302,6 +302,22 @@ All commands:
Run "npm help config" for more info
copy npm copy
Copy package to new location
Usage:
npm copy <destination>
Options:
[--omit <dev|optional|peer> [--omit <dev|optional|peer> ...]]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root]
alias: cp
Run "npm help copy" for more info
dedupe npm dedupe
Reduce duplication in the package tree
Expand Down
Loading

0 comments on commit 0f74929

Please sign in to comment.