@npmcli/template-oss 4.22.0 → 4.23.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.
Files changed (47) hide show
  1. package/bin/apply.js +2 -5
  2. package/bin/check.js +2 -4
  3. package/bin/release-manager.js +1 -1
  4. package/bin/release-please.js +22 -18
  5. package/lib/apply/apply-files.js +14 -19
  6. package/lib/apply/apply-version.js +1 -5
  7. package/lib/apply/index.js +1 -4
  8. package/lib/check/check-apply.js +38 -38
  9. package/lib/check/check-changelog.js +1 -4
  10. package/lib/check/check-engines.js +5 -6
  11. package/lib/check/check-gitignore.js +14 -14
  12. package/lib/check/check-required.js +13 -15
  13. package/lib/check/check-unwanted.js +2 -3
  14. package/lib/check/index.js +9 -8
  15. package/lib/config.js +86 -35
  16. package/lib/content/SECURITY-md.hbs +1 -1
  17. package/lib/content/_job-release-integration-yml.hbs +2 -0
  18. package/lib/content/action-create-check-yml.hbs +1 -1
  19. package/lib/content/action-install-latest-npm-yml.hbs +1 -1
  20. package/lib/content/ci-release-yml.hbs +2 -2
  21. package/lib/content/eslintrc-js.hbs +4 -5
  22. package/lib/content/gitignore.hbs +0 -3
  23. package/lib/content/index.js +33 -32
  24. package/lib/content/package-json.hbs +12 -2
  25. package/lib/content/post-dependabot-yml.hbs +2 -3
  26. package/lib/content/prettier-js.hbs +6 -0
  27. package/lib/content/prettierignore.hbs +3 -0
  28. package/lib/index.js +3 -3
  29. package/lib/release/changelog.js +28 -31
  30. package/lib/release/node-workspace-format.js +12 -12
  31. package/lib/release/release-manager.js +61 -76
  32. package/lib/release/release-please.js +50 -58
  33. package/lib/release/util.js +11 -8
  34. package/lib/util/ci-versions.js +3 -3
  35. package/lib/util/dependabot.js +2 -2
  36. package/lib/util/files.js +25 -22
  37. package/lib/util/git.js +7 -4
  38. package/lib/util/gitignore.js +13 -11
  39. package/lib/util/has-package.js +7 -12
  40. package/lib/util/import-or-require.js +1 -1
  41. package/lib/util/json-diff.js +22 -21
  42. package/lib/util/merge.js +19 -16
  43. package/lib/util/output.js +8 -5
  44. package/lib/util/parser.js +77 -70
  45. package/lib/util/path.js +4 -4
  46. package/lib/util/template.js +11 -10
  47. package/package.json +12 -7
@@ -39,16 +39,11 @@ const getSpecVersion = (spec, where) => {
39
39
  return null
40
40
  }
41
41
 
42
- const isVersion = (s) => s instanceof semver.SemVer
42
+ const isVersion = s => s instanceof semver.SemVer
43
43
 
44
44
  // Returns whether the pkg has the dependency in a semver
45
45
  // compatible version in one or more locationscccc
