Skip to content

Commit d1380df

Browse files
committed
fix: totally rework package installation to peer deps
1 parent d955ef3 commit d1380df

File tree

9 files changed

+145
-113
lines changed

9 files changed

+145
-113
lines changed

bin/npm-template-check.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env node
2+
3+
const check = require('../lib/check.js')
4+
5+
const main = async () => {
6+
const {
7+
npm_config_local_prefix: root,
8+
} = process.env
9+
10+
if (!root) {
11+
throw new Error('This package requires npm >7.21.1')
12+
}
13+
14+
const problems = await check(root)
15+
if (problems.length) {
16+
console.error('Some problems were detected:')
17+
console.error()
18+
console.error(problems.map((problem) => problem.message).join('\n'))
19+
console.error()
20+
console.error('To correct them:')
21+
console.error(problems.map((problem) => problem.solution).join('\n'))
22+
process.exitCode = 1
23+
}
24+
}
25+
26+
module.exports = main().catch((err) => {
27+
console.error(err.stack)
28+
process.exitCode = 1
29+
})

bin/postinstall.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#!/usr/bin/env node
22

33
const copyContent = require('../lib/content/index.js')
4-
const installPackages = require('../lib/install.js')
54
const patchPackage = require('../lib/package.js')
65

76
const main = async () => {
@@ -20,8 +19,7 @@ const main = async () => {
2019
return
2120
}
2221

23-
await copyContent(root)
24-
return installPackages(root)
22+
return copyContent(root)
2523
}
2624

2725
// we export the promise so it can be awaited in tests, coverage is disabled

lib/check.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const { join } = require('path')
2+
const fs = require('@npmcli/fs')
3+
4+
const patchPackage = require('./package.js')
5+
6+
const unwantedPackages = [
7+
'@npmcli/lint',
8+
'eslint-plugin-promise',
9+
'eslint-plugin-standard',
10+
'eslint-plugin-import',
11+
]
12+
13+
const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key)
14+
15+
const check = async (root) => {
16+
const pkgPath = join(root, 'package.json')
17+
try {
18+
var contents = await fs.readFile(pkgPath, { encoding: 'utf8' })
19+
} catch (err) {
20+
throw new Error('No package.json found')
21+
}
22+
23+
const pkg = JSON.parse(contents)
24+
25+
const problems = []
26+
27+
const incorrectFields = []
28+
// 1. ensure package.json changes have been applied
29+
for (const [key, value] of Object.entries(patchPackage.changes)) {
30+
if (!hasOwn(pkg, key)) {
31+
incorrectFields.push({
32+
name: key,
33+
found: pkg[key],
34+
expected: value,
35+
})
36+
continue
37+
}
38+
39+
if (value && typeof value === 'object') {
40+
for (const [subKey, subValue] of Object.entries(value)) {
41+
if (!hasOwn(pkg[key], subKey) ||
42+
pkg[key][subKey] !== subValue) {
43+
incorrectFields.push({
44+
name: `${key}.${subKey}`,
45+
found: pkg[key][subKey],
46+
expected: subValue,
47+
})
48+
}
49+
}
50+
} else {
51+
if (pkg[key] !== patchPackage.changes[key]) {
52+
incorrectFields.push({
53+
name: key,
54+
found: pkg[key],
55+
expected: value,
56+
})
57+
}
58+
}
59+
}
60+
61+
if (incorrectFields.length) {
62+
problems.push({
63+
message: [
64+
`The following package.json fields are incorrect:`,
65+
...incorrectFields.map((field) => {
66+
const { name, found, expected } = field
67+
return ` Field: "${name}" Expected: "${expected}" Found: "${found}"`
68+
}),
69+
].join('\n'),
70+
solution: 'npm rm @npmcli/template-oss && npm i -D @npmcli/template-oss',
71+
})
72+
}
73+
74+
// 2. ensure packages that should not be present are removed
75+
const mustRemove = unwantedPackages.filter((name) => {
76+
return hasOwn(pkg.dependencies || {}, name) ||
77+
hasOwn(pkg.devDependencies || {}, name)
78+
})
79+
80+
if (mustRemove.length) {
81+
problems.push({
82+
message: [
83+
'The following unwanted packages were found:',
84+
...mustRemove,
85+
].join(' '),
86+
solution: `npm rm ${mustRemove.join(' ')}`,
87+
})
88+
}
89+
90+
return problems
91+
}
92+
93+
check.unwantedPackages = unwantedPackages
94+
95+
module.exports = check

