From 4b0c29a7c5860410c7b453bec389c54cb21dbde3 Mon Sep 17 00:00:00 2001 From: nlf Date: Thu, 2 Dec 2021 14:45:44 -0800 Subject: [PATCH 01/13] deps: @npmcli/arborist@4.1.0 PR-URL: https://github.com/npm/cli/pull/4116 Credit: @nlf Close: #4116 Reviewed-by: @wraithgar --- .../arborist/lib/arborist/build-ideal-tree.js | 6 + .../arborist/lib/arborist/load-actual.js | 24 +++- .../arborist/lib/arborist/load-virtual.js | 1 + node_modules/@npmcli/arborist/lib/edge.js | 41 +++++- node_modules/@npmcli/arborist/lib/node.js | 45 +++++++ .../@npmcli/arborist/lib/override-set.js | 123 ++++++++++++++++++ .../@npmcli/arborist/lib/place-dep.js | 1 + .../@npmcli/arborist/lib/printable.js | 13 +- node_modules/@npmcli/arborist/package.json | 7 +- package-lock.json | 14 +- package.json | 2 +- 11 files changed, 258 insertions(+), 19 deletions(-) create mode 100644 node_modules/@npmcli/arborist/lib/override-set.js diff --git a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js index aa37acfe52d31..899d92ca937cc 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js +++ b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js @@ -379,6 +379,7 @@ module.exports = cls => class IdealTreeBuilder extends cls { optional: false, global: this[_global], legacyPeerDeps: this.legacyPeerDeps, + loadOverrides: true, }) if (root.isLink) { root.target = new Node({ @@ -676,6 +677,7 @@ module.exports = cls => class IdealTreeBuilder extends cls { // calls rather than walking over everything in the tree. const set = this.idealTree.inventory .filter(n => this[_shouldUpdateNode](n)) + // XXX add any invalid edgesOut to the queue for (const node of set) { for (const edge of node.edgesIn) { this.addTracker('idealTree', edge.from.name, edge.from.location) @@ -772,7 +774,10 @@ This is a one-time fix-up, please be patient... [_buildDeps] () { process.emit('time', 'idealTree:buildDeps') const tree = this.idealTree.target + tree.assertRootOverrides() this[_depsQueue].push(tree) + // XXX also push anything that depends on a node with a name + // in the override list this.log.silly('idealTree', 'buildDeps') this.addTracker('idealTree', tree.name, '') return this[_buildDepStep]() @@ -1112,6 +1117,7 @@ This is a one-time fix-up, please be patient... path: node.realpath, sourceReference: node, legacyPeerDeps: this.legacyPeerDeps, + overrides: node.overrides, }) // also need to set up any targets from any link deps, so that diff --git a/node_modules/@npmcli/arborist/lib/arborist/load-actual.js b/node_modules/@npmcli/arborist/lib/arborist/load-actual.js index a232bf32b32d0..0d260858d81c6 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/load-actual.js +++ b/node_modules/@npmcli/arborist/lib/arborist/load-actual.js @@ -127,6 +127,7 @@ module.exports = cls => class ActualLoader extends cls { realpath: real, pkg: {}, global, + loadOverrides: true, }) return this[_loadActualActually]({ root, ignoreMissing, global }) } @@ -135,8 +136,11 @@ module.exports = cls => class ActualLoader extends cls { this[_actualTree] = await this[_loadFSNode]({ path: this.path, real: await realpath(this.path, this[_rpcache], this[_stcache]), + loadOverrides: true, }) + this[_actualTree].assertRootOverrides() + // Note: hidden lockfile will be rejected if it's not the latest thing // in the folder, or if any of the entries in the hidden lockfile are // missing. @@ -236,13 +240,26 @@ module.exports = cls => class ActualLoader extends cls { this[_actualTree] = root } - [_loadFSNode] ({ path, parent, real, root }) { + [_loadFSNode] ({ path, parent, real, root, loadOverrides }) { if (!real) { return realpath(path, this[_rpcache], this[_stcache]) .then( - real => this[_loadFSNode]({ path, parent, real, root }), + real => this[_loadFSNode]({ + path, + parent, + real, + root, + loadOverrides, + }), // if realpath fails, just provide a dummy error node - error => new Node({ error, path, realpath: path, parent, root }) + error => new Node({ + error, + path, + realpath: path, + parent, + root, + loadOverrides, + }) ) } @@ -271,6 +288,7 @@ module.exports = cls => class ActualLoader extends cls { error, parent, root, + loadOverrides, }) }) .then(node => { diff --git a/node_modules/@npmcli/arborist/lib/arborist/load-virtual.js b/node_modules/@npmcli/arborist/lib/arborist/load-virtual.js index 7761380e9f71f..4d65e3da6f683 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/load-virtual.js +++ b/node_modules/@npmcli/arborist/lib/arborist/load-virtual.js @@ -72,6 +72,7 @@ module.exports = cls => class VirtualLoader extends cls { this[rootOptionProvided] = options.root await this[loadFromShrinkwrap](s, root) + root.assertRootOverrides() return treeCheck(this.virtualTree) } diff --git a/node_modules/@npmcli/arborist/lib/edge.js b/node_modules/@npmcli/arborist/lib/edge.js index 1881001ef143e..87439e7645366 100644 --- a/node_modules/@npmcli/arborist/lib/edge.js +++ b/node_modules/@npmcli/arborist/lib/edge.js @@ -29,6 +29,7 @@ class ArboristEdge {} const printableEdge = (edge) => { const edgeFrom = edge.from && edge.from.location const edgeTo = edge.to && edge.to.location + const override = edge.overrides && edge.overrides.value return Object.assign(new ArboristEdge(), { name: edge.name, @@ -38,12 +39,13 @@ const printableEdge = (edge) => { ...(edgeTo ? { to: edgeTo } : {}), ...(edge.error ? { error: edge.error } : {}), ...(edge.peerConflicted ? { peerConflicted: true } : {}), + ...(override ? { overridden: override } : {}), }) } class Edge { constructor (options) { - const { type, name, spec, accept, from } = options + const { type, name, spec, accept, from, overrides } = options if (typeof spec !== 'string') { throw new TypeError('must provide string spec') @@ -55,6 +57,10 @@ class Edge { this[_spec] = spec + if (overrides !== undefined) { + this.overrides = overrides + } + if (accept !== undefined) { if (typeof accept !== 'string') { throw new TypeError('accept field must be a string if provided') @@ -82,8 +88,11 @@ class Edge { } satisfiedBy (node) { - return node.name === this.name && - depValid(node, this.spec, this.accept, this.from) + if (node.name !== this.name) { + return false + } + + return depValid(node, this.spec, this.accept, this.from) } explain (seen = []) { @@ -101,6 +110,10 @@ class Edge { type: this.type, name: this.name, spec: this.spec, + ...(this.rawSpec !== this.spec ? { + rawSpec: this.rawSpec, + overridden: true, + } : {}), ...(bundled ? { bundled } : {}), ...(error ? { error } : {}), ...(from ? { from: from.explain(null, seen) } : {}), @@ -143,7 +156,28 @@ class Edge { return this[_name] } + get rawSpec () { + return this[_spec] + } + get spec () { + if (this.overrides && this.overrides.value && this.overrides.name === this.name) { + if (this.overrides.value.startsWith('$')) { + const ref = this.overrides.value.slice(1) + const pkg = this.from.root.package + const overrideSpec = (pkg.devDependencies && pkg.devDependencies[ref]) || + (pkg.optionalDependencies && pkg.optionalDependencies[ref]) || + (pkg.dependencies && pkg.dependencies[ref]) || + (pkg.peerDependencies && pkg.peerDependencies[ref]) + + if (overrideSpec) { + return overrideSpec + } + + throw new Error(`Unable to resolve reference ${this.overrides.value}`) + } + return this.overrides.value + } return this[_spec] } @@ -213,6 +247,7 @@ class Edge { if (node.edgesOut.has(this.name)) { node.edgesOut.get(this.name).detach() } + node.addEdgeOut(this) this.reload() } diff --git a/node_modules/@npmcli/arborist/lib/node.js b/node_modules/@npmcli/arborist/lib/node.js index d311b6a837817..45c288bcf6cf7 100644 --- a/node_modules/@npmcli/arborist/lib/node.js +++ b/node_modules/@npmcli/arborist/lib/node.js @@ -32,6 +32,7 @@ const semver = require('semver') const nameFromFolder = require('@npmcli/name-from-folder') const Edge = require('./edge.js') const Inventory = require('./inventory.js') +const OverrideSet = require('./override-set.js') const { normalize } = require('read-package-json-fast') const { getPaths: getBinPaths } = require('bin-links') const npa = require('npm-package-arg') @@ -88,6 +89,8 @@ class Node { legacyPeerDeps = false, linksIn, hasShrinkwrap, + overrides, + loadOverrides = false, extraneous = true, dev = true, optional = true, @@ -190,6 +193,17 @@ class Node { // because this.package is read when adding to inventory this[_package] = pkg && typeof pkg === 'object' ? pkg : {} + if (overrides) { + this.overrides = overrides + } else if (loadOverrides) { + const overrides = this[_package].overrides || {} + if (Object.keys(overrides).length > 0) { + this.overrides = new OverrideSet({ + overrides: this[_package].overrides, + }) + } + } + // only relevant for the root and top nodes this.meta = meta @@ -963,6 +977,11 @@ class Node { return false } + // XXX need to check for two root nodes? + if (node.overrides !== this.overrides) { + return false + } + ignorePeers = new Set(ignorePeers) // gather up all the deps of this node and that are only depended @@ -1208,6 +1227,10 @@ class Node { this[_changePath](newPath) } + if (parent.overrides) { + this.overrides = parent.overrides.getNodeRule(this) + } + // clobbers anything at that path, resets all appropriate references this.root = parent.root } @@ -1279,11 +1302,33 @@ class Node { } } + assertRootOverrides () { + if (!this.isProjectRoot || !this.overrides) { + return + } + + for (const edge of this.edgesOut.values()) { + // if these differ an override has been applied, those are not allowed + // for top level dependencies so throw an error + if (edge.spec !== edge.rawSpec && !edge.spec.startsWith('$')) { + throw Object.assign(new Error(`Override for ${edge.name}@${edge.rawSpec} conflicts with direct dependency`), { code: 'EOVERRIDE' }) + } + } + } + addEdgeOut (edge) { + if (this.overrides) { + edge.overrides = this.overrides.getEdgeRule(edge) + } + this.edgesOut.set(edge.name, edge) } addEdgeIn (edge) { + if (edge.overrides) { + this.overrides = edge.overrides + } + this.edgesIn.add(edge) // try to get metadata from the yarn.lock file diff --git a/node_modules/@npmcli/arborist/lib/override-set.js b/node_modules/@npmcli/arborist/lib/override-set.js new file mode 100644 index 0000000000000..e2e04e03e911e --- /dev/null +++ b/node_modules/@npmcli/arborist/lib/override-set.js @@ -0,0 +1,123 @@ +const npa = require('npm-package-arg') +const semver = require('semver') + +class OverrideSet { + constructor ({ overrides, key, parent }) { + this.parent = parent + this.children = new Map() + + if (typeof overrides === 'string') { + overrides = { '.': overrides } + } + + // change a literal empty string to * so we can use truthiness checks on + // the value property later + if (overrides['.'] === '') { + overrides['.'] = '*' + } + + if (parent) { + const spec = npa(key) + if (!spec.name) { + throw new Error(`Override without name: ${key}`) + } + + this.name = spec.name + spec.name = '' + this.key = key + this.keySpec = spec.rawSpec === '' ? '' : spec.toString() + this.value = overrides['.'] || this.keySpec + } + + for (const [key, childOverrides] of Object.entries(overrides)) { + if (key === '.') { + continue + } + + const child = new OverrideSet({ + parent: this, + key, + overrides: childOverrides, + }) + + this.children.set(child.key, child) + } + } + + getEdgeRule (edge) { + for (const rule of this.ruleset.values()) { + if (rule.name !== edge.name) { + continue + } + + if (rule.keySpec === '' || + semver.intersects(edge.spec, rule.keySpec)) { + return rule + } + } + + return this + } + + getNodeRule (node) { + for (const rule of this.ruleset.values()) { + if (rule.name !== node.name) { + continue + } + + if (rule.keySpec === '' || + semver.satisfies(node.version, rule.keySpec) || + semver.satisfies(node.version, rule.value)) { + return rule + } + } + + return this + } + + getMatchingRule (node) { + for (const rule of this.ruleset.values()) { + if (rule.name !== node.name) { + continue + } + + if (rule.keySpec === '' || + semver.satisfies(node.version, rule.keySpec) || + semver.satisfies(node.version, rule.value)) { + return rule + } + } + + return null + } + + * ancestry () { + for (let ancestor = this; ancestor; ancestor = ancestor.parent) { + yield ancestor + } + } + + get isRoot () { + return !this.parent + } + + get ruleset () { + const ruleset = new Map() + + for (const override of this.ancestry()) { + for (const kid of override.children.values()) { + if (!ruleset.has(kid.key)) { + ruleset.set(kid.key, kid) + } + } + + if (!override.isRoot && !ruleset.has(override.key)) { + ruleset.set(override.key, override) + } + } + + return ruleset + } +} + +module.exports = OverrideSet diff --git a/node_modules/@npmcli/arborist/lib/place-dep.js b/node_modules/@npmcli/arborist/lib/place-dep.js index be735d5fc1c4b..c0cbe91fe3667 100644 --- a/node_modules/@npmcli/arborist/lib/place-dep.js +++ b/node_modules/@npmcli/arborist/lib/place-dep.js @@ -295,6 +295,7 @@ class PlaceDep { integrity: dep.integrity, legacyPeerDeps: this.legacyPeerDeps, error: dep.errors[0], + ...(dep.overrides ? { overrides: dep.overrides } : {}), ...(dep.isLink ? { target: dep.target, realpath: dep.realpath } : {}), }) diff --git a/node_modules/@npmcli/arborist/lib/printable.js b/node_modules/@npmcli/arborist/lib/printable.js index 018e569b1d2f1..7c8d52a4207aa 100644 --- a/node_modules/@npmcli/arborist/lib/printable.js +++ b/node_modules/@npmcli/arborist/lib/printable.js @@ -1,6 +1,5 @@ // helper function to output a clearer visualization // of the current node and its descendents - const localeCompare = require('@isaacs/string-locale-compare')('en') const util = require('util') const relpath = require('./relpath.js') @@ -65,6 +64,11 @@ class ArboristNode { this.errors = tree.errors.map(treeError) } + if (tree.overrides) { + this.overrides = new Map([...tree.overrides.ruleset.values()] + .map((override) => [override.key, override.value])) + } + // edgesOut sorted by name if (tree.edgesOut.size) { this.edgesOut = new Map([...tree.edgesOut.entries()] @@ -126,7 +130,10 @@ class Edge { constructor (edge) { this.type = edge.type this.name = edge.name - this.spec = edge.spec || '*' + this.spec = edge.rawSpec || '*' + if (edge.rawSpec !== edge.spec) { + this.override = edge.spec + } if (edge.error) { this.error = edge.error } @@ -145,6 +152,8 @@ class EdgeOut extends Edge { [util.inspect.custom] () { return `{ ${this.type} ${this.name}@${this.spec}${ + this.override ? ` overridden:${this.override}` : '' + }${ this.to ? ' -> ' + this.to : '' }${ this.error ? ' ' + this.error : '' diff --git a/node_modules/@npmcli/arborist/package.json b/node_modules/@npmcli/arborist/package.json index 34d38572d38d7..12fede6857d65 100644 --- a/node_modules/@npmcli/arborist/package.json +++ b/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "4.0.5", + "version": "4.1.0", "description": "Manage node_modules trees", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", @@ -37,10 +37,11 @@ "walk-up-path": "^1.0.0" }, "devDependencies": { - "@npmcli/template-oss": "^2.3.0", + "@npmcli/template-oss": "^2.3.1", "benchmark": "^2.1.4", "chalk": "^4.1.0", "minify-registry-metadata": "^2.1.0", + "nock": "^13.2.0", "tap": "^15.1.2", "tcompare": "^5.0.6" }, @@ -93,7 +94,7 @@ "engines": { "node": "^12.13.0 || ^14.15.0 || >=16" }, - "templateVersion": "2.3.0", + "templateVersion": "2.3.1", "eslintIgnore": [ "test/fixtures/", "!test/fixtures/*.js" diff --git a/package-lock.json b/package-lock.json index b7fc90a9c6f55..b6df88dfee279 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,7 +87,7 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^4.0.5", + "@npmcli/arborist": "^4.1.0", "@npmcli/ci-detect": "^1.4.0", "@npmcli/config": "^2.3.2", "@npmcli/map-workspaces": "^2.0.0", @@ -801,9 +801,9 @@ } }, "node_modules/@npmcli/arborist": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.0.5.tgz", - "integrity": "sha512-WR2cqxzjsvmHJ9sKCdqBYG/qeiAXB9ev1iq1W2Rry7LxeJ7eDtTr4mOWe/TBvp6xFzevGecQc2YEWwExTuLZLg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.1.0.tgz", + "integrity": "sha512-bkaOqCuTUtpVOe1vaAP7TUihu64wIbnSDpsbqBJUsGFTLYXbjKwi6xj8Zx5cfHkM3nqyeEEbPYlGkt0TXjKrUg==", "inBundle": true, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", @@ -10664,9 +10664,9 @@ "dev": true }, "@npmcli/arborist": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.0.5.tgz", - "integrity": "sha512-WR2cqxzjsvmHJ9sKCdqBYG/qeiAXB9ev1iq1W2Rry7LxeJ7eDtTr4mOWe/TBvp6xFzevGecQc2YEWwExTuLZLg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.1.0.tgz", + "integrity": "sha512-bkaOqCuTUtpVOe1vaAP7TUihu64wIbnSDpsbqBJUsGFTLYXbjKwi6xj8Zx5cfHkM3nqyeEEbPYlGkt0TXjKrUg==", "requires": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/installed-package-contents": "^1.0.7", diff --git a/package.json b/package.json index f9ba8cd3c801b..73199f1795d42 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ }, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^4.0.5", + "@npmcli/arborist": "^4.1.0", "@npmcli/ci-detect": "^1.4.0", "@npmcli/config": "^2.3.2", "@npmcli/map-workspaces": "^2.0.0", From e1da1fa4ba7d95616928d2192b5b9db09b3120bc Mon Sep 17 00:00:00 2001 From: Gar Date: Wed, 8 Dec 2021 13:27:35 -0800 Subject: [PATCH 02/13] deps: @npmcli/arborist@4.1.1 parse-conflict-json@2.0.1 * Fixes object property assignment bug in resolving package-locks with conflicts PR-URL: https://github.com/npm/cli/pull/4141 Credit: @wraithgar Close: #4141 Reviewed-by: @fritzy --- node_modules/@npmcli/arborist/package.json | 4 +- node_modules/just-diff-apply/index.mjs | 110 +++++++++++++ node_modules/just-diff-apply/package.json | 12 +- node_modules/just-diff-apply/rollup.config.js | 3 + node_modules/just-diff/index.mjs | 146 ++++++++++++++++++ node_modules/just-diff/index.tests.ts | 2 +- node_modules/just-diff/package.json | 14 +- node_modules/just-diff/rollup.config.js | 3 + node_modules/parse-conflict-json/LICENSE | 15 -- node_modules/parse-conflict-json/LICENSE.md | 20 +++ .../parse-conflict-json/{ => lib}/index.js | 38 +++-- node_modules/parse-conflict-json/package.json | 30 ++-- package-lock.json | 71 +++++---- package.json | 4 +- 14 files changed, 391 insertions(+), 81 deletions(-) create mode 100644 node_modules/just-diff-apply/index.mjs create mode 100644 node_modules/just-diff-apply/rollup.config.js create mode 100644 node_modules/just-diff/index.mjs create mode 100644 node_modules/just-diff/rollup.config.js delete mode 100644 node_modules/parse-conflict-json/LICENSE create mode 100644 node_modules/parse-conflict-json/LICENSE.md rename node_modules/parse-conflict-json/{ => lib}/index.js (74%) diff --git a/node_modules/@npmcli/arborist/package.json b/node_modules/@npmcli/arborist/package.json index 12fede6857d65..cea3d5ecd7e4e 100644 --- a/node_modules/@npmcli/arborist/package.json +++ b/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "4.1.0", + "version": "4.1.1", "description": "Manage node_modules trees", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", @@ -24,7 +24,7 @@ "npm-pick-manifest": "^6.1.0", "npm-registry-fetch": "^11.0.0", "pacote": "^12.0.2", - "parse-conflict-json": "^1.1.1", + "parse-conflict-json": "^2.0.1", "proc-log": "^1.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^1.0.1", diff --git a/node_modules/just-diff-apply/index.mjs b/node_modules/just-diff-apply/index.mjs new file mode 100644 index 0000000000000..fcd26c2f5a23e --- /dev/null +++ b/node_modules/just-diff-apply/index.mjs @@ -0,0 +1,110 @@ +/* + const obj1 = {a: 3, b: 5}; + diffApply(obj1, + [ + { "op": "remove", "path": ['b'] }, + { "op": "replace", "path": ['a'], "value": 4 }, + { "op": "add", "path": ['c'], "value": 5 } + ] + ); + obj1; // {a: 4, c: 5} + + // using converter to apply jsPatch standard paths + // see http://jsonpatch.com + import {diff, jsonPatchPathConverter} from 'just-diff' + const obj2 = {a: 3, b: 5}; + diffApply(obj2, [ + { "op": "remove", "path": '/b' }, + { "op": "replace", "path": '/a', "value": 4 } + { "op": "add", "path": '/c', "value": 5 } + ], jsonPatchPathConverter); + obj2; // {a: 4, c: 5} + + // arrays + const obj3 = {a: 4, b: [1, 2, 3]}; + diffApply(obj3, [ + { "op": "replace", "path": ['a'], "value": 3 } + { "op": "replace", "path": ['b', 2], "value": 4 } + { "op": "add", "path": ['b', 3], "value": 9 } + ]); + obj3; // {a: 3, b: [1, 2, 4, 9]} + + // nested paths + const obj4 = {a: 4, b: {c: 3}}; + diffApply(obj4, [ + { "op": "replace", "path": ['a'], "value": 5 } + { "op": "remove", "path": ['b', 'c']} + { "op": "add", "path": ['b', 'd'], "value": 4 } + ]); + obj4; // {a: 5, b: {d: 4}} +*/ + +var REMOVE = 'remove'; +var REPLACE = 'replace'; +var ADD = 'add'; + +function diffApply(obj, diff, pathConverter) { + if (!obj || typeof obj != 'object') { + throw new Error('base object must be an object or an array'); + } + + if (!Array.isArray(diff)) { + throw new Error('diff must be an array'); + } + + var diffLength = diff.length; + for (var i = 0; i < diffLength; i++) { + var thisDiff = diff[i]; + var subObject = obj; + var thisOp = thisDiff.op; + var thisPath = thisDiff.path; + if (pathConverter) { + thisPath = pathConverter(thisPath); + if (!Array.isArray(thisPath)) { + throw new Error('pathConverter must return an array'); + } + } else { + if (!Array.isArray(thisPath)) { + throw new Error( + 'diff path must be an array, consider supplying a path converter' + ); + } + } + var pathCopy = thisPath.slice(); + var lastProp = pathCopy.pop(); + if (lastProp == null) { + return false; + } + var thisProp; + while ((thisProp = pathCopy.shift()) != null) { + if (!(thisProp in subObject)) { + subObject[thisProp] = {}; + } + subObject = subObject[thisProp]; + } + if (thisOp === REMOVE || thisOp === REPLACE) { + if (!subObject.hasOwnProperty(lastProp)) { + throw new Error( + ['expected to find property', thisDiff.path, 'in object', obj].join( + ' ' + ) + ); + } + } + if (thisOp === REMOVE) { + Array.isArray(subObject) + ? subObject.splice(lastProp, 1) + : delete subObject[lastProp]; + } + if (thisOp === REPLACE || thisOp === ADD) { + subObject[lastProp] = thisDiff.value; + } + } + return subObject; +} + +function jsonPatchPathConverter(stringPath) { + return stringPath.split('/').slice(1); +} + +export {diffApply, jsonPatchPathConverter}; diff --git a/node_modules/just-diff-apply/package.json b/node_modules/just-diff-apply/package.json index a5cc8a1feee9e..c38bd47aa6990 100644 --- a/node_modules/just-diff-apply/package.json +++ b/node_modules/just-diff-apply/package.json @@ -1,10 +1,18 @@ { "name": "just-diff-apply", - "version": "3.0.0", + "version": "4.0.1", "description": "Apply a diff to an object. Optionally supports jsonPatch protocol", "main": "index.js", + "module": "index.mjs", + "exports": { + ".": { + "require": "./index.js", + "default": "./index.mjs" + } + }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "build": "rollup -c" }, "repository": "https://github.com/angus-c/just", "keywords": [ diff --git a/node_modules/just-diff-apply/rollup.config.js b/node_modules/just-diff-apply/rollup.config.js new file mode 100644 index 0000000000000..fb9d24a3d845b --- /dev/null +++ b/node_modules/just-diff-apply/rollup.config.js @@ -0,0 +1,3 @@ +const createRollupConfig = require('../../config/createRollupConfig'); + +module.exports = createRollupConfig(__dirname); diff --git a/node_modules/just-diff/index.mjs b/node_modules/just-diff/index.mjs new file mode 100644 index 0000000000000..8da5b5cea8dab --- /dev/null +++ b/node_modules/just-diff/index.mjs @@ -0,0 +1,146 @@ +/* + const obj1 = {a: 4, b: 5}; + const obj2 = {a: 3, b: 5}; + const obj3 = {a: 4, c: 5}; + + diff(obj1, obj2); + [ + { "op": "replace", "path": ['a'], "value": 3 } + ] + + diff(obj2, obj3); + [ + { "op": "remove", "path": ['b'] }, + { "op": "replace", "path": ['a'], "value": 4 } + { "op": "add", "path": ['c'], "value": 5 } + ] + + // using converter to generate jsPatch standard paths + // see http://jsonpatch.com + import {diff, jsonPatchPathConverter} from 'just-diff' + diff(obj1, obj2, jsonPatchPathConverter); + [ + { "op": "replace", "path": '/a', "value": 3 } + ] + + diff(obj2, obj3, jsonPatchPathConverter); + [ + { "op": "remove", "path": '/b' }, + { "op": "replace", "path": '/a', "value": 4 } + { "op": "add", "path": '/c', "value": 5 } + ] + + // arrays + const obj4 = {a: 4, b: [1, 2, 3]}; + const obj5 = {a: 3, b: [1, 2, 4]}; + const obj6 = {a: 3, b: [1, 2, 4, 5]}; + + diff(obj4, obj5); + [ + { "op": "replace", "path": ['a'], "value": 3 } + { "op": "replace", "path": ['b', 2], "value": 4 } + ] + + diff(obj5, obj6); + [ + { "op": "add", "path": ['b', 3], "value": 5 } + ] + + // nested paths + const obj7 = {a: 4, b: {c: 3}}; + const obj8 = {a: 4, b: {c: 4}}; + const obj9 = {a: 5, b: {d: 4}}; + + diff(obj7, obj8); + [ + { "op": "replace", "path": ['b', 'c'], "value": 4 } + ] + + diff(obj8, obj9); + [ + { "op": "replace", "path": ['a'], "value": 5 } + { "op": "remove", "path": ['b', 'c']} + { "op": "add", "path": ['b', 'd'], "value": 4 } + ] +*/ + +function diff(obj1, obj2, pathConverter) { + if (!obj1 || typeof obj1 != 'object' || !obj2 || typeof obj2 != 'object') { + throw new Error('both arguments must be objects or arrays'); + } + + pathConverter || + (pathConverter = function(arr) { + return arr; + }); + + function getDiff(obj1, obj2, basePath, diffs) { + var obj1Keys = Object.keys(obj1); + var obj1KeysLength = obj1Keys.length; + var obj2Keys = Object.keys(obj2); + var obj2KeysLength = obj2Keys.length; + var path; + + for (var i = 0; i < obj1KeysLength; i++) { + var key = Array.isArray(obj1) ? Number(obj1Keys[i]) : obj1Keys[i]; + if (!(key in obj2)) { + path = basePath.concat(key); + diffs.remove.push({ + op: 'remove', + path: pathConverter(path), + }); + } + } + + for (var i = 0; i < obj2KeysLength; i++) { + var key = Array.isArray(obj2) ? Number(obj2Keys[i]) : obj2Keys[i]; + var obj1AtKey = obj1[key]; + var obj2AtKey = obj2[key]; + if (!(key in obj1)) { + path = basePath.concat(key); + var obj2Value = obj2[key]; + diffs.add.push({ + op: 'add', + path: pathConverter(path), + value: obj2Value, + }); + } else if (obj1AtKey !== obj2AtKey) { + if ( + Object(obj1AtKey) !== obj1AtKey || + Object(obj2AtKey) !== obj2AtKey + ) { + path = pushReplace(path, basePath, key, diffs, pathConverter, obj2); + } else { + if ( + !Object.keys(obj1AtKey).length && + !Object.keys(obj2AtKey).length && + String(obj1AtKey) != String(obj2AtKey) + ) { + path = pushReplace(path, basePath, key, diffs, pathConverter, obj2); + } else { + getDiff(obj1[key], obj2[key], basePath.concat(key), diffs); + } + } + } + } + + return diffs.remove.reverse().concat(diffs.replace).concat(diffs.add); + } + return getDiff(obj1, obj2, [], {remove: [], replace: [], add: []}); +} + +function pushReplace(path, basePath, key, diffs, pathConverter, obj2) { + path = basePath.concat(key); + diffs.replace.push({ + op: 'replace', + path: pathConverter(path), + value: obj2[key], + }); + return path; +} + +function jsonPatchPathConverter(arrayPath) { + return [''].concat(arrayPath).join('/'); +} + +export {diff, jsonPatchPathConverter}; diff --git a/node_modules/just-diff/index.tests.ts b/node_modules/just-diff/index.tests.ts index c7ebb70d3dc64..91eaecd8d49e8 100644 --- a/node_modules/just-diff/index.tests.ts +++ b/node_modules/just-diff/index.tests.ts @@ -1,4 +1,4 @@ -import diffObj = require('./index'); +import * as diffObj from './index' const {diff, jsonPatchPathConverter} = diffObj; const obj1 = {a: 2, b: 3}; diff --git a/node_modules/just-diff/package.json b/node_modules/just-diff/package.json index 00be1d50fddbc..bab8a29ae93a4 100644 --- a/node_modules/just-diff/package.json +++ b/node_modules/just-diff/package.json @@ -1,11 +1,19 @@ { "name": "just-diff", - "version": "3.1.1", + "version": "5.0.1", "description": "Return an object representing the diffs between two objects. Supports jsonPatch protocol", "main": "index.js", + "module": "index.mjs", + "exports": { + ".": { + "require": "./index.js", + "default": "./index.mjs" + } + }, "types": "index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "build": "rollup -c" }, "repository": "https://github.com/angus-c/just", "keywords": [ @@ -20,4 +28,4 @@ "bugs": { "url": "https://github.com/angus-c/just/issues" } -} \ No newline at end of file +} diff --git a/node_modules/just-diff/rollup.config.js b/node_modules/just-diff/rollup.config.js new file mode 100644 index 0000000000000..fb9d24a3d845b --- /dev/null +++ b/node_modules/just-diff/rollup.config.js @@ -0,0 +1,3 @@ +const createRollupConfig = require('../../config/createRollupConfig'); + +module.exports = createRollupConfig(__dirname); diff --git a/node_modules/parse-conflict-json/LICENSE b/node_modules/parse-conflict-json/LICENSE deleted file mode 100644 index 20a4762540923..0000000000000 --- a/node_modules/parse-conflict-json/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/parse-conflict-json/LICENSE.md b/node_modules/parse-conflict-json/LICENSE.md new file mode 100644 index 0000000000000..5fc208ff122e0 --- /dev/null +++ b/node_modules/parse-conflict-json/LICENSE.md @@ -0,0 +1,20 @@ + + +ISC License + +Copyright 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 NPM DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO +EVENT SHALL NPM 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/parse-conflict-json/index.js b/node_modules/parse-conflict-json/lib/index.js similarity index 74% rename from node_modules/parse-conflict-json/index.js rename to node_modules/parse-conflict-json/lib/index.js index 8b5dbde40c08b..21b295d04b902 100644 --- a/node_modules/parse-conflict-json/index.js +++ b/node_modules/parse-conflict-json/lib/index.js @@ -2,13 +2,16 @@ const parseJSON = require('json-parse-even-better-errors') const { diff } = require('just-diff') const { diffApply } = require('just-diff-apply') +const globalObjectProperties = Object.getOwnPropertyNames(Object.prototype) + const stripBOM = content => { content = content.toString() // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) // because the buffer-to-string conversion in `fs.readFileSync()` // translates it to FEFF, the UTF-16 BOM. - if (content.charCodeAt(0) === 0xFEFF) + if (content.charCodeAt(0) === 0xFEFF) { content = content.slice(1) + } return content } @@ -22,37 +25,42 @@ const isDiff = str => const parseConflictJSON = (str, reviver, prefer) => { prefer = prefer || 'ours' - if (prefer !== 'theirs' && prefer !== 'ours') + if (prefer !== 'theirs' && prefer !== 'ours') { throw new TypeError('prefer param must be "ours" or "theirs" if set') + } str = stripBOM(str) - if (!isDiff(str)) + if (!isDiff(str)) { return parseJSON(str) + } const pieces = str.split(/[\n\r]+/g).reduce((acc, line) => { - if (line.match(PARENT_RE)) + if (line.match(PARENT_RE)) { acc.state = 'parent' - else if (line.match(OURS_RE)) + } else if (line.match(OURS_RE)) { acc.state = 'ours' - else if (line.match(THEIRS_RE)) + } else if (line.match(THEIRS_RE)) { acc.state = 'theirs' - else if (line.match(END_RE)) + } else if (line.match(END_RE)) { acc.state = 'top' - else { - if (acc.state === 'top' || acc.state === 'ours') + } else { + if (acc.state === 'top' || acc.state === 'ours') { acc.ours += line - if (acc.state === 'top' || acc.state === 'theirs') + } + if (acc.state === 'top' || acc.state === 'theirs') { acc.theirs += line - if (acc.state === 'top' || acc.state === 'parent') + } + if (acc.state === 'top' || acc.state === 'parent') { acc.parent += line + } } return acc }, { state: 'top', ours: '', theirs: '', - parent: '' + parent: '', }) // this will throw if either piece is not valid JSON, that's intended @@ -70,8 +78,9 @@ const isObj = obj => obj && typeof obj === 'object' const copyPath = (to, from, path, i) => { const p = path[i] if (isObj(to[p]) && isObj(from[p]) && - Array.isArray(to[p]) === Array.isArray(from[p])) + Array.isArray(to[p]) === Array.isArray(from[p])) { return copyPath(to[p], from[p], path, i + 1) + } to[p] = from[p] } @@ -80,6 +89,9 @@ const copyPath = (to, from, path, i) => { const resolve = (parent, ours, theirs) => { const dours = diff(parent, ours) for (let i = 0; i < dours.length; i++) { + if (globalObjectProperties.find(prop => dours[i].path.includes(prop))) { + continue + } try { diffApply(theirs, [dours[i]]) } catch (e) { diff --git a/node_modules/parse-conflict-json/package.json b/node_modules/parse-conflict-json/package.json index 3962e22f33901..bb633e158b5d1 100644 --- a/node_modules/parse-conflict-json/package.json +++ b/node_modules/parse-conflict-json/package.json @@ -1,32 +1,44 @@ { "name": "parse-conflict-json", - "version": "1.1.1", + "version": "2.0.1", "description": "Parse a JSON string that has git merge conflicts, resolving if possible", - "author": "Isaac Z. Schlueter (https://izs.me)", + "author": "GitHub Inc.", "license": "ISC", + "main": "lib", "scripts": { "test": "tap", "snap": "tap", "preversion": "npm test", "postversion": "npm publish", - "postpublish": "git push origin --follow-tags" + "postpublish": "git push origin --follow-tags", + "lint": "eslint '**/*.js'", + "postlint": "npm-template-check", + "lintfix": "npm run lint -- --fix", + "prepublishOnly": "git push origin --follow-tags", + "posttest": "npm run lint" }, "tap": { "check-coverage": true }, "devDependencies": { - "tap": "^14.6.1" + "@npmcli/template-oss": "^2.3.1", + "tap": "^15.1.5" }, "dependencies": { - "just-diff": "^3.0.1", - "just-diff-apply": "^3.0.0", - "json-parse-even-better-errors": "^2.3.0" + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^4.0.1" }, "repository": { "type": "git", "url": "git+https://github.com/npm/parse-conflict-json.git" }, "files": [ - "index.js" - ] + "bin", + "lib" + ], + "templateVersion": "2.3.1", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } } diff --git a/package-lock.json b/package-lock.json index b6df88dfee279..e38318d1ca525 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,7 +87,7 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^4.1.0", + "@npmcli/arborist": "^4.1.1", "@npmcli/ci-detect": "^1.4.0", "@npmcli/config": "^2.3.2", "@npmcli/map-workspaces": "^2.0.0", @@ -140,7 +140,7 @@ "npmlog": "^6.0.0", "opener": "^1.5.2", "pacote": "^12.0.2", - "parse-conflict-json": "^1.1.1", + "parse-conflict-json": "^2.0.1", "proc-log": "^1.0.0", "qrcode-terminal": "^0.12.0", "read": "~1.0.7", @@ -801,9 +801,9 @@ } }, "node_modules/@npmcli/arborist": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.1.0.tgz", - "integrity": "sha512-bkaOqCuTUtpVOe1vaAP7TUihu64wIbnSDpsbqBJUsGFTLYXbjKwi6xj8Zx5cfHkM3nqyeEEbPYlGkt0TXjKrUg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.1.1.tgz", + "integrity": "sha512-sASzHngGWt8l6ic1VP0Qf3+ral/RL8L+MculTp2w8NYjjkDiurByOT39KiYmLwpeJ2GQoDR/rdhEwnII8wZQ9g==", "inBundle": true, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", @@ -827,7 +827,7 @@ "npm-pick-manifest": "^6.1.0", "npm-registry-fetch": "^11.0.0", "pacote": "^12.0.2", - "parse-conflict-json": "^1.1.1", + "parse-conflict-json": "^2.0.1", "proc-log": "^1.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^1.0.1", @@ -4584,15 +4584,15 @@ } }, "node_modules/just-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-3.1.1.tgz", - "integrity": "sha512-sdMWKjRq8qWZEjDcVA6llnUT8RDEBIfOiGpYFPYa9u+2c39JCsejktSP7mj5eRid5EIvTzIpQ2kDOCw1Nq9BjQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-5.0.1.tgz", + "integrity": "sha512-X00TokkRIDotUIf3EV4xUm6ELc/IkqhS/vPSHdWnsM5y0HoNMfEqrazizI7g78lpHvnRSRt/PFfKtRqJCOGIuQ==", "inBundle": true }, "node_modules/just-diff-apply": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-3.0.0.tgz", - "integrity": "sha512-K2MLc+ZC2DVxX4V61bIKPeMUUfj1YYZ3h0myhchDXOW1cKoPZMnjIoNCqv9bF2n5Oob1PFxuR2gVJxkxz4e58w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-4.0.1.tgz", + "integrity": "sha512-AKOkzB5P6FkfP21UlZVX/OPXx/sC2GagpLX9cBxqHqDuRjwmZ/AJRKSNrB9jHPpRW1W1ONs6gly1gW46t055nQ==", "inBundle": true }, "node_modules/lcov-parse": { @@ -5835,14 +5835,17 @@ } }, "node_modules/parse-conflict-json": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-1.1.1.tgz", - "integrity": "sha512-4gySviBiW5TRl7XHvp1agcS7SOe0KZOjC//71dzZVWJrY9hCrgtvl5v3SyIxCZ4fZF47TxD9nfzmxcx76xmbUw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-2.0.1.tgz", + "integrity": "sha512-Y7nYw+QaSGBto1LB9lgwOR05Rtz5SbuTf+Oe7HJ6SYQ/DHsvRjQ8O03oWdJbvkt6GzDWospgyZbGmjDYL0sDgA==", "inBundle": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "just-diff": "^3.0.1", - "just-diff-apply": "^3.0.0" + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" } }, "node_modules/parse-entities": { @@ -10664,9 +10667,9 @@ "dev": true }, "@npmcli/arborist": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.1.0.tgz", - "integrity": "sha512-bkaOqCuTUtpVOe1vaAP7TUihu64wIbnSDpsbqBJUsGFTLYXbjKwi6xj8Zx5cfHkM3nqyeEEbPYlGkt0TXjKrUg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.1.1.tgz", + "integrity": "sha512-sASzHngGWt8l6ic1VP0Qf3+ral/RL8L+MculTp2w8NYjjkDiurByOT39KiYmLwpeJ2GQoDR/rdhEwnII8wZQ9g==", "requires": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/installed-package-contents": "^1.0.7", @@ -10689,7 +10692,7 @@ "npm-pick-manifest": "^6.1.0", "npm-registry-fetch": "^11.0.0", "pacote": "^12.0.2", - "parse-conflict-json": "^1.1.1", + "parse-conflict-json": "^2.0.1", "proc-log": "^1.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^1.0.1", @@ -13435,14 +13438,14 @@ } }, "just-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-3.1.1.tgz", - "integrity": "sha512-sdMWKjRq8qWZEjDcVA6llnUT8RDEBIfOiGpYFPYa9u+2c39JCsejktSP7mj5eRid5EIvTzIpQ2kDOCw1Nq9BjQ==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-5.0.1.tgz", + "integrity": "sha512-X00TokkRIDotUIf3EV4xUm6ELc/IkqhS/vPSHdWnsM5y0HoNMfEqrazizI7g78lpHvnRSRt/PFfKtRqJCOGIuQ==" }, "just-diff-apply": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-3.0.0.tgz", - "integrity": "sha512-K2MLc+ZC2DVxX4V61bIKPeMUUfj1YYZ3h0myhchDXOW1cKoPZMnjIoNCqv9bF2n5Oob1PFxuR2gVJxkxz4e58w==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-4.0.1.tgz", + "integrity": "sha512-AKOkzB5P6FkfP21UlZVX/OPXx/sC2GagpLX9cBxqHqDuRjwmZ/AJRKSNrB9jHPpRW1W1ONs6gly1gW46t055nQ==" }, "lcov-parse": { "version": "1.0.0", @@ -14379,13 +14382,13 @@ } }, "parse-conflict-json": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-1.1.1.tgz", - "integrity": "sha512-4gySviBiW5TRl7XHvp1agcS7SOe0KZOjC//71dzZVWJrY9hCrgtvl5v3SyIxCZ4fZF47TxD9nfzmxcx76xmbUw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-2.0.1.tgz", + "integrity": "sha512-Y7nYw+QaSGBto1LB9lgwOR05Rtz5SbuTf+Oe7HJ6SYQ/DHsvRjQ8O03oWdJbvkt6GzDWospgyZbGmjDYL0sDgA==", "requires": { - "json-parse-even-better-errors": "^2.3.0", - "just-diff": "^3.0.1", - "just-diff-apply": "^3.0.0" + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^4.0.1" } }, "parse-entities": { diff --git a/package.json b/package.json index 73199f1795d42..6e02b7ca24a00 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ }, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^4.1.0", + "@npmcli/arborist": "^4.1.1", "@npmcli/ci-detect": "^1.4.0", "@npmcli/config": "^2.3.2", "@npmcli/map-workspaces": "^2.0.0", @@ -108,7 +108,7 @@ "npmlog": "^6.0.0", "opener": "^1.5.2", "pacote": "^12.0.2", - "parse-conflict-json": "^1.1.1", + "parse-conflict-json": "^2.0.1", "proc-log": "^1.0.0", "qrcode-terminal": "^0.12.0", "read": "~1.0.7", From 08c663931ec1f56d777ffdb38f94926b9eac13ef Mon Sep 17 00:00:00 2001 From: Luke Karrys Date: Mon, 6 Dec 2021 20:44:06 -0700 Subject: [PATCH 03/13] fix: dont warn on error cleaning individual log files Closes: #4128 This also refactors the logic for cleaning log files to use the `ignore` option from `glob` to not clean current log files instead of relying on updating the `logs-max` count. I also discovered a bug where we weren't cleaning log files written with the old naming convention, so this fixes that as well. PR-URL: https://github.com/npm/cli/pull/4134 Credit: @lukekarrys Close: #4134 Reviewed-by: @wraithgar --- lib/utils/log-file.js | 43 +++++++++++++++++--------------------- test/lib/utils/log-file.js | 25 ++++++++++++++++++---- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/lib/utils/log-file.js b/lib/utils/log-file.js index b37fd23e079c0..0bf1e0054ea2b 100644 --- a/lib/utils/log-file.js +++ b/lib/utils/log-file.js @@ -8,6 +8,8 @@ const fsMiniPass = require('fs-minipass') const log = require('./log-shim') const withChownSync = require('./with-chown-sync') +const padZero = (n, length) => n.toString().padStart(length.toString().length, '0') + const _logHandler = Symbol('logHandler') const _formatLogItem = Symbol('formatLogItem') const _getLogFilePath = Symbol('getLogFilePath') @@ -34,7 +36,7 @@ class LogFiles { // here for infinite loops that still log. This is also partially handled // by the config.get('max-files') option, but this is a failsafe to // prevent runaway log file creation - #MAX_LOG_FILES_PER_PROCESS = null + #MAX_FILES_PER_PROCESS = null #fileLogCount = 0 #totalLogCount = 0 @@ -48,7 +50,7 @@ class LogFiles { } = {}) { this.#logId = LogFiles.logId(new Date()) this.#MAX_LOGS_PER_FILE = maxLogsPerFile - this.#MAX_LOG_FILES_PER_PROCESS = maxFilesPerProcess + this.#MAX_FILES_PER_PROCESS = maxFilesPerProcess this.on() } @@ -56,10 +58,6 @@ class LogFiles { return d.toISOString().replace(/[.:]/g, '_') } - static fileName (prefix, suffix) { - return `${prefix}-debug-${suffix}.log` - } - static format (count, level, title, ...args) { let prefix = `${count} ${level}` if (title) { @@ -149,7 +147,7 @@ class LogFiles { if (this.#fileLogCount >= this.#MAX_LOGS_PER_FILE) { // Write last chunk to the file and close it this[_endStream](logOutput) - if (this.#files.length >= this.#MAX_LOG_FILES_PER_PROCESS) { + if (this.#files.length >= this.#MAX_FILES_PER_PROCESS) { // but if its way too many then we just stop listening this.off() } else { @@ -166,23 +164,21 @@ class LogFiles { return LogFiles.format(this.#totalLogCount++, ...args) } - [_getLogFilePath] (prefix, suffix) { - return path.resolve(this.#dir, LogFiles.fileName(prefix, suffix)) + [_getLogFilePath] (prefix, suffix, sep = '-') { + return path.resolve(this.#dir, prefix + sep + 'debug' + sep + suffix + '.log') } [_openLogFile] () { // Count in filename will be 0 indexed const count = this.#files.length - // Pad with zeros so that our log files are always sorted properly - // We never want to write files ending in `-9.log` and `-10.log` because - // log file cleaning is done by deleting the oldest so in this example - // `-10.log` would be deleted next - const countDigits = this.#MAX_LOG_FILES_PER_PROCESS.toString().length - try { const logStream = withChownSync( - this[_getLogFilePath](this.#logId, count.toString().padStart(countDigits, '0')), + // Pad with zeros so that our log files are always sorted properly + // We never want to write files ending in `-9.log` and `-10.log` because + // log file cleaning is done by deleting the oldest so in this example + // `-10.log` would be deleted next + this[_getLogFilePath](this.#logId, padZero(count, this.#MAX_FILES_PER_PROCESS)), // Some effort was made to make the async, but we need to write logs // during process.on('exit') which has to be synchronous. So in order // to never drop log messages, it is easiest to make it sync all the time @@ -214,14 +210,13 @@ class LogFiles { return } - // Add 1 to account for the current log file and make - // minimum config 0 so current log file is never deleted - // XXX: we should make a separate documented option to - // disable log file writing - const max = Math.max(this.#logsMax, 0) + 1 try { - const files = await glob(this[_getLogFilePath]('*', '*')) - const toDelete = files.length - max + // Handle the old (prior to 8.2.0) log file names which did not have an counter suffix + // so match by anything after `-debug` and before `.log` (including nothing) + const logGlob = this[_getLogFilePath]('*-', '*', '') + // Always ignore the currently written files + const files = await glob(logGlob, { ignore: this.#files }) + const toDelete = files.length - this.#logsMax if (toDelete <= 0) { return @@ -233,7 +228,7 @@ class LogFiles { try { await rimraf(file) } catch (e) { - log.warn('logfile', 'error removing log file', file, e) + log.silly('logfile', 'error removing log file', file, e) } } } catch (e) { diff --git a/test/lib/utils/log-file.js b/test/lib/utils/log-file.js index adc1a2e03ff3d..007ce221b0940 100644 --- a/test/lib/utils/log-file.js +++ b/test/lib/utils/log-file.js @@ -12,15 +12,20 @@ t.cleanSnapshot = (path) => cleanCwd(path) const last = arr => arr[arr.length - 1] const range = (n) => Array.from(Array(n).keys()) -const makeOldLogs = (count) => { +const makeOldLogs = (count, oldStyle) => { const d = new Date() d.setHours(-1) d.setSeconds(0) - return range(count / 2).reduce((acc, i) => { + return range(oldStyle ? count : (count / 2)).reduce((acc, i) => { const cloneDate = new Date(d.getTime()) cloneDate.setSeconds(i) - acc[LogFile.fileName(LogFile.logId(cloneDate), 0)] = 'hello' - acc[LogFile.fileName(LogFile.logId(cloneDate), 1)] = 'hello' + const dateId = LogFile.logId(cloneDate) + if (oldStyle) { + acc[`${dateId}-debug.log`] = 'hello' + } else { + acc[`${dateId}-debug-0.log`] = 'hello' + acc[`${dateId}-debug-1.log`] = 'hello' + } return acc }, {}) } @@ -247,6 +252,18 @@ t.test('glob error', async t => { t.match(last(logs).content, /error cleaning log files .* bad glob/) }) +t.test('cleans old style logs too', async t => { + const logsMax = 5 + const oldLogs = 10 + const { readLogs } = await loadLogFile(t, { + logsMax, + testdir: makeOldLogs(oldLogs, false), + }) + + const logs = await readLogs() + t.equal(logs.length, logsMax + 1) +}) + t.test('rimraf error', async t => { const logsMax = 5 const oldLogs = 10 From db1885d7fec012f018093c76dec5a9c01a0ca2b0 Mon Sep 17 00:00:00 2001 From: nlf Date: Wed, 24 Nov 2021 11:49:58 -0800 Subject: [PATCH 04/13] chore(docs): document overrides PR-URL: https://github.com/npm/cli/pull/4092 Credit: @nlf Close: #4092 Reviewed-by: @wraithgar --- docs/content/configuring-npm/package-json.md | 103 +++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/docs/content/configuring-npm/package-json.md b/docs/content/configuring-npm/package-json.md index 856adb3366cb0..5d61aac52a182 100644 --- a/docs/content/configuring-npm/package-json.md +++ b/docs/content/configuring-npm/package-json.md @@ -871,6 +871,109 @@ if (foo) { Entries in `optionalDependencies` will override entries of the same name in `dependencies`, so it's usually best to only put in one place. +### overrides + +If you need to make specific changes to dependencies of your dependencies, for +example replacing the version of a dependency with a known security issue, +replacing an existing dependency with a fork, or making sure that the same +version of a package is used everywhere, then you may add an override. + +Overrides provide a way to replace a package in your dependency tree with +another version, or another package entirely. These changes can be scoped as +specific or as vague as desired. + +To make sure the package `foo` is always installed as version `1.0.0` no matter +what version your dependencies rely on: + +```json +{ + "overrides": { + "foo": "1.0.0" + } +} +``` + +The above is a short hand notation, the full object form can be used to allow +overriding a package itself as well as a child of the package. This will cause +`foo` to always be `1.0.0` while also making `bar` at any depth beyond `foo` +also `1.0.0`: + +```json +{ + "overrides": { + "foo": { + ".": "1.0.0", + "bar": "1.0.0" + } + } +} +``` + +To only override `foo` to be `1.0.0` when it's a child (or grandchild, or great +grandchild, etc) of the package `bar`: + +```json +{ + "overrides": { + "bar": { + "foo": "1.0.0" + } + } +} +``` + +Keys can be nested to any arbitrary length. To override `foo` only when it's a +child of `bar` and only when `bar` is a child of `baz`: + +```json +{ + "overrides": { + "baz": { + "bar": { + "foo": "1.0.0" + } + } + } +} +``` + +The key of an override can also include a version, or range of versions. +To override `foo` to `1.0.0`, but only when it's a child of `bar@2.0.0`: + +```json +{ + "overrides": { + "bar@2.0.0": { + "foo": "1.0.0" + } + } +} +``` + +You may not set an override for a package that you directly depend on unless +both the dependency and the override itself share the exact same spec. To make +this limitation easier to deal with, overrides may also be defined as a +reference to a spec for a direct dependency by prefixing the name of the +package you wish the version to match with a `$`. + +```json +{ + "dependencies": { + "foo": "^1.0.0" + }, + "overrides": { + // BAD, will throw an EOVERRIDE error + // "foo": "^2.0.0" + // GOOD, specs match so override is allowed + // "foo": "^1.0.0" + // BEST, the override is defined as a reference to the dependency + "foo": "$foo", + // the referenced package does not need to match the overridden one + "bar": "$foo" + } +} +``` + ### engines You can specify the version of node that your stuff works on: From 9e9a76a21d3b8649b7debfaf0782433cbd8179b3 Mon Sep 17 00:00:00 2001 From: Felipe Plets Date: Thu, 2 Dec 2021 18:28:25 -0500 Subject: [PATCH 05/13] chore(ci): fix license validation CI step name PR-URL: https://github.com/npm/cli/pull/4118 Credit: @felipeplets Close: #4118 Reviewed-by: @darcyclarke --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 473e30f37a57d..b6b9125938e1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: run: | node ./bin/npm-cli.js install --ignore-scripts --no-audit node ./bin/npm-cli.js rebuild - - name: Run linting + - name: Validate licenses run: node ./bin/npm-cli.js run licenses smoke-tests: From e605b128c87620aae843cdbd8f35cc614da3f8a2 Mon Sep 17 00:00:00 2001 From: Luke Karrys Date: Wed, 8 Dec 2021 15:39:19 -0700 Subject: [PATCH 06/13] fix: redact all private keys from config output PR-URL: https://github.com/npm/cli/pull/4142 Credit: @lukekarrys Close: #4142 Reviewed-by: @wraithgar --- lib/commands/config.js | 14 ++++++++++++-- lib/utils/exit-handler.js | 1 + test/lib/commands/config.js | 8 +++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/commands/config.js b/lib/commands/config.js index eb1d570c6ea25..6553a26620cb7 100644 --- a/lib/commands/config.js +++ b/lib/commands/config.js @@ -28,7 +28,17 @@ const keyValues = args => { return kv } -const publicVar = k => !/^(\/\/[^:]+:)?_/.test(k) +const publicVar = k => { + // _password + if (k.startsWith('_')) { + return false + } + // //localhost:8080/:_password + if (k.startsWith('//') && k.includes(':_')) { + return false + } + return true +} const BaseCommand = require('../base-command.js') class Config extends BaseCommand { @@ -147,7 +157,7 @@ class Config extends BaseCommand { const out = [] for (const key of keys) { if (!publicVar(key)) { - throw `The ${key} option is protected, and cannot be retrieved in this way` + throw new Error(`The ${key} option is protected, and cannot be retrieved in this way`) } const pref = keys.length > 1 ? `${key}=` : '' diff --git a/lib/utils/exit-handler.js b/lib/utils/exit-handler.js index 32434662422ae..22c774101751b 100644 --- a/lib/utils/exit-handler.js +++ b/lib/utils/exit-handler.js @@ -116,6 +116,7 @@ const exitHandler = err => { exitCode = err.code noLogMessage = true } else if (typeof err === 'string') { + // XXX: we should stop throwing strings log.error('', err) noLogMessage = true } else if (!(err instanceof Error)) { diff --git a/test/lib/commands/config.js b/test/lib/commands/config.js index b37088c06b9cd..d78e0290a850b 100644 --- a/test/lib/commands/config.js +++ b/test/lib/commands/config.js @@ -333,7 +333,13 @@ t.test('config get private key', async t => { await t.rejects( sandbox.run('config', ['get', '_authToken']), - '_authToken is protected', + /_authToken option is protected/, + 'rejects with protected string' + ) + + await t.rejects( + sandbox.run('config', ['get', '//localhost:8080/:_password']), + /_password option is protected/, 'rejects with protected string' ) }) From 166d9e144b38087ee5e7d8aaf6ec7d602cf2957c Mon Sep 17 00:00:00 2001 From: Luke Karrys Date: Wed, 8 Dec 2021 16:07:23 -0700 Subject: [PATCH 07/13] fix: output configured registry during publish Closes: npm/statusboard#416 PR-URL: https://github.com/npm/cli/pull/4143 Credit: @lukekarrys Close: #4143 Reviewed-by: @wraithgar --- lib/commands/publish.js | 10 +++++++--- test/lib/commands/publish.js | 18 ++++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/commands/publish.js b/lib/commands/publish.js index ad538668b63a3..b8209374925fe 100644 --- a/lib/commands/publish.js +++ b/lib/commands/publish.js @@ -104,11 +104,15 @@ class Publish extends BaseCommand { const resolved = npa.resolve(manifest.name, manifest.version) const registry = npmFetch.pickRegistry(resolved, opts) const creds = this.npm.config.getCredentialsByURI(registry) + const outputRegistry = replaceInfo(registry) if (!creds.token && !creds.username) { - throw Object.assign(new Error('This command requires you to be logged in.'), { - code: 'ENEEDAUTH', - }) + throw Object.assign( + new Error(`This command requires you to be logged in to ${outputRegistry}`), { + code: 'ENEEDAUTH', + } + ) } + log.notice('', `Publishing to ${outputRegistry}`) await otplease(opts, opts => libpub(manifest, tarballData, opts)) } diff --git a/test/lib/commands/publish.js b/test/lib/commands/publish.js index 1178cd6ee1edf..2a591fd4c7534 100644 --- a/test/lib/commands/publish.js +++ b/test/lib/commands/publish.js @@ -341,8 +341,10 @@ t.test('can publish a tarball', async t => { t.test('should check auth for default registry', async t => { t.plan(2) - const Publish = t.mock('../../../lib/commands/publish.js') const npm = mockNpm() + const registry = npm.config.get('registry') + const errorMessage = `This command requires you to be logged in to ${registry}` + const Publish = t.mock('../../../lib/commands/publish.js') npm.config.getCredentialsByURI = uri => { t.same(uri, npm.config.get('registry'), 'gets credentials for expected registry') return {} @@ -351,7 +353,7 @@ t.test('should check auth for default registry', async t => { await t.rejects( publish.exec([]), - { message: 'This command requires you to be logged in.', code: 'ENEEDAUTH' }, + { message: errorMessage, code: 'ENEEDAUTH' }, 'throws when not logged in' ) }) @@ -359,6 +361,7 @@ t.test('should check auth for default registry', async t => { t.test('should check auth for configured registry', async t => { t.plan(2) const registry = 'https://some.registry' + const errorMessage = 'This command requires you to be logged in to https://some.registry' const Publish = t.mock('../../../lib/commands/publish.js') const npm = mockNpm({ flatOptions: { registry }, @@ -371,7 +374,7 @@ t.test('should check auth for configured registry', async t => { await t.rejects( publish.exec([]), - { message: 'This command requires you to be logged in.', code: 'ENEEDAUTH' }, + { message: errorMessage, code: 'ENEEDAUTH' }, 'throws when not logged in' ) }) @@ -379,6 +382,7 @@ t.test('should check auth for configured registry', async t => { t.test('should check auth for scope specific registry', async t => { t.plan(2) const registry = 'https://some.registry' + const errorMessage = 'This command requires you to be logged in to https://some.registry' const testDir = t.testdir({ 'package.json': JSON.stringify( { @@ -402,7 +406,7 @@ t.test('should check auth for scope specific registry', async t => { await t.rejects( publish.exec([testDir]), - { message: 'This command requires you to be logged in.', code: 'ENEEDAUTH' }, + { message: errorMessage, code: 'ENEEDAUTH' }, 'throws when not logged in' ) }) @@ -735,7 +739,7 @@ t.test('private workspaces', async t => { }) t.test('unexpected error', async t => { - t.plan(1) + t.plan(2) const Publish = t.mock('../../../lib/commands/publish.js', { ...mocks, @@ -749,7 +753,9 @@ t.test('private workspaces', async t => { }, }, 'proc-log': { - notice () {}, + notice (__, msg) { + t.match(msg, 'Publishing to https://registry.npmjs.org/') + }, verbose () {}, }, }) From 1d8bec566cb08ff5ff220f53083323fa8c3fb72e Mon Sep 17 00:00:00 2001 From: nlf Date: Wed, 8 Dec 2021 15:41:15 -0800 Subject: [PATCH 08/13] deps: minipass@3.1.6 PR-URL: https://github.com/npm/cli/pull/4144 Credit: @nlf Close: #4144 Reviewed-by: @wraithgar --- node_modules/minipass/index.js | 7 ++++++- node_modules/minipass/package.json | 2 +- package-lock.json | 14 +++++++------- package.json | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/node_modules/minipass/index.js b/node_modules/minipass/index.js index ae134a066d77f..1835dd9bcf512 100644 --- a/node_modules/minipass/index.js +++ b/node_modules/minipass/index.js @@ -165,7 +165,12 @@ module.exports = class Minipass extends Stream { // because we're mid-write, so that'd be bad. if (this[BUFFERLENGTH] !== 0) this[FLUSH](true) - this.emit('data', chunk) + + // if we are still flowing after flushing the buffer we can emit the + // chunk otherwise we have to buffer it. + this.flowing + ? this.emit('data', chunk) + : this[BUFFERPUSH](chunk) } else this[BUFFERPUSH](chunk) diff --git a/node_modules/minipass/package.json b/node_modules/minipass/package.json index 165fa662ab4a7..1728e5108c4c2 100644 --- a/node_modules/minipass/package.json +++ b/node_modules/minipass/package.json @@ -1,6 +1,6 @@ { "name": "minipass", - "version": "3.1.5", + "version": "3.1.6", "description": "minimal implementation of a PassThrough stream", "main": "index.js", "dependencies": { diff --git a/package-lock.json b/package-lock.json index e38318d1ca525..b1824525529b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -123,7 +123,7 @@ "libnpmteam": "^2.0.3", "libnpmversion": "^2.0.1", "make-fetch-happen": "^9.1.0", - "minipass": "^3.1.3", + "minipass": "^3.1.6", "minipass-pipeline": "^1.2.4", "mkdirp": "^1.0.4", "mkdirp-infer-owner": "^2.0.0", @@ -5074,9 +5074,9 @@ "dev": true }, "node_modules/minipass": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", - "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "inBundle": true, "dependencies": { "yallist": "^4.0.0" @@ -13819,9 +13819,9 @@ "dev": true }, "minipass": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", - "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "requires": { "yallist": "^4.0.0" } diff --git a/package.json b/package.json index 6e02b7ca24a00..ab40290cd082b 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "libnpmteam": "^2.0.3", "libnpmversion": "^2.0.1", "make-fetch-happen": "^9.1.0", - "minipass": "^3.1.3", + "minipass": "^3.1.6", "minipass-pipeline": "^1.2.4", "mkdirp": "^1.0.4", "mkdirp-infer-owner": "^2.0.0", From 6b80faa694d9a42c588921fa5a0664671172c2dc Mon Sep 17 00:00:00 2001 From: nlf Date: Wed, 8 Dec 2021 16:02:34 -0800 Subject: [PATCH 09/13] chore(benchmarks): tidy up benchmark workflows PR-URL: https://github.com/npm/cli/pull/4145 Credit: @nlf Close: #4145 Reviewed-by: @wraithgar --- .github/workflows/benchmark-comment.yml | 13 ++++-------- .github/workflows/benchmark.yml | 28 ++++++------------------- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/.github/workflows/benchmark-comment.yml b/.github/workflows/benchmark-comment.yml index de86c3c132bb7..285b2938462a2 100644 --- a/.github/workflows/benchmark-comment.yml +++ b/.github/workflows/benchmark-comment.yml @@ -17,15 +17,12 @@ jobs: DISPATCH_REPO: "benchmarks" DISPATCH_OWNER: "npm" EVENT_NAME: ${{ github.event_name }} - EVENT_ACTION: ${{ github.event.action }} OWNER: ${{ github.event.repository.owner.login }} REPO: ${{ github.event.repository.name }} ISSUE_NUMBER: ${{ github.event.issue.number }} - COMMENT_BODY: ${{ github.event.comment.body }} - COMMENT_ID: ${{ github.event.comment.id }} COMMENT_NODE_ID: ${{ github.event.comment.node_id }} COMMENT_ACTIONABLE: ${{ startsWith(github.event.comment.body, 'test this please ✅') }} - AUTH_TOKEN: ${{ secrets.NPM_DEPLOY_USER_PAT }} + AUTH_TOKEN: ${{ secrets.NPM_BENCHMARKS_TOKEN }} run: | # Comment Handler @@ -46,24 +43,22 @@ jobs: PR_DATA=$(curl -s "${IS_PR}") PR_OWNER=$(echo "${PR_DATA}" | jq '.head.repo.owner.login') PR_REPO=$(echo "${PR_DATA}" | jq '.head.repo.name') - PR_COMMIT_SHA=$(curl -s "${IS_PR}/commits" | jq -r '.[0].sha') # dispatch request for benchmarks echo "Dispatching request..." curl \ -s \ -X POST https://api.github.com/repos/${DISPATCH_OWNER}/${DISPATCH_REPO}/dispatches \ - -H "Accept: application/vnd.github.everest-preview+json" \ + -H "Accept: application/vnd.github.v3+json" \ -H "Authorization: token ${AUTH_TOKEN}" \ -d \ ' { - "event_type": "'"${EVENT_NAME}"'", + "event_type": "'"${EVENT_NAME} ${PR_OWNER}/${PR_REPO}#${ISSUE_NUMBER}"'", "client_payload": { "pr_id": "'"${ISSUE_NUMBER}"'", "repo": "'"${PR_REPO}"'", - "owner": "'"${PR_OWNER}"'", - "commit_sha": "'"${PR_COMMIT_SHA}"'" + "owner": "'"${PR_OWNER}"'" } }' diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 0920f711c5aff..b9da2ddfdbe22 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -2,9 +2,6 @@ name: Benchmark Suite on: - push: - branches: - - "latest" pull_request: branches: - "**" @@ -21,48 +18,35 @@ jobs: DISPATCH_REPO: "benchmarks" DISPATCH_OWNER: "npm" EVENT_NAME: ${{ github.event_name }} - EVENT_ACTION: ${{ github.event.action }} REPO: ${{ github.event.repository.name }} PR_NUMBER: ${{ github.event.pull_request.number }} PR_OWNER: ${{ github.event.pull_request.head.repo.owner.login }} - PR_COMMITS_URL: ${{ github.event.pull_request.commits_url }} - PR_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} - AUTH_TOKEN: ${{ secrets.NPM_DEPLOY_USER_PAT }} + AUTH_TOKEN: ${{ secrets.NPM_BENCHMARKS_TOKEN }} run: | # Dispatch Handler dispatch_request () { echo "Dispatching request..." - REF_SHA=$1 curl \ -s \ -X POST https://api.github.com/repos/${DISPATCH_OWNER}/${DISPATCH_REPO}/dispatches \ - -H "Accept: application/vnd.github.everest-preview+json" \ + -H "Accept: application/vnd.github.v3+json" \ -H "Authorization: token ${AUTH_TOKEN}" \ -d \ ' { - "event_type": "'"${EVENT_NAME}"'", + "event_type": "'"${EVENT_NAME} ${PR_OWNER}/${REPO}#${PR_NUMBER}"'", "client_payload": { "pr_id": "'"${PR_NUMBER}"'", "repo": "'"${REPO}"'", - "owner": "'"${PR_OWNER}"'", - "commit_sha": "'"${REF_SHA}"'" + "owner": "'"${PR_OWNER}"'" } }' } if [ "${AUTH_TOKEN}" != "" ]; then - if [ "${EVENT_ACTION}" == "opened" ]; then - # Fetch the head commit sha, since it doesn't exist in the body of this event - COMMIT_SHA=$(curl -s "${PR_COMMITS_URL}" | jq -r '.[0].sha') - - # Dispatch request for benchmarks - dispatch_request "${COMMIT_SHA}" - else - # Dispatch request for benchmarks - dispatch_request "${PR_COMMIT_SHA}" - fi + # Dispatch request for benchmarks + dispatch_request else echo "NO AUTH - FORK PULL REQUEST" fi From 71777be17e57179d203cb9162664ecd0c36ca633 Mon Sep 17 00:00:00 2001 From: Luke Karrys Date: Wed, 8 Dec 2021 17:10:46 -0700 Subject: [PATCH 10/13] feat: display `publishConfig` during `config list` Closes: npm/statusboard#417 If the file at `$NPM_CONFIG_PREFIX/package.json` contains a `publishConfig`, the key/value pairs will be printed along with the rest of the output from `npm config ls`. PR-URL: https://github.com/npm/cli/pull/4146 Credit: @lukekarrys Close: #4146 Reviewed-by: @nlf --- lib/commands/config.js | 20 ++++++++- .../test/lib/commands/config.js.test.cjs | 41 +++++++++++++++++++ test/lib/commands/config.js | 20 +++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/lib/commands/config.js b/lib/commands/config.js index 6553a26620cb7..96524e00817f5 100644 --- a/lib/commands/config.js +++ b/lib/commands/config.js @@ -2,7 +2,7 @@ const configDefs = require('../utils/config/index.js') const mkdirp = require('mkdirp-infer-owner') -const { dirname } = require('path') +const { dirname, resolve } = require('path') const { promisify } = require('util') const fs = require('fs') const readFile = promisify(fs.readFile) @@ -11,6 +11,7 @@ const { spawn } = require('child_process') const { EOL } = require('os') const ini = require('ini') const localeCompare = require('@isaacs/string-locale-compare')('en') +const rpj = require('read-package-json-fast') const log = require('../utils/log-shim.js') // take an array of `[key, value, k2=v2, k3, v3, ...]` and turn into @@ -267,6 +268,23 @@ ${defData} `; HOME = ${process.env.HOME}`, '; Run `npm config ls -l` to show all defaults.' ) + msg.push('') + } + + if (!this.npm.config.get('global')) { + const pkgPath = resolve(this.npm.prefix, 'package.json') + const pkg = await rpj(pkgPath).catch(() => ({})) + + if (pkg.publishConfig) { + msg.push(`; "publishConfig" from ${pkgPath}`) + msg.push('; This set of config values will be used at publish-time.', '') + const pkgKeys = Object.keys(pkg.publishConfig).sort(localeCompare) + for (const k of pkgKeys) { + const v = publicVar(k) ? JSON.stringify(pkg.publishConfig[k]) : '(protected)' + msg.push(`${k} = ${v}`) + } + msg.push('') + } } this.npm.output(msg.join('\n').trim()) diff --git a/tap-snapshots/test/lib/commands/config.js.test.cjs b/tap-snapshots/test/lib/commands/config.js.test.cjs index da7a89baede40..8e97915230719 100644 --- a/tap-snapshots/test/lib/commands/config.js.test.cjs +++ b/tap-snapshots/test/lib/commands/config.js.test.cjs @@ -342,3 +342,44 @@ userconfig = "{HOME}/.npmrc" ; HOME = {HOME} ; Run \`npm config ls -l\` to show all defaults. ` + +exports[`test/lib/commands/config.js TAP config list with publishConfig > output matches snapshot 1`] = ` +; "cli" config from command line options + +cache = "{NPMDIR}/test/lib/commands/tap-testdir-config-config-list-with-publishConfig-sandbox/cache" +prefix = "{LOCALPREFIX}" +userconfig = "{HOME}/.npmrc" + +; node bin location = {EXECPATH} +; cwd = {NPMDIR} +; HOME = {HOME} +; Run \`npm config ls -l\` to show all defaults. + +; "publishConfig" from {LOCALPREFIX}/package.json +; This set of config values will be used at publish-time. + +_authToken = (protected) +registry = "https://some.registry" +; "env" config from environment + +; cache = "{NPMDIR}/test/lib/commands/tap-testdir-config-config-list-with-publishConfig-sandbox/cache" ; overridden by cli +global-prefix = "{LOCALPREFIX}" +globalconfig = "{GLOBALPREFIX}/npmrc" +init-module = "{HOME}/.npm-init.js" +local-prefix = "{LOCALPREFIX}" +; prefix = "{LOCALPREFIX}" ; overridden by cli +user-agent = "npm/{NPM-VERSION} node/{NODE-VERSION} {PLATFORM} {ARCH} workspaces/false" +; userconfig = "{HOME}/.npmrc" ; overridden by cli + +; "cli" config from command line options + +cache = "{NPMDIR}/test/lib/commands/tap-testdir-config-config-list-with-publishConfig-sandbox/cache" +global = true +prefix = "{LOCALPREFIX}" +userconfig = "{HOME}/.npmrc" + +; node bin location = {EXECPATH} +; cwd = {NPMDIR} +; HOME = {HOME} +; Run \`npm config ls -l\` to show all defaults. +` diff --git a/test/lib/commands/config.js b/test/lib/commands/config.js index d78e0290a850b..8217131479fe4 100644 --- a/test/lib/commands/config.js +++ b/test/lib/commands/config.js @@ -107,6 +107,26 @@ t.test('config list --json', async t => { t.matchSnapshot(sandbox.output, 'output matches snapshot') }) +t.test('config list with publishConfig', async t => { + const temp = t.testdir({ + project: { + 'package.json': JSON.stringify({ + publishConfig: { + registry: 'https://some.registry', + _authToken: 'mytoken', + }, + }), + }, + }) + const project = join(temp, 'project') + + const sandbox = new Sandbox(t, { project }) + await sandbox.run('config', ['list', '']) + await sandbox.run('config', ['list', '--global']) + + t.matchSnapshot(sandbox.output, 'output matches snapshot') +}) + t.test('config delete no args', async t => { const sandbox = new Sandbox(t) From 6ef69a388a0bc21d7d7f69886f91ad0d883c9e2c Mon Sep 17 00:00:00 2001 From: nlf Date: Thu, 9 Dec 2021 13:03:07 -0800 Subject: [PATCH 11/13] docs: changelog for v8.3.0 --- CHANGELOG.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2d5ddaf2ff4c..6031e6fffb401 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,55 @@ +## v8.3.0 (2021-12-09) + +### Features + +* [`4b0c29a7c`](https://github.com/npm/cli/commit/4b0c29a7c5860410c7b453bec389c54cb21dbde3) + [#4116](https://github.com/npm/cli/issues/4116) + feat: `@npmcli/arborist@4.1.0` + * introduces overrides + ([@nlf](https://github.com/nlf)) +* [`166d9e144`](https://github.com/npm/cli/commit/166d9e144b38087ee5e7d8aaf6ec7d602cf2957c) + [npm/statusboard#416](https://github.com/npm/statusboard/issues/416) + [#4143](https://github.com/npm/cli/issues/4143) + feat: output configured registry during publish + ([@lukekarrys](https://github.com/lukekarrys)) +* [`71777be17`](https://github.com/npm/cli/commit/71777be17e57179d203cb9162664ecd0c36ca633) + [npm/statusboard#417](https://github.com/npm/statusboard/issues/417) + [#4146](https://github.com/npm/cli/issues/4146) + feat: display `publishConfig` during `config list` + ([@lukekarrys](https://github.com/lukekarrys)) + +### Bug Fixes + +* [`08c663931`](https://github.com/npm/cli/commit/08c663931ec1f56d777ffdb38f94926b9eac13ef) + [#4128](https://github.com/npm/cli/issues/4128) + [#4134](https://github.com/npm/cli/issues/4134) + fix: dont warn on error cleaning individual log files + ([@lukekarrys](https://github.com/lukekarrys)) +* [`e605b128c`](https://github.com/npm/cli/commit/e605b128c87620aae843cdbd8f35cc614da3f8a2) + [#4142](https://github.com/npm/cli/issues/4142) + fix: redact all private keys from config output + ([@lukekarrys](https://github.com/lukekarrys)) + +### Documentation + +* [`db1885d7f`](https://github.com/npm/cli/commit/db1885d7fec012f018093c76dec5a9c01a0ca2b0) + [#4092](https://github.com/npm/cli/issues/4092) + chore(docs): document overrides + ([@nlf](https://github.com/nlf)) + +### Dependencies + +* [`e1da1fa4b`](https://github.com/npm/cli/commit/e1da1fa4ba7d95616928d2192b5b9db09b3120bc) + [#4141](https://github.com/npm/cli/issues/4141) + deps: `@npmcli/arborist@4.1.1`: `parse-conflict-json@2.0.1` + * Fixes object property assignment bug in resolving package-locks with + conflicts +* [`1d8bec566`](https://github.com/npm/cli/commit/1d8bec566cb08ff5ff220f53083323fa8c3fb72e) + [#4144](https://github.com/npm/cli/issues/4144) + [#3884](https://github.com/npm/cli/issues/3884) + deps: `minipass@3.1.6` + * fixes some TAR_ENTRY_INVALID and Z_DATA_ERROR errors + ## v8.2.0 (2021-12-02) ### Features From 3f87d016cf3e48e85f83387c5c37a671d36d76db Mon Sep 17 00:00:00 2001 From: nlf Date: Thu, 9 Dec 2021 13:03:21 -0800 Subject: [PATCH 12/13] update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index daf24791e622c..76fc4c91b1a3a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -812,3 +812,4 @@ Yucel Okcu Takuya Fukuju Jan T. Sott yotamselementor <83912471+yotamselementor@users.noreply.github.com> +Felipe Plets From d8aac8448e983692cacb427e03f4688cd1b62e30 Mon Sep 17 00:00:00 2001 From: nlf Date: Thu, 9 Dec 2021 13:03:21 -0800 Subject: [PATCH 13/13] 8.3.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b1824525529b5..b59a94e843134 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "npm", - "version": "8.2.0", + "version": "8.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "npm", - "version": "8.2.0", + "version": "8.3.0", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", diff --git a/package.json b/package.json index ab40290cd082b..636ef21e5fb64 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "8.2.0", + "version": "8.3.0", "name": "npm", "description": "a package manager for JavaScript", "workspaces": [