Skip to content

Commit 82879f6

Browse files
authoredApr 18, 2023
fix: lazy loading of arborist and pacote (#6225)
1 parent f4e73ab commit 82879f6

25 files changed

+76
-81
lines changed
 

‎lib/commands/audit.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
const Arborist = require('@npmcli/arborist')
21
const auditReport = require('npm-audit-report')
32
const fetch = require('npm-registry-fetch')
43
const localeCompare = require('@isaacs/string-locale-compare')('en')
@@ -413,6 +412,7 @@ class Audit extends ArboristWorkspaceCmd {
413412

414413
async auditAdvisories (args) {
415414
const reporter = this.npm.config.get('json') ? 'json' : 'detail'
415+
const Arborist = require('@npmcli/arborist')
416416
const opts = {
417417
...this.npm.flatOptions,
418418
audit: true,
@@ -445,6 +445,7 @@ class Audit extends ArboristWorkspaceCmd {
445445
}
446446

447447
log.verbose('loading installed dependencies')
448+
const Arborist = require('@npmcli/arborist')
448449
const opts = {
449450
...this.npm.flatOptions,
450451
path: this.npm.prefix,

‎lib/commands/cache.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const cacache = require('cacache')
2-
const Arborist = require('@npmcli/arborist')
32
const pacote = require('pacote')
43
const fs = require('fs/promises')
54
const { join } = require('path')
@@ -162,7 +161,7 @@ class Cache extends BaseCommand {
162161
return pacote.tarball.stream(spec, stream => {
163162
stream.resume()
164163
return stream.promise()
165-
}, { ...this.npm.flatOptions, Arborist })
164+
}, { ...this.npm.flatOptions })
166165
}))
167166
}
168167

‎lib/commands/ci.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
const Arborist = require('@npmcli/arborist')
21
const reifyFinish = require('../utils/reify-finish.js')
32
const runScript = require('@npmcli/run-script')
43
const fs = require('fs/promises')
@@ -36,6 +35,7 @@ class CI extends ArboristWorkspaceCmd {
3635
}
3736

