@plugjs/plug 0.4.27 → 0.4.29

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 (59) hide show
  1. package/dist/async.cjs +3 -3
  2. package/dist/async.cjs.map +1 -1
  3. package/dist/async.d.ts +1 -1
  4. package/dist/async.mjs +3 -3
  5. package/dist/async.mjs.map +1 -1
  6. package/dist/build.cjs +6 -6
  7. package/dist/build.cjs.map +1 -1
  8. package/dist/build.d.ts +1 -1
  9. package/dist/build.mjs +4 -4
  10. package/dist/build.mjs.map +1 -1
  11. package/dist/cli.mjs +1 -1
  12. package/dist/fork.cjs +1 -1
  13. package/dist/fork.cjs.map +1 -1
  14. package/dist/fork.mjs +1 -1
  15. package/dist/fork.mjs.map +1 -1
  16. package/dist/helpers.cjs +15 -3
  17. package/dist/helpers.cjs.map +1 -1
  18. package/dist/helpers.d.ts +2 -2
  19. package/dist/helpers.mjs +17 -5
  20. package/dist/helpers.mjs.map +1 -1
  21. package/dist/logging/colors.cjs +5 -0
  22. package/dist/logging/colors.cjs.map +2 -2
  23. package/dist/logging/colors.d.ts +2 -0
  24. package/dist/logging/colors.mjs +4 -0
  25. package/dist/logging/colors.mjs.map +2 -2
  26. package/dist/logging/report.cjs +3 -3
  27. package/dist/logging/report.cjs.map +1 -1
  28. package/dist/logging/report.mjs +5 -5
  29. package/dist/logging/report.mjs.map +1 -1
  30. package/dist/logging/spinner.cjs +2 -2
  31. package/dist/logging/spinner.cjs.map +1 -1
  32. package/dist/logging/spinner.mjs +3 -3
  33. package/dist/logging/spinner.mjs.map +1 -1
  34. package/dist/types.cjs +0 -12
  35. package/dist/types.cjs.map +1 -1
  36. package/dist/types.d.ts +1 -10
  37. package/dist/types.mjs +0 -5
  38. package/dist/types.mjs.map +2 -2
  39. package/dist/utils/jsonc.cjs +76 -0
  40. package/dist/utils/jsonc.cjs.map +6 -0
  41. package/dist/utils/jsonc.d.ts +17 -0
  42. package/dist/utils/jsonc.mjs +50 -0
  43. package/dist/utils/jsonc.mjs.map +6 -0
  44. package/dist/utils.cjs +2 -0
  45. package/dist/utils.cjs.map +1 -1
  46. package/dist/utils.d.ts +1 -0
  47. package/dist/utils.mjs +1 -0
  48. package/dist/utils.mjs.map +1 -1
  49. package/package.json +2 -1
  50. package/src/async.ts +2 -3
  51. package/src/build.ts +17 -10
  52. package/src/fork.ts +1 -1
  53. package/src/helpers.ts +18 -6
  54. package/src/logging/colors.ts +11 -0
  55. package/src/logging/report.ts +5 -5
  56. package/src/logging/spinner.ts +3 -3
  57. package/src/types.ts +1 -15
  58. package/src/utils/jsonc.ts +62 -0
  59. package/src/utils.ts +1 -0
package/src/helpers.ts CHANGED
@@ -2,11 +2,11 @@ import { mkdtempSync, readFileSync } from 'node:fs'
2
2
  import { tmpdir } from 'node:os'
3
3
  import { join } from 'node:path'
4
4
 
5
- import { assert, assertPromises } from './asserts'
5
+ import { BuildFailure, assert, assertPromises } from './asserts'
6
6
  import { requireContext } from './async'
7
7
  import { Files } from './files'
8
8
  import { rm } from './fs'
