@npmcli/template-oss 2.9.2 → 3.1.1
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/README.md +77 -68
- package/bin/apply.js +22 -0
- package/bin/check.js +26 -0
- package/lib/apply/apply-files.js +31 -0
- package/lib/apply/index.js +5 -0
- package/lib/check/check-apply.js +73 -0
- package/lib/check/check-changelog.js +31 -0
- package/lib/check/check-gitignore.js +67 -0
- package/lib/check/check-required.js +55 -0
- package/lib/check/check-unwanted.js +23 -0
- package/lib/check/index.js +9 -0
- package/lib/config.js +151 -40
- package/lib/content/CODEOWNERS +1 -1
- package/lib/content/LICENSE.md +0 -2
- package/lib/content/SECURITY.md +0 -2
- package/lib/content/audit.yml +5 -12
- package/lib/content/bug.yml +45 -46
- package/lib/content/ci.yml +35 -38
- package/lib/content/codeql-analysis.yml +11 -9
- package/lib/content/commitlintrc.js +1 -4
- package/lib/content/config.yml +0 -2
- package/lib/content/dependabot.yml +13 -14
- package/lib/content/eslintrc.js +0 -2
- package/lib/content/gitignore +8 -14
- package/lib/content/index.js +92 -0
- package/lib/content/npmrc +0 -2
- package/lib/content/pkg.json +27 -0
- package/lib/content/post-dependabot.yml +12 -15
- package/lib/content/pull-request.yml +11 -13
- package/lib/content/release-please.yml +18 -11
- package/lib/content/setup-git.yml +11 -0
- package/lib/content/setup-node.yml +25 -0
- package/lib/index.js +100 -0
- package/lib/util/files.js +43 -0
- package/lib/util/get-git-url.js +24 -0
- package/lib/util/has-package.js +83 -0
- package/lib/util/json-diff.js +33 -0
- package/lib/util/output.js +35 -0
- package/lib/util/parse-ci-versions.js +78 -0
- package/lib/util/parser.js +280 -0
- package/lib/util/template.js +41 -0
- package/package.json +27 -25
- package/bin/.gitattributes +0 -3
- package/bin/npm-template-check.js +0 -44
- package/bin/postinstall.js +0 -31
- package/lib/content/ci-no-windows.yml +0 -48
- package/lib/content/ci-workspace.yml +0 -63
- package/lib/content/release-please-workspace.yml +0 -29
- package/lib/postinstall/copy-content.js +0 -133
- package/lib/postinstall/update-package.js +0 -100
- package/lib/postlint/check-gitignore.js +0 -59
- package/lib/postlint/check-package.js +0 -90
package/README.md
CHANGED
|
@@ -7,106 +7,115 @@ single devDependency.
|
|
|
7
7
|
|
|
8
8
|
### Configuration
|
|
9
9
|
|
|
10
|
-
Configure the use of
|
|
10
|
+
Configure the use of `@npmcli/template-oss` in your `package.json` using the
|
|
11
|
+
`templateOSS` property.
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
```js
|
|
13
15
|
{
|
|
14
16
|
name: 'my-package',
|
|
15
|
-
// ...
|
|
16
17
|
templateOSS: {
|
|
17
18
|
// copy repo specific files for the root pkg
|
|
18
|
-
|
|
19
|
+
rootRepo: true,
|
|
19
20
|
// modify package.json and copy module specific files for the root pkg
|
|
20
|
-
|
|
21
|
-
// copy repo files for
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
|
|
21
|
+
rootModule: true,
|
|
22
|
+
// copy repo files for all workspaces
|
|
23
|
+
workspaceRepo: true,
|
|
24
|
+
// copy module files for all workspaces
|
|
25
|
+
workspaceModule: true,
|
|
26
|
+
// filter allowed workspaces by package name
|
|
27
|
+
// defaults to all workspaces
|
|
25
28
|
workspaces: ['workspace-package-name'],
|
|
26
|
-
|
|
29
|
+
// The rest of the config is passed in as variables
|
|
30
|
+
// that can be used to template files in the content
|
|
31
|
+
// directory. Some common ones are:
|
|
32
|
+
// Turns off ci in windows
|
|
33
|
+
windowsCI: false,
|
|
34
|
+
// Change the versions tested in CI and engines
|
|
35
|
+
ciVersions: ['10', '12', '14']
|
|
27
36
|
}
|
|
28
37
|
}
|
|
38
|
+
```
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
#### Workspaces
|
|
31
41
|
|
|
32
|
-
|
|
42
|
+
Individual workspaces can also supply their own config, if they are included by
|
|
43
|
+
the root package's `templateOSS.workspaces` array. These settings will override
|
|
44
|
+
any of the same settings in the root.
|
|
33
45
|
|
|
34
46
|
```js
|
|
35
47
|
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
preversion: 'npm test',
|
|
46
|
-
postversion: 'npm publish',
|
|
47
|
-
prepublishOnly: 'git push origin --follow-tags',
|
|
48
|
-
snap: 'tap',
|
|
49
|
-
test: 'tap',
|
|
50
|
-
posttest: 'npm run lint',
|
|
51
|
-
},
|
|
52
|
-
engines: {
|
|
53
|
-
node: '^12.13.0 || ^14.15.0 || >=16',
|
|
54
|
-
},
|
|
48
|
+
name: 'my-workspace',
|
|
49
|
+
templateOSS: {
|
|
50
|
+
// copy repo files for this workspace
|
|
51
|
+
workspaceRepo: true,
|
|
52
|
+
// copy module files for this workspace
|
|
53
|
+
moduleRepo: true,
|
|
54
|
+
// Changes windowsCI setting for this workspace
|
|
55
|
+
windowsCI: false,
|
|
56
|
+
}
|
|
55
57
|
}
|
|
56
58
|
```
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
### Content
|
|
61
|
+
|
|
62
|
+
All the templated content for this repo lives in
|
|
63
|
+
[`lib/content/`](./lib/content/). The `index.js`[./lib/content/index.js] file
|
|
64
|
+
controls how and where this content is written.
|
|
65
|
+
|
|
66
|
+
Content files can be overwritten or merged with the existing target file.
|
|
67
|
+
Currently mergining is only supported for `package.json` files.
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
Each content file goes through the following pipeline:
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
1. It is read from its source location
|
|
72
|
+
1. It is are templated using Handlebars with the variables from each packages's
|
|
73
|
+
config (with some derived values generated in [`config.js`](./lib/config.js)
|
|
74
|
+
1. It is parsed based on its file extension in
|
|
75
|
+
[`parser.js`](./lib/util/parser.js)
|
|
76
|
+
1. Additional logic is applied by the parser
|
|
77
|
+
1. It is written to its target location
|
|
67
78
|
|
|
68
|
-
###
|
|
79
|
+
### Usage
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
pattern `.eslintrc.local.*`
|
|
81
|
+
This package provides two bin scripts:
|
|
72
82
|
|
|
73
|
-
|
|
83
|
+
#### `template-oss-check`
|
|
74
84
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
- `.github/ISSUE_TEMPLATE/config.yml`
|
|
79
|
-
- `.github/CODEOWNERS`
|
|
80
|
-
- `.gitignore`
|
|
81
|
-
- `LICENSE.md`
|
|
82
|
-
- `SECURITY.md`
|
|
85
|
+
This will check if any of the applied files different from the target content,
|
|
86
|
+
or if any of the other associated checks fail. The diffs of each file or check
|
|
87
|
+
will be reported with instructions on how to fix it.
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
#### `template-oss-apply [--force]`
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
This will write all source files to their target locations in the cwd. It will
|
|
92
|
+
do nothing if `package.json#templateOSS.version` is the same as the version
|
|
93
|
+
being run. This can be overridden by `--force`.
|
|
88
94
|
|
|
89
|
-
|
|
95
|
+
This is the script that is run on `postinsall`.
|
|
90
96
|
|
|
91
|
-
|
|
92
|
-
any leading `.` characters (i.e. `.github/workflows/ci.yml` becomes `ci.yml`
|
|
93
|
-
and `.gitignore` becomes `gitignore`).
|
|
97
|
+
### Extending
|
|
94
98
|
|
|
95
|
-
|
|
96
|
-
your new file. The object keys are destination paths, and values are source.
|
|
99
|
+
#### `lib/apply`
|
|
97
100
|
|
|
98
|
-
|
|
101
|
+
This directory is where all the logic for applying files lives. It should be
|
|
102
|
+
possible to add new files without modifying anything in this directory. To add a
|
|
103
|
+
file, add the templated file to `lib/content/$FILENAME` and add entry for it in
|
|
104
|
+
`lib/content/index.js` depending on where and when it should be written (root vs
|
|
105
|
+
workspace, repo vs module, add vs remove, etc).
|
|
99
106
|
|
|
100
|
-
`
|
|
101
|
-
is not configured properly, with steps to run to correct any problems.
|
|
107
|
+
#### `lib/check`
|
|
102
108
|
|
|
103
|
-
|
|
109
|
+
All checks live in this directory and have the same signature. A check must be
|
|
110
|
+
added to `lib/check/index.js` for it to be run.
|
|
104
111
|
|
|
105
|
-
|
|
106
|
-
You can force an update with `npm run template-copy`.
|
|
112
|
+
#### Generic vs specific extensions
|
|
107
113
|
|
|
108
|
-
|
|
114
|
+
This repo is designed so that all (fine, most) of the logic in `lib/` is generic
|
|
115
|
+
and could be applied across projects of many different types.
|
|
109
116
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
in
|
|
117
|
+
The files in `lib/content` are extremely specific to the npm CLI. It would be
|
|
118
|
+
trivial to swap out this content directory for a different one as it is only
|
|
119
|
+
referenced in a single place in `lib/config.js`. However, it's not currently
|
|
120
|
+
possible to change this value at runtime, but that might become possible in
|
|
121
|
+
future versions of this package.
|
package/bin/apply.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const apply = require('../lib/apply/index.js')
|
|
4
|
+
|
|
5
|
+
const main = async () => {
|
|
6
|
+
const {
|
|
7
|
+
npm_config_global: globalMode,
|
|
8
|
+
npm_config_local_prefix: root,
|
|
9
|
+
} = process.env
|
|
10
|
+
|
|
11
|
+
// do nothing in global mode or when the local prefix isn't set
|
|
12
|
+
if (globalMode === 'true' || !root) {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
await apply(root)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = main().catch((err) => {
|
|
20
|
+
console.error(err.stack)
|
|
21
|
+
process.exitCode = 1
|
|
22
|
+
})
|
package/bin/check.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const check = require('../lib/check/index.js')
|
|
4
|
+
const output = require('../lib/util/output.js')
|
|
5
|
+
|
|
6
|
+
const main = async () => {
|
|
7
|
+
const {
|
|
8
|
+
npm_config_local_prefix: root,
|
|
9
|
+
} = process.env
|
|
10
|
+
|
|
11
|
+
if (!root) {
|
|
12
|
+
throw new Error('This package requires npm >7.21.1')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const problems = await check(root)
|
|
16
|
+
|
|
17
|
+
if (problems.length) {
|
|
18
|
+
process.exitCode = 1
|
|
19
|
+
console.error(output(problems))
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = main().catch((err) => {
|
|
24
|
+
console.error(err.stack)
|
|
25
|
+
process.exitCode = 1
|
|
26
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const fs = require('@npmcli/fs')
|
|
2
|
+
const log = require('proc-log')
|
|
3
|
+
const { rmEach, parseEach } = require('../util/files.js')
|
|
4
|
+
|
|
5
|
+
const run = async (dir, files, options) => {
|
|
6
|
+
const { rm = [], add = {} } = files
|
|
7
|
+
|
|
8
|
+
log.verbose('apply-files', 'rm', rm)
|
|
9
|
+
await rmEach(dir, rm, options, (f) => fs.rm(f))
|
|
10
|
+
|
|
11
|
+
log.verbose('apply-files', 'add', add)
|
|
12
|
+
await parseEach(dir, add, options, (p) => p.applyWrite())
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = [{
|
|
16
|
+
run: (options) => run(
|
|
17
|
+
options.config.repoDir,
|
|
18
|
+
options.config.repoFiles,
|
|
19
|
+
options
|
|
20
|
+
),
|
|
21
|
+
when: ({ config: c }) => c.isForce || (c.needsUpdate && c.applyRepo),
|
|
22
|
+
name: 'apply-repo',
|
|
23
|
+
}, {
|
|
24
|
+
run: (options) => run(
|
|
25
|
+
options.config.moduleDir,
|
|
26
|
+
options.config.moduleFiles,
|
|
27
|
+
options
|
|
28
|
+
),
|
|
29
|
+
when: ({ config: c }) => c.isForce || (c.needsUpdate && c.applyModule),
|
|
30
|
+
name: 'apply-module',
|
|
31
|
+
}]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const log = require('proc-log')
|
|
2
|
+
const { relative, basename } = require('path')
|
|
3
|
+
const { rmEach, parseEach } = require('../util/files.js')
|
|
4
|
+
const { partition } = require('lodash')
|
|
5
|
+
|
|
6
|
+
const solution = 'npx template-oss-apply --force'
|
|
7
|
+
|
|
8
|
+
const run = async (type, dir, files, options) => {
|
|
9
|
+
const res = []
|
|
10
|
+
const rel = (f) => relative(options.root, f)
|
|
11
|
+
const { add: addFiles = {}, rm: rmFiles = [] } = files
|
|
12
|
+
|
|
13
|
+
const rm = await rmEach(dir, rmFiles, options, (f) => rel(f))
|
|
14
|
+
const [add, update] = partition(await parseEach(dir, addFiles, options, async (p) => {
|
|
15
|
+
const diff = await p.applyDiff()
|
|
16
|
+
const target = rel(p.target)
|
|
17
|
+
if (diff === null) {
|
|
18
|
+
// needs to be added
|
|
19
|
+
return target
|
|
20
|
+
} else if (diff === true) {
|
|
21
|
+
// its ok, no diff, this is filtered out
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
return { file: target, diff }
|
|
25
|
+
}), (d) => typeof d === 'string')
|
|
26
|
+
|
|
27
|
+
log.verbose('check-apply', 'rm', rm)
|
|
28
|
+
if (rm.length) {
|
|
29
|
+
res.push({
|
|
30
|
+
title: `The following ${type} files need to be deleted:`,
|
|
31
|
+
body: rm,
|
|
32
|
+
solution,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
log.verbose('check-apply', 'add', add)
|
|
37
|
+
if (add.length) {
|
|
38
|
+
res.push({
|
|
39
|
+
title: `The following ${type} files need to be added:`,
|
|
40
|
+
body: add,
|
|
41
|
+
solution,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
log.verbose('check-apply', 'update', update)
|
|
46
|
+
res.push(...update.map(({ file, diff }) => ({
|
|
47
|
+
title: `The ${type} file ${basename(file)} needs to be updated:`,
|
|
48
|
+
body: [`${file}\n${'='.repeat(40)}\n${diff}`],
|
|
49
|
+
solution,
|
|
50
|
+
})))
|
|
51
|
+
|
|
52
|
+
return res
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = [{
|
|
56
|
+
run: (options) => run(
|
|
57
|
+
'repo',
|
|
58
|
+
options.config.repoDir,
|
|
59
|
+
options.config.repoFiles,
|
|
60
|
+
options
|
|
61
|
+
),
|
|
62
|
+
when: ({ config: c }) => c.applyRepo,
|
|
63
|
+
name: 'check-repo',
|
|
64
|
+
}, {
|
|
65
|
+
run: (options) => run(
|
|
66
|
+
'module',
|
|
67
|
+
options.config.moduleDir,
|
|
68
|
+
options.config.moduleFiles,
|
|
69
|
+
options
|
|
70
|
+
),
|
|
71
|
+
when: ({ config: c }) => c.applyModule,
|
|
72
|
+
name: 'check-module',
|
|
73
|
+
}]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const fs = require('@npmcli/fs')
|
|
2
|
+
const { EOL } = require('os')
|
|
3
|
+
const { join, relative } = require('path')
|
|
4
|
+
|
|
5
|
+
const run = async ({ root, path }) => {
|
|
6
|
+
// XXX: our changelogs are always markdown
|
|
7
|
+
// but they could be other extensions so
|
|
8
|
+
// make this glob for possible matches
|
|
9
|
+
const changelog = join(path, 'CHANGELOG.md')
|
|
10
|
+
|
|
11
|
+
if (await fs.exists(changelog)) {
|
|
12
|
+
const content = await fs.readFile(changelog, { encoding: 'utf8' })
|
|
13
|
+
const mustStart = `# Changelog${EOL}${EOL}#`
|
|
14
|
+
if (!content.startsWith(mustStart)) {
|
|
15
|
+
return {
|
|
16
|
+
title: `The ${relative(root, changelog)} is incorrect:`,
|
|
17
|
+
body: [
|
|
18
|
+
'The changelog should start with',
|
|
19
|
+
`"${mustStart}"`,
|
|
20
|
+
],
|
|
21
|
+
solution: 'reformat the changelog to have the correct heading',
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
run,
|
|
29
|
+
when: ({ config: c }) => c.applyModule,
|
|
30
|
+
name: 'check-changelog',
|
|
31
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const log = require('proc-log')
|
|
2
|
+
const { EOL } = require('os')
|
|
3
|
+
const { resolve, relative, basename } = require('path')
|
|
4
|
+
const fs = require('@npmcli/fs')
|
|
5
|
+
const git = require('@npmcli/git')
|
|
6
|
+
|
|
7
|
+
const NAME = 'check-gitignore'
|
|
8
|
+
|
|
9
|
+
// The problem we are trying to solve is when a new .gitignore file
|
|
10
|
+
// is copied into an existing repo, there could be files already checked in
|
|
11
|
+
// to git that are now ignored by new gitignore rules. We want to warn
|
|
12
|
+
// about these files.
|
|
13
|
+
const run = async ({ root, path, config }) => {
|
|
14
|
+
log.verbose(NAME, { root, path })
|
|
15
|
+
|
|
16
|
+
const relativeToRoot = (f) => relative(root, resolve(path, f))
|
|
17
|
+
|
|
18
|
+
// use the root to detect a git repo but the project directory (path) for the
|
|
19
|
+
// ignore check
|
|
20
|
+
const ignoreFile = resolve(path, '.gitignore')
|
|
21
|
+
if (!await git.is({ cwd: root }) || !await fs.exists(ignoreFile)) {
|
|
22
|
+
log.verbose(NAME, 'no git or no gitignore')
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
log.verbose(NAME, `using ignore file ${ignoreFile}`)
|
|
27
|
+
|
|
28
|
+
const res = await git.spawn([
|
|
29
|
+
'ls-files',
|
|
30
|
+
'--cached',
|
|
31
|
+
'--ignored',
|
|
32
|
+
// https://git-scm.com/docs/git-ls-files#_exclude_patterns
|
|
33
|
+
`--${config.isRoot ? 'exclude-from' : 'exclude-per-directory'}=${basename(ignoreFile)}`,
|
|
34
|
+
], { cwd: path })
|
|
35
|
+
|
|
36
|
+
log.verbose(NAME, 'ls-files', res)
|
|
37
|
+
|
|
38
|
+
// TODO: files should be filtered if they have already been moved/deleted
|
|
39
|
+
// but not committed. Currently you must commit for this check to pass.
|
|
40
|
+
const files = res.stdout
|
|
41
|
+
.trim()
|
|
42
|
+
.split('\n')
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
|
|
45
|
+
if (!files.length) {
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const ignores = (await fs.readFile(ignoreFile))
|
|
50
|
+
.toString()
|
|
51
|
+
.split(EOL)
|
|
52
|
+
.filter((l) => l && !l.trim().startsWith('#'))
|
|
53
|
+
|
|
54
|
+
const relIgnore = relativeToRoot(ignoreFile)
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
title: `The following files are tracked by git but matching a pattern in ${relIgnore}:`,
|
|
58
|
+
body: files.map(relativeToRoot),
|
|
59
|
+
solution: ['move files to not match one of the following patterns:', ...ignores],
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = {
|
|
64
|
+
run,
|
|
65
|
+
when: ({ config: c }) => c.applyModule,
|
|
66
|
+
name: NAME,
|
|
67
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const log = require('proc-log')
|
|
2
|
+
const npa = require('npm-package-arg')
|
|
3
|
+
const { partition } = require('lodash')
|
|
4
|
+
const hasPackage = require('../util/has-package.js')
|
|
5
|
+
|
|
6
|
+
const rmCommand = (specs) =>
|
|
7
|
+
`npm rm ${specs.map((s) => s.name).join(' ')}`.trim()
|
|
8
|
+
|
|
9
|
+
const installCommand = (specs, flags) => specs.length ?
|
|
10
|
+
`npm i ${specs.map((s) => `${s.name}@${s.fetchSpec}`).join(' ')} ${flags.join(' ')}`.trim() : ''
|
|
11
|
+
|
|
12
|
+
// ensure required packages are present in the correct place
|
|
13
|
+
const run = ({ pkg, path, config: { requiredPackages = {} } }) => {
|
|
14
|
+
// keys are the dependency location in package.json
|
|
15
|
+
// values are a filtered list of parsed specs that dont exist in the current package
|
|
16
|
+
// { [location]: [spec1, spec2] }
|
|
17
|
+
const requiredByLocation = Object.entries(requiredPackages)
|
|
18
|
+
.reduce((acc, [location, pkgs]) => {
|
|
19
|
+
acc[location] = pkgs
|
|
20
|
+
.filter((spec) => !hasPackage(pkg, spec, [location], path))
|
|
21
|
+
.map((spec) => npa(spec))
|
|
22
|
+
log.verbose(location, pkg, pkgs)
|
|
23
|
+
return acc
|
|
24
|
+
}, {})
|
|
25
|
+
|
|
26
|
+
const requiredEntries = Object.entries(requiredByLocation)
|
|
27
|
+
|
|
28
|
+
log.verbose('check-required', requiredEntries)
|
|
29
|
+
|
|
30
|
+
if (requiredEntries.flatMap(([, specs]) => specs).length) {
|
|
31
|
+
return requiredEntries.map(([location, specs]) => {
|
|
32
|
+
const locationFlag = hasPackage.flags[location]
|
|
33
|
+
const [exactSpecs, saveSpecs] = partition(specs, (s) => s.type === 'version')
|
|
34
|
+
|
|
35
|
+
log.verbose('check-required', location, specs)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
title: `The following required ${location} were not found:`,
|
|
39
|
+
body: specs.map((s) => s.rawSpec ? `${s.name}@${s.rawSpec}` : s.name),
|
|
40
|
+
// solution is to remove any existing all at once but add back in by --save-<location>
|
|
41
|
+
solution: [
|
|
42
|
+
rmCommand(specs),
|
|
43
|
+
installCommand(saveSpecs, [locationFlag]),
|
|
44
|
+
installCommand(exactSpecs, [locationFlag, '--save-exact']),
|
|
45
|
+
].filter(Boolean).join(' && '),
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
run,
|
|
53
|
+
when: ({ config: c }) => c.applyModule,
|
|
54
|
+
name: 'check-required-packages',
|
|
55
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
const hasPackage = require('../util/has-package.js')
|
|
3
|
+
|
|
4
|
+
const run = ({ pkg, config: { allowedPackages = [], unwantedPackages = [] } }) => {
|
|
5
|
+
// ensure packages that should not be present are removed
|
|
6
|
+
const hasUnwanted = unwantedPackages
|
|
7
|
+
.filter((name) => !allowedPackages.includes(name))
|
|
8
|
+
.filter((name) => hasPackage(pkg, name))
|
|
9
|
+
|
|
10
|
+
if (hasUnwanted.length) {
|
|
11
|
+
return {
|
|
12
|
+
title: 'The following unwanted packages were found:',
|
|
13
|
+
body: hasUnwanted,
|
|
14
|
+
solution: `npm rm ${hasUnwanted.join(' ')}`,
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
run,
|
|
21
|
+
when: ({ config: c }) => c.applyModule,
|
|
22
|
+
name: 'check-unwanted-packages',
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const run = require('../index.js')
|
|
2
|
+
|
|
3
|
+
module.exports = (root, content) => run(root, content, [
|
|
4
|
+
require('./check-apply.js'),
|
|
5
|
+
require('./check-required.js'),
|
|
6
|
+
require('./check-unwanted.js'),
|
|
7
|
+
require('./check-gitignore.js'),
|
|
8
|
+
require('./check-changelog.js'),
|
|
9
|
+
])
|