3837
const where = this.npm.prefix
38+
const Arborist = require('@npmcli/arborist')
3939
const opts = {
4040
...this.npm.flatOptions,
4141
packageLock: true, // npm ci should never skip lock files

‎lib/commands/dedupe.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// dedupe duplicated packages, or find them in the tree
2-
const Arborist = require('@npmcli/arborist')
32
const reifyFinish = require('../utils/reify-finish.js')
43

54
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
@@ -31,6 +30,7 @@ class Dedupe extends ArboristWorkspaceCmd {
3130

3231
const dryRun = this.npm.config.get('dry-run')
3332
const where = this.npm.prefix
33+
const Arborist = require('@npmcli/arborist')
3434
const opts = {
3535
...this.npm.flatOptions,
3636
path: where,

‎lib/commands/diff.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ const { resolve } = require('path')
22
const semver = require('semver')
33
const libnpmdiff = require('libnpmdiff')
44
const npa = require('npm-package-arg')
5-
const Arborist = require('@npmcli/arborist')
65
const pacote = require('pacote')
76
const pickManifest = require('npm-pick-manifest')
87
const log = require('../utils/log-shim')
@@ -146,6 +145,7 @@ class Diff extends BaseCommand {
146145
if (spec.registry) {
147146
let actualTree
148147
let node
148+
const Arborist = require('@npmcli/arborist')
149149
try {
150150
const opts = {
151151
...this.npm.flatOptions,
@@ -257,6 +257,7 @@ class Diff extends BaseCommand {
257257

258258
async findVersionsByPackageName (specs) {
259259
let actualTree
260+
const Arborist = require('@npmcli/arborist')
260261
try {
261262
const opts = {
262263
...this.npm.flatOptions,

‎lib/commands/explain.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
const { explainNode } = require('../utils/explain-dep.js')
2-
const completion = require('../utils/completion/installed-deep.js')
3-
const Arborist = require('@npmcli/arborist')
42
const npa = require('npm-package-arg')
53
const semver = require('semver')
64
const { relative, resolve } = require('path')
@@ -21,6 +19,7 @@ class Explain extends ArboristWorkspaceCmd {
2119
// TODO
2220
/* istanbul ignore next */
2321
async completion (opts) {
22+
const completion = require('../utils/completion/installed-deep.js')
2423
return completion(this.npm, opts)
2524
}
2625

@@ -29,6 +28,7 @@ class Explain extends ArboristWorkspaceCmd {
2928
throw this.usageError()
3029
}
3130

31+
const Arborist = require('@npmcli/arborist')
3232
const arb = new Arborist({ path: this.npm.prefix, ...this.npm.flatOptions })
3333
const tree = await arb.loadActual()
3434

‎lib/commands/fund.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
const archy = require('archy')
2-
const Arborist = require('@npmcli/arborist')
32
const pacote = require('pacote')
43
const semver = require('semver')
54
const npa = require('npm-package-arg')
65
const { depth } = require('treeverse')
76
const { readTree: getFundingInfo, normalizeFunding, isValidFunding } = require('libnpmfund')
87

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

@@ -39,6 +37,7 @@ class Fund extends ArboristWorkspaceCmd {
3937
// TODO
4038
/* istanbul ignore next */
4139
async completion (opts) {
40+
const completion = require('../utils/completion/installed-deep.js')
4241
return completion(this.npm, opts)
4342
}
4443

@@ -64,6 +63,7 @@ class Fund extends ArboristWorkspaceCmd {
6463
}
6564

6665
const where = this.npm.prefix
66+
const Arborist = require('@npmcli/arborist')
6767
const arb = new Arborist({ ...this.npm.flatOptions, path: where })
6868
const tree = await arb.loadActual()
6969

@@ -80,6 +80,7 @@ class Fund extends ArboristWorkspaceCmd {
8080
// TODO: add !workspacesEnabled option handling to libnpmfund
8181
const fundingInfo = getFundingInfo(tree, {
8282
...this.flatOptions,
83+
Arborist,
8384
workspaces: this.workspaceNames,
8485
})
8586

‎lib/commands/install.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ const readdir = util.promisify(fs.readdir)
55
const reifyFinish = require('../utils/reify-finish.js')
66
const log = require('../utils/log-shim.js')
77
const { resolve, join } = require('path')
8-
const Arborist = require('@npmcli/arborist')
98
const runScript = require('@npmcli/run-script')
109
const pacote = require('pacote')
1110
const checks = require('npm-install-checks')
@@ -136,6 +135,7 @@ class Install extends ArboristWorkspaceCmd {
136135
throw this.usageError()
137136
}
138137

138+
const Arborist = require('@npmcli/arborist')
139139
const opts = {
140140
...this.npm.flatOptions,
141141
auditLevel: null,

‎lib/commands/link.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const util = require('util')
33
const readdir = util.promisify(fs.readdir)
44
const { resolve } = require('path')
55

6-
const Arborist = require('@npmcli/arborist')
76
const npa = require('npm-package-arg')
87
const rpj = require('read-package-json-fast')
98
const semver = require('semver')
@@ -67,8 +66,10 @@ class Link extends ArboristWorkspaceCmd {
6766
// load current packages from the global space,
6867
// and then add symlinks installs locally
6968
const globalTop = resolve(this.npm.globalDir, '..')
69+
const Arborist = require('@npmcli/arborist')
7070
const globalOpts = {
7171
...this.npm.flatOptions,
72+
Arborist,
7273
path: globalTop,
7374
global: true,
7475
prune: false,
@@ -138,8 +139,10 @@ class Link extends ArboristWorkspaceCmd {
138139
const paths = wsp && wsp.length ? wsp : [this.npm.prefix]
139140
const add = paths.map(path => `file:${path.replace(/#/g, '%23')}`)
140141
const globalTop = resolve(this.npm.globalDir, '..')
142+
const Arborist = require('@npmcli/arborist')
141143
const arb = new Arborist({
142144
...this.npm.flatOptions,
145+
Arborist,
143146
path: globalTop,
144147
global: true,
145148
})

‎lib/commands/ls.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ const relativePrefix = `.${sep}`
33
const { EOL } = require('os')
44

55
const archy = require('archy')
6-
const Arborist = require('@npmcli/arborist')
76
const { breadth } = require('treeverse')
87
const npa = require('npm-package-arg')
98

10-
const completion = require('../utils/completion/installed-deep.js')
11-
129
const _depth = Symbol('depth')
1310
const _dedupe = Symbol('dedupe')
1411
const _filteredBy = Symbol('filteredBy')
@@ -44,6 +41,7 @@ class LS extends ArboristWorkspaceCmd {
4441
// TODO
4542
/* istanbul ignore next */
4643
async completion (opts) {
44+
const completion = require('../utils/completion/installed-deep.js')
4745
return completion(this.npm, opts)
4846
}
4947

@@ -63,6 +61,8 @@ class LS extends ArboristWorkspaceCmd {
6361

6462
const path = global ? resolve(this.npm.globalDir, '..') : this.npm.prefix
6563

64+
const Arborist = require('@npmcli/arborist')
65+
6666
const arb = new Arborist({
6767
global,
6868
...this.npm.flatOptions,

‎lib/commands/outdated.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ const npa = require('npm-package-arg')
66
const pickManifest = require('npm-pick-manifest')
77
const localeCompare = require('@isaacs/string-locale-compare')('en')
88

9-
const Arborist = require('@npmcli/arborist')
10-
119
const ansiTrim = require('../utils/ansi-trim.js')
1210
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
1311

@@ -30,6 +28,7 @@ class Outdated extends ArboristWorkspaceCmd {
3028
? global
3129
: this.npm.prefix
3230

31+
const Arborist = require('@npmcli/arborist')
3332
const arb = new Arborist({
3433
...this.npm.flatOptions,
3534
path: where,

‎lib/commands/prune.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// prune extraneous packages
2-
const Arborist = require('@npmcli/arborist')
32
const reifyFinish = require('../utils/reify-finish.js')
43

54
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
@@ -19,6 +18,7 @@ class Prune extends ArboristWorkspaceCmd {
1918

2019
async exec () {
2120
const where = this.npm.prefix
21+
const Arborist = require('@npmcli/arborist')
2222
const opts = {
2323
...this.npm.flatOptions,
2424
path: where,

‎lib/commands/query.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict'
22

33
const { resolve } = require('path')
4-
const Arborist = require('@npmcli/arborist')
54
const BaseCommand = require('../base-command.js')
65

76
class QuerySelectorItem {
@@ -58,6 +57,7 @@ class Query extends BaseCommand {
5857
async exec (args) {
5958
// one dir up from wherever node_modules lives
6059
const where = resolve(this.npm.dir, '..')
60+
const Arborist = require('@npmcli/arborist')
6161
const opts = {
6262
...this.npm.flatOptions,
6363
path: where,
@@ -73,6 +73,7 @@ class Query extends BaseCommand {
7373

7474
async execWorkspaces (args) {
7575
await this.setWorkspaces()
76+
const Arborist = require('@npmcli/arborist')
7677
const opts = {
7778
...this.npm.flatOptions,
7879
path: this.npm.prefix,

‎lib/commands/rebuild.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
const { resolve } = require('path')
2-
const Arborist = require('@npmcli/arborist')
32
const npa = require('npm-package-arg')
43
const semver = require('semver')
5-
const completion = require('../utils/completion/installed-deep.js')
64

75
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
86
class Rebuild extends ArboristWorkspaceCmd {
@@ -21,12 +19,14 @@ class Rebuild extends ArboristWorkspaceCmd {
2119
// TODO
2220
/* istanbul ignore next */
2321
async completion (opts) {
22+
const completion = require('../utils/completion/installed-deep.js')
2423
return completion(this.npm, opts)
2524
}
2625

2726
async exec (args) {
2827
const globalTop = resolve(this.npm.globalDir, '..')
2928
const where = this.npm.global ? globalTop : this.npm.prefix
29+
const Arborist = require('@npmcli/arborist')
3030
const arb = new Arborist({
3131
...this.npm.flatOptions,
3232
path: where,

‎lib/commands/shrinkwrap.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
const { resolve, basename } = require('path')
22
const { unlink } = require('fs').promises
3-
const Arborist = require('@npmcli/arborist')
43
const log = require('../utils/log-shim')
54
const BaseCommand = require('../base-command.js')
65
class Shrinkwrap extends BaseCommand {
@@ -21,6 +20,7 @@ class Shrinkwrap extends BaseCommand {
2120
throw er
2221
}
2322

23+
const Arborist = require('@npmcli/arborist')
2424
const path = this.npm.prefix
2525
const sw = resolve(path, 'npm-shrinkwrap.json')
2626
const arb = new Arborist({ ...this.npm.flatOptions, path })

‎lib/commands/uninstall.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const { resolve } = require('path')
2-
const Arborist = require('@npmcli/arborist')
32
const rpj = require('read-package-json-fast')
43

54
const reifyFinish = require('../utils/reify-finish.js')
@@ -42,6 +41,7 @@ class Uninstall extends ArboristWorkspaceCmd {
4241
? resolve(this.npm.globalDir, '..')
4342
: this.npm.localPrefix
4443

44+
const Arborist = require('@npmcli/arborist')
4545
const opts = {
4646
...this.npm.flatOptions,
4747
path,

‎lib/commands/update.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
const path = require('path')
22

3-
const Arborist = require('@npmcli/arborist')
43
const log = require('../utils/log-shim.js')
54

65
const reifyFinish = require('../utils/reify-finish.js')
7-
const completion = require('../utils/completion/installed-deep.js')
86

97
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
108
class Update extends ArboristWorkspaceCmd {
@@ -34,6 +32,7 @@ class Update extends ArboristWorkspaceCmd {
3432
// TODO
3533
/* istanbul ignore next */
3634
async completion (opts) {
35+
const completion = require('../utils/completion/installed-deep.js')
3736
return completion(this.npm, opts)
3837
}
3938

@@ -53,6 +52,7 @@ class Update extends ArboristWorkspaceCmd {
5352
'https://github.com/npm/rfcs/blob/latest/implemented/0019-remove-update-depth-option.md')
5453
}
5554

55+
const Arborist = require('@npmcli/arborist')
5656
const opts = {
5757
...this.npm.flatOptions,
5858
path: where,

‎lib/npm.js

-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
const Arborist = require('@npmcli/arborist')
21
const EventEmitter = require('events')
32
const { resolve, dirname, join } = require('path')
43
const Config = require('@npmcli/config')
@@ -310,10 +309,6 @@ class Npm extends EventEmitter {
310309

311310
get flatOptions () {
312311
const { flat } = this.config
313-
// the Arborist constructor is used almost everywhere we call pacote, it's
314-
// easiest to attach it to flatOptions so it goes everywhere without having
315-
// to touch every call
316-
flat.Arborist = Arborist
317312
flat.nodeVersion = process.version
318313
flat.npmVersion = pkg.version
319314
if (this.command) {

‎lib/package-url-cmd.js

-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
const pacote = require('pacote')
44
const hostedGitInfo = require('hosted-git-info')
5-
const Arborist = require('@npmcli/arborist')
65

76
const openUrl = require('./utils/open-url.js')
87
const log = require('./utils/log-shim')
@@ -33,7 +32,6 @@ class PackageUrlCommand extends BaseCommand {
3332
...this.npm.flatOptions,
3433
where: this.npm.localPrefix,
3534
fullMetadata: true,
36-
Arborist,
3735
}
3836
const mani = await pacote.manifest(arg, opts)
3937
const url = this.getUrl(arg, mani)

‎lib/utils/completion/installed-deep.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
const { resolve } = require('path')
2-
const Arborist = require('@npmcli/arborist')
32
const localeCompare = require('@isaacs/string-locale-compare')('en')
43

54
const installedDeep = async (npm) => {
5+
const Arborist = require('@npmcli/arborist')
66
const {
77
depth,
88
global,

‎lib/utils/update-notifier.js

+42-46
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,11 @@
22
// but not in CI, and not if we're doing that already.
33
// Check daily for betas, and weekly otherwise.
44

5-
const pacote = require('pacote')
65
const ciInfo = require('ci-info')
76
const semver = require('semver')
87
const { stat, writeFile } = require('fs/promises')
98
const { resolve } = require('path')
109

11-
const SKIP = Symbol('SKIP')
12-
13-
const isGlobalNpmUpdate = npm => {
14-
return npm.flatOptions.global &&
15-
['install', 'update'].includes(npm.command) &&
16-
npm.argv.some(arg => /^npm(@|$)/.test(arg))
17-
}
18-
1910
// update check frequency
2011
const DAILY = 1000 * 60 * 60 * 24
2112
const WEEKLY = DAILY * 7
@@ -24,39 +15,10 @@ const WEEKLY = DAILY * 7
2415
const lastCheckedFile = npm =>
2516
resolve(npm.flatOptions.cache, '../_update-notifier-last-checked')
2617

27-
const checkTimeout = async (npm, duration) => {
28-
const t = new Date(Date.now() - duration)
29-
const f = lastCheckedFile(npm)
30-
// if we don't have a file, then definitely check it.
31-
const st = await stat(f).catch(() => ({ mtime: t - 1 }))
32-
return t > st.mtime
33-
}
34-
35-
const updateNotifier = async (npm, spec = 'latest') => {
36-
// never check for updates in CI, when updating npm already, or opted out
37-
if (!npm.config.get('update-notifier') ||
38-
isGlobalNpmUpdate(npm) ||
39-
ciInfo.isCI) {
40-
return SKIP
41-
}
42-
43-
// if we're on a prerelease train, then updates are coming fast
44-
// check for a new one daily. otherwise, weekly.
45-
const { version } = npm
46-
const current = semver.parse(version)
47-
48-
// if we're on a beta train, always get the next beta
49-
if (current.prerelease.length) {
50-
spec = `^${version}`
51-
}
52-
53-
// while on a beta train, get updates daily
54-
const duration = spec !== 'latest' ? DAILY : WEEKLY
55-
56-
// if we've already checked within the specified duration, don't check again
57-
if (!(await checkTimeout(npm, duration))) {
58-
return null
59-
}
18+
// Actual check for updates. This is a separate function so that we only load
19+
// this if we are doing the actual update
20+
const updateCheck = async (npm, spec, version, current) => {
21+
const pacote = require('pacote')
6022

6123
const mani = await pacote.manifest(`npm@${spec}`, {
6224
// always prefer latest, even if doing --tag=whatever on the cmd
@@ -112,15 +74,49 @@ const updateNotifier = async (npm, spec = 'latest') => {
11274
return message
11375
}
11476

77+
const updateNotifier = async (npm, spec = 'latest') => {
78+
// if we're on a prerelease train, then updates are coming fast
79+
// check for a new one daily. otherwise, weekly.
80+
const { version } = npm
81+
const current = semver.parse(version)
82+
83+
// if we're on a beta train, always get the next beta
84+
if (current.prerelease.length) {
85+
spec = `^${version}`
86+
}
87+
88+
// while on a beta train, get updates daily
89+
const duration = spec !== 'latest' ? DAILY : WEEKLY
90+
91+
const t = new Date(Date.now() - duration)
92+
// if we don't have a file, then definitely check it.
93+
const st = await stat(lastCheckedFile(npm)).catch(() => ({ mtime: t - 1 }))
94+
95+
// if we've already checked within the specified duration, don't check again
96+
if (!(t > st.mtime)) {
97+
return null
98+
}
99+
100+
return updateCheck(npm, spec, version, current)
101+
}
102+
115103
// only update the notification timeout if we actually finished checking
116104
module.exports = async npm => {
117-
const notification = await updateNotifier(npm)
118-
119-
// dont write the file if we skipped checking altogether
120-
if (notification === SKIP) {
105+
if (
106+
// opted out
107+
!npm.config.get('update-notifier')
108+
// global npm update
109+
|| (npm.flatOptions.global &&
110+
['install', 'update'].includes(npm.command) &&
111+
npm.argv.some(arg => /^npm(@|$)/.test(arg)))
112+
// CI
113+
|| ciInfo.isCI
114+
) {
121115
return null
122116
}
123117

118+
const notification = await updateNotifier(npm)
119+
124120
// intentional. do not await this. it's a best-effort update. if this
125121
// fails, it's ok. might be using /dev/null as the cache or something weird
126122
// like that.

‎lib/workspaces/update-workspaces.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use strict'
22

3-
const Arborist = require('@npmcli/arborist')
43
const reifyFinish = require('../utils/reify-finish.js')
54

65
async function updateWorkspaces ({
@@ -31,6 +30,7 @@ async function updateWorkspaces ({
3130
path: localPrefix,
3231
save,
3332
}
33+
const Arborist = require('@npmcli/arborist')
3434
const arb = new Arborist(opts)
3535

3636
await arb.reify({ ...opts, update: workspaces })

‎test/lib/utils/completion/installed-deep.js

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const _flatOptions = {
88
depth: Infinity,
99
global: false,
1010
workspacesEnabled: true,
11+
Arborist: require('@npmcli/arborist'),
1112
get prefix () {
1213
return prefix
1314
},

‎workspaces/arborist/lib/arborist/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class Arborist extends Base {
7171
this.options = {
7272
nodeVersion: process.version,
7373
...options,
74+
Arborist: this.constructor,
7475
path: options.path || '.',
7576
cache: options.cache || `${homedir()}/.npm/_cacache`,
7677
packumentCache: options.packumentCache || new Map(),

‎workspaces/arborist/lib/arborist/reify.js

-1
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,6 @@ module.exports = cls => class Reifier extends cls {
702702
})
703703
await pacote.extract(res, node.path, {
704704
...this.options,
705-
Arborist: this.constructor,
706705
resolved: node.resolved,
707706
integrity: node.integrity,
708707
})

0 commit comments

Comments
 (0)
Please sign in to comment.