9
- import { $p, log } from './logging'
9
+ import { $gry, $p, $plur, $wht, $ylw, log } from './logging'
10
10
  import {
11
11
  commonPath,
12
12
  getAbsoluteParent,
@@ -16,6 +16,7 @@ import {
16
16
  } from './paths'
17
17
  import { PipeImpl } from './pipe'
18
18
  import { RunBuild } from './plugs/build'
19
+ import { JsoncError, parseJsonc } from './utils'
19
20
  import { execChild } from './utils/exec'
20
21
  import { parseOptions } from './utils/options'
21
22
  import { walk } from './utils/walk'
@@ -260,11 +261,11 @@ export function exec(
260
261
  }
261
262
 
262
263
  /**
263
- * Read and parse a JSON file, throwing an error if not found.
264
+ * Read and parse a JSON/JSONC file, throwing an error if not found.
264
265
  *
265
266
  * @params file The JSON file to parse
266
267
  */
267
- export function parseJson(file: string): any {
268
+ export function parseJson(file: string, strict: boolean = false): any {
268
269
  const jsonFile = requireContext().resolve(file)
269
270
  let jsonText: string
270
271
  try {
@@ -276,8 +277,19 @@ export function parseJson(file: string): any {
276
277
  }
277
278
 
278
279
  try {
279
- return JSON.parse(jsonText)
280
+ return parseJsonc(jsonText, {
281
+ disallowComments: strict,
282
+ allowTrailingComma: ! strict,
283
+ })
280
284
  } catch (error) {
281
- log.fail(`Error parsing ${$p(jsonFile)}`, error)
285
+ if (error instanceof JsoncError) {
286
+ const errors = error.errors
287
+ log.error(`Found ${$plur(errors.length, 'error', 'errors')} parsing ${$p(jsonFile)}`)
288
+ for (const e of errors) {
289
+ log.error(` ${$wht(e.code)} ${$gry('at line')} ${$ylw(e.line)}${$gry(', column')} ${$ylw(e.column)}`)
290
+ }
291
+
292
+ throw new BuildFailure()
293
+ } else throw error
282
294
  }
283
295
  }
@@ -127,3 +127,14 @@ export function $wht(string: any): string {
127
127
  export function $und(string: any): string {
128
128
  return colorize(und, string)
129
129
  }
130
+
131
+ /** Pluralize (not really colors, but peace, love unity and respect). */
132
+ export function $plur(number: number, singular: string, plural: string, colorize: boolean = _colors): string {
133
+ return colorize ?
134
+ number === 1 ?
135
+ `${$ylw(number)} ${singular}` :
136
+ `${$ylw(number)} ${plural}` :
137
+ number === 1 ?
138
+ `${number} ${singular}` :
139
+ `${number} ${plural}`
140
+ }
@@ -1,9 +1,9 @@
1
1
  import { BuildFailure } from '../asserts'
2
2
  import { readFile } from '../fs'
3
- import { $blu, $cyn, $gry, $red, $und, $wht, $ylw } from './colors'
4
- import { ERROR, logLevels, NOTICE, WARN } from './levels'
5
- import { logOptions } from './options'
3
+ import { $blu, $cyn, $gry, $plur, $red, $und, $wht, $ylw } from './colors'
6
4
  import { githubAnnotation } from './github'
5
+ import { ERROR, NOTICE, WARN, logLevels } from './levels'
6
+ import { logOptions } from './options'
7
7
 
8
8
  import type { AbsolutePath } from '../paths'
9
9
  import type { LogEmitter } from './emit'
@@ -427,12 +427,12 @@ export class ReportImpl implements Report {
427
427
 
428
428
  const status: any[] = [ 'Found' ]
429
429
  if (this.errors) {
430
- status.push($red(this.errors), this.errors === 1 ? 'error' : 'errors' )
430
+ status.push($plur(this.errors, 'error', 'errors'))
431
431
  }
432
432
 
433
433
  if (this.warnings) {
434
434
  if (this.errors) status.push('and')
435
- status.push($ylw(this.warnings), this.warnings === 1 ? 'warning' : 'warnings' )
435
+ status.push($plur(this.warnings, 'warning', 'warnings'))
436
436
  }
437
437
 
438
438
  if (this.errors || this.warnings) {
@@ -1,7 +1,7 @@
1
1
  /* coverage ignore file */
2
2
 
3
3
  import { runningTasks } from '../async'
4
- import { $cyn, $gry, $t } from './colors'
4
+ import { $cyn, $gry, $plur, $t } from './colors'
5
5
  import { logOptions } from './options'
6
6
 
7
7
  /* ========================================================================== */
@@ -60,11 +60,11 @@ function spin(): void {
60
60
  const pad = ''.padStart(_taskLength, ' ')
61
61
  const names = tasks.map((task) => $t(task)).join($gry(', '))
62
62
 
63
- const task = tasks.length > 1 ? 'tasks' : 'task'
63
+ const task = $plur(tasks.length, 'task', 'tasks')
64
64
 
65
65
  _nextSpin = (++ _nextSpin) % _spins.length
66
66
 
67
- _output.write(`${zapSpinner}${pad} ${_spins[_nextSpin]} Running ${tasks.length} ${task}: ${$gry(names)}`)
67
+ _output.write(`${zapSpinner}${pad} ${_spins[_nextSpin]} Running ${task}: ${$gry(names)}`)
68
68
  }
69
69
 
70
70
  /* Start or stop the spinner */
package/src/types.ts CHANGED
@@ -123,25 +123,11 @@ export type ThisBuild<D extends BuildDef> = {
123
123
  never
124
124
  }
125
125
 
126
- /**
127
- * Symbol indicating that an object is a {@link Build}.
128
- *
129
- * In a compiled {@link Build} this symbol will be associated with a function
130
- * taking an array of strings (task names) and record of props to override
131
- */
132
- export const buildMarker = Symbol.for('plugjs:plug:types:Build')
133
-
134
126
  /**
135
127
  * The {@link Build} type represents the collection of {@link Task}s
136
128
  * and _properties_ compiled from a {@link BuildDef | build definition}.
137
129
  */
138
- export type Build<D extends BuildDef = BuildDef> = Props<D> & Tasks<D> & {
139
- readonly [buildMarker]: (
140
- tasks: readonly string[],
141
- props?: Record<string, string | undefined>,
142
- ) => Promise<void>
143
- }
144
-
130
+ export type Build<D extends BuildDef = BuildDef> = Props<D> & Tasks<D>
145
131
  /** A type identifying all _task names_ in a {@link Build}. */
146
132
  export type BuildTasks<B extends Build> = string & keyof {
147
133
  [ name in keyof B as B[name] extends Function ? name : never ] : any
@@ -0,0 +1,62 @@
1
+ import { EOL } from 'node:os'
2
+
3
+ import { parse, printParseErrorCode } from 'jsonc-parser'
4
+
5
+ import { $plur } from '../logging/colors'
6
+
7
+ import type { ParseError } from 'jsonc-parser'
8
+
9
+ export interface JsoncOptions {
10
+ disallowComments?: boolean;
11
+ allowTrailingComma?: boolean;
12
+ }
13
+
14
+ export class JsoncError extends Error {
15
+ constructor(public errors: { code: string, line: number, column: number } []) {
16
+ const message = [ `Found ${$plur(errors.length, 'error', 'errors', false)} parsing` ]
17
+
18
+ for (const { code, line, column } of errors) {
19
+ message.push(` ${code} at line ${line}, column ${column}`)
20
+ }
21
+
22
+ super(message.join('\n'))
23
+ }
24
+ }
25
+
26
+ export function parseJsonc<T = any>(
27
+ data: string | null | undefined,
28
+ options: JsoncOptions = {},
29
+ ): T {
30
+ const { disallowComments = false, allowTrailingComma = true } = options
31
+
32
+ if (! data) return undefined as T
33
+ const errors: ParseError[] = []
34
+ const result = parse(data, errors, {
35
+ disallowComments,
36
+ allowTrailingComma,
37
+ allowEmptyContent: false,
38
+ })
39
+
40
+ if (errors.length === 0) return result
41
+
42
+ const offsets = data.split(EOL).reduce(({ offsets, offset }, line) => {
43
+ offset += line.length + EOL.length
44
+ offsets.push(offset)
45
+ return { offsets, offset }
46
+ }, { offset: 0, offsets: [ 0 ] }).offsets
47
+
48
+ function resolveOffset(offset: number): { line: number, column: number } {
49
+ for (let i = offsets.length - 1; i > 0; i--) {
50
+ const lineOffset = offsets[i]!
51
+ if (offset < lineOffset) continue
52
+ return { line: i + 1, column: offset - lineOffset + 1 }
53
+ }
54
+
55
+ return { line: 1, column: offset + 1 }
56
+ }
57
+
58
+ throw new JsoncError(errors.map((error) => {
59
+ const code = printParseErrorCode(error.error)
60
+ return { code, ...resolveOffset(error.offset) }
61
+ }))
62
+ }
package/src/utils.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './utils/diff'
2
2
  export * from './utils/exec'
3
+ export * from './utils/jsonc'
3
4
  export * from './utils/match'
4
5
  export * from './utils/options'
5
6
  export * from './utils/singleton'