pacote 21.5.0 → 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/dir.js CHANGED
@@ -1,8 +1,11 @@
1
- const { resolve } = require('node:path')
1
+ const { resolve, join } = require('node:path')
2
+ const { mkdtemp, writeFile, rm } = require('node:fs/promises')
3
+ const { tmpdir } = require('node:os')
2
4
  const packlist = require('npm-packlist')
3
5
  const runScript = require('@npmcli/run-script')
4
6
  const tar = require('tar')
5
7
  const { Minipass } = require('minipass')
8
+ const PackageJson = require('@npmcli/package-json')
6
9
  const Fetcher = require('./fetcher.js')
7
10
  const FileFetcher = require('./file.js')
8
11
  const _ = require('./util/protected.js')
@@ -66,7 +69,7 @@ class DirFetcher extends Fetcher {
66
69
  stream.resolved = this.resolved
67
70
  stream.integrity = this.integrity
68
71
 
69
- const { prefix, workspaces } = this.opts
72
+ const { prefix, workspaces, globalIgnoreFile } = this.opts
70
73
 
71
74
  // run the prepare script, get the list of files, and tar it up
72
75
  // pipe to the stream, and proxy errors the chain.
@@ -76,14 +79,66 @@ class DirFetcher extends Fetcher {
76
79
  const arb = new this.Arborist({ path: this.resolved })
77
80
  this.tree = await arb.loadActual()
78
81
  }
79
- return packlist(this.tree, { path: this.resolved, prefix, workspaces })
82
+ return packlist(this.tree, { path: this.resolved, prefix, workspaces, globalIgnoreFile })
83
+ })
84
+ .then(async files => {
85
+ const { options, cleanup } = await this.#tarOptions()
86
+ const source = tar.c(options, files)
87
+ // the strip temp file must outlive content consumption, so clean up once the stream is done
88
+ source.once('end', cleanup)
89
+ source.once('error', cleanup)
90
+ return source.on('error', er => stream.emit('error', er)).pipe(stream)
80
91
  })
81
- .then(files => tar.c(tarCreateOptions(this.package), files)
82
- .on('error', er => stream.emit('error', er)).pipe(stream))
83
92
  .catch(er => stream.emit('error', er))
84
93
  return stream
85
94
  }
86
95
 
