Skip to content

Commit 24344a2

Browse files
fix: exclude non-root README.md/LICENSE files (#173)
BREAKING CHANGE: The files array can now be used to exclude non-root readme, license, licence, and copying files. Co-authored-by: rahulio96 <[email protected]>
1 parent 21ed893 commit 24344a2

File tree

3 files changed

+104
-5
lines changed

3 files changed

+104
-5
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ arborist.loadActual().then((tree) => {
3030
This uses the following rules:
3131

3232
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
33+
then ignore everything that isn't in `files`. Always include the root
3434
readme, license, licence and copying files, if they exist, as well
35-
as the package.json file itself.
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"`.
3638
2. If there's no `package.json` file (or it has no `files` list), and
3739
there is a `.npmignore` file, then ignore all the files in the
3840
`.npmignore` file.

lib/index.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,22 @@ const defaults = [
3838
]
3939

4040
const strictDefaults = [
41-
// these are forcibly included at all levels
41+
// these are forcibly excluded
42+
'/.git',
43+
]
44+
45+
const allLevels = [
46+
// these are included by default but can be excluded by package.json files array
4247
'!/readme{,.*[^~$]}',
4348
'!/copying{,.*[^~$]}',
4449
'!/license{,.*[^~$]}',
4550
'!/licence{,.*[^~$]}',
46-
// these are forcibly excluded
47-
'/.git',
51+
]
52+
53+
const rootOnly = [
54+
/^!.*readme/i,
55+
/^!.*copying/i,
56+
/^!.*licen[sc]e/i,
4857
]
4958

5059
const normalizePath = (path) => path.split('\\').join('/')
@@ -132,6 +141,7 @@ class PackWalker extends IgnoreWalker {
132141
// known required files for this directory
133142
this.injectRules(strictRules, [
134143
...strictDefaults,
144+
...allLevels,
135145
...this.requiredFiles.map((file) => `!${file}`),
136146
])
137147
}
@@ -284,6 +294,7 @@ class PackWalker extends IgnoreWalker {
284294
const ignores = []
285295
const strict = [
286296
...strictDefaults,
297+
...allLevels,
287298
'!/package.json',
288299
'/.git',
289300
'/node_modules',
@@ -304,6 +315,9 @@ class PackWalker extends IgnoreWalker {
304315
file = file.slice(0, -2)
305316
}
306317
const inverse = `!${file}`
318+
319+
this.excludeNonRoot(file)
320+
307321
try {
308322
// if an entry in the files array is a specific file, then we need to include it as a
309323
// strict requirement for this package. if it's a directory or a pattern, it's a default
@@ -352,6 +366,20 @@ class PackWalker extends IgnoreWalker {
352366
this.injectRules(strictRules, strict, callback)
353367
}
354368

369+
// excludes non root files by checking if elements from the files array in
370+
// package.json contain an ! and readme/license/licence/copying, and then
371+
// removing readme/license/licence/copying accordingly from strict defaults
372+
excludeNonRoot (file) {
373+
// Find the pattern
374+
const matchingPattern = rootOnly.find(regex => regex.test(file))
375+
376+
if (matchingPattern) {
377+
// Find which index matches the pattern and remove it from allLevels
378+
const indexToRemove = allLevels.findIndex(element => matchingPattern.test(element))
379+
allLevels.splice(indexToRemove, 1)
380+
}
381+
}
382+
355383
// custom method: after we've finished gathering the files for the root package, we call this
356384
// before emitting the 'done' event in order to gather all of the files for bundled deps
357385
async gatherBundles () {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// exclude readme, license, and licnce files if package.json
2+
// files array includes !readme, !license, or !licence
3+
'use strict'
4+
5+
const Arborist = require('@npmcli/arborist')
6+
const t = require('tap')
7+
const packlist = require('../')
8+
9+
const pkg = t.testdir({
10+
'package.json': JSON.stringify({
11+
files: [
12+
'**/*.js',
13+
'!readme.md',
14+
'!licence',
15+
'!license',
16+
'!copying',
17+
],
18+
19+
}),
20+
'readme.md': 'one',
21+
licence: 'two',
22+
license: 'tre',
23+
copying: 'for',
24+
lib: {
25+
'readme.md': 'one',
26+
licence: 'two',
27+
license: 'tre',
28+
copying: 'for',
29+
a: {
30+
'readme.md': 'one',
31+
licence: 'two',
32+
license: 'tre',
33+
copying: 'for',
34+
b: {
35+
'readme.md': 'one',
36+
licence: 'two',
37+
license: 'tre',
38+
copying: 'for',
39+
c: {
40+
'readme.md': 'one',
41+
licence: 'two',
42+
license: 'tre',
43+
copying: 'for',
44+
'file.txt': 'one',
45+
'c.js': 'two',
46+
},
47+
'file.txt': 'one',
48+
'b.js': 'two',
49+
},
50+
'file.txt': 'one',
51+
'a.js': 'two',
52+
},
53+
} })
54+
55+
t.test('package with negated readme, licence and license files', async (t) => {
56+
const arborist = new Arborist({ path: pkg })
57+
const tree = await arborist.loadActual()
58+
const files = await packlist(tree)
59+
t.same(files, [
60+
'copying',
61+
'licence',
62+
'license',
63+
'lib/a/a.js',
64+
'lib/a/b/b.js',
65+
'lib/a/b/c/c.js',
66+
'package.json',
67+
'readme.md',
68+
])
69+
})

0 commit comments

Comments
 (0)