npm-packlist 11.0.0 → 11.2.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.
Files changed (2) hide show
  1. package/lib/index.js +67 -2
  2. package/package.json +2 -2
package/lib/index.js CHANGED
@@ -8,6 +8,7 @@ const { log } = require('proc-log')
8
8
 
9
9
  // symbols used to represent synthetic rule sets
10
10
  const defaultRules = Symbol('npm-packlist.rules.default')
11
+ const globalRules = Symbol('npm-packlist.rules.global')
11
12
  const strictRules = Symbol('npm-packlist.rules.strict')
12
13
 
13
14
  // There may be others, but :?|<> are handled by node-tar
@@ -84,13 +85,23 @@ class PackWalker extends IgnoreWalker {
84
85
  follow: false,
85
86
  // we path.resolve() here because ignore-walk doesn't do it and we want full paths
86
87
  path: resolve(opts?.path || tree.path).replace(/\\/g, '/'),
87
- ignoreFiles: opts?.ignoreFiles || [
88
+ // globalRules belongs on the root walker only. Child walkers reach it through
89
+ // parent.filterEntry, where package.json rules can still override it; reapplying
90
+ // globalRules at the child level would defeat that override.
91
+ ignoreFiles: opts?.ignoreFiles || (opts?.parent ? [
88
92
  defaultRules,
89
93
  'package.json',
90
94
  '.npmignore',
91
95
  '.gitignore',
92
96
  strictRules,
93
- ],
97
+ ] : [
98
+ defaultRules,
99
+ globalRules,
100
+ 'package.json',
101
+ '.npmignore',
102
+ '.gitignore',
103
+ strictRules,
104
+ ]),
94
105
  }
95
106
 
96
107
  super(options)
@@ -98,6 +109,8 @@ class PackWalker extends IgnoreWalker {
98
109
  this.isPackage = options.isPackage
99
110
  this.seen = options.seen || new Set()
100
111
  this.tree = tree
112
+ this.globalIgnoreFile = options.globalIgnoreFile || null
113
+ this.globalIgnoreRules = options.globalIgnoreRules || null
101
114
 
102
115
  const additionalDefaults = []
103
116
  if (options.prefix && options.workspaces) {
@@ -124,6 +137,31 @@ class PackWalker extends IgnoreWalker {
124
137
  // go ahead and inject the default rules now
125
138
  this.injectRules(defaultRules, [...defaults, ...additionalDefaults])
126
139
 
140
+ // inject the global rules from the user's ignore file. positioned between
141
+ // defaultRules and package.json so an explicit entry in the package "files"
142
+ // field can still override. only the root walker carries globalRules in its
143
+ // ignoreFiles list; child walkers see global exclusions via parent.filterEntry,
144
+ // which is what lets the package.json `!/path/**` override win.
145
+ if (this.globalIgnoreRules === null && this.globalIgnoreFile) {
146
+ try {
147
+ this.globalIgnoreRules = readFile(this.globalIgnoreFile, { encoding: 'utf8' })
148
+ } catch (err) {
149
+ // istanbul ignore next -- only ENOENT is expected here
150
+ if (err.code !== 'ENOENT') {
151
+ throw err
152
+ }
153
+ this.globalIgnoreRules = ''
154
+ }
155
+ }
156
+ if (this.globalIgnoreRules) {
157
+ this.injectRules(globalRules, [this.globalIgnoreRules])
158
+ // istanbul ignore else -- globalIgnoreRules without a filename only happens when
159
+ // a caller pre-populates rules directly instead of going through globalIgnoreFile
160
+ if (this.globalIgnoreFile) {
161
+ log.silly('global-ignore-file', `applied rules from ${this.globalIgnoreFile}`)
162
+ }
163
+ }
164
+
127
165
  if (!this.isPackage) {
128
166
  // if this instance is not a package, then place some strict default rules
129
167
  this.injectRules(strictRules, [...strictDefaults])
@@ -267,6 +305,7 @@ class PackWalker extends IgnoreWalker {
267
305
  browser,
268
306
  files,
269
307
  main,
308
+ patchedDependencies,
270
309
  } = this.tree.package
271
310
 
272
311
  // rules in these arrays are inverted since they are patterns we want to _not_ ignore
@@ -356,6 +395,32 @@ class PackWalker extends IgnoreWalker {
356
395
  }
357
396
  }
358
397
 
398
+ // patch files declared in patchedDependencies are project-local fixes and must never ship.
399
+ // patchedDependencies is root-only state, so this applies only to the package being packed.
400
+ // each declared patch file is force-excluded even when it was added via "files".
401
+ // only the exact files are excluded, never their directory, so a shared dir keeps its other contents.
402
+ if (this.tree.isProjectRoot && patchedDependencies && typeof patchedDependencies === 'object') {
403
+ for (const patchPath of Object.values(patchedDependencies)) {
404
+ if (typeof patchPath !== 'string') {
405
+ continue
406
+ }
407
+ const rel = normalizePath(patchPath).replace(/^\.?\//, '').replace(/\/+$/, '')
408
+ // skip absolute paths or paths that escape the package root, which are never packed anyway
409
+ if (!rel || rel.startsWith('/') || rel === '..' || rel.startsWith('../') || rel.includes('/../')) {
410
+ continue
411
+ }
412
+ // warn when a "files" entry pulled the patch file in, either directly or via its directory
413
+ const parentGlob = `!/${normalizePath(dirname(rel))}/**`
414
+ if (files && (ignores.includes(`!/${rel}`) || ignores.includes(parentGlob))) {
415
+ log.warn(
416
+ 'patched-dependencies',
417
+ `excluding "${rel}" from the package tarball: patch files in patchedDependencies must not be published`
418
+ )
419
+ }
420
+ strict.push(`/${rel}`)
421
+ }
422
+ }
423
+
359
424
  // and now we add all of the strict rules to our synthetic file
360
425
  this.injectRules(strictRules, strict, callback)
361
426
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-packlist",
3
- "version": "11.0.0",
3
+ "version": "11.2.0",
4
4
  "description": "Get a list of the files to add from a folder into an npm package",
5
5
  "directories": {
6
6
  "test": "test"
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "devDependencies": {
21
21
  "@npmcli/arborist": "^9.0.0",
22
- "@npmcli/eslint-config": "^6.0.0",
22
+ "@npmcli/eslint-config": "^7.0.0",
23
23
  "@npmcli/template-oss": "5.1.0",
24
24
  "mutate-fs": "^2.1.1",
25
25
  "tap": "^16.0.1"