lib/install.js

Lines changed: 0 additions & 39 deletions
This file was deleted.

lib/package.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const changes = {
99
templateVersion: TEMPLATE_VERSION,
1010
scripts: {
1111
lint: `eslint '**/*.js'`,
12+
postlint: 'npm-template-check',
1213
lintfix: 'npm run lint -- --fix',
1314
preversion: 'npm test',
1415
postversion: 'npm publish',

package-lock.json

Lines changed: 8 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33
"version": "1.0.2",
44
"description": "templated files used in npm CLI team oss projects",
55
"main": "lib/index.js",
6+
"bin": {
7+
"npm-template-check": "bin/npm-template-check.js"
8+
},
69
"scripts": {
10+
"prelint": "ln -sf ../../bin/npm-template-check.js node_modules/.bin/npm-template-check",
711
"lint": "eslint '**/*.js'",
12+
"postlint": "npm-template-check",
813
"lintfix": "npm run lint -- --fix",
914
"preversion": "npm test",
1015
"postversion": "npm publish",
@@ -34,11 +39,14 @@
3439
"lib"
3540
],
3641
"devDependencies": {
37-
"@npmcli/eslint-config": "^1.0.0",
42+
"@npmcli/eslint-config": "*",
3843
"eslint": "^7.32.0",
3944
"eslint-plugin-node": "^11.1.0",
40-
"spawk": "^1.7.1",
45+
"tap": "*"
46+
},
47+
"peerDependencies": {
48+
"@npmcli/eslint-config": "^1.0.0",
4149
"tap": "^15.0.9"
4250
},
43-
"templateVersion": "1.0.1"
51+
"templateVersion": "1.0.2"
4452
}

test/install.js

Lines changed: 0 additions & 32 deletions
This file was deleted.

test/postinstall.js

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
const { basename, dirname, join, normalize } = require('path')
22
const fs = require('@npmcli/fs')
3-
const spawk = require('spawk')
43
const t = require('tap')
54

65
const TEMPLATE_VERSION = require('../package.json').version
76

87
const copyContents = require('../lib/content/index.js')
9-
const installPackages = require('../lib/install.js')
108
const patchPackage = require('../lib/package.js')
119

12-
spawk.preventUnmatched()
13-
1410
t.test('when npm_config_global is true, does nothing', async (t) => {
1511
// this is set by virtue of running tests with npm, save it and remove it
1612
const _env = process.env.npm_config_global
@@ -94,24 +90,9 @@ t.test('sets up a new project', async (t) => {
9490
process.env.npm_config_local_prefix = _prefix
9591
})
9692

97-
const uninstall = spawk.spawn('npm', (args) => {
98-
return args[0] === 'uninstall' &&
99-
installPackages.removeDeps.every((dep) => args.includes(dep))
100-
}, { cwd: root })
101-
102-
const install = spawk.spawn('npm', (args) => {
103-
return args[0] === 'install' &&
104-
args[1] === '--save-dev' &&
105-
installPackages.devDeps.every((dep) => args.includes(dep))
106-
}, { cwd: root })
107-
10893
// t.mock instead of require so the cache doesn't interfere
10994
await t.mock('../bin/postinstall.js')
11095

111-
// we mock the npm commands so this test doesn't take forever
112-
t.ok(uninstall.called, 'ran uninstalls')
113-
t.ok(install.called, 'ran installs')
114-
11596
// verify file contents were copied
11697
const contents = await fs.readdir(root)
11798
for (const file in copyContents.content) {

0 commit comments

Comments
 (0)