@npmcli/template-oss 2.0.0 → 2.3.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 CHANGED
@@ -9,22 +9,26 @@ single devDependency.
9
9
 
10
10
  These fields will be set in the project's `package.json`:
11
11
 
12
- ```json
12
+ ```js
13
13
  {
14
- "author": "GitHub Inc.",
15
- "files": ["bin", "lib"],
16
- "license": "ISC",
17
- "templateVersion": "1.0.0",
18
- "scripts": {
19
- "lint": "eslint '**/*.js'",
20
- "lintfix": "npm run lint -- --fix",
21
- "preversion": "npm test",
22
- "postversion": "npm publish",
23
- "prepublishOnly": "git push origin --follow-tags",
24
- "snap": "tap",
25
- "test": "tap",
26
- "posttest": "npm run lint",
27
- }
14
+ author: 'GitHub Inc.',
15
+ files: ['bin', 'lib'],
16
+ license: 'ISC',
17
+ templateVersion: $TEMPLATE_VERSION,
18
+ scripts: {
19
+ lint: `eslint '**/*.js'`,
20
+ postlint: 'npm-template-check',
21
+ lintfix: 'npm run lint -- --fix',
22
+ preversion: 'npm test',
23
+ postversion: 'npm publish',
24
+ prepublishOnly: 'git push origin --follow-tags',
25
+ snap: 'tap',
26
+ test: 'tap',
27
+ posttest: 'npm run lint',
28
+ },
29
+ engines: {
30
+ node: '^12.13.0 || ^14.15.0 || >=16',
31
+ },
28
32
  }