96
+ // Build the tar create options.
97
+ // When the packed package.json declares patchedDependencies, redirect it to a stripped copy so project-local patches never ship.
98
+ // Non-patched packs are unchanged.
99
+ async #tarOptions () {
100
+ const options = tarCreateOptions(this.package)
101
+
102
+ // read package.json from disk after prepare so the strip reflects the actually-packed manifest.
103
+ const pkgJson = await PackageJson.load(this.resolved)
104
+ if (!('patchedDependencies' in pkgJson.content)) {
105
+ return { options, cleanup: () => {} }
106
+ }
107
+
108
+ // serialize the package.json minus patchedDependencies, preserving its indent and newline.
109
+ // JSON.stringify ignores the indent and newline symbols @npmcli/package-json attaches to content.
110
+ delete pkgJson.content.patchedDependencies
111
+ const { content } = pkgJson
112
+ const indent = content[Symbol.for('indent')]
113
+ const newline = content[Symbol.for('newline')]
114
+ const stripped = `${JSON.stringify(content, null, indent)}\n`.replace(/\n/g, newline)
115
+
116
+ // write the stripped copy to a temp dir, removing it if the write itself fails.
117
+ const dir = await mkdtemp(join(tmpdir(), 'pacote-pack-'))
118
+ const strippedPath = join(dir, 'package.json')
119
+ try {
120
+ await writeFile(strippedPath, stripped)
121
+ } catch (er) {
122
+ /* istanbul ignore next - writing to a freshly created temp dir is not deterministically failable */
123
+ await rm(dir, { recursive: true, force: true })
124
+ /* istanbul ignore next */
125
+ throw er
126
+ }
127
+ const size = Buffer.byteLength(stripped)
128
+
129
+ // point only the top-level package.json entry at the stripped copy; every other file is untouched.
130
+ // onWriteEntry runs before the tar header and the file's hardlink check, so size and nlink here are honored.
131
+ options.onWriteEntry = (entry) => {
132
+ if (entry.path === 'package.json') {
133
+ entry.absolute = strippedPath
134
+ entry.stat.size = size
135
+ entry.stat.nlink = 1
136
+ }
137
+ }
138
+
139
+ return { options, cleanup: () => rm(dir, { recursive: true, force: true }) }
140
+ }
141
+
87
142
  manifest () {
88
143
  if (this.package) {
89
144
  return Promise.resolve(this.package)
package/lib/fetcher.js CHANGED
@@ -118,6 +118,10 @@ class FetcherBase {
118
118
  // we need the actual things, not just the lockfile
119
119
  '--no-package-lock-only',
120
120
  '--no-dry-run',
121
+ // override npm_config_global from the parent process: this inner
122
+ // `npm install` is preparing deps inside a tmp git clone, and it
123
+ // must reify into that clone's cwd, never the outer global prefix.
124
+ '--global=false',
121
125
  ]
122
126
  }
123
127
 
package/lib/git.js CHANGED
@@ -19,7 +19,7 @@ const hashre = /^[a-f0-9]{40,64}$/
19
19
  // otherwise, prefer ssh if available (more secure).
20
20
  // We have to add the git+ back because npa suppresses it.
21
21
  const repoUrl = (h, opts) =>
22
- h.sshurl && !(h.https && h.auth) && addGitPlus(h.sshurl(opts)) ||
22
+ h.sshurl && !(h.https && (h.auth || h.default === 'https')) && addGitPlus(h.sshurl(opts)) ||
23
23
  h.https && addGitPlus(h.https(opts))
24
24
 
25
25
  // add git+ to the url, but only one time.
@@ -171,6 +171,11 @@ class GitFetcher extends Fetcher {
171
171
  return
172
172
  }
173
173
 
174
+ // honor ignoreScripts: spawning `npm install` here would run lifecycle scripts (install, preinstall, postinstall, prepare) from the cloned repo, defeating the caller's explicit opt-out.
175
+ if (this.opts.ignoreScripts) {
176
+ return
177
+ }
178
+
174
179
  // to avoid cases where we have an cycle of git deps that depend
175
180
  // on one another, we only ever do preparation for one instance
176
181
  // of a given git dep along the chain of installations.
@@ -254,8 +259,11 @@ class GitFetcher extends Fetcher {
254
259
  resolved: this.resolved,
255
260
  integrity: null, // it'll always be different, if we have one
256
261
  }).extract(tmp).then(() => handler(`${tmp}${this.spec.gitSubdir || ''}`), er => {
257
- // fall back to ssh download if tarball fails
258
- if (er.constructor.name.match(/^Http/)) {
262
+ // fall back to clone if the tarball download fails due to an
263
+ // HTTP error or if the response is not a valid tarball (e.g.
264
+ // a hosted provider returning an HTML sign-in page with 200)
265
+ if ((typeof er.statusCode === 'number' && er.statusCode >= 400) ||
266
+ /^TAR_/.test(er.code)) {
259
267
  return this.#clone(handler, false)
260
268
  } else {
261
269
  throw er
@@ -3,12 +3,15 @@ const addGitSha = (spec, sha) => {
3
3
  if (spec.hosted) {
4
4
  const h = spec.hosted
5
5
  const opt = { noCommittish: true }
6
- const base = h.https && h.auth ? h.https(opt) : h.shortcut(opt)
6
+ const base = h.https && (h.auth || h.default === 'https') ? h.https(opt) : h.shortcut(opt)
7
7
 
8
8
  return `${base}#${sha}`
9
9
  } else {
10
10
  // don't use new URL for this, because it doesn't handle scp urls
11
- return spec.rawSpec.replace(/#.*$/, '') + `#${sha}`
11
+ // strip the committish with indexOf/slice to avoid a regexp redos
12
+ const hashIndex = spec.rawSpec.indexOf('#')
13
+ const base = hashIndex === -1 ? spec.rawSpec : spec.rawSpec.slice(0, hashIndex)
14
+ return `${base}#${sha}`
12
15
  }
13
16
  }
14
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pacote",
3
- "version": "21.5.0",
3
+ "version": "22.0.0",
4
4
  "description": "JavaScript package downloader",
5
5
  "author": "GitHub Inc.",
6
6
  "bin": {
@@ -27,9 +27,9 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@npmcli/arborist": "^9.0.2",
30
- "@npmcli/eslint-config": "^6.0.0",
31
- "@npmcli/template-oss": "4.29.0",
32
- "hosted-git-info": "^9.0.0",
30
+ "@npmcli/eslint-config": "^7.0.0",
31
+ "@npmcli/template-oss": "5.1.0",
32
+ "hosted-git-info": "^10.1.1",
33
33
  "mutate-fs": "^2.1.1",
34
34
  "nock": "^13.2.4",
35
35
  "npm-registry-mock": "^1.3.2",
@@ -47,25 +47,25 @@
47
47
  ],
48
48
  "dependencies": {
49
49
  "@gar/promise-retry": "^1.0.0",
50
- "@npmcli/git": "^7.0.0",
51
- "@npmcli/installed-package-contents": "^4.0.0",
52
- "@npmcli/package-json": "^7.0.0",
53
- "@npmcli/promise-spawn": "^9.0.0",
54
- "@npmcli/run-script": "^10.0.0",
55
- "cacache": "^20.0.0",
50
+ "@npmcli/git": "^8.0.0",
51
+ "@npmcli/installed-package-contents": "^5.0.0",
52
+ "@npmcli/package-json": "^8.0.0",
53
+ "@npmcli/promise-spawn": "^10.0.0",
54
+ "@npmcli/run-script": "^11.0.0",
55
+ "cacache": "^21.0.1",
56
56
  "fs-minipass": "^3.0.0",
57
57
  "minipass": "^7.0.2",
58
- "npm-package-arg": "^13.0.0",
59
- "npm-packlist": "^10.0.1",
60
- "npm-pick-manifest": "^11.0.1",
61
- "npm-registry-fetch": "^19.0.0",
62
- "proc-log": "^6.0.0",
63
- "sigstore": "^4.0.0",
64
- "ssri": "^13.0.0",
58
+ "npm-package-arg": "^14.0.0",
59
+ "npm-packlist": "^11.2.0",
60
+ "npm-pick-manifest": "^12.0.0",
61
+ "npm-registry-fetch": "^20.0.1",
62
+ "proc-log": "^7.0.0",
63
+ "sigstore": "^5.0.0",
64
+ "ssri": "^14.0.0",
65
65
  "tar": "^7.4.3"
66
66
  },
67
67
  "engines": {
68
- "node": "^20.17.0 || >=22.9.0"
68
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
69
69
  },
70
70
  "repository": {
71
71
  "type": "git",
@@ -73,8 +73,9 @@
73
73
  },
74
74
  "templateOSS": {
75
75
  "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
76
- "version": "4.29.0",
76
+ "version": "5.1.0",
77
77
  "windowsCI": false,
78
- "publish": "true"
78
+ "publish": "true",
79
+ "updateNpm": false
79
80
  }
80
81
  }