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 Apr 4, 2022
1 parent f37f7d2 commit 4933c68
Show file tree
Hide file tree
Showing 8 changed files with 433 additions and 173 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 ignoreImplicitWorkspace = false

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.
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
await 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.
await copyPacklist(node.target.realpath, dest)
} else {
// copy the modules files but not dependencies.
const nm = join(node.realpath, 'node_modules')
await 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)))
}
}
}
}
}
}
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
)
}
29 changes: 2 additions & 27 deletions lib/utils/cmd-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,33 +39,7 @@ const aliases = {
'clean-install-test': 'cit',
x: 'exec',
why: 'explain',
la: 'll',
verison: 'version',
ic: 'ci',

// typos
innit: 'init',
// manually abbrev so that install-test doesn't make insta stop working
in: 'install',
ins: 'install',
inst: 'install',
insta: 'install',
instal: 'install',
isnt: 'install',
isnta: 'install',
isntal: 'install',
isntall: 'install',
'install-clean': 'ci',
'isntall-clean': 'ci',
hlep: 'help',
'dist-tags': 'dist-tag',
upgrade: 'update',
udpate: 'update',
rum: 'run-script',
sit: 'cit',
urn: 'run-script',
ogr: 'org',
'add-user': 'adduser',
cp: 'copy',
}

// these are filenames in .
Expand Down Expand Up @@ -137,6 +111,7 @@ const cmdList = [
'version',
'view',
'whoami',
'copy',
]

const plumbing = ['birthday', 'help-search']
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
27 changes: 2 additions & 25 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 [
version
view
whoami
copy
login
author
home
Expand Down Expand Up @@ -143,30 +144,7 @@ Array [
clean-install-test
x
why
la
verison
ic
innit
in
ins
inst
insta
instal
isnt
isnta
isntal
isntall
install-clean
isntall-clean
hlep
dist-tags
upgrade
udpate
rum
sit
urn
ogr
add-user
cp
),
],
]
Expand Down Expand Up @@ -203,7 +181,6 @@ Array [
audit
author
add
add-user
),
],
]
Expand Down
40 changes: 23 additions & 17 deletions tap-snapshots/test/lib/load-all-commands.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ npm adduser
Options:
[--registry <registry>] [--scope <@scope>]
aliases: login, add-user
alias: login
Run "npm help adduser" for more info
`
Expand Down Expand Up @@ -120,7 +120,7 @@ Options:
[--no-audit] [--foreground-scripts] [--ignore-scripts]
[--script-shell <script-shell>]
aliases: clean-install, ic, install-clean, isntall-clean
alias: clean-install
Run "npm help ci" for more info
`
Expand Down Expand Up @@ -153,6 +153,22 @@ 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`] = `
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`] = `
Reduce duplication in the package tree
Expand Down Expand Up @@ -212,8 +228,6 @@ Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root]
alias: dist-tags
Run "npm help dist-tag" for more info
`

Expand Down Expand Up @@ -351,8 +365,6 @@ npm help <term> [<terms..>]
Options:
[--viewer <viewer>]
alias: hlep
Run "npm help help" for more info
`

Expand Down Expand Up @@ -396,7 +408,7 @@ Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root]
aliases: create, innit
alias: create
Run "npm help init" for more info
`
Expand Down Expand Up @@ -425,7 +437,7 @@ Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root]
aliases: add, i, in, ins, inst, insta, instal, isnt, isnta, isntal, isntall
aliases: add, i
Run "npm help install" for more info
`
Expand Down Expand Up @@ -508,8 +520,6 @@ Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root]
alias: la
Run "npm help ll" for more info
`

Expand All @@ -522,7 +532,7 @@ npm adduser
Options:
[--registry <registry>] [--scope <@scope>]
aliases: login, add-user
alias: login
Run "npm help adduser" for more info
`
Expand Down Expand Up @@ -568,8 +578,6 @@ npm org ls orgname [<username>]
Options:
[--registry <registry>] [--otp <otp>] [--json] [-p|--parseable]
alias: ogr
Run "npm help org" for more info
`

Expand Down Expand Up @@ -767,7 +775,7 @@ Options:
[-ws|--workspaces] [--include-workspace-root] [--if-present] [--ignore-scripts]
[--script-shell <script-shell>]
aliases: run, rum, urn
alias: run
Run "npm help run-script" for more info
`
Expand Down Expand Up @@ -969,7 +977,7 @@ Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root]
aliases: up, upgrade, udpate
alias: up
Run "npm help update" for more info
`
Expand All @@ -986,8 +994,6 @@ Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--no-workspaces-update] [--include-workspace-root]
alias: verison
Run "npm help version" for more info
`

Expand Down
Loading

0 comments on commit 4933c68

Please sign in to comment.