29
33
  ```
30
34
 
@@ -34,9 +38,9 @@ action.
34
38
 
35
39
  #### Extending
36
40
 
37
- The `changes` constant located in `lib/package.js` should contain all patches
38
- for the `package.json` file. Be sure to correctly expand any object/array based
39
- values with the original package content.
41
+ The `changes` constant located in `lib/postinstall/update-package.js` should contain
42
+ all patches for the `package.json` file. Be sure to correctly expand any object/array
43
+ based values with the original package content.
40
44
 
41
45
  ### Static files
42
46
 
@@ -47,8 +51,12 @@ These files will be copied, overwriting any existing files:
47
51
 
48
52
  - `.eslintrc.js`
49
53
  - `.github/workflows/ci.yml`
54
+ - `.github/ISSUE_TEMPLATE/bug.yml`
55
+ - `.github/ISSUE_TEMPLATE/config.yml`
56
+ - `.github/CODEOWNERS`
50
57
  - `.gitignore`
51
58
  - `LICENSE.md`
59
+ - `SECURITY.md`
52
60
 
53
61
  #### Extending
54
62
 
@@ -56,27 +64,16 @@ Place files in the `lib/content/` directory, use only the file name and remove
56
64
  any leading `.` characters (i.e. `.github/workflows/ci.yml` becomes `ci.yml`
57
65
  and `.gitignore` becomes `gitignore`).
58
66
 
59
- Modify the `content` object at the top of `lib/content/index.js` to include
67
+ Modify the `content` object at the top of `lib/postinstall/copy-content.js` to include
60
68
  your new file. The object keys are destination paths, and values are source.
61
69
 
70
+ ### `package.json` checks
62
71
 
63
- ### Package installation and removal
64
-
65
- These packages will be removed:
66
-
67
- - `eslint-plugin-import`
68
- - `eslint-plugin-promise`
69
- - `eslint-plugin-standard`
70
- - `@npmcli/lint`
71
-
72
-
73
- Afterwards, these packages will be installed as devDependencies:
74
-
75
- - `eslint`
76
- - `eslint-plugin-node`
77
- - `@npmcli/eslint-config`
78
- - `tap`
72
+ `npm-template-check` is run by `postlint` and will error if the `package.json`
73
+ is not configured properly, with steps to run to correct any problems.
79
74
 
80
75
  #### Extending
81
76
 
82
- Make changes to the `removeDeps` and `devDeps` arrays in `lib/install.js`.
77
+ Add any unwanted packages to `unwantedPackages` in `lib/check.js`. Currently
78
+ the best way to install any packages is to include them as `peerDependencies`
79
+ in this repo.
@@ -0,0 +1,3 @@
1
+ # Dont modify line endings of our bin scripts
2
+ # so git status stays clean for windows tests
3
+ *.js -crlf
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const check = require('../lib/check.js')
3
+ const checkPackage = require('../lib/postlint/check-package.js')
4
+ const checkGitIgnore = require('../lib/postlint/check-gitignore.js')
4
5
 
5
6
  const main = async () => {
6
7
  const {
@@ -11,7 +12,11 @@ const main = async () => {
11
12
  throw new Error('This package requires npm >7.21.1')
12
13
  }
13
14
 
14
- const problems = await check(root)
15
+ const problems = [
16
+ ...(await checkPackage(root)),
17
+ ...(await checkGitIgnore(root)),
18
+ ]
19
+
15
20
  if (problems.length) {
16
21
  console.error('Some problems were detected:')
17
22
  console.error()
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const copyContent = require('../lib/content/index.js')
4
- const patchPackage = require('../lib/package.js')
3
+ const copyContent = require('../lib/postinstall/copy-content.js')
4
+ const patchPackage = require('../lib/postinstall/update-package.js')
5
5
 
6
6
  const main = async () => {
7
7
  const {
@@ -19,12 +19,10 @@ const main = async () => {
19
19
  return
20
20
  }
21
21
 
22
- return copyContent(root)
22
+ await copyContent(root)
23
23
  }
24
24
 
25
- // we export the promise so it can be awaited in tests, coverage is disabled
26
- // for the catch handler because it does so little it's not worth testing
27
- module.exports = main().catch(/* istanbul ignore next */ (err) => {
25
+ module.exports = main().catch((err) => {
28
26
  console.error(err.stack)
29
27
  process.exitCode = 1
30
28
  })
@@ -0,0 +1 @@
1
+ * @npm/cli-team
@@ -1 +1,3 @@
1
+ <!-- This file is automatically added by @npmcli/template-oss. Do not edit. -->
2
+
1
3
  Please send vulnerability reports through [hackerone](https://hackerone.com/github).
@@ -26,7 +26,7 @@ jobs:
26
26
  strategy:
27
27
  fail-fast: false
28
28
  matrix:
29
- node-version: [12.13.0, 12.x, 14.15.0, 14.x, 16.x]
29
+ node-version: [12.13.0, 12.x, 14.15.0, 14.x, 16.13.0, 16.x]
30
30
  platform:
31
31
  - os: ubuntu-latest
32
32
  shell: bash
@@ -51,4 +51,3 @@ jobs:
51
51
  - run: npm i --prefer-online -g npm@latest
52
52
  - run: npm ci
53
53
  - run: npm test --ignore-scripts
54
- - run: npm ls -a
@@ -9,10 +9,14 @@
9
9
  !**/.gitignore
10
10
  !/package.json
11
11
  !/package-lock.json
12
+ !/docs
12
13
  !/bin
13
14
  !/lib
14
15
  !/map.js
15
16
  !/tap-snapshots
16
17
  !/test
18
+ !/scripts
17
19
  !/README*
18
20
  !/LICENSE*
21
+ !/SECURITY*
22
+ !/CHANGELOG*
@@ -1,32 +1,41 @@
1
- const { dirname, join } = require('path')
1
+ const { dirname, join, resolve } = require('path')
2
2
  const fs = require('@npmcli/fs')
3
3
 
4
+ const contentDir = resolve(__dirname, '..', 'content')
5
+
4
6
  // keys are destination paths in the target project
5
- // values are paths to contents relative to this file
7
+ // values are paths to contents relative to '../content/'
6
8
  const content = {
7
9
  '.eslintrc.js': './eslintrc.js',
8
10
  '.github/workflows/ci.yml': './ci.yml',
9
11
  '.github/ISSUE_TEMPLATE/bug.yml': './bug.yml',
10
12
  '.github/ISSUE_TEMPLATE/config.yml': './config.yml',
13
+ '.github/CODEOWNERS': './CODEOWNERS',
11
14
  '.gitignore': './gitignore',
12
15
  'LICENSE.md': './LICENSE.md',
13
16
  'SECURITY.md': './SECURITY.md',
14
17
  }
15
18
 
19
+ const filesToDelete = [
20
+ // remove any other license files
21
+ /^LICENSE*/,
22
+ // remove any eslint config files that aren't local to the project
23
+ /^\.eslintrc\.(?!(local\.)).*/,
24
+ ]
25
+
16
26
  // given a root directory, copy all files in the content map
17
- // after purging any non-local .eslintrc config files
27
+ // after purging any files we need to delete
18
28
  const copyContent = async (root) => {
19
29
  const contents = await fs.readdir(root)
20
30
 
21
31
  for (const file of contents) {
22
- // remove any eslint config files that aren't local to the project
23
- if (file.startsWith('.eslintrc.') && !file.startsWith('.eslintrc.local.')) {
32
+ if (filesToDelete.some((p) => p.test(file))) {
24
33
  await fs.rm(join(root, file))
25
34
  }
26
35
  }
27
36
 
28
37
  for (let [target, source] of Object.entries(content)) {
29
- source = join(__dirname, source)
38
+ source = join(contentDir, source)
30
39
  target = join(root, target)
31
40
  // if the target is a subdirectory of the root, mkdirp it first
32
41
  if (dirname(target) !== root) {
@@ -1,6 +1,9 @@
1
1
  const PackageJson = require('@npmcli/package-json')
2
2
 
3
- const TEMPLATE_VERSION = require('../package.json').version
3
+ const {
4
+ version: TEMPLATE_VERSION,
5
+ name: TEMPLATE_NAME,
6
+ } = require('../../package.json')
4
7
 
5
8
  const changes = {
6
9
  author: 'GitHub Inc.',
@@ -26,25 +29,38 @@ const changes = {
26
29
  const patchPackage = async (root) => {
27
30
  const pkg = await PackageJson.load(root)
28
31
 
32
+ // If we are running this on itself, we always run the script.
33
+ // We also don't set templateVersion in package.json because
34
+ // its not relavent and would cause git churn after running
35
+ // `npm version`.
36
+ const isDogfood = pkg.content.name === TEMPLATE_NAME
37
+
29
38
  // if the target package.json has a templateVersion field matching our own
30
39
  // current version, we return false here so the postinstall script knows to
31
40
  // exit early instead of running everything again
32
- if (pkg.content.templateVersion === TEMPLATE_VERSION) {
41
+ if (pkg.content.templateVersion === TEMPLATE_VERSION && !isDogfood) {
33
42
  return false
34
43
  }
35
44
 
36
45
  // we build a new object here so our exported set of changes is not modified
37
- pkg.update({
46
+ const update = {
38
47
  ...changes,
39
48
  scripts: {
40
49
  ...pkg.content.scripts,
41
50
  ...changes.scripts,
42
51
  },
43
- })
52
+ }
53
+
54
+ if (isDogfood) {
55
+ delete update.templateVersion
56
+ }
57
+
58
+ pkg.update(update)
44
59
 
45
60
  await pkg.save()
46
61
  return true
47
62
  }
63
+
48
64
  patchPackage.changes = changes
49
65
 
50
66
  module.exports = patchPackage
@@ -0,0 +1,59 @@
1
+ const path = require('path')
2
+ const fs = require('fs')
3
+ const { sync: which } = require('which')
4
+ const { spawnSync } = require('child_process')
5
+
6
+ // The problem we are trying to solve is when a new .gitignore file
7
+ // is copied into an existing repo, there could be files already checked in
8
+ // to git that are now ignored by new gitignore rules. We want to warn
9
+ // about these files.
10
+ const check = async (root) => {
11
+ const git = path.resolve(root, '.git')
12
+ const gitignore = path.resolve(root, '.gitignore')
13
+
14
+ if (!fs.existsSync(git)) {
15
+ return []
16
+ }
17
+
18
+ if (!fs.existsSync(gitignore)) {
19
+ throw new Error(`${gitignore} must exist to run npm-template-check`)
20
+ }
21
+
22
+ const res = spawnSync(which('git'), [
23
+ 'ls-files',
24
+ '--cached',
25
+ '--ignored',
26
+ // The .gitignore file has already been placed by now with the postinstall
27
+ // script so when this script runs via postlint, it can use that file
28
+ `--exclude-from=${gitignore}`,
29
+ ], { encoding: 'utf-8', cwd: root })
30
+
31
+ const files = res.stdout
32
+ .trim()
33
+ .split('\n')
34
+ .filter(Boolean)
35
+
36
+ if (!files.length) {
37
+ return []
38
+ }
39
+
40
+ const relativeGitignore = path.relative(root, gitignore)
41
+ const ignores = fs.readFileSync(gitignore)
42
+ .toString()
43
+ .split('\n')
44
+ .filter((l) => l && !l.trim().startsWith('#'))
45
+
46
+ const message = [
47
+ `The following files are tracked by git but matching a pattern in ${relativeGitignore}:`,
48
+ ...files.map((f) => ` ${f}`),
49
+ ].join('\n')
50
+
51
+ const solution = [
52
+ 'Move files to not match one of these patterns:',
53
+ ...ignores.map((i) => ` ${i}`),
54
+ ].join('\n')
55
+
56
+ return [{ message, solution }]
57
+ }
58
+
59
+ module.exports = check
@@ -1,7 +1,7 @@
1
- const { join } = require('path')
2
- const fs = require('@npmcli/fs')
1
+ const PackageJson = require('@npmcli/package-json')
3
2
 
4
- const patchPackage = require('./package.js')
3
+ const { name: TEMPLATE_NAME } = require('../../package.json')
4
+ const patchPackage = require('../postinstall/update-package.js')
5
5
 
6
6
  const unwantedPackages = [
7
7
  '@npmcli/lint',
@@ -13,30 +13,29 @@ const unwantedPackages = [
13
13
  const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key)
14
14
 
15
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
- }
16
+ const pkg = (await PackageJson.load(root)).content
22
17
 
23
- const pkg = JSON.parse(contents)
18
+ // templateVersion doesn't apply if we're on this repo
19
+ // since we always run the scripts here
20
+ const changes = Object.entries(patchPackage.changes).filter(([key]) => {
21
+ if (pkg.name === TEMPLATE_NAME && key === 'templateVersion') {
22
+ return false
23
+ }
24
+ return true
25
+ })
24
26
 
25
27
  const problems = []
26
28
 
27
29
  const incorrectFields = []
28
30
  // 1. ensure package.json changes have been applied
29
- for (const [key, value] of Object.entries(patchPackage.changes)) {
31
+ for (const [key, value] of changes) {
30
32
  if (!hasOwn(pkg, key)) {
31
33
  incorrectFields.push({
32
34
  name: key,
33
35
  found: pkg[key],
34
36
  expected: value,
35
37
  })
36
- continue
37
- }
38
-
39
- if (value && typeof value === 'object') {
38
+ } else if (value && typeof value === 'object') {
40
39
  for (const [subKey, subValue] of Object.entries(value)) {
41
40
  if (!hasOwn(pkg[key], subKey) ||
42
41
  pkg[key][subKey] !== subValue) {
@@ -63,8 +62,15 @@ const check = async (root) => {
63
62
  message: [
64
63
  `The following package.json fields are incorrect:`,
65
64
  ...incorrectFields.map((field) => {
66
- const { name, found, expected } = field
67
- return ` Field: "${name}" Expected: "${expected}" Found: "${found}"`
65
+ const message = [
66
+ 'Field:',
67
+ `${JSON.stringify(field.name)}`,
68
+ 'Expected:',
69
+ `${JSON.stringify(field.expected)}`,
70
+ 'Found:',
71
+ `${JSON.stringify(field.found)}`,
72
+ ].join(' ')
73
+ return ` ${message}`
68
74
  }),
69
75
  ].join('\n'),
70
76
  solution: 'npm rm @npmcli/template-oss && npm i -D @npmcli/template-oss',
@@ -81,8 +87,8 @@ const check = async (root) => {
81
87
  problems.push({
82
88
  message: [
83
89
  'The following unwanted packages were found:',
84
- ...mustRemove,
85
- ].join(' '),
90
+ ...mustRemove.map((p) => ` ${p}`),
91
+ ].join('\n'),
86
92
  solution: `npm rm ${mustRemove.join(' ')}`,
87
93
  })
88
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npmcli/template-oss",
3
- "version": "2.0.0",
3
+ "version": "2.3.1",
4
4
  "description": "templated files used in npm CLI team oss projects",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -13,7 +13,6 @@
13
13
  "postlint": "npm-template-check",
14
14
  "posttest": "npm run lint",
15
15
  "postversion": "npm publish",
16
- "prelint": "ln -sf ../../bin/npm-template-check.js node_modules/.bin/npm-template-check",
17
16
  "prepublishOnly": "git push origin --follow-tags",
18
17
  "preversion": "npm test",
19
18
  "snap": "tap",
@@ -32,7 +31,7 @@
32
31
  "dependencies": {
33
32
  "@npmcli/fs": "^1.0.0",
34
33
  "@npmcli/package-json": "^1.0.1",
35
- "@npmcli/promise-spawn": "^2.0.0"
34
+ "which": "^2.0.2"
36
35
  },
37
36
  "files": [
38
37
  "bin",
@@ -40,7 +39,9 @@
40
39
  ],
41
40
  "devDependencies": {
42
41
  "@npmcli/eslint-config": "*",
43
- "eslint": "^7.32.0",
42
+ "@npmcli/promise-spawn": "^2.0.0",
43
+ "@npmcli/template-oss": "file:./",
44
+ "eslint": "^8.1.0",
44
45
  "eslint-plugin-node": "^11.1.0",
45
46
  "tap": "*"
46
47
  },
@@ -48,7 +49,9 @@
48
49
  "@npmcli/eslint-config": "^1.0.0",
49
50
  "tap": "^15.0.9"
50
51
  },
51
- "templateVersion": "1.0.3",
52
+ "tap": {
53
+ "coverage-map": "map.js"
54
+ },
52
55
  "engines": {
53
56
  "node": "^12.13.0 || ^14.15.0 || >=16"
54
57
  }