46
- const hasPackage = (
47
- pkg,
48
- spec,
49
- locations = installLocations,
50
- path
51
- ) => {
46
+ const hasPackage = (pkg, spec, locations = installLocations, path) => {
52
47
  const name = npa(spec).name
53
48
  const requested = getSpecVersion(spec)
54
49
 
@@ -57,16 +52,16 @@ const hasPackage = (
57
52
  }
58
53
 
59
54
  const existingByLocation = locations
60
- .map((location) => pkg[location])
61
- .filter((deps) => has(deps, name))
62
- .map((deps) => getSpecVersion(`${name}@${deps[name]}`, path))
55
+ .map(location => pkg[location])
56
+ .filter(deps => has(deps, name))
57
+ .map(deps => getSpecVersion(`${name}@${deps[name]}`, path))
63
58
  .filter(Boolean)
64
59
 
65
- return existingByLocation.some((existing) => {
60
+ return existingByLocation.some(existing => {
66
61
  if (existing === true) {
67
62
  return true
68
63
  }
69
- switch ([existing, requested].map((t) => isVersion(t) ? 'VER' : 'RNG').join('-')) {
64
+ switch ([existing, requested].map(t => (isVersion(t) ? 'VER' : 'RNG')).join('-')) {
70
65
  case `VER-VER`:
71
66
  // two versions, use semver.eq to check equality
72
67
  return semver.eq(existing, requested)
@@ -7,7 +7,7 @@ const { pathToFileURL } = require('url')
7
7
 
8
8
  const importOrRequireCache = new Map()
9
9
 
10
- const importOrRequire = async (path) => {
10
+ const importOrRequire = async path => {
11
11
  if (importOrRequireCache.has(path)) {
12
12
  return importOrRequireCache.get(path)
13
13
  }
@@ -6,28 +6,29 @@ const j = (obj, replacer = null) => JSON.stringify(obj, replacer, 2)
6
6
 
7
7
  // DELETE is a special string that will be the value of updated if it exists
8
8
  // but should be deleted
9
- const jsonDiff = (s1, s2, DELETE) => diff(s1, s2)
10
- .map(({ op, path, value }) => {
11
- // there could be cases where a whole object is reported
12
- // as missing and the expected value does not need to show
13
- // special DELETED values so filter those out here
14
- const msgVal = j(value, (_, v) => v === DELETE ? undefined : v)
15
- const prev = j(get(s1, path))
16
- const key = j(path.reduce((acc, p) => acc + (typeof p === 'number' ? `[${p}]` : `.${p}`)))
9
+ const jsonDiff = (s1, s2, DELETE) =>
10
+ diff(s1, s2)
11
+ .map(({ op, path, value }) => {
12
+ // there could be cases where a whole object is reported
13
+ // as missing and the expected value does not need to show
14
+ // special DELETED values so filter those out here
15
+ const msgVal = j(value, (_, v) => (v === DELETE ? undefined : v))
16
+ const prev = j(get(s1, path))
17
+ const key = j(path.reduce((acc, p) => acc + (typeof p === 'number' ? `[${p}]` : `.${p}`)))
17
18
 
18
- const msg = (...args) => format('%s is %s, expected %s', ...args)
19
- const AD = msg(key, 'missing', msgVal)
20
- const RM = msg(key, prev, 'to be removed')
21
- const UP = msg(key, prev, msgVal)
19
+ const msg = (...args) => format('%s is %s, expected %s', ...args)
20
+ const AD = msg(key, 'missing', msgVal)
21
+ const RM = msg(key, prev, 'to be removed')
22
+ const UP = msg(key, prev, msgVal)
22
23
 
23
- if (op === 'replace') {
24
- return value === DELETE ? RM : UP
25
- } else if (op === 'add' && value !== DELETE) {
26
- return AD
27
- }
28
- })
29
- .filter(Boolean)
30
- .sort((a, b) => a.localeCompare(b))
31
- .join('\n')
24
+ if (op === 'replace') {
25
+ return value === DELETE ? RM : UP
26
+ } else if (op === 'add' && value !== DELETE) {
27
+ return AD
28
+ }
29
+ })
30
+ .filter(Boolean)
31
+ .sort((a, b) => a.localeCompare(b))
32
+ .join('\n')
32
33
 
33
34
  module.exports = jsonDiff
package/lib/util/merge.js CHANGED
@@ -32,16 +32,17 @@ const mergeWith = (...args) => {
32
32
 
33
33
  // Create a merge function that will run a set of customizer functions
34
34
  const mergeWithCustomizers = (...customizers) => {
35
- return (...objects) => mergeWith({}, ...objects, (...args) => {
36
- for (const customizer of customizers) {
37
- const result = customizer(...args)
38
- // undefined means the customizer will defer to the next one
39
- // the default behavior of undefined in lodash is to merge
40
- if (result !== undefined) {
41
- return result
35
+ return (...objects) =>
36
+ mergeWith({}, ...objects, (...args) => {
37
+ for (const customizer of customizers) {
38
+ const result = customizer(...args)
39
+ // undefined means the customizer will defer to the next one
40
+ // the default behavior of undefined in lodash is to merge
41
+ if (result !== undefined) {
42
+ return result
43
+ }
42
44
  }
43
- }
44
- })
45
+ })
45
46
  }
46
47
 
47
48
  const customizers = {
@@ -52,14 +53,16 @@ const customizers = {
52
53
  }
53
54
  },
54
55
  // Merge arrays if their key matches one of the passed in keys
55
- mergeArrays: (...keys) => (value, srcValue, key) => {
56
- if (Array.isArray(srcValue)) {
57
- if (keys.includes(key)) {
58
- return (Array.isArray(value) ? value : []).concat(srcValue)
56
+ mergeArrays:
57
+ (...keys) =>
58
+ (value, srcValue, key) => {
59
+ if (Array.isArray(srcValue)) {
60
+ if (keys.includes(key)) {
61
+ return (Array.isArray(value) ? value : []).concat(srcValue)
62
+ }
63
+ return srcValue
59
64
  }
60
- return srcValue
61
- }
62
- },
65
+ },
63
66
  }
64
67
 
65
68
  module.exports = {
@@ -1,8 +1,12 @@
1
1
  const indent = (v, i = 2) => {
2
2
  if (Array.isArray(v)) {
3
- return v.map((a) => indent(a, i)).join('\n')
3
+ return v.map(a => indent(a, i)).join('\n')
4
4
  }
5
- return v.toString().split('\n').map((l) => ' '.repeat(i) + l).join('\n')
5
+ return v
6
+ .toString()
7
+ .split('\n')
8
+ .map(l => ' '.repeat(i) + l)
9
+ .join('\n')
6
10
  }
7
11
 
8
12
  const output = () => {
@@ -15,13 +19,12 @@ const output = () => {
15
19
  }
16
20
  }
17
21
 
18
- const outputProblems = (problems) => {
22
+ const outputProblems = problems => {
19
23
  const o = output()
20
24
  o.push('', 'Some problems were detected:')
21
25
  o.sep()
22
26
  for (const { title, body, solution } of problems) {
23
- const [solutionTitle, ...solutionRest] = Array.isArray(solution)
24
- ? solution : [solution]
27
+ const [solutionTitle, ...solutionRest] = Array.isArray(solution) ? solution : [solution]
25
28
  o.push(title, '', indent(body), '', `To correct it: ${solutionTitle}`)
26
29
  if (solutionRest.length) {
27
30
  o.push('', indent(solutionRest))
@@ -27,7 +27,7 @@ const traverse = (value, visit, keys = []) => {
27
27
  }
28
28
  }
29
29
 
30
- const fsOk = (code) => (error) => {
30
+ const fsOk = code => error => {
31
31
  if (error.code === 'ENOENT') {
32
32
  return null
33
33
  }
@@ -37,107 +37,114 @@ const fsOk = (code) => (error) => {
37
37
  class Base {
38
38
  static types = []
39
39
  static header = 'This file is automatically added by {{ __NAME__ }}. Do not edit.'
40
- comment = (v) => v
40
+ comment = v => v
41
41
  merge = false // supply a merge function which runs on prepare for certain types
42
42
  DELETE = template.DELETE
43
43
 
44
- constructor (target, source, options, fileOptions) {
44
+ constructor(target, source, options, fileOptions) {
45
45
  this.target = target
46
46
  this.source = source
47
47
  this.options = options
48
48
  this.fileOptions = fileOptions
49
49
  }
50
50
 
51
- header () {
51
+ header() {
52
52
  if (typeof this.comment === 'function') {
53
53
  return this.comment(this.template(this.constructor.header || ''))
54
54
  }
55
55
  }
56
56
 
57
- clean () {
57
+ clean() {
58
58
  if (this.fileOptions.clean) {
59
59
  return fs.rm(this.target).catch(fsOk())
60
60
  }
61
61
  return null
62
62
  }
63
63
 
64
- read (s) {
64
+ read(s) {
65
65
  if (Array.isArray(s)) {
66
66
  return Promise.all(s.map(f => this.read(f)))
67
67
  }
68
68
  return fs.readFile(s, { encoding: 'utf-8' })
69
69
  }
70
70
 
71
- template (s) {
71
+ template(s) {
72
72
  if (Array.isArray(s)) {
73
73
  return Promise.all(s.map(f => this.template(f)))
74
74
  }
75
75
  return template(s, this.options)
76
76
  }
77
77
 
78
- parse (s) {
78
+ parse(s) {
79
79
  return s
80
80
  }
81
81
 
82
- prepare (s) {
82
+ prepare(s) {
83
83
  const header = this.header()
84
84
  return header ? `${header}\n\n${s}` : s
85
85
  }
86
86
 
87
- prepareTarget (s) {
87
+ prepareTarget(s) {
88
88
  return s
89
89
  }
90
90
 
91
- toString (s) {
91
+ toString(s) {
92
92
  return s.toString()
93
93
  }
94
94
 
95
- async write (s) {
95
+ async write(s) {
96
96
  // XXX: find more efficient way to do this. we can build all possible dirs before get here
97
97
  await fs.mkdir(dirname(this.target), { owner: 'inherit', recursive: true, force: true })
98
98
  return fs.writeFile(this.target, this.toString(s), { owner: 'inherit' })
99
99
  }
100
100
 
101
- diffPatch (t, s) {
101
+ diffPatch(t, s) {
102
102
  // create a patch and strip out the filename. if it ends up an empty string
103
103
  // then return true since the files are equal
104
- return Diff.createPatch('', t.replace(/\r\n/g, '\n'), s.replace(/\r\n/g, '\n'))
105
- .split('\n').slice(4).join('\n')
104
+ return Diff.createPatch('', t.replace(/\r\n/g, '\n'), s.replace(/\r\n/g, '\n')).split('\n').slice(4).join('\n')
106
105
  }
107
106
 
108
- diff (t, s) {
107
+ diff(t, s) {
109
108
  return this.diffPatch(t, s)
110
109
  }
111
110
 
112
111
  // the apply methods are the only ones that should be called publically
113
112
  // XXX: everything is allowed to be overridden in base classes but we could
114
113
  // find a different solution than making everything public
115
- applyWrite () {
116
- return Promise.resolve(this.clean())
117
- .then(() => this.read(this.source))
118
- // replace template vars first, this will throw for nonexistant vars
119
- // because it must be parseable after this step
120
- .then((s) => this.template(s))
121
- // parse into whatever data structure is necessary for maniuplating
122
- // diffing, merging, etc. by default its a string
123
- .then((s) => {
124
- this.sourcePreParse = s
125
- return this.parse(s)
126
- })
127
- // prepare the source for writing and diffing, pass in current
128
- // target for merging. errors parsing or preparing targets are ok here
129
- .then((s) => this.applyTarget().catch(() => null).then((t) => this.prepare(s, t)))
130
- .then((s) => this.write(s))
131
- }
132
-
133
- applyTarget () {
134
- return Promise.resolve(this.read(this.target))
135
- .then((s) => this.parse(s))
136
- // for only preparing the target for diffing
137
- .then((s) => this.prepareTarget(s))
138
- }
139
-
140
- async applyDiff () {
114
+ applyWrite() {
115
+ return (
116
+ Promise.resolve(this.clean())
117
+ .then(() => this.read(this.source))
118
+ // replace template vars first, this will throw for nonexistant vars
119
+ // because it must be parseable after this step
120
+ .then(s => this.template(s))
121
+ // parse into whatever data structure is necessary for maniuplating
122
+ // diffing, merging, etc. by default its a string
123
+ .then(s => {
124
+ this.sourcePreParse = s
125
+ return this.parse(s)
126
+ })
127
+ // prepare the source for writing and diffing, pass in current
128
+ // target for merging. errors parsing or preparing targets are ok here
129
+ .then(s =>
130
+ this.applyTarget()
131
+ .catch(() => null)
132
+ .then(t => this.prepare(s, t)),
133
+ )
134
+ .then(s => this.write(s))
135
+ )
136
+ }
137
+
138
+ applyTarget() {
139
+ return (
140
+ Promise.resolve(this.read(this.target))
141
+ .then(s => this.parse(s))
142
+ // for only preparing the target for diffing
143
+ .then(s => this.prepareTarget(s))
144
+ )
145
+ }
146
+
147
+ async applyDiff() {
141
148
  // handle if old does not exist
142
149
  const targetError = 'ETARGETERROR'
143
150
  const target = await this.applyTarget().catch(fsOk(targetError))
@@ -148,10 +155,10 @@ class Base {
148
155
  }
149
156
 
150
157
  const source = await Promise.resolve(this.read(this.source))
151
- .then((s) => this.template(s))
152
- .then((s) => this.parse(s))
158
+ .then(s => this.template(s))
159
+ .then(s => this.parse(s))
153
160
  // gets the target to diff against in case it needs to merge, etc
154
- .then((s) => this.prepare(s, target))
161
+ .then(s => this.prepare(s, target))
155
162
 
156
163
  // if there was a target error then there is no need to diff
157
164
  // so we just show the source with an error message
@@ -174,28 +181,28 @@ class Base {
174
181
  }
175
182
 
176
183
  class Gitignore extends Base {
177
- static types = ['codeowners', '.gitignore']
178
- comment = (c) => `# ${c}`
184
+ static types = ['codeowners', '.gitignore', '.prettierignore']
185
+ comment = c => `# ${c}`
179
186
  }
180
187
 
181
188
  class Js extends Base {
182
189
  static types = ['*.js', '*.cjs']
183
- comment = (c) => `/* ${c} */`
190
+ comment = c => `/* ${c} */`
184
191
  }
185
192
 
186
193
  class Ini extends Base {
187
194
  static types = ['*.ini']
188
- comment = (c) => `; ${c}`
195
+ comment = c => `; ${c}`
189
196
 
190
- toString (s) {
197
+ toString(s) {
191
198
  return typeof s === 'string' ? s : ini.stringify(s)
192
199
  }
193
200
 
194
- parse (s) {
201
+ parse(s) {
195
202
  return typeof s === 'string' ? ini.parse(s) : s
196
203
  }
197
204
 
198
- prepare (s, t) {
205
+ prepare(s, t) {
199
206
  let source = s
200
207
  if (typeof this.merge === 'function' && t) {
201
208
  source = this.merge(t, s)
@@ -203,7 +210,7 @@ class Ini extends Base {
203
210
  return super.prepare(this.toString(source))
204
211
  }
205
212
 
206
- diff (t, s) {
213
+ diff(t, s) {
207
214
  return jsonDiff(this.parse(t), this.parse(s), this.DELETE)
208
215
  }
209
216
  }
@@ -215,14 +222,14 @@ class IniMerge extends Ini {
215
222
 
216
223
  class Markdown extends Base {
217
224
  static types = ['*.md']
218
- comment = (c) => `<!-- ${c} -->`
225
+ comment = c => `<!-- ${c} -->`
219
226
  }
220
227
 
221
228
  class Yml extends Base {
222
229
  static types = ['*.yml']
223
- comment = (c) => ` ${c}`
230
+ comment = c => ` ${c}`
224
231
 
225
- toString (s) {
232
+ toString(s) {
226
233
  try {
227
234
  return s.toString({ lineWidth: 0, indent: 2 })
228
235
  } catch (err) {
@@ -231,22 +238,22 @@ class Yml extends Base {
231
238
  }
232
239
  }
233
240
 
234
- parse (s) {
241
+ parse(s) {
235
242
  return yaml.parseDocument(s)
236
243
  }
237
244
 
238
- prepare (s) {
245
+ prepare(s) {
239
246
  s.commentBefore = this.header()
240
247
  return this.toString(s)
241
248
  }
242
249
 
243
- prepareTarget (s) {
250
+ prepareTarget(s) {
244
251
  return this.toString(s)
245
252
  }
246
253
  }
247
254
 
248
255
  class YmlMerge extends Yml {
249
- prepare (source, t) {
256
+ prepare(source, t) {
250
257
  if (t === null) {
251
258
  // If target does not exist or is in an
252
259
  // error state, we cant do anything but write
@@ -256,7 +263,7 @@ class YmlMerge extends Yml {
256
263
 
257
264
  const key = [].concat(this.key)
258
265
 
259
- const getId = (node) => {
266
+ const getId = node => {
260
267
  const index = node.items.findIndex(p => p.key?.value === this.id)
261
268
  return index !== -1 ? node.items[index].value?.value : node.toJSON()
262
269
  }
@@ -284,20 +291,20 @@ class Json extends Base {
284
291
  static types = ['*.json']
285
292
  // its a json comment! not really but we do add a special key
286
293
  // to json objects
287
- comment = (c) => ({ [`//${this.options.config.__NAME__}`]: c })
294
+ comment = c => ({ [`//${this.options.config.__NAME__}`]: c })
288
295
 
289
- toString (s) {
290
- return JSON.stringify(s, (_, v) => v === this.DELETE ? undefined : v, 2).trim() + '\n'
296
+ toString(s) {
297
+ return JSON.stringify(s, (_, v) => (v === this.DELETE ? undefined : v), 2).trim() + '\n'
291
298
  }
292
299
 
293
- parse (s) {
300
+ parse(s) {
294
301
  if (Array.isArray(s)) {
295
302
  return s.map(f => this.parse(f)).reduce((a, f) => this.merge(a, f), {})
296
303
  }
297
304
  return jsonParse(s)
298
305
  }
299
306
 
300
- prepare (s, t) {
307
+ prepare(s, t) {
301
308
  let source = s
302
309
  if (typeof this.merge === 'function' && t) {
303
310
  source = this.merge(t, s)
@@ -305,7 +312,7 @@ class Json extends Base {
305
312
  return setFirst(this.header(), source)
306
313
  }
307
314
 
308
- diff (t, s) {
315
+ diff(t, s) {
309
316
  return jsonDiff(t, s, this.DELETE)
310
317
  }
311
318
  }
@@ -322,7 +329,7 @@ class JsonMergeNoComment extends JsonMerge {
322
329
  class PackageJson extends JsonMerge {
323
330
  static types = ['package.json']
324
331
 
325
- async prepare (s, t) {
332
+ async prepare(s, t) {
326
333
  // merge new source with current pkg content
327
334
  const update = super.prepare(s, t)
328
335
 
@@ -336,7 +343,7 @@ class PackageJson extends JsonMerge {
336
343
  return update
337
344
  }
338
345
 
339
- async write (s) {
346
+ async write(s) {
340
347
  const pkg = await NpmPackageJson.load(dirname(this.target))
341
348
  pkg.update(s)
342
349
  traverse(pkg.content, (keys, value) => {
@@ -378,7 +385,7 @@ for (const parser of Object.values(Parsers)) {
378
385
  }
379
386
  }
380
387
 
381
- const getParser = (file) => {
388
+ const getParser = file => {
382
389
  for (const [type, parser] of parserLookup) {
383
390
  if (minimatch(file, type, { nocase: true, dot: true, matchBase: true })) {
384
391
  return parser
package/lib/util/path.js CHANGED
@@ -1,9 +1,9 @@
1
1
  const { posix, win32 } = require('path')
2
2
 
3
- const makePosix = (v) => v.split(win32.sep).join(posix.sep)
4
- const deglob = (v) => makePosix(v).replace(/[/*]+$/, '')
5
- const posixDir = (v) => `${v === '.' ? '' : deglob(v).replace(/\/$/, '')}${posix.sep}`
6
- const posixGlob = (str) => `${posixDir(str)}**`
3
+ const makePosix = v => v.split(win32.sep).join(posix.sep)
4
+ const deglob = v => makePosix(v).replace(/[/*]+$/, '')
5
+ const posixDir = v => `${v === '.' ? '' : deglob(v).replace(/\/$/, '')}${posix.sep}`
6
+ const posixGlob = str => `${posixDir(str)}**`
7
7
 
8
8
  module.exports = {
9
9
  makePosix,
@@ -4,12 +4,12 @@ const { Range } = require('semver')
4
4
  const fs = require('fs')
5
5
  const DELETE = '__DELETE__'
6
6
 
7
- const safeValues = (obj) => Object.entries(obj).map(([key, value]) =>
8
- [key, new Handlebars.SafeString(value)])
7
+ const safeValues = obj => Object.entries(obj).map(([key, value]) => [key, new Handlebars.SafeString(value)])
9
8
 
10
- const partialName = (s) => basename(s, extname(s)) // remove extension
11
- .replace(/^_/, '') // remove leading underscore
12
- .replace(/-([a-z])/g, (_, g) => g.toUpperCase()) // camelcase
9
+ const partialName = s =>
10
+ basename(s, extname(s)) // remove extension
11
+ .replace(/^_/, '') // remove leading underscore
12
+ .replace(/-([a-z])/g, (_, g) => g.toUpperCase()) // camelcase
13
13
 
14
14
  const makePartials = (dir, isBase) => {
15
15
  const partials = fs.readdirSync(dir).reduce((acc, f) => {
@@ -32,15 +32,16 @@ const makePartials = (dir, isBase) => {
32
32
  Handlebars.registerPartial(partials)
33
33
  }
34
34
 
35
- const setupHandlebars = (dirs) => {
35
+ const setupHandlebars = dirs => {
36
36
  Handlebars.registerHelper('obj', ({ hash }) => Object.fromEntries(safeValues(hash)))
37
+ Handlebars.registerHelper('extGlob', arr => `{${arr.join(',')}}`)
37
38
  Handlebars.registerHelper('join', (arr, sep) => arr.join(typeof sep === 'string' ? sep : ', '))
38
39
  Handlebars.registerHelper('pluck', (arr, key) => arr.map(a => a[key]))
39
- Handlebars.registerHelper('quote', (arr) => arr.map(a => `'${a}'`))
40
- Handlebars.registerHelper('last', (arr) => arr[arr.length - 1])
41
- Handlebars.registerHelper('json', (c) => JSON.stringify(c))
40
+ Handlebars.registerHelper('quote', arr => arr.map(a => `'${a}'`))
41
+ Handlebars.registerHelper('last', arr => arr[arr.length - 1])
42
+ Handlebars.registerHelper('json', c => JSON.stringify(c))
42
43
  Handlebars.registerHelper('del', () => JSON.stringify(DELETE))
43
- Handlebars.registerHelper('semverRangeMajor', (v) => new Range(v).set[0][0].semver.major)
44
+ Handlebars.registerHelper('semverRangeMajor', v => new Range(v).set[0][0].semver.major)
44
45
  Handlebars.registerHelper('lte', (a, b) => a <= b)
45
46
 
46
47
  if (Array.isArray(dirs)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npmcli/template-oss",
3
- "version": "4.22.0",
3
+ "version": "4.23.1",
4
4
  "description": "templated files used in npm CLI team oss projects",
5
5
  "main": "lib/content/index.js",
6
6
  "bin": {
@@ -10,17 +10,18 @@
10
10
  "template-oss-release-manager": "bin/release-manager.js"
11
11
  },
12
12
  "scripts": {
13
- "lint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"",
14
- "lintfix": "npm run lint -- --fix",
13
+ "lint": "npm run eslint && npm run prettier -- --check",
14
+ "lintfix": "npm run eslint -- --fix && npm run prettier -- --write",
15
15
  "posttest": "npm run lint",
16
16
  "snap": "tap",
17
17
  "test": "tap",
18
18
  "template-oss-apply": "template-oss-apply --force",
19
19
  "postlint": "template-oss-check",
20
20
  "postinstall": "template-oss-apply",
21
+ "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"",
22
+ "prettier": "prettier \"**/*.{js,cjs,ts,mjs,jsx,tsx,json}\"",
21
23
  "test-all": "npm run test -ws -iwr --if-present",
22
- "lint-all": "npm run lint -ws -iwr --if-present",
23
- "test:record": "TAP_SNAPSHOT=1 NOCK_RECORD=1 tap"
24
+ "lint-all": "npm run lint -ws -iwr --if-present"
24
25
  },
25
26
  "repository": {
26
27
  "type": "git",
@@ -56,7 +57,7 @@
56
57
  "minimatch": "^9.0.2",
57
58
  "npm-package-arg": "^11.0.1",
58
59
  "proc-log": "^4.0.0",
59
- "release-please": "16.10.2",
60
+ "release-please": "16.12.0",
60
61
  "semver": "^7.3.5",
61
62
  "undici": "^6.7.0",
62
63
  "yaml": "^2.1.1"
@@ -66,9 +67,12 @@
66
67
  "lib/"
67
68
  ],
68
69
  "devDependencies": {
70
+ "@github/prettier-config": "0.0.6",
69
71
  "@npmcli/eslint-config": "^4.0.0",
70
72
  "@npmcli/template-oss": "file:./",
73
+ "eslint-config-prettier": "^9.1.0",
71
74
  "nock": "^13.3.8",
75
+ "prettier": "^3.2.5",
72
76
  "tap": "^16.0.0"
73
77
  },
74
78
  "tap": {
@@ -86,7 +90,8 @@
86
90
  },
87
91
  "templateOSS": {
88
92
  "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
89
- "publish": true
93
+ "publish": true,
94
+ "prettier": true
90
95
  },
91
96
  "engines": {
92
97
  "node": "^18.17.0 || >=20.5.0"