@npmcli/template-oss 3.0.0 → 3.1.2
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/check/check-required.js +40 -21
- package/lib/content/audit.yml +1 -1
- package/lib/content/ci.yml +3 -3
- package/lib/content/dependabot.yml +1 -1
- package/lib/content/index.js +15 -13
- package/lib/content/{package.json → pkg.json} +0 -0
- package/lib/content/post-dependabot.yml +2 -1
- package/lib/content/pull-request.yml +3 -6
- package/lib/content/setup-node.yml +8 -4
- package/lib/util/has-package.js +64 -11
- package/lib/util/json-diff.js +8 -13
- package/lib/util/parser.js +6 -5
- package/package.json +5 -6
|
@@ -1,29 +1,48 @@
|
|
|
1
|
+
const log = require('proc-log')
|
|
2
|
+
const npa = require('npm-package-arg')
|
|
3
|
+
const { partition } = require('lodash')
|
|
1
4
|
const hasPackage = require('../util/has-package.js')
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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)
|
|
21
36
|
|
|
22
37
|
return {
|
|
23
38
|
title: `The following required ${location} were not found:`,
|
|
24
|
-
body: specs.map((
|
|
39
|
+
body: specs.map((s) => s.rawSpec ? `${s.name}@${s.rawSpec}` : s.name),
|
|
25
40
|
// solution is to remove any existing all at once but add back in by --save-<location>
|
|
26
|
-
solution: [
|
|
41
|
+
solution: [
|
|
42
|
+
rmCommand(specs),
|
|
43
|
+
installCommand(saveSpecs, [locationFlag]),
|
|
44
|
+
installCommand(exactSpecs, [locationFlag, '--save-exact']),
|
|
45
|
+
].filter(Boolean).join(' && '),
|
|
27
46
|
}
|
|
28
47
|
})
|
|
29
48
|
}
|
package/lib/content/audit.yml
CHANGED
package/lib/content/ci.yml
CHANGED
|
@@ -7,7 +7,7 @@ on:
|
|
|
7
7
|
- '*'
|
|
8
8
|
{{#if pkgRelPath}}
|
|
9
9
|
paths:
|
|
10
|
-
- {{pkgRelPath}}
|
|
10
|
+
- {{pkgRelPath}}/**
|
|
11
11
|
{{/if}}
|
|
12
12
|
push:
|
|
13
13
|
branches:
|
|
@@ -16,7 +16,7 @@ on:
|
|
|
16
16
|
{{/each}}
|
|
17
17
|
{{#if pkgRelPath}}
|
|
18
18
|
paths:
|
|
19
|
-
- {{pkgRelPath}}
|
|
19
|
+
- {{pkgRelPath}}/**
|
|
20
20
|
{{/if}}
|
|
21
21
|
schedule:
|
|
22
22
|
# "At 02:00 on Monday" https://crontab.guru/#0_2_*_*_1
|
|
@@ -54,6 +54,6 @@ jobs:
|
|
|
54
54
|
shell: $\{{ matrix.platform.shell }}
|
|
55
55
|
steps:
|
|
56
56
|
{{> setupGit}}
|
|
57
|
-
{{> setupNode}}
|
|
57
|
+
{{> setupNode useMatrix=true}}
|
|
58
58
|
- run: npm i
|
|
59
59
|
- run: npm test --ignore-scripts {{~#if isWorkspace}} -w {{pkgName}}{{/if}}
|
package/lib/content/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { name: NAME, version: LATEST_VERSION } = require('../../package.json')
|
|
2
|
+
|
|
1
3
|
// Changes applied to the root of the repo
|
|
2
4
|
const rootRepo = {
|
|
3
5
|
add: {
|
|
@@ -26,7 +28,7 @@ const rootModule = {
|
|
|
26
28
|
'.gitignore': 'gitignore',
|
|
27
29
|
'.npmrc': 'npmrc',
|
|
28
30
|
'SECURITY.md': 'SECURITY.md',
|
|
29
|
-
'package.json': '
|
|
31
|
+
'package.json': 'pkg.json',
|
|
30
32
|
},
|
|
31
33
|
rm: [
|
|
32
34
|
'.eslintrc.!(js|local.*)',
|
|
@@ -37,7 +39,7 @@ const rootModule = {
|
|
|
37
39
|
const workspaceRepo = {
|
|
38
40
|
add: {
|
|
39
41
|
'.github/workflows/release-please-{{pkgNameFs}}.yml': 'release-please.yml',
|
|
40
|
-
'.github/workflows/ci-{{pkgNameFs}}.yml': 'ci.yml'
|
|
42
|
+
'.github/workflows/ci-{{pkgNameFs}}.yml': 'ci.yml',
|
|
41
43
|
},
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -46,7 +48,7 @@ const workspaceModule = {
|
|
|
46
48
|
add: {
|
|
47
49
|
'.eslintrc.js': 'eslintrc.js',
|
|
48
50
|
'.gitignore': 'gitignore',
|
|
49
|
-
'package.json': '
|
|
51
|
+
'package.json': 'pkg.json',
|
|
50
52
|
},
|
|
51
53
|
rm: [
|
|
52
54
|
'.npmrc',
|
|
@@ -73,18 +75,18 @@ module.exports = {
|
|
|
73
75
|
'standard',
|
|
74
76
|
],
|
|
75
77
|
requiredPackages: {
|
|
76
|
-
devDependencies:
|
|
77
|
-
|
|
78
|
-
'@npmcli/eslint-config'
|
|
79
|
-
tap
|
|
80
|
-
|
|
78
|
+
devDependencies: [
|
|
79
|
+
`${NAME}@${LATEST_VERSION}`,
|
|
80
|
+
'@npmcli/eslint-config',
|
|
81
|
+
'tap',
|
|
82
|
+
],
|
|
81
83
|
},
|
|
82
84
|
allowedPackages: [],
|
|
83
85
|
changelogTypes: [
|
|
84
|
-
{ type:
|
|
85
|
-
{ type:
|
|
86
|
-
{ type:
|
|
87
|
-
{ type:
|
|
88
|
-
{ type:
|
|
86
|
+
{ type: 'feat', section: 'Features', hidden: false },
|
|
87
|
+
{ type: 'fix', section: 'Bug Fixes', hidden: false },
|
|
88
|
+
{ type: 'docs', section: 'Documentation', hidden: false },
|
|
89
|
+
{ type: 'deps', section: 'Dependencies', hidden: false },
|
|
90
|
+
{ type: 'chore', hidden: true },
|
|
89
91
|
],
|
|
90
92
|
}
|
|
File without changes
|
|
@@ -5,6 +5,7 @@ on:
|
|
|
5
5
|
|
|
6
6
|
# https://docs.github.com/en/rest/overview/permissions-required-for-github-apps
|
|
7
7
|
permissions:
|
|
8
|
+
actions: write
|
|
8
9
|
contents: write
|
|
9
10
|
|
|
10
11
|
jobs:
|
|
@@ -20,7 +21,7 @@ jobs:
|
|
|
20
21
|
with:
|
|
21
22
|
github-token: "$\{{ secrets.GITHUB_TOKEN }}"
|
|
22
23
|
- name: npm install and commit
|
|
23
|
-
if: contains(steps.metadata.outputs.dependency-names, '
|
|
24
|
+
if: contains(steps.metadata.outputs.dependency-names, '{{__NAME__}}')
|
|
24
25
|
env:
|
|
25
26
|
GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }}
|
|
26
27
|
run: |
|
|
@@ -16,13 +16,10 @@ jobs:
|
|
|
16
16
|
{{> setupGit with=(obj fetch-depth=0)}}
|
|
17
17
|
{{> setupNode}}
|
|
18
18
|
- name: Install deps
|
|
19
|
-
run:
|
|
20
|
-
npm i -D @commitlint/cli @commitlint/config-conventional
|
|
19
|
+
run: npm i -D @commitlint/cli @commitlint/config-conventional
|
|
21
20
|
- name: Check commits OR PR title
|
|
22
21
|
env:
|
|
23
22
|
PR_TITLE: $\{{ github.event.pull_request.title }}
|
|
24
23
|
run: |
|
|
25
|
-
npx commitlint -
|
|
26
|
-
|
|
27
|
-
|| echo $PR_TITLE | \
|
|
28
|
-
npx commitlint -x @commitlint/config-conventional -V
|
|
24
|
+
npx --offline commitlint -V --from origin/main --to $\{{ github.event.pull_request.head.sha }} \
|
|
25
|
+
|| echo $PR_TITLE | npx --offline commitlint -V
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
- uses: actions/setup-node@v3
|
|
2
2
|
with:
|
|
3
|
-
node-version: {{#each ciVersions}}{{#if @last}}{{.}}{{/if}}{{/each}}
|
|
3
|
+
node-version: {{#if useMatrix}}$\{{ matrix.node-version }}{{else}}{{#each ciVersions}}{{#if @last}}{{.}}{{/if}}{{/each}}{{/if}}
|
|
4
|
+
{{#if useMatrix}}
|
|
4
5
|
- name: Update to workable npm (windows)
|
|
5
6
|
# node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows
|
|
6
|
-
if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12') || startsWith(matrix.node-version, '14'))
|
|
7
|
+
if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.'))
|
|
7
8
|
run: |
|
|
8
9
|
curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz
|
|
9
10
|
tar xf npm-7.5.4.tgz
|
|
@@ -13,9 +14,12 @@
|
|
|
13
14
|
rmdir /s /q package
|
|
14
15
|
- name: Update npm to 7
|
|
15
16
|
# If we do test on npm 10 it needs npm7
|
|
16
|
-
if: matrix.node-version
|
|
17
|
+
if: startsWith(matrix.node-version, '10.')
|
|
17
18
|
run: npm i --prefer-online --no-fund --no-audit -g npm@7
|
|
18
19
|
- name: Update npm to latest
|
|
19
|
-
if: matrix.node-version
|
|
20
|
+
if: $\{{ !startsWith(matrix.node-version, '10.') }}
|
|
21
|
+
{{else}}
|
|
22
|
+
- name: Update npm to latest
|
|
23
|
+
{{/if}}
|
|
20
24
|
run: npm i --prefer-online --no-fund --no-audit -g npm@latest
|
|
21
25
|
- run: npm -v
|
package/lib/util/has-package.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
const
|
|
1
|
+
const semver = require('semver')
|
|
2
|
+
const npa = require('npm-package-arg')
|
|
2
3
|
const { has } = require('lodash')
|
|
4
|
+
const { join } = require('path')
|
|
3
5
|
|
|
4
6
|
const installLocations = [
|
|
5
7
|
'dependencies',
|
|
@@ -9,20 +11,71 @@ const installLocations = [
|
|
|
9
11
|
'optionalDependencies',
|
|
10
12
|
]
|
|
11
13
|
|
|
14
|
+
// from a spec get either a semver version or range. it gets parsed with npa and
|
|
15
|
+
// only a few appropriate types are handled. eg this doesnt match any git
|
|
16
|
+
// shas/tags, etc
|
|
17
|
+
const getSpecVersion = (spec, where) => {
|
|
18
|
+
const arg = npa(spec, where)
|
|
19
|
+
switch (arg.type) {
|
|
20
|
+
case 'range':
|
|
21
|
+
return new semver.Range(arg.fetchSpec)
|
|
22
|
+
case 'tag': {
|
|
23
|
+
// special case an empty spec to mean any version
|
|
24
|
+
return arg.rawSpec === '' && new semver.Range('*')
|
|
25
|
+
}
|
|
26
|
+
case 'version':
|
|
27
|
+
return new semver.SemVer(arg.fetchSpec)
|
|
28
|
+
case 'directory': {
|
|
29
|
+
// allows this repo to use a file spec as a devdep and pass this check
|
|
30
|
+
const pkg = require(join(arg.fetchSpec, 'package.json'))
|
|
31
|
+
return new semver.SemVer(pkg.version)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const isVersion = (s) => s instanceof semver.SemVer
|
|
38
|
+
|
|
39
|
+
// Returns whether the pkg has the dependency in a semver
|
|
40
|
+
// compatible version in one or more locationscccc
|
|
12
41
|
const hasPackage = (
|
|
13
42
|
pkg,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
) =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
has(deps, name) &&
|
|
21
|
-
(version === '*' || intersects(deps[name], version))
|
|
22
|
-
)
|
|
43
|
+
spec,
|
|
44
|
+
locations = installLocations,
|
|
45
|
+
path
|
|
46
|
+
) => {
|
|
47
|
+
const name = npa(spec).name
|
|
48
|
+
const requested = getSpecVersion(spec)
|
|
23
49
|
|
|
24
|
-
|
|
50
|
+
if (!requested) {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
25
53
|
|
|
54
|
+
const existingByLocation = locations
|
|
55
|
+
.map((location) => pkg[location])
|
|
56
|
+
.filter((deps) => has(deps, name))
|
|
57
|
+
.map((deps) => getSpecVersion(`${name}@${deps[name]}`, path))
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
|
|
60
|
+
return existingByLocation.some((existing) => {
|
|
61
|
+
switch ([existing, requested].map((t) => isVersion(t) ? 'VER' : 'RNG').join('-')) {
|
|
62
|
+
case `VER-VER`:
|
|
63
|
+
// two versions, use semver.eq to check equality
|
|
64
|
+
return semver.eq(existing, requested)
|
|
65
|
+
case `RNG-RNG`:
|
|
66
|
+
// two ranges, existing must be entirely within the requested
|
|
67
|
+
return semver.subset(existing, requested)
|
|
68
|
+
case `VER-RNG`:
|
|
69
|
+
// requesting a range with existing version is ok if it satisfies
|
|
70
|
+
return semver.satisfies(existing, requested)
|
|
71
|
+
case `RNG-VER`:
|
|
72
|
+
// requesting a pinned version but has a range, always false
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = hasPackage
|
|
26
79
|
module.exports.flags = installLocations.reduce((acc, location) => {
|
|
27
80
|
const type = location.replace(/dependencies/i, '')
|
|
28
81
|
acc[location] = '--save' + (type ? `-${type}` : '')
|
package/lib/util/json-diff.js
CHANGED
|
@@ -4,11 +4,10 @@ const { diff } = require('just-diff')
|
|
|
4
4
|
|
|
5
5
|
const j = (obj, replacer = null) => JSON.stringify(obj, replacer, 2)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const ops = diff(s1, s2).map(({ op, path, value }) => {
|
|
7
|
+
// DELETE is a special string that will be the value of updated if it exists
|
|
8
|
+
// but should be deleted
|
|
9
|
+
const jsonDiff = (s1, s2, DELETE) => diff(s1, s2)
|
|
10
|
+
.map(({ op, path, value }) => {
|
|
12
11
|
// there could be cases where a whole object is reported
|
|
13
12
|
// as missing and the expected value does not need to show
|
|
14
13
|
// special DELETED values so filter those out here
|
|
@@ -26,13 +25,9 @@ const jsonDiff = (s1, s2, DELETE) => {
|
|
|
26
25
|
} else if (op === 'add' && value !== DELETE) {
|
|
27
26
|
return AD
|
|
28
27
|
}
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return ops.join('\n')
|
|
36
|
-
}
|
|
28
|
+
})
|
|
29
|
+
.filter(Boolean)
|
|
30
|
+
.sort((a, b) => a.localeCompare(b))
|
|
31
|
+
.join('\n')
|
|
37
32
|
|
|
38
33
|
module.exports = jsonDiff
|
package/lib/util/parser.js
CHANGED
|
@@ -77,7 +77,7 @@ class Base {
|
|
|
77
77
|
// create a patch and strip out the filename. if it ends up an empty string
|
|
78
78
|
// then return true since the files are equal
|
|
79
79
|
return Diff.createPatch('', t.replace(/\r\n/g, '\n'), s.replace(/\r\n/g, '\n'))
|
|
80
|
-
.split('\n').slice(4).join('\n')
|
|
80
|
+
.split('\n').slice(4).join('\n')
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
diff (t, s) {
|
|
@@ -142,9 +142,10 @@ class Base {
|
|
|
142
142
|
].join('\n')
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
// individual diff methods are responsible for
|
|
146
|
-
//
|
|
147
|
-
|
|
145
|
+
// individual diff methods are responsible for returning a string
|
|
146
|
+
// representing the diff. an empty trimmed string means no diff
|
|
147
|
+
const diffRes = this.diff(target, source).trim()
|
|
148
|
+
return diffRes || true
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
|
|
@@ -224,7 +225,7 @@ class JsonMerge extends Json {
|
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
class PackageJson extends JsonMerge {
|
|
227
|
-
static types = ['
|
|
228
|
+
static types = ['pkg.json']
|
|
228
229
|
|
|
229
230
|
async prepare (s, t) {
|
|
230
231
|
// merge new source with current pkg content
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/template-oss",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"description": "templated files used in npm CLI team oss projects",
|
|
5
5
|
"main": "lib/content/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"snap": "tap",
|
|
18
18
|
"test": "tap",
|
|
19
19
|
"template-oss-apply": "template-oss-apply --force",
|
|
20
|
-
"postlint": "template-oss-check"
|
|
20
|
+
"postlint": "template-oss-check",
|
|
21
|
+
"postinstall": "template-oss-apply"
|
|
21
22
|
},
|
|
22
23
|
"repository": {
|
|
23
24
|
"type": "git",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"json-parse-even-better-errors": "^2.3.1",
|
|
41
42
|
"just-diff": "^5.0.1",
|
|
42
43
|
"lodash": "^4.17.21",
|
|
44
|
+
"npm-package-arg": "^9.0.1",
|
|
43
45
|
"proc-log": "^2.0.0",
|
|
44
46
|
"semver": "^7.3.5",
|
|
45
47
|
"yaml": "^2.0.0-10"
|
|
@@ -58,8 +60,5 @@
|
|
|
58
60
|
},
|
|
59
61
|
"engines": {
|
|
60
62
|
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
|
61
|
-
}
|
|
62
|
-
"eslintIgnore": [
|
|
63
|
-
"lib/content/"
|
|
64
|
-
]
|
|
63
|
+
}
|
|
65
64
|
}
|