diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index e14cecf0dc7fe..0ce063561c744 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -25,6 +25,7 @@ graph LR; libnpmaccess-->npmcli-eslint-config["@npmcli/eslint-config"]; libnpmaccess-->npmcli-template-oss["@npmcli/template-oss"]; libnpmdiff-->npm-package-arg; + libnpmdiff-->npmcli-arborist["@npmcli/arborist"]; libnpmdiff-->npmcli-disparity-colors["@npmcli/disparity-colors"]; libnpmdiff-->npmcli-eslint-config["@npmcli/eslint-config"]; libnpmdiff-->npmcli-installed-package-contents["@npmcli/installed-package-contents"]; @@ -54,6 +55,7 @@ graph LR; libnpmorg-->npmcli-eslint-config["@npmcli/eslint-config"]; libnpmorg-->npmcli-template-oss["@npmcli/template-oss"]; libnpmpack-->npm-package-arg; + libnpmpack-->npmcli-arborist["@npmcli/arborist"]; libnpmpack-->npmcli-eslint-config["@npmcli/eslint-config"]; libnpmpack-->npmcli-run-script["@npmcli/run-script"]; libnpmpack-->npmcli-template-oss["@npmcli/template-oss"]; @@ -139,8 +141,6 @@ graph LR; npm-package-arg-->semver; npm-package-arg-->validate-npm-package-name; npm-packlist-->ignore-walk; - npm-packlist-->npm-bundled; - npm-packlist-->npm-normalize-package-bin; npm-profile-->npm-registry-fetch; npm-profile-->proc-log; npm-registry-fetch-->make-fetch-happen; @@ -333,6 +333,7 @@ graph LR; libnpmdiff-->diff; libnpmdiff-->minimatch; libnpmdiff-->npm-package-arg; + libnpmdiff-->npmcli-arborist["@npmcli/arborist"]; libnpmdiff-->npmcli-disparity-colors["@npmcli/disparity-colors"]; libnpmdiff-->npmcli-eslint-config["@npmcli/eslint-config"]; libnpmdiff-->npmcli-installed-package-contents["@npmcli/installed-package-contents"]; @@ -379,6 +380,7 @@ graph LR; libnpmorg-->tap; libnpmpack-->nock; libnpmpack-->npm-package-arg; + libnpmpack-->npmcli-arborist["@npmcli/arborist"]; libnpmpack-->npmcli-eslint-config["@npmcli/eslint-config"]; libnpmpack-->npmcli-run-script["@npmcli/run-script"]; libnpmpack-->npmcli-template-oss["@npmcli/template-oss"]; @@ -554,10 +556,7 @@ graph LR; npm-package-arg-->proc-log; npm-package-arg-->semver; npm-package-arg-->validate-npm-package-name; - npm-packlist-->glob; npm-packlist-->ignore-walk; - npm-packlist-->npm-bundled; - npm-packlist-->npm-normalize-package-bin; npm-pick-manifest-->npm-install-checks; npm-pick-manifest-->npm-normalize-package-bin; npm-pick-manifest-->npm-package-arg; @@ -756,12 +755,13 @@ Each group depends on packages lower down the chain, nothing depends on packages higher up the chain. - npm - - libnpmexec, libnpmfund - - @npmcli/arborist, libnpmpublish - - @npmcli/metavuln-calculator, libnpmdiff, libnpmpack + - libnpmpublish + - libnpmdiff, libnpmexec, libnpmfund, libnpmpack + - @npmcli/arborist + - @npmcli/metavuln-calculator - pacote, libnpmaccess, libnpmhook, libnpmorg, libnpmsearch, libnpmteam, npm-profile - npm-registry-fetch - make-fetch-happen, libnpmversion, @npmcli/config, init-package-json - - @npmcli/installed-package-contents, @npmcli/map-workspaces, cacache, @npmcli/git, @npmcli/run-script, npm-packlist, read-package-json, @npmcli/query, readdir-scoped-modules, promzard - - npm-bundled, read-package-json-fast, @npmcli/fs, unique-filename, @npmcli/promise-spawn, npm-package-arg, normalize-package-data, bin-links, nopt, npm-install-checks, npmlog, dezalgo, read + - @npmcli/installed-package-contents, @npmcli/map-workspaces, cacache, @npmcli/git, @npmcli/run-script, read-package-json, @npmcli/query, readdir-scoped-modules, promzard + - npm-bundled, read-package-json-fast, @npmcli/fs, unique-filename, @npmcli/promise-spawn, npm-package-arg, npm-packlist, normalize-package-data, bin-links, nopt, npm-install-checks, npmlog, dezalgo, read - npm-normalize-package-bin, @npmcli/name-from-folder, semver, @npmcli/move-file, fs-minipass, infer-owner, ssri, unique-slug, proc-log, @npmcli/node-gyp, hosted-git-info, validate-npm-package-name, ignore-walk, minipass-fetch, @npmcli/package-json, cmd-shim, read-cmd-shim, write-file-atomic, abbrev, are-we-there-yet, gauge, parse-conflict-json, wrappy, treeverse, @npmcli/eslint-config, @npmcli/template-oss, @npmcli/disparity-colors, @npmcli/ci-detect, mute-stream, ini, npm-audit-report, npm-user-validate \ No newline at end of file diff --git a/lib/commands/cache.js b/lib/commands/cache.js index bc52889c0006f..a2e6434b34cab 100644 --- a/lib/commands/cache.js +++ b/lib/commands/cache.js @@ -1,4 +1,5 @@ const cacache = require('cacache') +const Arborist = require('@npmcli/arborist') const { promisify } = require('util') const pacote = require('pacote') const path = require('path') @@ -164,7 +165,7 @@ class Cache extends BaseCommand { return pacote.tarball.stream(spec, stream => { stream.resume() return stream.promise() - }, this.npm.flatOptions) + }, { ...this.npm.flatOptions, Arborist }) })) } diff --git a/lib/package-url-cmd.js b/lib/package-url-cmd.js index 4254dde4517ba..eac2bbe1b6d51 100644 --- a/lib/package-url-cmd.js +++ b/lib/package-url-cmd.js @@ -2,6 +2,7 @@ const pacote = require('pacote') const hostedGitInfo = require('hosted-git-info') +const Arborist = require('@npmcli/arborist') const openUrl = require('./utils/open-url.js') const log = require('./utils/log-shim') @@ -31,6 +32,7 @@ class PackageUrlCommand extends BaseCommand { ...this.npm.flatOptions, where: this.npm.localPrefix, fullMetadata: true, + Arborist, } const mani = await pacote.manifest(arg, opts) const url = this.getUrl(arg, mani) diff --git a/node_modules/@npmcli/metavuln-calculator/package.json b/node_modules/@npmcli/metavuln-calculator/package.json index 2e7209ffc7da0..90b4d2ecddce4 100644 --- a/node_modules/@npmcli/metavuln-calculator/package.json +++ b/node_modules/@npmcli/metavuln-calculator/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/metavuln-calculator", - "version": "3.1.1", + "version": "4.0.0-pre.0", "main": "lib/index.js", "files": [ "bin/", @@ -18,9 +18,6 @@ "posttest": "npm run lint", "snap": "tap", "postsnap": "npm run lint", - "preversion": "npm test", - "postversion": "npm publish", - "prepublishOnly": "git push origin --follow-tags", "eslint": "eslint", "lint": "eslint \"**/*.js\"", "lintfix": "npm run lint -- --fix", @@ -29,25 +26,29 @@ }, "tap": { "check-coverage": true, - "coverage-map": "map.js" + "coverage-map": "map.js", + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] }, "devDependencies": { "@npmcli/eslint-config": "^3.0.1", - "@npmcli/template-oss": "3.5.0", + "@npmcli/template-oss": "4.4.2", "require-inject": "^1.4.4", "tap": "^16.0.1" }, "dependencies": { "cacache": "^16.0.0", "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", + "pacote": "^14.0.0 || ^14.0.0-pre.0", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.5.0" + "version": "4.4.2" } } diff --git a/node_modules/npm-bundled/LICENSE b/node_modules/npm-bundled/LICENSE deleted file mode 100644 index 20a4762540923..0000000000000 --- a/node_modules/npm-bundled/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -The ISC License - -Copyright (c) npm, Inc. and Contributors - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/npm-bundled/lib/index.js b/node_modules/npm-bundled/lib/index.js deleted file mode 100644 index 4f54ca647c087..0000000000000 --- a/node_modules/npm-bundled/lib/index.js +++ /dev/null @@ -1,254 +0,0 @@ -'use strict' - -// walk the tree of deps starting from the top level list of bundled deps -// Any deps at the top level that are depended on by a bundled dep that -// does not have that dep in its own node_modules folder are considered -// bundled deps as well. This list of names can be passed to npm-packlist -// as the "bundled" argument. Additionally, packageJsonCache is shared so -// packlist doesn't have to re-read files already consumed in this pass - -const fs = require('fs') -const path = require('path') -const EE = require('events').EventEmitter -// we don't care about the package bins, but we share a pj cache -// with other modules that DO care about it, so keep it nice. -const normalizePackageBin = require('npm-normalize-package-bin') - -class BundleWalker extends EE { - constructor (opt) { - opt = opt || {} - super(opt) - this.path = path.resolve(opt.path || process.cwd()) - - this.parent = opt.parent || null - if (this.parent) { - this.result = this.parent.result - // only collect results in node_modules folders at the top level - // since the node_modules in a bundled dep is included always - if (!this.parent.parent) { - const base = path.basename(this.path) - const scope = path.basename(path.dirname(this.path)) - this.result.add(/^@/.test(scope) ? scope + '/' + base : base) - } - this.root = this.parent.root - this.packageJsonCache = this.parent.packageJsonCache - } else { - this.result = new Set() - this.root = this.path - this.packageJsonCache = opt.packageJsonCache || new Map() - } - - this.seen = new Set() - this.didDone = false - this.children = 0 - this.node_modules = [] - this.package = null - this.bundle = null - } - - addListener (ev, fn) { - return this.on(ev, fn) - } - - on (ev, fn) { - const ret = super.on(ev, fn) - if (ev === 'done' && this.didDone) { - this.emit('done', this.result) - } - return ret - } - - done () { - if (!this.didDone) { - this.didDone = true - if (!this.parent) { - const res = Array.from(this.result) - this.result = res - this.emit('done', res) - } else { - this.emit('done') - } - } - } - - start () { - const pj = path.resolve(this.path, 'package.json') - if (this.packageJsonCache.has(pj)) { - this.onPackage(this.packageJsonCache.get(pj)) - } else { - this.readPackageJson(pj) - } - return this - } - - readPackageJson (pj) { - fs.readFile(pj, (er, data) => - er ? this.done() : this.onPackageJson(pj, data)) - } - - onPackageJson (pj, data) { - try { - this.package = normalizePackageBin(JSON.parse(data + '')) - } catch (er) { - return this.done() - } - this.packageJsonCache.set(pj, this.package) - this.onPackage(this.package) - } - - allDepsBundled (pkg) { - return Object.keys(pkg.dependencies || {}).concat( - Object.keys(pkg.optionalDependencies || {})) - } - - onPackage (pkg) { - // all deps are bundled if we got here as a child. - // otherwise, only bundle bundledDeps - // Get a unique-ified array with a short-lived Set - const bdRaw = this.parent ? this.allDepsBundled(pkg) - : pkg.bundleDependencies || pkg.bundledDependencies || [] - - const bd = Array.from(new Set( - Array.isArray(bdRaw) ? bdRaw - : bdRaw === true ? this.allDepsBundled(pkg) - : Object.keys(bdRaw))) - - if (!bd.length) { - return this.done() - } - - this.bundle = bd - this.readModules() - } - - readModules () { - readdirNodeModules(this.path + '/node_modules', (er, nm) => - er ? this.onReaddir([]) : this.onReaddir(nm)) - } - - onReaddir (nm) { - // keep track of what we have, in case children need it - this.node_modules = nm - - this.bundle.forEach(dep => this.childDep(dep)) - if (this.children === 0) { - this.done() - } - } - - childDep (dep) { - if (this.node_modules.indexOf(dep) !== -1) { - if (!this.seen.has(dep)) { - this.seen.add(dep) - this.child(dep) - } - } else if (this.parent) { - this.parent.childDep(dep) - } - } - - child (dep) { - const p = this.path + '/node_modules/' + dep - this.children += 1 - const child = new BundleWalker({ - path: p, - parent: this, - }) - child.on('done', _ => { - if (--this.children === 0) { - this.done() - } - }) - child.start() - } -} - -class BundleWalkerSync extends BundleWalker { - start () { - super.start() - this.done() - return this - } - - readPackageJson (pj) { - try { - this.onPackageJson(pj, fs.readFileSync(pj)) - } catch { - // empty catch - } - return this - } - - readModules () { - try { - this.onReaddir(readdirNodeModulesSync(this.path + '/node_modules')) - } catch { - this.onReaddir([]) - } - } - - child (dep) { - new BundleWalkerSync({ - path: this.path + '/node_modules/' + dep, - parent: this, - }).start() - } -} - -const readdirNodeModules = (nm, cb) => { - fs.readdir(nm, (er, set) => { - if (er) { - cb(er) - } else { - const scopes = set.filter(f => /^@/.test(f)) - if (!scopes.length) { - cb(null, set) - } else { - const unscoped = set.filter(f => !/^@/.test(f)) - let count = scopes.length - scopes.forEach(scope => { - fs.readdir(nm + '/' + scope, (readdirEr, pkgs) => { - if (readdirEr || !pkgs.length) { - unscoped.push(scope) - } else { - unscoped.push.apply(unscoped, pkgs.map(p => scope + '/' + p)) - } - if (--count === 0) { - cb(null, unscoped) - } - }) - }) - } - } - }) -} - -const readdirNodeModulesSync = nm => { - const set = fs.readdirSync(nm) - const unscoped = set.filter(f => !/^@/.test(f)) - const scopes = set.filter(f => /^@/.test(f)).map(scope => { - try { - const pkgs = fs.readdirSync(nm + '/' + scope) - return pkgs.length ? pkgs.map(p => scope + '/' + p) : [scope] - } catch (er) { - return [scope] - } - }).reduce((a, b) => a.concat(b), []) - return unscoped.concat(scopes) -} - -const walk = (options, callback) => { - const p = new Promise((resolve, reject) => { - new BundleWalker(options).on('done', resolve).on('error', reject).start() - }) - return callback ? p.then(res => callback(null, res), callback) : p -} - -const walkSync = options => { - return new BundleWalkerSync(options).start().result -} - -module.exports = walk -walk.sync = walkSync -walk.BundleWalker = BundleWalker -walk.BundleWalkerSync = BundleWalkerSync diff --git a/node_modules/npm-bundled/node_modules/npm-normalize-package-bin/LICENSE b/node_modules/npm-bundled/node_modules/npm-normalize-package-bin/LICENSE deleted file mode 100644 index 19cec97b18468..0000000000000 --- a/node_modules/npm-bundled/node_modules/npm-normalize-package-bin/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -The ISC License - -Copyright (c) npm, Inc. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/npm-bundled/node_modules/npm-normalize-package-bin/lib/index.js b/node_modules/npm-bundled/node_modules/npm-normalize-package-bin/lib/index.js deleted file mode 100644 index d6f0a581b9e66..0000000000000 --- a/node_modules/npm-bundled/node_modules/npm-normalize-package-bin/lib/index.js +++ /dev/null @@ -1,64 +0,0 @@ -// pass in a manifest with a 'bin' field here, and it'll turn it -// into a properly santized bin object -const { join, basename } = require('path') - -const normalize = pkg => - !pkg.bin ? removeBin(pkg) - : typeof pkg.bin === 'string' ? normalizeString(pkg) - : Array.isArray(pkg.bin) ? normalizeArray(pkg) - : typeof pkg.bin === 'object' ? normalizeObject(pkg) - : removeBin(pkg) - -const normalizeString = pkg => { - if (!pkg.name) { - return removeBin(pkg) - } - pkg.bin = { [pkg.name]: pkg.bin } - return normalizeObject(pkg) -} - -const normalizeArray = pkg => { - pkg.bin = pkg.bin.reduce((acc, k) => { - acc[basename(k)] = k - return acc - }, {}) - return normalizeObject(pkg) -} - -const removeBin = pkg => { - delete pkg.bin - return pkg -} - -const normalizeObject = pkg => { - const orig = pkg.bin - const clean = {} - let hasBins = false - Object.keys(orig).forEach(binKey => { - const base = join('/', basename(binKey.replace(/\\|:/g, '/'))).slice(1) - - if (typeof orig[binKey] !== 'string' || !base) { - return - } - - const binTarget = join('/', orig[binKey]) - .replace(/\\/g, '/').slice(1) - - if (!binTarget) { - return - } - - clean[base] = binTarget - hasBins = true - }) - - if (hasBins) { - pkg.bin = clean - } else { - delete pkg.bin - } - - return pkg -} - -module.exports = normalize diff --git a/node_modules/npm-bundled/node_modules/npm-normalize-package-bin/package.json b/node_modules/npm-bundled/node_modules/npm-normalize-package-bin/package.json deleted file mode 100644 index 02de808d9b702..0000000000000 --- a/node_modules/npm-bundled/node_modules/npm-normalize-package-bin/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "npm-normalize-package-bin", - "version": "2.0.0", - "description": "Turn any flavor of allowable package.json bin into a normalized object", - "main": "lib/index.js", - "repository": { - "type": "git", - "url": "https://github.com/npm/npm-normalize-package-bin.git" - }, - "author": "GitHub Inc.", - "license": "ISC", - "scripts": { - "test": "tap", - "snap": "tap", - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "git push origin --follow-tags", - "lint": "eslint \"**/*.js\"", - "postlint": "template-oss-check", - "template-oss-apply": "template-oss-apply --force", - "lintfix": "npm run lint -- --fix", - "prepublishOnly": "git push origin --follow-tags", - "posttest": "npm run lint" - }, - "devDependencies": { - "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.5.0", - "tap": "^16.3.0" - }, - "files": [ - "bin/", - "lib/" - ], - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "templateOSS": { - "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.5.0" - } -} diff --git a/node_modules/npm-bundled/package.json b/node_modules/npm-bundled/package.json deleted file mode 100644 index e4c0106c2d504..0000000000000 --- a/node_modules/npm-bundled/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "npm-bundled", - "version": "2.0.1", - "description": "list things in node_modules that are bundledDependencies, or transitive dependencies thereof", - "main": "lib/index.js", - "repository": { - "type": "git", - "url": "https://github.com/npm/npm-bundled.git" - }, - "author": "GitHub Inc.", - "license": "ISC", - "devDependencies": { - "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.5.0", - "mkdirp": "^1.0.4", - "mutate-fs": "^2.1.1", - "rimraf": "^3.0.2", - "tap": "^16.3.0" - }, - "scripts": { - "test": "tap", - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "git push origin --all; git push origin --tags", - "lint": "eslint \"**/*.js\"", - "postlint": "template-oss-check", - "template-oss-apply": "template-oss-apply --force", - "lintfix": "npm run lint -- --fix", - "prepublishOnly": "git push origin --follow-tags", - "snap": "tap", - "posttest": "npm run lint" - }, - "files": [ - "bin/", - "lib/" - ], - "dependencies": { - "npm-normalize-package-bin": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "templateOSS": { - "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.5.0" - } -} diff --git a/node_modules/npm-packlist/bin/index.js b/node_modules/npm-packlist/bin/index.js deleted file mode 100755 index 48a6b879aa823..0000000000000 --- a/node_modules/npm-packlist/bin/index.js +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env node -'use strict' - -const packlist = require('../') - -const dirs = [] -let doSort = false -process.argv.slice(2).forEach(arg => { - if (arg === '-h' || arg === '--help') { - console.log('usage: npm-packlist [-s --sort] [directory, directory, ...]') - process.exit(0) - } else if (arg === '-s' || arg === '--sort') { - doSort = true - } else { - dirs.push(arg) - } -}) - -const sort = list => doSort ? list.sort((a, b) => a.localeCompare(b, 'en')) : list - -const main = async () => { - if (!dirs.length) { - const results = await packlist({ path: process.cwd() }) - console.log(sort(results).join('\n')) - } else { - for (const dir of dirs) { - console.group(`> ${dir}`) - const results = await packlist({ path: dir }) - console.log(sort(results).join('\n')) - console.groupEnd() - } - } -} - -// coverage disabled for catch handler because we don't need to test that -main().catch(/* istanbul ignore next */(err) => { - process.exitCode = 1 - console.error(err.stack) -}) diff --git a/node_modules/npm-packlist/lib/index.js b/node_modules/npm-packlist/lib/index.js index bd72329f027e6..1b2cdbb8df641 100644 --- a/node_modules/npm-packlist/lib/index.js +++ b/node_modules/npm-packlist/lib/index.js @@ -1,87 +1,18 @@ 'use strict' -// Do a two-pass walk, first to get the list of packages that need to be -// bundled, then again to get the actual files and folders. -// Keep a cache of node_modules content and package.json data, so that the -// second walk doesn't have to re-do all the same work. +const { Walker: IgnoreWalker } = require('ignore-walk') +const { lstatSync: lstat, readFileSync: readFile } = require('fs') +const { basename, dirname, extname, join, relative, resolve, sep } = require('path') -const bundleWalk = require('npm-bundled') -const BundleWalker = bundleWalk.BundleWalker +// symbols used to represent synthetic rule sets +const defaultRules = Symbol('npm-packlist.rules.default') +const strictRules = Symbol('npm-packlist.rules.strict') -const ignoreWalk = require('ignore-walk') -const IgnoreWalker = ignoreWalk.Walker - -const rootBuiltinRules = Symbol('root-builtin-rules') -const packageNecessaryRules = Symbol('package-necessary-rules') -const path = require('path') - -const normalizePackageBin = require('npm-normalize-package-bin') - -// Weird side-effect of this: a readme (etc) file will be included -// if it exists anywhere within a folder with a package.json file. -// The original intent was only to include these files in the root, -// but now users in the wild are dependent on that behavior for -// localized documentation and other use cases. Adding a `/` to -// these rules, while tempting and arguably more "correct", is a -// significant change that will break existing use cases. -const packageMustHaveFileNames = 'readme|copying|license|licence' - -const packageMustHaves = `@(${packageMustHaveFileNames}){,.*[^~$]}` -const packageMustHavesRE = new RegExp(`^(${packageMustHaveFileNames})(\\..*[^~$])?$`, 'i') - -const fs = require('fs') -const glob = require('glob') -const globify = pattern => pattern.split('\\').join('/') - -const readOutOfTreeIgnoreFiles = (root, rel, result = '') => { - for (const file of ['.npmignore', '.gitignore']) { - try { - const ignoreContent = fs.readFileSync(path.join(root, file), { encoding: 'utf8' }) - result += ignoreContent + '\n' - // break the loop immediately after concatting, this allows us to prioritize the - // .npmignore and discard the .gitignore if one exists - break - } catch (err) { - // we ignore ENOENT errors completely because we don't care if the file doesn't exist - // but we throw everything else because failing to read a file that does exist is - // something that the user likely wants to know about. we don't need to test this. - /* istanbul ignore next */ - if (err.code !== 'ENOENT') { - throw err - } - } - } - - if (!rel) { - return result - } - - const firstRel = rel.split(path.sep)[0] - const newRoot = path.join(root, firstRel) - const newRel = path.relative(newRoot, path.join(root, rel)) - - return readOutOfTreeIgnoreFiles(newRoot, newRel, result) -} - -const pathHasPkg = (input) => { - if (!input.startsWith('node_modules/')) { - return false - } - - const segments = input.slice('node_modules/'.length).split('/', 2) - return segments[0].startsWith('@') - ? segments.length === 2 - : true -} - -const pkgFromPath = (input) => { - const segments = input.slice('node_modules/'.length).split('/', 2) - return segments[0].startsWith('@') - ? segments.join('/') - : segments[0] -} +// There may be others, but :?|<> are handled by node-tar +const nameIsBadForWindows = file => /\*/.test(file) -const defaultRules = [ +// these are the default rules that are applied to everything except for non-link bundled deps +const defaults = [ '.npmignore', '.gitignore', '**/.git', @@ -103,410 +34,413 @@ const defaultRules = [ '._*', '**/._*/**', '*.orig', - '/package-lock.json', - '/yarn.lock', - '/pnpm-lock.yaml', '/archived-packages/**', ] -// There may be others, but :?|<> are handled by node-tar -const nameIsBadForWindows = file => /\*/.test(file) +const strictDefaults = [ + // these are forcibly included at all levels + '!/readme{,.*[^~$]}', + '!/copying{,.*[^~$]}', + '!/license{,.*[^~$]}', + '!/licence{,.*[^~$]}', + // these are forcibly excluded + '/.git', +] -class Walker extends IgnoreWalker { - constructor (opt) { - opt = opt || {} - - // the order in which rules are applied. - opt.ignoreFiles = [ - rootBuiltinRules, - 'package.json', - '.npmignore', - '.gitignore', - packageNecessaryRules, - ] +const normalizePath = (path) => path.split('\\').join('/') - opt.includeEmpty = false - opt.path = opt.path || process.cwd() - - // only follow links in the root node_modules folder, because if those - // folders are included, it's because they're bundled, and bundles - // should include the contents, not the symlinks themselves. - // This regexp tests to see that we're either a node_modules folder, - // or a @scope within a node_modules folder, in the root's node_modules - // hierarchy (ie, not in test/foo/node_modules/ or something). - const followRe = /^(?:\/node_modules\/(?:@[^/]+\/[^/]+|[^/]+)\/)*\/node_modules(?:\/@[^/]+)?$/ - const rootPath = opt.parent ? opt.parent.root : opt.path - const followTestPath = opt.path.replace(/\\/g, '/').slice(rootPath.length) - opt.follow = followRe.test(followTestPath) - - super(opt) - - // ignore a bunch of things by default at the root level. - // also ignore anything in the main project node_modules hierarchy, - // except bundled dependencies - if (this.isProject) { - this.bundled = opt.bundled || [] - this.bundledScopes = Array.from(new Set( - this.bundled.filter(f => /^@/.test(f)) - .map(f => f.split('/')[0]))) - this.packageJsonCache = this.parent ? this.parent.packageJsonCache - : (opt.packageJsonCache || new Map()) - let rules = defaultRules.join('\n') + '\n' - - if (opt.prefix && opt.workspaces) { - const gPath = globify(opt.path) - const gPrefix = globify(opt.prefix) - const gWorkspaces = opt.workspaces.map((ws) => globify(ws)) - // if opt.path and opt.prefix are not the same directory, and opt.workspaces has opt.path - // in it, then we know that opt.path is a workspace directory. in order to not drop ignore - // rules from directories between the workspace root (opt.prefix) and the workspace itself - // (opt.path), we need to find and read those now - /* istanbul ignore else */ - if (gPath !== gPrefix && gWorkspaces.includes(gPath)) { - // relpath is the relative path between the prefix and the parent of opt.path - // we use the parent because ignore-walk will read the files in opt.path already - const relpath = path.relative(opt.prefix, path.dirname(opt.path)) - rules += readOutOfTreeIgnoreFiles(opt.prefix, relpath) - } else if (gPath === gPrefix) { - // on the other hand, if the path and the prefix are the same, then we ignore workspaces - // so that we don't pack workspaces inside of a root project - rules += opt.workspaces.map((ws) => globify(path.relative(opt.path, ws))).join('\n') - } +const readOutOfTreeIgnoreFiles = (root, rel, result = []) => { + for (const file of ['.npmignore', '.gitignore']) { + try { + const ignoreContent = readFile(join(root, file), { encoding: 'utf8' }) + result.push(ignoreContent) + // break the loop immediately after reading, this allows us to prioritize + // the .npmignore and discard the .gitignore if one is present + break + } catch (err) { + // we ignore ENOENT errors completely because we don't care if the file doesn't exist + // but we throw everything else because failing to read a file that does exist is + // something that the user likely wants to know about + // istanbul ignore next -- we do not need to test a thrown error + if (err.code !== 'ENOENT') { + throw err } - - super.onReadIgnoreFile(rootBuiltinRules, rules, _ => _) - } else { - this.bundled = [] - this.bundledScopes = [] - this.packageJsonCache = this.parent.packageJsonCache } } - get isProject () { - return !this.parent || this.parent.follow && this.isSymbolicLink + if (!rel) { + return result } - onReaddir (entries) { - if (this.isProject) { - entries = entries.filter(e => - e !== '.git' && - !(e === 'node_modules' && this.bundled.length === 0) - ) - } - - // if we have a package.json, then look in it for 'files' - // we _only_ do this in the root project, not bundled deps - // or other random folders. Bundled deps are always assumed - // to be in the state the user wants to include them, and - // a package.json somewhere else might be a template or - // test or something else entirely. - if (!this.isProject || !entries.includes('package.json')) { - return super.onReaddir(entries) - } + const firstRel = rel.split(sep, 1)[0] + const newRoot = join(root, firstRel) + const newRel = relative(newRoot, join(root, rel)) - // when the cache has been seeded with the root manifest, - // we must respect that (it may differ from the filesystem) - const ig = path.resolve(this.path, 'package.json') + return readOutOfTreeIgnoreFiles(newRoot, newRel, result) +} - if (this.packageJsonCache.has(ig)) { - const pkg = this.packageJsonCache.get(ig) +class PackWalker extends IgnoreWalker { + constructor (tree, opts) { + const options = { + ...opts, + includeEmpty: false, + follow: false, + // we path.resolve() here because ignore-walk doesn't do it and we want full paths + path: resolve(opts?.path || tree.path).replace(/\\/g, '/'), + ignoreFiles: opts?.ignoreFiles || [ + defaultRules, + 'package.json', + '.npmignore', + '.gitignore', + strictRules, + ], + } - // fall back to filesystem when seeded manifest is invalid - if (!pkg || typeof pkg !== 'object') { - return this.readPackageJson(entries) + super(options) + this.isPackage = options.isPackage + this.seen = options.seen || new Set() + this.tree = tree + this.requiredFiles = options.requiredFiles || [] + + const additionalDefaults = [] + if (options.prefix && options.workspaces) { + const path = normalizePath(options.path) + const prefix = normalizePath(options.prefix) + const workspaces = options.workspaces.map((ws) => normalizePath(ws)) + + // istanbul ignore else - this does nothing unless we need it to + if (path !== prefix && workspaces.includes(path)) { + // if path and prefix are not the same directory, and workspaces has path in it + // then we know path is a workspace directory. in order to not drop ignore rules + // from directories between the workspaces root (prefix) and the workspace itself + // (path) we need to find and read those now + const relpath = relative(options.prefix, dirname(options.path)) + additionalDefaults.push(...readOutOfTreeIgnoreFiles(options.prefix, relpath)) + } else if (path === prefix) { + // on the other hand, if the path and prefix are the same, then we ignore workspaces + // so that we don't pack a workspace as part of the root project. append them as + // normalized relative paths from the root + additionalDefaults.push(...workspaces.map((w) => normalizePath(relative(options.path, w)))) } - - // feels wonky, but this ensures package bin is _always_ - // normalized, as well as guarding against invalid JSON - return this.getPackageFiles(entries, JSON.stringify(pkg)) } - this.readPackageJson(entries) - } + // go ahead and inject the default rules now + this.injectRules(defaultRules, [...defaults, ...additionalDefaults]) - onReadPackageJson (entries, er, pkg) { - if (er) { - this.emit('error', er) - } else { - this.getPackageFiles(entries, pkg) + if (!this.isPackage) { + // if this instance is not a package, then place some strict default rules, and append + // known required files for this directory + this.injectRules(strictRules, [ + ...strictDefaults, + ...this.requiredFiles.map((file) => `!${file}`), + ]) } } - mustHaveFilesFromPackage (pkg) { - const files = [] - if (pkg.browser) { - files.push('/' + pkg.browser) - } - if (pkg.main) { - files.push('/' + pkg.main) + // overridden method: we intercept the reading of the package.json file here so that we can + // process it into both the package.json file rules as well as the strictRules synthetic rule set + addIgnoreFile (file, callback) { + // if we're adding anything other than package.json, then let ignore-walk handle it + if (file !== 'package.json' || !this.isPackage) { + return super.addIgnoreFile(file, callback) } - if (pkg.bin) { - // always an object because normalized already - for (const key in pkg.bin) { - files.push('/' + pkg.bin[key]) - } + + return this.processPackage(callback) + } + + // overridden method: if we're done, but we're a package, then we also need to evaluate bundles + // before we actually emit our done event + emit (ev, data) { + if (ev !== 'done' || !this.isPackage) { + return super.emit(ev, data) } - files.push( - '/package.json', - '/npm-shrinkwrap.json', - '!/package-lock.json', - packageMustHaves - ) - return files + + // we intentionally delay the done event while keeping the function sync here + // eslint-disable-next-line promise/catch-or-return, promise/always-return + this.gatherBundles().then(() => { + super.emit('done', this.result) + }) + return true } - getPackageFiles (entries, pkg) { - try { - // XXX this could be changed to use read-package-json-fast - // which handles the normalizing of bins for us, and simplifies - // the test for bundleDependencies and bundledDependencies later. - // HOWEVER if we do this, we need to be sure that we're careful - // about what we write back out since rpj-fast removes some fields - // that the user likely wants to keep. it also would add a second - // file read that we would want to optimize away. - pkg = normalizePackageBin(JSON.parse(pkg.toString())) - } catch (er) { - // not actually a valid package.json - return super.onReaddir(entries) + // overridden method: before actually filtering, we make sure that we've removed the rules for + // files that should no longer take effect due to our order of precedence + filterEntries () { + if (this.ignoreRules['package.json']) { + // package.json means no .npmignore or .gitignore + this.ignoreRules['.npmignore'] = null + this.ignoreRules['.gitignore'] = null + } else if (this.ignoreRules['.npmignore']) { + // .npmignore means no .gitignore + this.ignoreRules['.gitignore'] = null } - const ig = path.resolve(this.path, 'package.json') - this.packageJsonCache.set(ig, pkg) + return super.filterEntries() + } - // no files list, just return the normal readdir() result - if (!Array.isArray(pkg.files)) { - return super.onReaddir(entries) + // overridden method: we never want to include anything that isn't a file or directory + onstat (opts, callback) { + if (!opts.st.isFile() && !opts.st.isDirectory()) { + return callback() } - pkg.files.push(...this.mustHaveFilesFromPackage(pkg)) + return super.onstat(opts, callback) + } - // If the package has a files list, then it's unlikely to include - // node_modules, because why would you do that? but since we use - // the files list as the effective readdir result, that means it - // looks like we don't have a node_modules folder at all unless we - // include it here. - if ((pkg.bundleDependencies || pkg.bundledDependencies) && entries.includes('node_modules')) { - pkg.files.push('node_modules') + // overridden method: we want to refuse to pack files that are invalid, node-tar protects us from + // a lot of them but not all + stat (opts, callback) { + if (nameIsBadForWindows(opts.entry)) { + return callback() } - const patterns = Array.from(new Set(pkg.files)).reduce((set, pattern) => { - const excl = pattern.match(/^!+/) - if (excl) { - pattern = pattern.slice(excl[0].length) - } - // strip off any / or ./ from the start of the pattern. /foo => foo, ./foo => foo - pattern = pattern.replace(/^\.?\/+/, '') - // an odd number of ! means a negated pattern. !!foo ==> foo - const negate = excl && excl[0].length % 2 === 1 - set.push({ pattern, negate }) - return set - }, []) - - let n = patterns.length - const set = new Set() - const negates = new Set() - const results = [] - const then = (pattern, negate, er, fileList, i) => { - if (er) { - return this.emit('error', er) - } + return super.stat(opts, callback) + } - results[i] = { negate, fileList } - if (--n === 0) { - processResults(results) + // overridden method: this is called to create options for a child walker when we step + // in to a normal child directory (this will never be a bundle). the default method here + // copies the root's `ignoreFiles` value, but we don't want to respect package.json for + // subdirectories, so we override it with a list that intentionally omits package.json + walkerOpt (entry, opts) { + let ignoreFiles = null + + // however, if we have a tree, and we have workspaces, and the directory we're about + // to step into is a workspace, then we _do_ want to respect its package.json + if (this.tree.workspaces) { + const workspaceDirs = [...this.tree.workspaces.values()] + .map((dir) => dir.replace(/\\/g, '/')) + + const entryPath = join(this.path, entry).replace(/\\/g, '/') + if (workspaceDirs.includes(entryPath)) { + ignoreFiles = [ + defaultRules, + 'package.json', + '.npmignore', + '.gitignore', + strictRules, + ] } + } else { + ignoreFiles = [ + defaultRules, + '.npmignore', + '.gitignore', + strictRules, + ] } - const processResults = processed => { - for (const { negate, fileList } of processed) { - if (negate) { - fileList.forEach(f => { - f = f.replace(/\/+$/, '') - set.delete(f) - negates.add(f) - }) - } else { - fileList.forEach(f => { - f = f.replace(/\/+$/, '') - set.add(f) - negates.delete(f) - }) - } - } - const list = Array.from(set) - // replace the files array with our computed explicit set - pkg.files = list.concat(Array.from(negates).map(f => '!' + f)) - const rdResult = Array.from(new Set( - list.map(f => f.replace(/^\/+/, '')) - )) - super.onReaddir(rdResult) + return { + ...super.walkerOpt(entry, opts), + ignoreFiles, + // we map over our own requiredFiles and pass ones that are within this entry + requiredFiles: this.requiredFiles + .map((file) => { + if (relative(file, entry) === '..') { + return relative(entry, file).replace(/\\/g, '/') + } + return false + }) + .filter(Boolean), } + } - // maintain the index so that we process them in-order only once all - // are completed, otherwise the parallelism messes things up, since a - // glob like **/*.js will always be slower than a subsequent !foo.js - patterns.forEach(({ pattern, negate }, i) => - this.globFiles(pattern, (er, res) => then(pattern, negate, er, res, i))) + // overridden method: we want child walkers to be instances of this class, not ignore-walk + walker (entry, opts, callback) { + new PackWalker(this.tree, this.walkerOpt(entry, opts)).on('done', callback).start() } - filterEntry (entry, partial) { - // get the partial path from the root of the walk - const p = this.path.slice(this.root.length + 1) - const { isProject } = this - const pkg = isProject && pathHasPkg(entry) - ? pkgFromPath(entry) - : null - const rootNM = isProject && entry === 'node_modules' - const rootPJ = isProject && entry === 'package.json' - - return ( - // if we're in a bundled package, check with the parent. - /^node_modules($|\/)/i.test(p) && !this.isProject ? this.parent.filterEntry( - this.basename + '/' + entry, partial) - - // if package is bundled, all files included - // also include @scope dirs for bundled scoped deps - // they'll be ignored if no files end up in them. - // However, this only matters if we're in the root. - // node_modules folders elsewhere, like lib/node_modules, - // should be included normally unless ignored. - : pkg ? this.bundled.indexOf(pkg) !== -1 || - this.bundledScopes.indexOf(pkg) !== -1 - - // only walk top node_modules if we want to bundle something - : rootNM ? !!this.bundled.length - - // always include package.json at the root. - : rootPJ ? true - - // always include readmes etc in any included dir - : packageMustHavesRE.test(entry) ? true - - // npm-shrinkwrap and package.json always included in the root pkg - : isProject && (entry === 'npm-shrinkwrap.json' || entry === 'package.json') - ? true - - // package-lock never included - : isProject && entry === 'package-lock.json' ? false - - // otherwise, follow ignore-walk's logic - : super.filterEntry(entry, partial) - ) + // overridden method: we use a custom sort method to help compressibility + sort (a, b) { + // optimize for compressibility + // extname, then basename, then locale alphabetically + // https://twitter.com/isntitvacant/status/1131094910923231232 + const exta = extname(a).toLowerCase() + const extb = extname(b).toLowerCase() + const basea = basename(a).toLowerCase() + const baseb = basename(b).toLowerCase() + + return exta.localeCompare(extb, 'en') || + basea.localeCompare(baseb, 'en') || + a.localeCompare(b, 'en') } - filterEntries () { - if (this.ignoreRules['.npmignore']) { - this.ignoreRules['.gitignore'] = null - } - this.filterEntries = super.filterEntries - super.filterEntries() + // convenience method: this joins the given rules with newlines, appends a trailing newline, + // and calls the internal onReadIgnoreFile method + injectRules (filename, rules, callback = () => {}) { + this.onReadIgnoreFile(filename, `${rules.join('\n')}\n`, callback) } - addIgnoreFile (file, then) { - const ig = path.resolve(this.path, file) - if (file === 'package.json' && !this.isProject) { - then() - } else if (this.packageJsonCache.has(ig)) { - this.onPackageJson(ig, this.packageJsonCache.get(ig), then) - } else { - super.addIgnoreFile(file, then) + // custom method: this is called by addIgnoreFile when we find a package.json, it uses the + // arborist tree to pull both default rules and strict rules for the package + processPackage (callback) { + const { + bin, + browser, + files, + main, + } = this.tree.package + + // rules in these arrays are inverted since they are patterns we want to _not_ ignore + const ignores = [] + const strict = [ + ...strictDefaults, + '!/package.json', + '!/npm-shrinkwrap.json', + '/.git', + '/node_modules', + '/package-lock.json', + '/yarn.lock', + '/pnpm-lock.yaml', + ] + + // if we have a files array in our package, we need to pull rules from it + if (files) { + for (const file of files) { + // invert the rule because these are things we want to include + const inverse = `!${file}` + try { + // if an entry in the files array is a specific file, then we need to include it as a + // strict requirement for this package. if it's a directory or a pattern, it's a default + // pattern instead. this is ugly, but we have to stat to find out if it's a file + const stat = lstat(join(this.path, file.replace(/^!+/, '')).replace(/\\/g, '/')) + // if we have a file and we know that, it's strictly required + if (stat.isFile()) { + strict.unshift(inverse) + this.requiredFiles.push(file.startsWith('/') ? file.slice(1) : file) + } else if (stat.isDirectory()) { + // otherwise, it's a default ignore, and since we got here we know it's not a pattern + // so we include the directory contents + ignores.push(inverse) + ignores.push(`${inverse}/**`) + } + // if the thing exists, but is neither a file or a directory, we don't want it at all + } catch (err) { + // if lstat throws, then we assume we're looking at a pattern and treat it as a default + ignores.push(inverse) + } + } + + // we prepend a '*' to exclude everything, followed by our inverted file rules + // which now mean to include those + this.injectRules('package.json', ['*', ...ignores]) } - } - onPackageJson (ig, pkg, then) { - this.packageJsonCache.set(ig, pkg) + // browser is required + if (browser) { + strict.push(`!/${browser}`) + } - if (Array.isArray(pkg.files)) { - // in this case we already included all the must-haves - super.onReadIgnoreFile('package.json', pkg.files.map( - f => '!' + f - ).join('\n') + '\n', then) - } else { - // if there's a bin, browser or main, make sure we don't ignore it - // also, don't ignore the package.json itself, or any files that - // must be included in the package. - const rules = this.mustHaveFilesFromPackage(pkg).map(f => `!${f}`) - const data = rules.join('\n') + '\n' - super.onReadIgnoreFile(packageNecessaryRules, data, then) + // main is required + if (main) { + strict.push(`!/${main}`) } - } - // override parent stat function to completely skip any filenames - // that will break windows entirely. - // XXX(isaacs) Next major version should make this an error instead. - stat ({ entry, file, dir }, then) { - if (nameIsBadForWindows(entry)) { - then() - } else { - super.stat({ entry, file, dir }, then) + // each bin is required + if (bin) { + for (const key in bin) { + strict.push(`!/${bin[key]}`) + } } + + // and now we add all of the strict rules to our synthetic file + this.injectRules(strictRules, strict, callback) } - // override parent onstat function to nix all symlinks, other than - // those coming out of the followed bundled symlink deps - onstat ({ st, entry, file, dir, isSymbolicLink }, then) { - if (st.isSymbolicLink()) { - then() - } else { - super.onstat({ st, entry, file, dir, isSymbolicLink }, then) + // custom method: after we've finished gathering the files for the root package, we call this + // before emitting the 'done' event in order to gather all of the files for bundled deps + async gatherBundles () { + if (this.seen.has(this.tree)) { + return } - } - onReadIgnoreFile (file, data, then) { - if (file === 'package.json') { - try { - const ig = path.resolve(this.path, file) - this.onPackageJson(ig, JSON.parse(data), then) - } catch (er) { - // ignore package.json files that are not json - then() - } + // add this node to our seen tracker + this.seen.add(this.tree) + + // if we're the project root, then we look at our bundleDependencies, otherwise we got here + // because we're a bundled dependency of the root, which means we need to include all prod + // and optional dependencies in the bundle + let toBundle + if (this.tree.isProjectRoot) { + const { bundleDependencies } = this.tree.package + toBundle = bundleDependencies || [] } else { - super.onReadIgnoreFile(file, data, then) + const { dependencies, optionalDependencies } = this.tree.package + toBundle = Object.keys(dependencies || {}).concat(Object.keys(optionalDependencies || {})) } - } - sort (a, b) { - // optimize for compressibility - // extname, then basename, then locale alphabetically - // https://twitter.com/isntitvacant/status/1131094910923231232 - const exta = path.extname(a).toLowerCase() - const extb = path.extname(b).toLowerCase() - const basea = path.basename(a).toLowerCase() - const baseb = path.basename(b).toLowerCase() + for (const dep of toBundle) { + const edge = this.tree.edgesOut.get(dep) + // no edgeOut = missing node, so skip it. we can't pack it if it's not here + // we also refuse to pack peer dependencies and dev dependencies + if (!edge || edge.peer || edge.dev) { + continue + } - return exta.localeCompare(extb, 'en') || - basea.localeCompare(baseb, 'en') || - a.localeCompare(b, 'en') - } + // get a reference to the node we're bundling + const node = this.tree.edgesOut.get(dep).to + // we use node.path for the path because we want the location the node was linked to, + // not where it actually lives on disk + const path = node.path + // but link nodes don't have edgesOut, so we need to pass in the target of the node + // in order to make sure we correctly traverse its dependencies + const tree = node.target + + // and start building options to be passed to the walker for this package + const walkerOpts = { + path, + isPackage: true, + ignoreFiles: [], + seen: this.seen, // pass through seen so we can prevent infinite circular loops + } - globFiles (pattern, cb) { - glob(globify(pattern), { dot: true, cwd: this.path, nocase: true }, cb) - } + // if our node is a link, we apply defaultRules. we don't do this for regular bundled + // deps because their .npmignore and .gitignore files are excluded by default and may + // override defaults + if (node.isLink) { + walkerOpts.ignoreFiles.push(defaultRules) + } - readPackageJson (entries) { - fs.readFile(this.path + '/package.json', (er, pkg) => - this.onReadPackageJson(entries, er, pkg)) - } + // _all_ nodes will follow package.json rules from their package root + walkerOpts.ignoreFiles.push('package.json') + + // only link nodes will obey .npmignore or .gitignore + if (node.isLink) { + walkerOpts.ignoreFiles.push('.npmignore') + walkerOpts.ignoreFiles.push('.gitignore') + } - walker (entry, opt, then) { - new Walker(this.walkerOpt(entry, opt)).on('done', then).start() + // _all_ nodes follow strict rules + walkerOpts.ignoreFiles.push(strictRules) + + // create a walker for this dependency and gather its results + const walker = new PackWalker(tree, walkerOpts) + const bundled = await new Promise((pResolve, pReject) => { + walker.on('error', pReject) + walker.on('done', pResolve) + walker.start() + }) + + // now we make sure we have our paths correct from the root, and accumulate everything into + // our own result set to deduplicate + const relativeFrom = relative(this.root, walker.path) + for (const file of bundled) { + this.result.add(join(relativeFrom, file).replace(/\\/g, '/')) + } + } } } -const walk = (options, callback) => { - options = options || {} - const p = new Promise((resolve, reject) => { - const bw = new BundleWalker(options) - bw.on('done', bundled => { - options.bundled = bundled - options.packageJsonCache = bw.packageJsonCache - new Walker(options).on('done', resolve).on('error', reject).start() - }) - bw.start() +const walk = (tree, options, callback) => { + if (typeof options === 'function') { + callback = options + options = {} + } + const p = new Promise((pResolve, pReject) => { + new PackWalker(tree, { ...options, isPackage: true }) + .on('done', pResolve).on('error', pReject).start() }) return callback ? p.then(res => callback(null, res), callback) : p } module.exports = walk -walk.Walker = Walker +walk.Walker = PackWalker diff --git a/node_modules/npm-packlist/node_modules/npm-normalize-package-bin/LICENSE b/node_modules/npm-packlist/node_modules/npm-normalize-package-bin/LICENSE deleted file mode 100644 index 19cec97b18468..0000000000000 --- a/node_modules/npm-packlist/node_modules/npm-normalize-package-bin/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -The ISC License - -Copyright (c) npm, Inc. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/npm-packlist/node_modules/npm-normalize-package-bin/lib/index.js b/node_modules/npm-packlist/node_modules/npm-normalize-package-bin/lib/index.js deleted file mode 100644 index d6f0a581b9e66..0000000000000 --- a/node_modules/npm-packlist/node_modules/npm-normalize-package-bin/lib/index.js +++ /dev/null @@ -1,64 +0,0 @@ -// pass in a manifest with a 'bin' field here, and it'll turn it -// into a properly santized bin object -const { join, basename } = require('path') - -const normalize = pkg => - !pkg.bin ? removeBin(pkg) - : typeof pkg.bin === 'string' ? normalizeString(pkg) - : Array.isArray(pkg.bin) ? normalizeArray(pkg) - : typeof pkg.bin === 'object' ? normalizeObject(pkg) - : removeBin(pkg) - -const normalizeString = pkg => { - if (!pkg.name) { - return removeBin(pkg) - } - pkg.bin = { [pkg.name]: pkg.bin } - return normalizeObject(pkg) -} - -const normalizeArray = pkg => { - pkg.bin = pkg.bin.reduce((acc, k) => { - acc[basename(k)] = k - return acc - }, {}) - return normalizeObject(pkg) -} - -const removeBin = pkg => { - delete pkg.bin - return pkg -} - -const normalizeObject = pkg => { - const orig = pkg.bin - const clean = {} - let hasBins = false - Object.keys(orig).forEach(binKey => { - const base = join('/', basename(binKey.replace(/\\|:/g, '/'))).slice(1) - - if (typeof orig[binKey] !== 'string' || !base) { - return - } - - const binTarget = join('/', orig[binKey]) - .replace(/\\/g, '/').slice(1) - - if (!binTarget) { - return - } - - clean[base] = binTarget - hasBins = true - }) - - if (hasBins) { - pkg.bin = clean - } else { - delete pkg.bin - } - - return pkg -} - -module.exports = normalize diff --git a/node_modules/npm-packlist/node_modules/npm-normalize-package-bin/package.json b/node_modules/npm-packlist/node_modules/npm-normalize-package-bin/package.json deleted file mode 100644 index 02de808d9b702..0000000000000 --- a/node_modules/npm-packlist/node_modules/npm-normalize-package-bin/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "npm-normalize-package-bin", - "version": "2.0.0", - "description": "Turn any flavor of allowable package.json bin into a normalized object", - "main": "lib/index.js", - "repository": { - "type": "git", - "url": "https://github.com/npm/npm-normalize-package-bin.git" - }, - "author": "GitHub Inc.", - "license": "ISC", - "scripts": { - "test": "tap", - "snap": "tap", - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "git push origin --follow-tags", - "lint": "eslint \"**/*.js\"", - "postlint": "template-oss-check", - "template-oss-apply": "template-oss-apply --force", - "lintfix": "npm run lint -- --fix", - "prepublishOnly": "git push origin --follow-tags", - "posttest": "npm run lint" - }, - "devDependencies": { - "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.5.0", - "tap": "^16.3.0" - }, - "files": [ - "bin/", - "lib/" - ], - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "templateOSS": { - "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.5.0" - } -} diff --git a/node_modules/npm-packlist/package.json b/node_modules/npm-packlist/package.json index c3c8817202a39..4aaa524bf76a7 100644 --- a/node_modules/npm-packlist/package.json +++ b/node_modules/npm-packlist/package.json @@ -1,16 +1,13 @@ { "name": "npm-packlist", - "version": "5.1.3", + "version": "7.0.0-pre.0", "description": "Get a list of the files to add from a folder into an npm package", "directories": { "test": "test" }, - "main": "lib", + "main": "lib/index.js", "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" + "ignore-walk": "^5.0.1" }, "author": "GitHub Inc.", "license": "ISC", @@ -19,8 +16,9 @@ "lib/" ], "devDependencies": { + "@npmcli/arborist": "^6.0.0 || ^6.0.0-pre.0", "@npmcli/eslint-config": "^3.0.1", - "@npmcli/template-oss": "3.6.0", + "@npmcli/template-oss": "4.4.2", "mutate-fs": "^2.1.1", "tap": "^16.0.1" }, @@ -29,9 +27,6 @@ "posttest": "npm run lint", "snap": "tap", "postsnap": "npm run lintfix --", - "preversion": "npm test", - "postversion": "npm publish", - "prepublishOnly": "git push origin --follow-tags", "eslint": "eslint", "lint": "eslint \"**/*.js\"", "lintfix": "npm run lint -- --fix", @@ -46,16 +41,20 @@ "tap": { "test-env": [ "LC_ALL=sk" + ], + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ], + "files": [ + "test/*.js" ] }, - "bin": { - "npm-packlist": "bin/index.js" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.6.0" + "version": "4.4.2" } } diff --git a/node_modules/pacote/lib/dir.js b/node_modules/pacote/lib/dir.js index 502379810a006..df04cd08c51e5 100644 --- a/node_modules/pacote/lib/dir.js +++ b/node_modules/pacote/lib/dir.js @@ -16,6 +16,9 @@ class DirFetcher extends Fetcher { super(spec, opts) // just the fully resolved filename this.resolved = this.spec.fetchSpec + + this.tree = opts.tree || null + this.Arborist = opts.Arborist || null } // exposes tarCreateOptions as public API @@ -59,6 +62,10 @@ class DirFetcher extends Fetcher { } [_tarballFromResolved] () { + if (!this.tree && !this.Arborist) { + throw new Error('DirFetcher requires either a tree or an Arborist constructor to pack') + } + const stream = new Minipass() stream.resolved = this.resolved stream.integrity = this.integrity @@ -68,7 +75,13 @@ class DirFetcher extends Fetcher { // run the prepare script, get the list of files, and tar it up // pipe to the stream, and proxy errors the chain. this[_prepareDir]() - .then(() => packlist({ path: this.resolved, prefix, workspaces })) + .then(async () => { + if (!this.tree) { + const arb = new this.Arborist({ path: this.resolved }) + this.tree = await arb.loadActual() + } + return packlist(this.tree, { path: this.resolved, prefix, workspaces }) + }) .then(files => tar.c(tarCreateOptions(this.package), files) .on('error', er => stream.emit('error', er)).pipe(stream)) .catch(er => stream.emit('error', er)) diff --git a/node_modules/pacote/lib/git.js b/node_modules/pacote/lib/git.js index c4819b4fdf49c..1fa8b1f966334 100644 --- a/node_modules/pacote/lib/git.js +++ b/node_modules/pacote/lib/git.js @@ -61,6 +61,8 @@ class GitFetcher extends Fetcher { } else { this.resolvedSha = '' } + + this.Arborist = opts.Arborist || null } // just exposed to make it easier to test all the combinations @@ -206,8 +208,12 @@ class GitFetcher extends Fetcher { // check it out and then shell out to the DirFetcher tarball packer this[_clone](dir => this[_prepareDir](dir) .then(() => new Promise((res, rej) => { + if (!this.Arborist) { + throw new Error('GitFetcher requires an Arborist constructor to pack a tarball') + } const df = new DirFetcher(`file:${dir}`, { ...this.opts, + Arborist: this.Arborist, resolved: null, integrity: null, }) diff --git a/node_modules/pacote/lib/registry.js b/node_modules/pacote/lib/registry.js index c8eb6b0290702..eeb22e93c33d6 100644 --- a/node_modules/pacote/lib/registry.js +++ b/node_modules/pacote/lib/registry.js @@ -97,7 +97,6 @@ class RegistryFetcher extends Fetcher { integrity: null, }) const packument = await res.json() - packument._cached = res.headers.has('x-local-cache') packument._contentLength = +res.headers.get('content-length') if (this.packumentCache) { this.packumentCache.set(this.packumentUrl, packument) diff --git a/node_modules/pacote/package.json b/node_modules/pacote/package.json index 960530ec0b33d..f9b796d86cd20 100644 --- a/node_modules/pacote/package.json +++ b/node_modules/pacote/package.json @@ -1,6 +1,6 @@ { "name": "pacote", - "version": "13.6.2", + "version": "14.0.0-pre.3", "description": "JavaScript package downloader", "author": "GitHub Inc.", "bin": { @@ -11,9 +11,6 @@ "scripts": { "test": "tap", "snap": "tap", - "preversion": "npm test", - "postversion": "npm publish", - "prepublishOnly": "git push origin --follow-tags", "lint": "eslint \"**/*.js\"", "postlint": "template-oss-check", "lintfix": "npm run lint -- --fix", @@ -21,11 +18,16 @@ "template-oss-apply": "template-oss-apply --force" }, "tap": { - "timeout": 300 + "timeout": 300, + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] }, "devDependencies": { - "@npmcli/eslint-config": "^3.0.1", - "@npmcli/template-oss": "3.5.0", + "@npmcli/arborist": "^6.0.0 || ^6.0.0-pre.0", + "@npmcli/eslint-config": "^3.1.0", + "@npmcli/template-oss": "4.4.2", "hosted-git-info": "^5.0.0", "mutate-fs": "^2.1.1", "nock": "^13.2.4", @@ -53,7 +55,7 @@ "minipass": "^3.1.6", "mkdirp": "^1.0.4", "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", + "npm-packlist": "^7.0.0 || ^7.0.0-pre.0", "npm-pick-manifest": "^7.0.0", "npm-registry-fetch": "^13.0.1", "proc-log": "^2.0.0", @@ -65,7 +67,7 @@ "tar": "^6.1.11" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "repository": { "type": "git", @@ -73,7 +75,7 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.5.0", + "version": "4.4.2", "windowsCI": false } } diff --git a/package-lock.json b/package-lock.json index 2441009d92e20..e455502bd7466 100644 --- a/package-lock.json +++ b/package-lock.json @@ -143,7 +143,7 @@ "npmlog": "^6.0.2", "opener": "^1.5.2", "p-map": "^4.0.0", - "pacote": "^13.6.2", + "pacote": "^14.0.0-pre.3", "parse-conflict-json": "^2.0.2", "proc-log": "^2.0.1", "qrcode-terminal": "^0.12.0", @@ -2269,17 +2269,17 @@ } }, "node_modules/@npmcli/metavuln-calculator": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-3.1.1.tgz", - "integrity": "sha512-n69ygIaqAedecLeVH3KnO39M6ZHiJ2dEv5A7DGvcqCB8q17BGUgW8QaanIkbWUo2aYGZqJaOORTLAlIvKjNDKA==", + "version": "4.0.0-pre.0", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-4.0.0-pre.0.tgz", + "integrity": "sha512-2rJ7hovlcZMkqKm2cOWuZ0YsXIcP3iARsm+aYn/SLXK9aWRMVTW1f4fpDjtSvkZkaQVr48ofSG3YLYwlersSQA==", "dependencies": { "cacache": "^16.0.0", "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", + "pacote": "^14.0.0 || ^14.0.0-pre.0", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/move-file": { @@ -7966,27 +7966,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/npm-bundled": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", - "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", - "inBundle": true, - "dependencies": { - "npm-normalize-package-bin": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", - "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", - "inBundle": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/npm-install-checks": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", @@ -8025,30 +8004,15 @@ } }, "node_modules/npm-packlist": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", - "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "version": "7.0.0-pre.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.0-pre.0.tgz", + "integrity": "sha512-m98nCdY9RDDSJAODMf9afFwUyyuSO7rl1b8vvKRJD6s/isdTlOEgfdxJP4Pj31l973GNDe1n41e07QGPSIBQSw==", "inBundle": true, "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "bin": { - "npm-packlist": "bin/index.js" + "ignore-walk": "^5.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", - "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", - "inBundle": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-pick-manifest": { @@ -8587,9 +8551,9 @@ } }, "node_modules/pacote": { - "version": "13.6.2", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", - "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", + "version": "14.0.0-pre.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-14.0.0-pre.3.tgz", + "integrity": "sha512-WS8jos9mKpG6yRdMacwBc5WPEE4Z4xyJqyYiBoEU/0ayFlEPL8M8LUXlg86zjMWVpPobWIOIHvDO2i5oxOpIgQ==", "inBundle": true, "dependencies": { "@npmcli/git": "^3.0.0", @@ -8603,7 +8567,7 @@ "minipass": "^3.1.6", "mkdirp": "^1.0.4", "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", + "npm-packlist": "^7.0.0 || ^7.0.0-pre.0", "npm-pick-manifest": "^7.0.0", "npm-registry-fetch": "^13.0.1", "proc-log": "^2.0.0", @@ -8618,7 +8582,7 @@ "pacote": "lib/bin.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/parent-module": { @@ -13870,7 +13834,7 @@ "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/installed-package-contents": "^1.0.7", "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", + "@npmcli/metavuln-calculator": "^4.0.0-pre.0", "@npmcli/move-file": "^2.0.0", "@npmcli/name-from-folder": "^1.0.1", "@npmcli/node-gyp": "^2.0.0", @@ -13891,7 +13855,7 @@ "npm-pick-manifest": "^7.0.2", "npm-registry-fetch": "^13.0.0", "npmlog": "^6.0.2", - "pacote": "^13.6.1", + "pacote": "^14.0.0-pre.3", "parse-conflict-json": "^2.0.1", "proc-log": "^2.0.0", "promise-all-reject-late": "^1.0.0", @@ -13942,13 +13906,14 @@ "version": "5.0.0-pre.0", "license": "ISC", "dependencies": { + "@npmcli/arborist": "^6.0.0-pre.2", "@npmcli/disparity-colors": "^2.0.0", "@npmcli/installed-package-contents": "^1.0.7", "binary-extensions": "^2.2.0", "diff": "^5.1.0", "minimatch": "^5.0.1", "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1", + "pacote": "^14.0.0-pre.3", "tar": "^6.1.0" }, "devDependencies": { @@ -13972,7 +13937,7 @@ "mkdirp-infer-owner": "^2.0.0", "npm-package-arg": "^9.0.1", "npmlog": "^6.0.2", - "pacote": "^13.6.1", + "pacote": "^14.0.0-pre.3", "proc-log": "^2.0.0", "read": "^1.0.7", "read-package-json-fast": "^2.0.2", @@ -14045,9 +14010,10 @@ "version": "5.0.0-pre.0", "license": "ISC", "dependencies": { + "@npmcli/arborist": "^6.0.0-pre.2", "@npmcli/run-script": "^4.1.3", "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1" + "pacote": "^14.0.0-pre.3" }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", diff --git a/package.json b/package.json index 250f519e11994..a457bf5ed3aa2 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "npmlog": "^6.0.2", "opener": "^1.5.2", "p-map": "^4.0.0", - "pacote": "^13.6.2", + "pacote": "^14.0.0-pre.3", "parse-conflict-json": "^2.0.2", "proc-log": "^2.0.1", "qrcode-terminal": "^0.12.0", diff --git a/tap-snapshots/test/lib/utils/tar.js.test.cjs b/tap-snapshots/test/lib/utils/tar.js.test.cjs index d132d7af6e6f9..e4af36aeae0b6 100644 --- a/tap-snapshots/test/lib/utils/tar.js.test.cjs +++ b/tap-snapshots/test/lib/utils/tar.js.test.cjs @@ -11,10 +11,10 @@ exports[`test/lib/utils/tar.js TAP should log tarball contents > must match snap package: my-cool-pkg@1.0.0 === Tarball Contents === -4B cat -4B chai -4B dog -97B package.json +4B cat +4B chai +4B dog +114B package.json === Bundled Dependencies === bundle-dep @@ -23,10 +23,10 @@ bundle-dep name: my-cool-pkg version: 1.0.0 filename: my-cool-pkg-1.0.0.tgz -package size: 274 B -unpacked size: 113 B -shasum: cd0dfccff77dff944eb761854bc0b0497d974f67 -integrity: sha512-qeFip1jH05vkW[...]zHSdMdPpYogMA== +package size: 271 B +unpacked size: 126 B +shasum: 23e31c8ad422f96301c07730e61ff403b10306f1 +integrity: sha512-/Lg5tEGQv5A5y[...]gq8T9D5+Wat1A== bundled deps: 1 bundled files: 0 own files: 5 diff --git a/test/fixtures/mock-registry.js b/test/fixtures/mock-registry.js index d978929b6b0d8..a39532958b338 100644 --- a/test/fixtures/mock-registry.js +++ b/test/fixtures/mock-registry.js @@ -5,6 +5,7 @@ * for tests against any registry data. */ const pacote = require('pacote') +const Arborist = require('@npmcli/arborist') const npa = require('npm-package-arg') class MockRegistry { #tap @@ -250,7 +251,7 @@ class MockRegistry { async tarball ({ manifest, tarball }) { const nock = this.nock const dist = new URL(manifest.dist.tarball) - const tar = await pacote.tarball(tarball) + const tar = await pacote.tarball(tarball, { Arborist }) nock.get(dist.pathname).reply(200, tar) return nock } diff --git a/test/lib/commands/publish.js b/test/lib/commands/publish.js index 995abff88c2c1..00fba9ef218e0 100644 --- a/test/lib/commands/publish.js +++ b/test/lib/commands/publish.js @@ -2,6 +2,7 @@ const t = require('tap') const { load: loadMockNpm } = require('../../fixtures/mock-npm') const MockRegistry = require('../../fixtures/mock-registry.js') const pacote = require('pacote') +const Arborist = require('@npmcli/arborist') const path = require('path') const fs = require('@npmcli/fs') const npa = require('npm-package-arg') @@ -227,7 +228,7 @@ t.test('tarball', async t => { 'index.js': 'console.log("hello world"}', }, }) - const tarball = await pacote.tarball(home) + const tarball = await pacote.tarball(home, { Arborist }) const tarFilename = path.join(home, 'tarball.tgz') await fs.writeFile(tarFilename, tarball) const registry = new MockRegistry({ diff --git a/test/lib/utils/tar.js b/test/lib/utils/tar.js index adc5cb364997f..23f40703b5cf4 100644 --- a/test/lib/utils/tar.js +++ b/test/lib/utils/tar.js @@ -27,12 +27,17 @@ t.test('should log tarball contents', async (t) => { bundleDependencies: [ 'bundle-dep', ], - }, null, 2), + dependencies: { + 'bundle-dep': '1.0.0', + }, + }), cat: 'meow', chai: 'blub', dog: 'woof', node_modules: { - 'bundle-dep': 'toto', + 'bundle-dep': { + 'package.json': '', + }, }, }) diff --git a/workspaces/arborist/lib/arborist/build-ideal-tree.js b/workspaces/arborist/lib/arborist/build-ideal-tree.js index e9a8720d7322d..0260bd563ab2f 100644 --- a/workspaces/arborist/lib/arborist/build-ideal-tree.js +++ b/workspaces/arborist/lib/arborist/build-ideal-tree.js @@ -833,6 +833,7 @@ This is a one-time fix-up, please be patient... await cacache.tmp.withTmp(this.cache, opt, async path => { await pacote.extract(node.resolved, path, { ...opt, + Arborist, resolved: node.resolved, integrity: node.integrity, }) diff --git a/workspaces/arborist/lib/arborist/reify.js b/workspaces/arborist/lib/arborist/reify.js index 0c9026f5e4d1e..4f9db7575d79b 100644 --- a/workspaces/arborist/lib/arborist/reify.js +++ b/workspaces/arborist/lib/arborist/reify.js @@ -676,6 +676,7 @@ module.exports = cls => class Reifier extends cls { }) await pacote.extract(res, node.path, { ...this.options, + Arborist: this.constructor, resolved: node.resolved, integrity: node.integrity, }) diff --git a/workspaces/arborist/package.json b/workspaces/arborist/package.json index 8082cf820d2f2..243b9d4675017 100644 --- a/workspaces/arborist/package.json +++ b/workspaces/arborist/package.json @@ -6,7 +6,7 @@ "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/installed-package-contents": "^1.0.7", "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", + "@npmcli/metavuln-calculator": "^4.0.0-pre.0", "@npmcli/move-file": "^2.0.0", "@npmcli/name-from-folder": "^1.0.1", "@npmcli/node-gyp": "^2.0.0", @@ -27,7 +27,7 @@ "npm-pick-manifest": "^7.0.2", "npm-registry-fetch": "^13.0.0", "npmlog": "^6.0.2", - "pacote": "^13.6.1", + "pacote": "^14.0.0-pre.3", "parse-conflict-json": "^2.0.1", "proc-log": "^2.0.0", "promise-all-reject-late": "^1.0.0", diff --git a/workspaces/arborist/test/fixtures/registry-mocks/fetch-lock-contents.js b/workspaces/arborist/test/fixtures/registry-mocks/fetch-lock-contents.js index 0f756f00bd657..5fb010c13bea2 100644 --- a/workspaces/arborist/test/fixtures/registry-mocks/fetch-lock-contents.js +++ b/workspaces/arborist/test/fixtures/registry-mocks/fetch-lock-contents.js @@ -1,5 +1,6 @@ // fetch all the deps and tarballs in a v2 lockfile const pacote = require('pacote') +const Arborist = require('../../index.js') const url = require('url') const mkdirp = require('mkdirp') const {dirname, resolve} = require('path') @@ -29,7 +30,7 @@ const main = async lock => { continue const path = url.parse(meta.resolved).pathname.replace(/^\/@?/, '') const tgzFile = resolve(dir, path) - await pacote.tarball.file(meta.resolved, tgzFile) + await pacote.tarball.file(meta.resolved, tgzFile, { Arborist }) } console.log('OK!') } diff --git a/workspaces/libnpmdiff/lib/tarball.js b/workspaces/libnpmdiff/lib/tarball.js index 4d01d69c9c413..930d624f2d5b6 100644 --- a/workspaces/libnpmdiff/lib/tarball.js +++ b/workspaces/libnpmdiff/lib/tarball.js @@ -1,5 +1,6 @@ const { relative } = require('path') +const Arborist = require('@npmcli/arborist') const npa = require('npm-package-arg') const pkgContents = require('@npmcli/installed-package-contents') const pacote = require('pacote') @@ -28,7 +29,10 @@ const tarball = (manifest, opts) => { return nodeModulesTarball(manifest, opts) } - return pacote.tarball(manifest._resolved, opts) + return pacote.tarball(manifest._resolved, { + ...opts, + Arborist, + }) } module.exports = tarball diff --git a/workspaces/libnpmdiff/package.json b/workspaces/libnpmdiff/package.json index 6b3e12e4fc3fb..cb14dfa3116db 100644 --- a/workspaces/libnpmdiff/package.json +++ b/workspaces/libnpmdiff/package.json @@ -47,13 +47,14 @@ "tap": "^16.0.1" }, "dependencies": { + "@npmcli/arborist": "^6.0.0-pre.2", "@npmcli/disparity-colors": "^2.0.0", "@npmcli/installed-package-contents": "^1.0.7", "binary-extensions": "^2.2.0", "diff": "^5.1.0", "minimatch": "^5.0.1", "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1", + "pacote": "^14.0.0-pre.3", "tar": "^6.1.0" }, "templateOSS": { diff --git a/workspaces/libnpmexec/package.json b/workspaces/libnpmexec/package.json index 0f2dde78fcc23..2d4df8579047f 100644 --- a/workspaces/libnpmexec/package.json +++ b/workspaces/libnpmexec/package.json @@ -66,7 +66,7 @@ "mkdirp-infer-owner": "^2.0.0", "npm-package-arg": "^9.0.1", "npmlog": "^6.0.2", - "pacote": "^13.6.1", + "pacote": "^14.0.0-pre.3", "proc-log": "^2.0.0", "read": "^1.0.7", "read-package-json-fast": "^2.0.2", diff --git a/workspaces/libnpmpack/lib/index.js b/workspaces/libnpmpack/lib/index.js index dc9cfd2c4e781..93428b37cb269 100644 --- a/workspaces/libnpmpack/lib/index.js +++ b/workspaces/libnpmpack/lib/index.js @@ -5,6 +5,7 @@ const npa = require('npm-package-arg') const runScript = require('@npmcli/run-script') const path = require('path') const util = require('util') +const Arborist = require('@npmcli/arborist') const writeFile = util.promisify(require('fs').writeFile) module.exports = pack @@ -33,6 +34,7 @@ async function pack (spec = 'file:.', opts = {}) { // packs tarball const tarball = await pacote.tarball(manifest._resolved, { ...opts, + Arborist, integrity: manifest._integrity, }) diff --git a/workspaces/libnpmpack/package.json b/workspaces/libnpmpack/package.json index 96c9589a8cad3..20dc024ae1e13 100644 --- a/workspaces/libnpmpack/package.json +++ b/workspaces/libnpmpack/package.json @@ -35,9 +35,10 @@ "bugs": "https://github.com/npm/libnpmpack/issues", "homepage": "https://npmjs.com/package/libnpmpack", "dependencies": { + "@npmcli/arborist": "^6.0.0-pre.2", "@npmcli/run-script": "^4.1.3", "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1" + "pacote": "^14.0.0-pre.3" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0"