npm-packlist 10.0.4 → 11.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.
Files changed (3) hide show
  1. package/README.md +45 -30
  2. package/lib/index.js +46 -45
  3. package/package.json +9 -7
package/README.md CHANGED
@@ -30,11 +30,9 @@ arborist.loadActual().then((tree) => {
30
30
  This uses the following rules:
31
31
 
32
32
  1. If a `package.json` file is found, and it has a `files` list,
33
- then ignore everything that isn't in `files`. Always include the root
34
- readme, license, licence and copying files, if they exist, as well
35
- as the package.json file itself. Non-root readme, license, licence and
36
- copying files are included by default, but can be excluded using the
37
- `files` list e.g. `"!readme"`.
33
+ then ignore everything that isn't matched by `files`. Always include the
34
+ root readme, license, licence and copying files, if they exist, as well
35
+ as the package.json file itself.
38
36
  2. If there's no `package.json` file (or it has no `files` list), and
39
37
  there is a `.npmignore` file, then ignore all the files in the
40
38
  `.npmignore` file.
@@ -45,7 +43,7 @@ This uses the following rules:
45
43
  bundled dependency. If it IS a bundled dependency, and it's a
46
44
  symbolic link, then the target of the link is included, not the
47
45
  symlink itself.
48
- 4. Unless they're explicitly included (by being in a `files` list, or
46
+ 5. Unless they're explicitly included (by being in a `files` list, or
49
47
  a `!negated` rule in a relevant `.npmignore` or `.gitignore`),
50
48
  always ignore certain common cruft files:
51
49
 
@@ -58,48 +56,65 @@ This uses the following rules:
58
56
  6. Darwin's `.DS_Store` files because wtf are those even
59
57
  7. `npm-debug.log` files at the root of a project
60
58
 
61
- You can explicitly re-include any of these with a `files` list in
62
- `package.json` or a negated ignore file rule.
59
+ You can explicitly re-include most of these with a negated ignore file rule.
60
+ A small set of files are always ignored regardless of `files` rules:
61
+ `.git`, `.npmrc`, and `node_modules`.
63
62
 
64
63
  Only the `package.json` file in the very root of the project is ever
65
64
  inspected for a `files` list. Below the top level of the root package,
66
65
  `package.json` is treated as just another file, and no package-specific
67
66
  semantics are applied.
68
67
 
69
- ### Interaction between `package.json` and `.npmignore` rules
68
+ ### Interpretation of the `files` list
70
69
 
71
- In previous versions of this library, the `files` list in `package.json`
72
- was used as an initial filter to drive further tree walking. That is no
73
- longer the case as of version 6.0.0.
70
+ Each entry in the `package.json` `files` list is interpreted as a glob
71
+ pattern, expanded against the package root using node's built-in
72
+ [`fs.globSync`](https://nodejs.org/api/fs.html#fsglobsyncpattern-options).
73
+ Literal paths (`lib`, `src/index.js`) are valid globs and resolve to
74
+ themselves; pattern entries (`dist-*`, `**/*.js`) resolve to every
75
+ matching entry on disk; entries that match nothing are silently dropped.
74
76
 
75
- If you have a `package.json` file with a `files` array within, any top
76
- level `.npmignore` and `.gitignore` files *will be ignored*.
77
+ When an entry matches a directory, the directory and all of its contents
78
+ are included. This is a documented convenience: writing
79
+ `"files": ["dist"]` does not require also writing `"dist/**"`.
77
80
 
78
- If a _directory_ is listed in `files`, then any rules in nested `.npmignore` files within that directory will be honored.
81
+ A leading `!` makes the entry a negation, removing matches from the
82
+ result. Negations are evaluated in order, so a later positive entry
83
+ can re-include something a previous negation excluded.
79
84
 
80
- For example, with this package.json:
85
+ #### Pattern semantics are anchored to the package root
81
86
 
82
- ```json
83
- {
84
- "files": [ "dir" ]
85
- }
86
- ```
87
+ Glob patterns are evaluated as paths relative to the package root, not as
88
+ basename-anywhere matchers. In particular:
87
89
 
88
- a `.npmignore` file at `dir/.npmignore` (and any subsequent
89
- sub-directories) will be honored. However, a `.npmignore` at the root
90
- level will be skipped.
90
+ * `"*.js"` matches `.js` files at the package root, not at every depth.
91
+ Use `"**/*.js"` to match at every depth.
92
+ * `"!readme.md"` removes only the root `readme.md`. Use
93
+ `"!**/readme.md"` to remove `readme.md` at every depth.
91
94
 
92
- Additionally, with this package.json:
95
+ Negations only exclude paths that exist on disk at packlist time: a `!path`
96
+ entry that matches nothing is silently dropped, same as a positive entry
97
+ that matches nothing. A typo'd negation has no effect.
93
98
 
94
- ```
99
+ #### Interaction with `.npmignore`
100
+
101
+ If a `package.json` `files` array is present, any **top-level**
102
+ `.npmignore` and `.gitignore` files are ignored: the `files` array is
103
+ the sole source of inclusion at the package root.
104
+
105
+ `.npmignore` files in subdirectories are still honored within their
106
+ subtree. For example, with this package.json:
107
+
108
+ ```json
95
109
  {
96
- "files": ["dir/subdir"]
110
+ "files": [ "dir" ]
97
111
  }
98
112
  ```
99
113
 
100
- a `.npmignore` file at `dir/.npmignore` will be honored, as well as `dir/subdir/.npmignore`.
101
-
102
- Any specific file matched by an exact filename in the package.json `files` list will be included, and cannot be excluded, by any `.npmignore` files.
114
+ a `.npmignore` at `dir/.npmignore` (and any nested subdirectory) will
115
+ be applied. This means a file listed both in `files` and excluded by a
116
+ nested `.npmignore` will be excluded from the tarball; if you need such
117
+ a file to ship, remove the `.npmignore` rule.
103
118
 
104
119
  ## API
105
120
 
package/lib/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  'use strict'
2
2
 
3
3
  const { Walker: IgnoreWalker } = require('ignore-walk')
4
- const { lstatSync: lstat, readFileSync: readFile } = require('fs')
5
- const { basename, dirname, extname, join, relative, resolve, sep } = require('path')
4
+ const { globSync } = require('glob')
5
+ const { readFileSync: readFile } = require('node:fs')
6
+ const { basename, dirname, extname, join, relative, resolve, sep } = require('node:path')
6
7
  const { log } = require('proc-log')
7
8
 
8
9
  // symbols used to represent synthetic rule sets
@@ -97,7 +98,6 @@ class PackWalker extends IgnoreWalker {
97
98
  this.isPackage = options.isPackage
98
99
  this.seen = options.seen || new Set()
99
100
  this.tree = tree
100
- this.requiredFiles = options.requiredFiles || []
101
101
 
102
102
  const additionalDefaults = []
103
103
  if (options.prefix && options.workspaces) {
@@ -125,12 +125,8 @@ class PackWalker extends IgnoreWalker {
125
125
  this.injectRules(defaultRules, [...defaults, ...additionalDefaults])
126
126
 
127
127
  if (!this.isPackage) {
128
- // if this instance is not a package, then place some strict default rules, and append
129
- // known required files for this directory
130
- this.injectRules(strictRules, [
131
- ...strictDefaults,
132
- ...this.requiredFiles.map((file) => `!${file}`),
133
- ])
128
+ // if this instance is not a package, then place some strict default rules
129
+ this.injectRules(strictRules, [...strictDefaults])
134
130
  }
135
131
  }
136
132
 
@@ -234,15 +230,6 @@ class PackWalker extends IgnoreWalker {
234
230
  return {
235
231
  ...super.walkerOpt(entry, opts),
236
232
  ignoreFiles,
237
- // we map over our own requiredFiles and pass ones that are within this entry
238
- requiredFiles: this.requiredFiles
239
- .map((file) => {
240
- if (relative(file, entry) === '..') {
241
- return relative(entry, file).replace(/\\/g, '/')
242
- }
243
- return false
244
- })
245
- .filter(Boolean),
246
233
  }
247
234
  }
248
235
 
@@ -295,46 +282,60 @@ class PackWalker extends IgnoreWalker {
295
282
  '/node_modules',
296
283
  '.npmrc',
297
284
  '/package-lock.json',
285
+ '/npm-shrinkwrap.json',
298
286
  '/yarn.lock',
299
287
  '/pnpm-lock.yaml',
300
288
  '/bun.lockb',
289
+ '/bun.lock',
301
290
  ]
302
291
 
303
292
  // if we have a files array in our package, we need to pull rules from it
304
293
  if (files) {
305
- for (let file of files) {
306
- // invert the rule because these are things we want to include
307
- if (file.startsWith('./')) {
308
- file = file.slice(1)
309
- }
310
- if (file.endsWith('/*')) {
311
- file += '*'
294
+ for (const entry of files) {
295
+ const isNegation = entry.startsWith('!')
296
+ // normalize: strip leading `!`s, `./`, a single leading `/`, and trailing slashes.
297
+ // trailing slashes confuse glob; the leading forms are equivalent to their stripped versions for glob expansion.
298
+ const pattern = (isNegation ? entry.replace(/^!+/, '') : entry)
299
+ .replace(/^\.?\//, '')
300
+ .replace(/\/+$/, '')
301
+ if (!pattern) {
302
+ continue
312
303
  }
313
- const inverse = `!${file}`
304
+
305
+ // expand the entry as a glob against the package root.
306
+ // literal paths (e.g. `lib`, `lib/foo.js`) are valid globs and resolve to themselves;
307
+ // patterns (e.g. `dist-*`, `**/*.js`) resolve to all matching entries;
308
+ // typos and non-existent entries resolve to nothing.
309
+ let matches = []
314
310
  try {
315
- // if an entry in the files array is a specific file, then we need to include it as a
316
- // strict requirement for this package. if it's a directory or a pattern, it's a default
317
- // pattern instead. this is ugly, but we have to stat to find out if it's a file
318
- const stat = lstat(join(this.path, file.replace(/^!+/, '')).replace(/\\/g, '/'))
319
- // if we have a file and we know that, it's strictly required
320
- if (stat.isFile()) {
321
- strict.unshift(inverse)
322
- this.requiredFiles.push(file.startsWith('/') ? file.slice(1) : file)
323
- } else if (stat.isDirectory()) {
324
- // otherwise, it's a default ignore, and since we got here we know it's not a pattern
325
- // so we include the directory contents
326
- ignores.push(inverse)
327
- ignores.push(`${inverse}/**`)
311
+ matches = globSync(pattern, { cwd: this.path, withFileTypes: true, dot: true })
312
+ } catch {
313
+ // unparseable globs produce no matches and drop silently.
314
+ // (Pre-v11 instead fell through to push the raw entry as an ignore-walk pattern rule; we deliberately don't preserve that.)
315
+ }
316
+
317
+ // a positive entry produces `!path` rules (un-ignore from the surrounding `*` deny-all); a negation produces `path` rules (re-ignore them).
318
+ // directory matches expand to also include their contents; this is documented `files[]` behavior, not a glob hack.
319
+ const prefix = isNegation ? '' : '!'
320
+ for (const match of matches) {
321
+ const rel = match.relativePosix()
322
+ // istanbul ignore next -- defensive: glob with withFileTypes returns the cwd as an entry with an empty relative path; skip it.
323
+ if (!rel) {
324
+ continue
325
+ }
326
+ if (match.isDirectory()) {
327
+ ignores.push(`${prefix}/${rel}`)
328
+ ignores.push(`${prefix}/${rel}/**`)
329
+ continue
330
+ }
331
+ // istanbul ignore else -- non-file-non-dir matches (sockets, fifos, broken symlinks) drop silently.
332
+ if (match.isFile()) {
333
+ ignores.push(`${prefix}/${rel}`)
328
334
  }
329
- // if the thing exists, but is neither a file or a directory, we don't want it at all
330
- } catch (err) {
331
- // if lstat throws, then we assume we're looking at a pattern and treat it as a default
332
- ignores.push(inverse)
333
335
  }
334
336
  }
335
337
 
336
- // we prepend a '*' to exclude everything, followed by our inverted file rules
337
- // which now mean to include those
338
+ // we prepend a '*' to exclude everything, followed by our inverted file rules which now mean to include those
338
339
  this.injectRules('package.json', ['*', ...ignores])
339
340
  }
340
341
 
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "npm-packlist",
3
- "version": "10.0.4",
3
+ "version": "11.0.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"
7
7
  },
8
8
  "main": "lib/index.js",
9
9
  "dependencies": {
10
- "ignore-walk": "^8.0.0",
11
- "proc-log": "^6.0.0"
10
+ "glob": "^13.0.6",
11
+ "ignore-walk": "^9.0.0",
12
+ "proc-log": "^7.0.0"
12
13
  },
13
14
  "author": "GitHub Inc.",
14
15
  "license": "ISC",
@@ -19,7 +20,7 @@
19
20
  "devDependencies": {
20
21
  "@npmcli/arborist": "^9.0.0",
21
22
  "@npmcli/eslint-config": "^6.0.0",
22
- "@npmcli/template-oss": "4.29.0",
23
+ "@npmcli/template-oss": "5.1.0",
23
24
  "mutate-fs": "^2.1.1",
24
25
  "tap": "^16.0.1"
25
26
  },
@@ -52,11 +53,12 @@
52
53
  ]
53
54
  },
54
55
  "engines": {
55
- "node": "^20.17.0 || >=22.9.0"
56
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
56
57
  },
57
58
  "templateOSS": {
58
59
  "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
59
- "version": "4.29.0",
60
- "publish": true
60
+ "version": "5.1.0",
61
+ "publish": true,
62
+ "updateNpm": false
61
63
  }
62
64
  }