@plugjs/plug 0.0.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 (264) hide show
  1. package/LICENSE.md +211 -0
  2. package/NOTICE.md +13 -0
  3. package/README.md +7 -0
  4. package/dist/assert.cjs +72 -0
  5. package/dist/assert.cjs.map +6 -0
  6. package/dist/assert.mjs +41 -0
  7. package/dist/assert.mjs.map +6 -0
  8. package/dist/async.cjs +58 -0
  9. package/dist/async.cjs.map +6 -0
  10. package/dist/async.mjs +30 -0
  11. package/dist/async.mjs.map +6 -0
  12. package/dist/build.cjs +136 -0
  13. package/dist/build.cjs.map +6 -0
  14. package/dist/build.mjs +110 -0
  15. package/dist/build.mjs.map +6 -0
  16. package/dist/files.cjs +113 -0
  17. package/dist/files.cjs.map +6 -0
  18. package/dist/files.mjs +88 -0
  19. package/dist/files.mjs.map +6 -0
  20. package/dist/fork.cjs +177 -0
  21. package/dist/fork.cjs.map +6 -0
  22. package/dist/fork.mjs +151 -0
  23. package/dist/fork.mjs.map +6 -0
  24. package/dist/helpers.cjs +129 -0
  25. package/dist/helpers.cjs.map +6 -0
  26. package/dist/helpers.mjs +97 -0
  27. package/dist/helpers.mjs.map +6 -0
  28. package/dist/index.cjs +25 -0
  29. package/dist/index.cjs.map +6 -0
  30. package/dist/index.mjs +7 -0
  31. package/dist/index.mjs.map +6 -0
  32. package/dist/log/colors.cjs +129 -0
  33. package/dist/log/colors.cjs.map +6 -0
  34. package/dist/log/colors.mjs +93 -0
  35. package/dist/log/colors.mjs.map +6 -0
  36. package/dist/log/emit.cjs +109 -0
  37. package/dist/log/emit.cjs.map +6 -0
  38. package/dist/log/emit.mjs +83 -0
  39. package/dist/log/emit.mjs.map +6 -0
  40. package/dist/log/levels.cjs +75 -0
  41. package/dist/log/levels.cjs.map +6 -0
  42. package/dist/log/levels.mjs +41 -0
  43. package/dist/log/levels.mjs.map +6 -0
  44. package/dist/log/logger.cjs +129 -0
  45. package/dist/log/logger.cjs.map +6 -0
  46. package/dist/log/logger.mjs +104 -0
  47. package/dist/log/logger.mjs.map +6 -0
  48. package/dist/log/options.cjs +149 -0
  49. package/dist/log/options.cjs.map +6 -0
  50. package/dist/log/options.mjs +124 -0
  51. package/dist/log/options.mjs.map +6 -0
  52. package/dist/log/report.cjs +284 -0
  53. package/dist/log/report.cjs.map +6 -0
  54. package/dist/log/report.mjs +259 -0
  55. package/dist/log/report.mjs.map +6 -0
  56. package/dist/log/spinner.cjs +83 -0
  57. package/dist/log/spinner.cjs.map +6 -0
  58. package/dist/log/spinner.mjs +57 -0
  59. package/dist/log/spinner.mjs.map +6 -0
  60. package/dist/log.cjs +71 -0
  61. package/dist/log.cjs.map +6 -0
  62. package/dist/log.mjs +45 -0
  63. package/dist/log.mjs.map +6 -0
  64. package/dist/paths.cjs +158 -0
  65. package/dist/paths.cjs.map +6 -0
  66. package/dist/paths.mjs +122 -0
  67. package/dist/paths.mjs.map +6 -0
  68. package/dist/pipe.cjs +71 -0
  69. package/dist/pipe.cjs.map +6 -0
  70. package/dist/pipe.mjs +44 -0
  71. package/dist/pipe.mjs.map +6 -0
  72. package/dist/plugs/copy.cjs +95 -0
  73. package/dist/plugs/copy.cjs.map +6 -0
  74. package/dist/plugs/copy.mjs +64 -0
  75. package/dist/plugs/copy.mjs.map +6 -0
  76. package/dist/plugs/coverage/analysis.cjs +229 -0
  77. package/dist/plugs/coverage/analysis.cjs.map +6 -0
  78. package/dist/plugs/coverage/analysis.mjs +202 -0
  79. package/dist/plugs/coverage/analysis.mjs.map +6 -0
  80. package/dist/plugs/coverage/report.cjs +215 -0
  81. package/dist/plugs/coverage/report.cjs.map +6 -0
  82. package/dist/plugs/coverage/report.mjs +200 -0
  83. package/dist/plugs/coverage/report.mjs.map +6 -0
  84. package/dist/plugs/coverage.cjs +142 -0
  85. package/dist/plugs/coverage.cjs.map +6 -0
  86. package/dist/plugs/coverage.mjs +117 -0
  87. package/dist/plugs/coverage.mjs.map +6 -0
  88. package/dist/plugs/debug.cjs +50 -0
  89. package/dist/plugs/debug.cjs.map +6 -0
  90. package/dist/plugs/debug.mjs +25 -0
  91. package/dist/plugs/debug.mjs.map +6 -0
  92. package/dist/plugs/esbuild/bundle-locals.cjs +51 -0
  93. package/dist/plugs/esbuild/bundle-locals.cjs.map +6 -0
  94. package/dist/plugs/esbuild/bundle-locals.mjs +26 -0
  95. package/dist/plugs/esbuild/bundle-locals.mjs.map +6 -0
  96. package/dist/plugs/esbuild/check-dependencies.cjs +140 -0
  97. package/dist/plugs/esbuild/check-dependencies.cjs.map +6 -0
  98. package/dist/plugs/esbuild/check-dependencies.mjs +115 -0
  99. package/dist/plugs/esbuild/check-dependencies.mjs.map +6 -0
  100. package/dist/plugs/esbuild/fix-extensions.cjs +91 -0
  101. package/dist/plugs/esbuild/fix-extensions.cjs.map +6 -0
  102. package/dist/plugs/esbuild/fix-extensions.mjs +60 -0
  103. package/dist/plugs/esbuild/fix-extensions.mjs.map +6 -0
  104. package/dist/plugs/esbuild.cjs +109 -0
  105. package/dist/plugs/esbuild.cjs.map +6 -0
  106. package/dist/plugs/esbuild.mjs +83 -0
  107. package/dist/plugs/esbuild.mjs.map +6 -0
  108. package/dist/plugs/eslint/runner.cjs +91 -0
  109. package/dist/plugs/eslint/runner.cjs.map +6 -0
  110. package/dist/plugs/eslint/runner.mjs +68 -0
  111. package/dist/plugs/eslint/runner.mjs.map +6 -0
  112. package/dist/plugs/exec.cjs +128 -0
  113. package/dist/plugs/exec.cjs.map +6 -0
  114. package/dist/plugs/exec.mjs +96 -0
  115. package/dist/plugs/exec.mjs.map +6 -0
  116. package/dist/plugs/filter.cjs +59 -0
  117. package/dist/plugs/filter.cjs.map +6 -0
  118. package/dist/plugs/filter.mjs +34 -0
  119. package/dist/plugs/filter.mjs.map +6 -0
  120. package/dist/plugs/mocha/reporter.cjs +140 -0
  121. package/dist/plugs/mocha/reporter.cjs.map +6 -0
  122. package/dist/plugs/mocha/reporter.mjs +107 -0
  123. package/dist/plugs/mocha/reporter.mjs.map +6 -0
  124. package/dist/plugs/mocha/runner.cjs +73 -0
  125. package/dist/plugs/mocha/runner.cjs.map +6 -0
  126. package/dist/plugs/mocha/runner.mjs +44 -0
  127. package/dist/plugs/mocha/runner.mjs.map +6 -0
  128. package/dist/plugs/tsc/compiler.cjs +74 -0
  129. package/dist/plugs/tsc/compiler.cjs.map +6 -0
  130. package/dist/plugs/tsc/compiler.mjs +43 -0
  131. package/dist/plugs/tsc/compiler.mjs.map +6 -0
  132. package/dist/plugs/tsc/options.cjs +82 -0
  133. package/dist/plugs/tsc/options.cjs.map +6 -0
  134. package/dist/plugs/tsc/options.mjs +51 -0
  135. package/dist/plugs/tsc/options.mjs.map +6 -0
  136. package/dist/plugs/tsc/report.cjs +90 -0
  137. package/dist/plugs/tsc/report.cjs.map +6 -0
  138. package/dist/plugs/tsc/report.mjs +59 -0
  139. package/dist/plugs/tsc/report.mjs.map +6 -0
  140. package/dist/plugs/tsc/runner.cjs +101 -0
  141. package/dist/plugs/tsc/runner.cjs.map +6 -0
  142. package/dist/plugs/tsc/runner.mjs +72 -0
  143. package/dist/plugs/tsc/runner.mjs.map +6 -0
  144. package/dist/plugs.cjs +31 -0
  145. package/dist/plugs.cjs.map +6 -0
  146. package/dist/plugs.mjs +13 -0
  147. package/dist/plugs.mjs.map +6 -0
  148. package/dist/run.cjs +95 -0
  149. package/dist/run.cjs.map +6 -0
  150. package/dist/run.mjs +70 -0
  151. package/dist/run.mjs.map +6 -0
  152. package/dist/task.cjs +39 -0
  153. package/dist/task.cjs.map +6 -0
  154. package/dist/task.mjs +14 -0
  155. package/dist/task.mjs.map +6 -0
  156. package/dist/utils/asyncfs.cjs +143 -0
  157. package/dist/utils/asyncfs.cjs.map +6 -0
  158. package/dist/utils/asyncfs.mjs +83 -0
  159. package/dist/utils/asyncfs.mjs.map +6 -0
  160. package/dist/utils/caller.cjs +59 -0
  161. package/dist/utils/caller.cjs.map +6 -0
  162. package/dist/utils/caller.mjs +34 -0
  163. package/dist/utils/caller.mjs.map +6 -0
  164. package/dist/utils/match.cjs +69 -0
  165. package/dist/utils/match.cjs.map +6 -0
  166. package/dist/utils/match.mjs +38 -0
  167. package/dist/utils/match.mjs.map +6 -0
  168. package/dist/utils/options.cjs +41 -0
  169. package/dist/utils/options.cjs.map +6 -0
  170. package/dist/utils/options.mjs +16 -0
  171. package/dist/utils/options.mjs.map +6 -0
  172. package/dist/utils/walk.cjs +104 -0
  173. package/dist/utils/walk.cjs.map +6 -0
  174. package/dist/utils/walk.mjs +79 -0
  175. package/dist/utils/walk.mjs.map +6 -0
  176. package/extra/cli.mjs +212 -0
  177. package/extra/ts-loader.mjs +214 -0
  178. package/extra/webassembly.d.ts +11 -0
  179. package/package.json +57 -0
  180. package/src/assert.ts +47 -0
  181. package/src/async.ts +50 -0
  182. package/src/files.ts +129 -0
  183. package/src/fork.ts +263 -0
  184. package/src/helpers.ts +145 -0
  185. package/src/index.ts +20 -0
  186. package/src/log/colors.ts +119 -0
  187. package/src/log/emit.ts +125 -0
  188. package/src/log/levels.ts +65 -0
  189. package/src/log/logger.ts +171 -0
  190. package/src/log/options.ts +199 -0
  191. package/src/log/report.ts +433 -0
  192. package/src/log/spinner.ts +70 -0
  193. package/src/log.ts +68 -0
  194. package/src/paths.ts +213 -0
  195. package/src/pipe.ts +231 -0
  196. package/src/plugs/copy.ts +113 -0
  197. package/src/plugs/coverage/analysis.ts +395 -0
  198. package/src/plugs/coverage/report.ts +337 -0
  199. package/src/plugs/coverage.ts +194 -0
  200. package/src/plugs/debug.ts +35 -0
  201. package/src/plugs/esbuild/bundle-locals.ts +33 -0
  202. package/src/plugs/esbuild/check-dependencies.ts +158 -0
  203. package/src/plugs/esbuild/fix-extensions.ts +108 -0
  204. package/src/plugs/esbuild.ts +128 -0
  205. package/src/plugs/eslint/runner.ts +112 -0
  206. package/src/plugs/exec.ts +215 -0
  207. package/src/plugs/filter.ts +56 -0
  208. package/src/plugs/mocha/reporter.ts +152 -0
  209. package/src/plugs/mocha/runner.ts +77 -0
  210. package/src/plugs/tsc/compiler.ts +66 -0
  211. package/src/plugs/tsc/options.ts +97 -0
  212. package/src/plugs/tsc/report.ts +74 -0
  213. package/src/plugs/tsc/runner.ts +100 -0
  214. package/src/plugs.ts +33 -0
  215. package/src/run.ts +160 -0
  216. package/src/task.ts +26 -0
  217. package/src/utils/asyncfs.ts +82 -0
  218. package/src/utils/caller.ts +45 -0
  219. package/src/utils/match.ts +286 -0
  220. package/src/utils/options.ts +22 -0
  221. package/src/utils/walk.ts +136 -0
  222. package/types/assert.d.ts +18 -0
  223. package/types/async.d.ts +20 -0
  224. package/types/build.d.ts +56 -0
  225. package/types/files.d.ts +44 -0
  226. package/types/fork.d.ts +57 -0
  227. package/types/helpers.d.ts +49 -0
  228. package/types/index.d.ts +14 -0
  229. package/types/log/colors.d.ts +25 -0
  230. package/types/log/emit.d.ts +14 -0
  231. package/types/log/levels.d.ts +52 -0
  232. package/types/log/logger.d.ts +31 -0
  233. package/types/log/options.d.ts +40 -0
  234. package/types/log/report.d.ts +64 -0
  235. package/types/log/spinner.d.ts +2 -0
  236. package/types/log.d.ts +10 -0
  237. package/types/paths.d.ts +76 -0
  238. package/types/pipe.d.ts +152 -0
  239. package/types/plugs/copy.d.ts +27 -0
  240. package/types/plugs/coverage/analysis.d.ts +104 -0
  241. package/types/plugs/coverage/report.d.ts +53 -0
  242. package/types/plugs/coverage.d.ts +46 -0
  243. package/types/plugs/debug.d.ts +14 -0
  244. package/types/plugs/esbuild/bundle-locals.d.ts +6 -0
  245. package/types/plugs/esbuild/check-dependencies.d.ts +12 -0
  246. package/types/plugs/esbuild/fix-extensions.d.ts +29 -0
  247. package/types/plugs/esbuild.d.ts +24 -0
  248. package/types/plugs/eslint/runner.d.ts +22 -0
  249. package/types/plugs/exec.d.ts +90 -0
  250. package/types/plugs/filter.d.ts +23 -0
  251. package/types/plugs/mocha/reporter.d.ts +8 -0
  252. package/types/plugs/mocha/runner.d.ts +34 -0
  253. package/types/plugs/tsc/compiler.d.ts +24 -0
  254. package/types/plugs/tsc/options.d.ts +8 -0
  255. package/types/plugs/tsc/report.d.ts +5 -0
  256. package/types/plugs/tsc/runner.d.ts +13 -0
  257. package/types/plugs.d.ts +16 -0
  258. package/types/run.d.ts +89 -0
  259. package/types/task.d.ts +15 -0
  260. package/types/utils/asyncfs.d.ts +37 -0
  261. package/types/utils/caller.d.ts +7 -0
  262. package/types/utils/match.d.ts +216 -0
  263. package/types/utils/options.d.ts +15 -0
  264. package/types/utils/walk.d.ts +28 -0
@@ -0,0 +1,119 @@
1
+ import { sep } from 'node:path'
2
+ import { AbsolutePath, getCurrentWorkingDirectory, resolveRelativeChildPath } from '../paths.js'
3
+ import { logOptions } from './options.js'
4
+
5
+ /* ========================================================================== */
6
+
7
+ /* Initial value of log colors, and subscribe to changes */
8
+ let _colors = logOptions.colors
9
+ logOptions.on('changed', ({ colors }) => _colors = colors)
10
+
11
+ /* ========================================================================== *
12
+ * PRETTY COLORS *
13
+ * ========================================================================== */
14
+
15
+ const rst = '\u001b[0m' // reset all colors to default
16
+
17
+ const und = '\u001b[4m' // underline on
18
+
19
+ const gry = '\u001b[38;5;240m' // somewhat gray
20
+ const red = '\u001b[38;5;203m' // light red (Leo's favorite)
21
+ const grn = '\u001b[38;5;76m' // greenish
22
+ const ylw = '\u001b[38;5;220m' // yellow
23
+ const blu = '\u001b[38;5;69m' // brighter blue
24
+ const mgt = '\u001b[38;5;213m' // pinky magenta
25
+ const cyn = '\u001b[38;5;81m' // darker cyan
26
+ const wht = '\u001b[1;38;5;255m' // full-bright white
27
+
28
+ const tsk = '\u001b[38;5;141m' // the color for tasks (purple)
29
+
30
+ /* ========================================================================== */
31
+
32
+ function colorize(color: string, string: any): string {
33
+ if (! _colors) return `${string}`
34
+ const lines = `${string}`.split('\n')
35
+ return lines.map((line) => `${color}${line}${rst}`).join('\n')
36
+ }
37
+
38
+ /** Colorize an {@link AbsolutePath}. */
39
+ export function $p(path: AbsolutePath): string {
40
+ const directory = getCurrentWorkingDirectory()
41
+ const relative = resolveRelativeChildPath(directory, path)
42
+ const resolved = relative == null ? path : `.${sep}${relative}`
43
+ return _colors ? `${und}${gry}${resolved}${rst}` : `"${resolved}"`
44
+ }
45
+
46
+ /** Colorize a _task name_. */
47
+ export function $t(task: string): string {
48
+ return _colors ? `${tsk}${task}${rst}` : `"${task}"`
49
+ }
50
+
51
+ /** Colorize milliseconds. */
52
+ export function $ms(millis: number): string {
53
+ let string: string
54
+ if (millis >= 60000) {
55
+ // One minute or more: style is Xm Ys
56
+ const minutes = Math.floor(millis / 60000)
57
+ const seconds = Math.floor((millis % 60000) / 1000)
58
+ string = `${minutes}m ${seconds}s`
59
+ } else if (millis >= 10000) {
60
+ // Ten seconds or more: style is 12.3s
61
+ const seconds = Math.floor(millis / 1000)
62
+ const decimal = Math.floor(millis % 1000 / 100)
63
+ string = `${seconds}.${decimal}s`
64
+ } else if (millis >= 1000) {
65
+ // One second or more: style is 1.23s
66
+ const seconds = Math.floor(millis / 1000)
67
+ const decimal = Math.floor(millis % 1000 / 10)
68
+ string = `${seconds}.${decimal}s`
69
+ } else {
70
+ // Milliseconds: style is 123ms
71
+ string = `${millis}ms`
72
+ }
73
+ return _colors ? `${gry}[${string}]${rst}` : `[${string}]`
74
+ }
75
+
76
+ /** Colorize in gray. */
77
+ export function $gry(string: any): string {
78
+ return _colors ? `${gry}${string}${rst}` : string
79
+ }
80
+
81
+ /** Colorize in red. */
82
+ export function $red(string: any): string {
83
+ return colorize(red, string)
84
+ }
85
+
86
+ /** Colorize in green. */
87
+ export function $grn(string: any): string {
88
+ return colorize(grn, string)
89
+ }
90
+
91
+ /** Colorize in yellow. */
92
+ export function $ylw(string: any): string {
93
+ return colorize(ylw, string)
94
+ }
95
+
96
+ /** Colorize in blue. */
97
+ export function $blu(string: any): string {
98
+ return colorize(blu, string)
99
+ }
100
+
101
+ /** Colorize in magenta. */
102
+ export function $mgt(string: any): string {
103
+ return colorize(mgt, string)
104
+ }
105
+
106
+ /** Colorize in cyan. */
107
+ export function $cyn(string: any): string {
108
+ return colorize(cyn, string)
109
+ }
110
+
111
+ /** Colorize in white. */
112
+ export function $wht(string: any): string {
113
+ return colorize(wht, string)
114
+ }
115
+
116
+ /** Underline. */
117
+ export function $und(string: any): string {
118
+ return colorize(und, string)
119
+ }
@@ -0,0 +1,125 @@
1
+ import { formatWithOptions } from 'node:util'
2
+ import { $blu, $grn, $gry, $red, $t, $ylw } from './colors.js'
3
+ import { DEBUG, INFO, LogLevel, NOTICE, TRACE, WARN } from './levels.js'
4
+ import { logOptions } from './options.js'
5
+ import { zapSpinner } from './spinner.js'
6
+
7
+ /* ========================================================================== */
8
+
9
+ /* Initial values, and subscribe to changes */
10
+ let _output = logOptions.output
11
+ let _indentSize = logOptions.indentSize
12
+ let _taskLength = logOptions.taskLength
13
+ let _lineLength = logOptions.lineLength
14
+ let _inspectOptions = logOptions.inspectOptions
15
+ logOptions.on('changed', (options) => {
16
+ _output = options.output
17
+ _indentSize = options.indentSize
18
+ _taskLength = options.taskLength
19
+ _lineLength = options.lineLength
20
+ _inspectOptions = options.inspectOptions
21
+ })
22
+
23
+ /* ========================================================================== *
24
+ * EMIT TEXT FOR LOGS / REPORTS *
25
+ * ========================================================================== */
26
+
27
+ /** Options for the {@link LogEmitter} function family */
28
+ export interface LogEmitterOptions {
29
+ taskName: string,
30
+ level: LogLevel,
31
+ indent?: number,
32
+ prefix?: string,
33
+ }
34
+
35
+ /** Emit a line (or multiple lines) of text to the log */
36
+ export type LogEmitter = (options: LogEmitterOptions, args: any[]) => void
37
+
38
+ /* ========================================================================== */
39
+
40
+ /** Emit in full colors! */
41
+ export const emitColor: LogEmitter = (options: LogEmitterOptions, args: any[]): void => {
42
+ const { taskName, level, prefix, indent } = options
43
+ const logPrefix = prefix ? prefix : indent ? ''.padStart(indent * _indentSize) : ''
44
+
45
+ /* Prefixes, to prepend at the beginning of each line */
46
+ const prefixes: string[] = []
47
+
48
+ /* Task name or blank padding */
49
+ prefixes.push(''.padStart(_taskLength - taskName.length, ' ')) // padding
50
+ prefixes.push(`${$t(taskName)}`) // task name
51
+
52
+ /* Level indicator (our little colorful squares) */
53
+ if (level <= TRACE) {
54
+ prefixes.push(` ${$gry('\u25a1')} `) // trace: gray open
55
+ } else if (level <= DEBUG) {
56
+ prefixes.push(` ${$gry('\u25a0')} `) // debug: gray
57
+ } else if (level <= INFO) {
58
+ prefixes.push(` ${$grn('\u25a0')} `) // info: green
59
+ } else if (level <= NOTICE) {
60
+ prefixes.push(` ${$blu('\u25a0')} `) // notice: blue
61
+ } else if (level <= WARN) {
62
+ prefixes.push(` ${$ylw('\u25a0')} `) // warning: yellow
63
+ } else {
64
+ prefixes.push(` ${$red('\u25a0')} `) // error: red
65
+ }
66
+
67
+ /* The prefix (task name and level) */
68
+ prefixes.push(logPrefix)
69
+ const linePrefix = prefixes.join('')
70
+
71
+ /* Now for the normal logging of all our parameters */
72
+ const breakLength = _lineLength - _taskLength - logPrefix.length - 3 // 3 chas: space square space
73
+ const message = formatWithOptions({ ..._inspectOptions, breakLength }, ...args)
74
+
75
+ /* Write each individual line out */
76
+ for (const line of message.split('\n')) {
77
+ _output.write(zapSpinner)
78
+ _output.write(linePrefix)
79
+ _output.write(line)
80
+ _output.write('\n')
81
+ }
82
+ }
83
+
84
+ /* ========================================================================== */
85
+
86
+ /** Emit in plain text! (no colors) */
87
+ export const emitPlain: LogEmitter = (options: LogEmitterOptions, args: any[]): void => {
88
+ const { taskName, level, prefix, indent } = options
89
+ const logPrefix = prefix ? prefix : indent ? ''.padStart(indent * _indentSize) : ''
90
+
91
+ const prefixes: string[] = []
92
+
93
+ const pad = ''.padStart(_taskLength - taskName.length, ' ')
94
+ prefixes.push(`${pad}${taskName}`)
95
+
96
+ if (level <= TRACE) {
97
+ prefixes.push(' \u2502 trace \u2502 ')
98
+ } else if (level <= DEBUG) {
99
+ prefixes.push(' \u2502 debug \u2502 ')
100
+ } else if (level <= INFO) {
101
+ prefixes.push(' \u2502 info \u2502 ')
102
+ } else if (level <= NOTICE) {
103
+ prefixes.push(' \u2502 notice \u2502 ')
104
+ } else if (level <= WARN) {
105
+ prefixes.push(' \u2502 warn \u2502 ')
106
+ } else {
107
+ prefixes.push(' \u2502 error \u2502 ')
108
+ }
109
+
110
+ /* The prefix (task name and level) */
111
+ prefixes.push(logPrefix)
112
+ const linePrefix = prefixes.join('')
113
+
114
+ /* Now for the normal logging of all our parameters */
115
+ const breakLength = _lineLength - _taskLength - logPrefix.length - 12 // 12 chars of the level above
116
+ const message = formatWithOptions({ ..._inspectOptions, breakLength }, ...args)
117
+
118
+ /* Write each individual line out */
119
+ for (const line of message.split('\n')) {
120
+ _output.write(zapSpinner)
121
+ _output.write(linePrefix)
122
+ _output.write(line)
123
+ _output.write('\n')
124
+ }
125
+ }
@@ -0,0 +1,65 @@
1
+ /** Define a branded type for log levels */
2
+ export type Level<N extends number> = N & { __brand_log_level: never }
3
+
4
+ // Those are all our defined log levels: please note the absence of the ZERO
5
+ // as it's falsy and 0x1fffffffffffff is Number.MAX_SAFE_INTEGER as a constant
6
+
7
+ /** The `TRACE` log level */
8
+ export const TRACE = 10 as Level<10>
9
+ /** The `DEBUG` log level */
10
+ export const DEBUG = 20 as Level<20>
11
+ /** The `INFO` log level */
12
+ export const INFO = 30 as Level<30>
13
+ /** The `NOTICE` log level (our default) */
14
+ export const NOTICE = 40 as Level<40>
15
+ /** The `WARN` log level */
16
+ export const WARN = 50 as Level<50>
17
+ /** The `ERROR` log level */
18
+ export const ERROR = 60 as Level<60>
19
+ /** The `OFF` log level (no logs are emitted) */
20
+ export const OFF = 0x1fffffffffffff as Level<0x1fffffffffffff>
21
+
22
+ /** All our known log levels */
23
+ export const logLevels = Object.freeze({ TRACE, DEBUG, INFO, NOTICE, WARN, ERROR, OFF })
24
+
25
+ /** The type of all our known log levels */
26
+ export type LogLevels = typeof logLevels
27
+
28
+ /** ID of each one of our log levels (upper case) */
29
+ export type LogLevelKey = keyof LogLevels
30
+
31
+ /** Canonical name of each one of our log levels (lower case) */
32
+ export type LogLevelName = Lowercase<LogLevelKey>
33
+
34
+ /** A type identifying all our log level numbers */
35
+ export type LogLevel = LogLevels[LogLevelKey]
36
+
37
+ /** The type identifying a recognized log level as a `string`. */
38
+ export type LogLevelString = LogLevelKey | LogLevelName
39
+
40
+ /**
41
+ * Convert a `string` (a {@link LogLevelString}) into a {@link LogLevel}.
42
+ *
43
+ * If the level specified is not a {@link LogLevelString}, the {@link NOTICE}
44
+ * level (our default) will be returned.
45
+ */
46
+ export function getLevelNumber<L extends LogLevelString>(level: L): LogLevels[Uppercase<L>] {
47
+ const _level = level.toUpperCase() as LogLevelKey
48
+ return (_level in logLevels ? logLevels[_level] : NOTICE) as any
49
+ }
50
+
51
+ /**
52
+ * Convert a `number` (a {@link LogLevel}) into a {@link LogLevelName}.
53
+ *
54
+ * If the level specified is not precisely a {@link LogLevel}, the
55
+ * closer {@link LogLevelName} will be returned.
56
+ */
57
+ export function getLevelName<L extends LogLevel>(level: L): LogLevelName {
58
+ if (level <= TRACE) return 'trace'
59
+ if (level <= DEBUG) return 'debug'
60
+ if (level <= INFO) return 'info'
61
+ if (level <= NOTICE) return 'notice'
62
+ if (level <= WARN) return 'warn'
63
+ if (level <= ERROR) return 'error'
64
+ return 'off'
65
+ }
@@ -0,0 +1,171 @@
1
+ import { isBuildError, isBuildFailure } from '../assert.js'
2
+ import { emitColor, emitPlain, LogEmitter } from './emit.js'
3
+ import { DEBUG, ERROR, INFO, LogLevel, NOTICE, TRACE, WARN } from './levels.js'
4
+ import { logOptions } from './options.js'
5
+
6
+ /* ========================================================================== */
7
+
8
+ /* Initial value of log colors, and subscribe to changes */
9
+ let _level = logOptions.level
10
+ let _colors = logOptions.colors
11
+ let _defaultTaskName = logOptions.defaultTaskName
12
+ logOptions.on('changed', ({ defaultTaskName, colors, level }) => {
13
+ _defaultTaskName = defaultTaskName
14
+ _colors = colors
15
+ _level = level
16
+ })
17
+
18
+ /* ========================================================================== *
19
+ * LOGGER *
20
+ * ========================================================================== */
21
+
22
+ /** The basic interface giving access to log facilities. */
23
+ export interface Log {
24
+ /** Log a `TRACE` message */
25
+ trace(...args: [ any, ...any ]): this
26
+ /** Log a `DEBUG` message */
27
+ debug(...args: [ any, ...any ]): this
28
+ /** Log an `INFO` message */
29
+ info(...args: [ any, ...any ]): this
30
+ /** Log a `NOTICE` message */
31
+ notice(...args: [ any, ...any ]): this
32
+ /** Log a `WARNING` message */
33
+ warn(...args: [ any, ...any ]): this
34
+ /** Log an `ERROR` message */
35
+ error(...args: [ any, ...any ]): this
36
+ }
37
+
38
+ /** A {@link Logger} extends the basic {@link Log} adding some state. */
39
+ export interface Logger extends Log {
40
+ /** The current level for logging. */
41
+ level: LogLevel,
42
+
43
+ /** Enter a sub-level of logging, increasing indent */
44
+ enter(): this
45
+ /** Enter a sub-level of logging, increasing indent */
46
+ enter(evel: LogLevel, message: string): this
47
+ /** Leave a sub-level of logging, decreasing indent */
48
+ leave(): this
49
+ /** Leave a sub-level of logging, decreasing indent */
50
+ leave(level: LogLevel, message: string): this
51
+ }
52
+
53
+ /** Return a {@link Logger} associated with the specified task name. */
54
+ export function getLogger(task: string = _defaultTaskName): Logger {
55
+ let logger = _loggers.get(task)
56
+ if (! logger) {
57
+ const emitter = _colors ? emitColor : emitPlain
58
+ logger = new LoggerImpl(task, emitter)
59
+ _loggers.set(task, logger)
60
+ }
61
+ return logger
62
+ }
63
+
64
+ /* ========================================================================== */
65
+
66
+ /** Cache of loggers by task-name. */
67
+ const _loggers = new Map<string, Logger>()
68
+
69
+ /** Default implementation of the {@link Logger} interface. */
70
+ class LoggerImpl implements Logger {
71
+ private readonly _stack: { level: LogLevel, message: string, indent: number }[] = []
72
+ private _level = _level
73
+ private _indent = 0
74
+
75
+ constructor(
76
+ private readonly _task: string,
77
+ private readonly _emitter: LogEmitter,
78
+ ) {}
79
+
80
+ private _emit(level: LogLevel, args: [ any, ...any ]): this {
81
+ if (this._level > level) return this
82
+
83
+ // Filter out build failures (they are not to be logged)
84
+ const params = args.filter((arg) => ! isBuildFailure(arg))
85
+ if (params.length === 0) return this // no logging only build failures
86
+
87
+ // Prepare our options for logging
88
+ const options = { level, taskName: this._task, indent: this._indent }
89
+
90
+ // Dump any existing stack entry
91
+ if (this._stack.length) {
92
+ for (const { message, ...extras } of this._stack) {
93
+ this._emitter({ ...options, ...extras }, [ message ])
94
+ }
95
+ this._stack.splice(0)
96
+ }
97
+
98
+ // Print all `BuildError`s _before_ any other entry
99
+ const remaining = params.filter((arg) => {
100
+ if (isBuildError(arg)) {
101
+ this._emitter({ ...options, level: ERROR }, [ arg.message ])
102
+ return false
103
+ } else {
104
+ return true
105
+ }
106
+ })
107
+
108
+ // If we have any leftovers, dump them out, too!
109
+ if (remaining) this._emitter(options, remaining)
110
+ return this
111
+ }
112
+
113
+ get level(): LogLevel {
114
+ return this._level
115
+ }
116
+
117
+ set level(level: LogLevel) {
118
+ this._level = level
119
+ }
120
+
121
+ trace(...args: [ any, ...any ]): this {
122
+ return this._emit(TRACE, args)
123
+ }
124
+
125
+ debug(...args: [ any, ...any ]): this {
126
+ return this._emit(DEBUG, args)
127
+ }
128
+
129
+ info(...args: [ any, ...any ]): this {
130
+ return this._emit(INFO, args)
131
+ }
132
+
133
+ notice(...args: [ any, ...any ]): this {
134
+ return this._emit(NOTICE, args)
135
+ }
136
+
137
+ warn(...args: [ any, ...any ]): this {
138
+ return this._emit(WARN, args)
139
+ }
140
+
141
+ error(...args: [ any, ...any ]): this {
142
+ return this._emit(ERROR, args)
143
+ }
144
+
145
+ enter(): this
146
+ enter(level: LogLevel, message: string): this
147
+ enter(...args: [] | [ level: LogLevel, message: string ]): this {
148
+ if (args.length) {
149
+ const [ level, message ] = args
150
+ this._stack.push({ level, message, indent: this._indent })
151
+ }
152
+ this._indent ++
153
+ return this
154
+ }
155
+
156
+ leave(): this
157
+ leave(level: LogLevel, message: string): this
158
+ leave(...args: [] | [ level: LogLevel, message: string ]): this {
159
+ this._stack.pop()
160
+ this._indent --
161
+
162
+ if (this._indent < 0) this._indent = 0
163
+
164
+ if (args.length) {
165
+ const [ level, message ] = args
166
+ this._emit(level, [ message ])
167
+ }
168
+
169
+ return this
170
+ }
171
+ }
@@ -0,0 +1,199 @@
1
+ import { EventEmitter } from 'node:events'
2
+ import { Writable } from 'node:stream'
3
+ import { InspectOptions } from 'node:util'
4
+ import { getLevelNumber, LogLevel, LogLevelString, NOTICE } from './levels.js'
5
+
6
+ /* ========================================================================== */
7
+
8
+
9
+ /** Options for our {@link Logger} instances */
10
+ export interface LogOptions {
11
+ /** The current output. */
12
+ output: Writable,
13
+ /** The current level for logging. */
14
+ level: LogLevel,
15
+ /** Whether to log in colors or not. */
16
+ colors: boolean,
17
+ /** Whether to enable the tasks spinner or not. */
18
+ spinner: boolean,
19
+ /** Width of the current terminal (if any) or `80`. */
20
+ lineLength: number,
21
+ /** The maximum length of a task name (for pretty alignment). */
22
+ taskLength: number,
23
+ /** The number of spaces used for indenting. */
24
+ indentSize: number,
25
+ /** Whether to show sources in reports or not. */
26
+ showSources: boolean,
27
+ /** The task name to be used by default if a task is not contextualized. */
28
+ defaultTaskName: string,
29
+ /** The options used by NodeJS for object inspection. */
30
+ readonly inspectOptions: InspectOptions,
31
+
32
+ /** Set an inspect option in {@link LogOptions.inspectOptions}). */
33
+ setInspectOption<K extends keyof InspectOptions>(key: K, value: InspectOptions[K]): void
34
+
35
+ /** Add an event listener for the specified event. */
36
+ on(eventName: 'changed', listener: (logOptions: this) => void): this;
37
+ /** Add an event listener for the specified event triggering only once. */
38
+ once(eventName: 'changed', listener: (logOptions: this) => void): this;
39
+ /** Remove an event listener for the specified event. */
40
+ off(eventName: 'changed', listener: (logOptions: this) => void): this;
41
+
42
+ /** Convert for serialization, optionally overriding the default task name. */
43
+ fork(taskName?: string): Partial<LogOptions>
44
+ }
45
+
46
+ /* ========================================================================== *
47
+ * INTERNAL STATE *
48
+ * ========================================================================== */
49
+
50
+ class LogOptionsImpl extends EventEmitter implements LogOptions {
51
+ private _output: Writable = process.stderr
52
+ private _level: LogLevel = NOTICE
53
+ private _colors = (<NodeJS.WriteStream> this._output).isTTY
54
+ private _spinner = true // by default, the spinner is enabled
55
+ private _lineLength = (<NodeJS.WriteStream> this._output).columns || 80
56
+ private _showSources = true // by default, always show source snippets
57
+ private _inspectOptions: InspectOptions = {}
58
+ private _defaultTaskName = ''
59
+ private _taskLength = 0
60
+ private _indentSize = 2
61
+
62
+ constructor() {
63
+ super()
64
+
65
+ /* The `LOG_LEVEL` variable is one of our `debug`, `info`, ... */
66
+ if (process.env.LOG_LEVEL) {
67
+ this._level = getLevelNumber(process.env.LOG_LEVEL as LogLevelString)
68
+ }
69
+
70
+ /* If the `LOG_COLOR` variable is specified, it should be `true` or `false` */
71
+ if (process.env.LOG_COLOR) {
72
+ if (process.env.LOG_COLOR.toLowerCase() === 'true') this.colors = true
73
+ if (process.env.LOG_COLOR.toLowerCase() === 'false') this.colors = false
74
+ // Other values don't change the value of `options.colors`
75
+ }
76
+
77
+ /*
78
+ * The `__LOG_OPTIONS` variable is a JSON-serialized `LogOptions` object
79
+ * and it's processed _last_ as it's normally only created by fork below
80
+ * and consumed by the `Exec` plug (which has no other way of communicating)
81
+ */
82
+ Object.assign(this, JSON.parse(process.env.__LOG_OPTIONS || '{}'))
83
+ }
84
+
85
+ private _notifyListeners(): void {
86
+ super.emit('changed', this)
87
+ }
88
+
89
+ fork(taskName?: string): Partial<LogOptions> {
90
+ return {
91
+ level: this._level,
92
+ colors: this._colors,
93
+ lineLength: this._lineLength,
94
+ taskLength: this._taskLength,
95
+ defaultTaskName: taskName || this._defaultTaskName,
96
+ spinner: false, // forked spinner is always false
97
+ }
98
+ }
99
+
100
+ get output(): Writable {
101
+ return this._output
102
+ }
103
+
104
+ set output(output: Writable) {
105
+ this._output = output
106
+ this._colors = !! (<NodeJS.WriteStream> output).isTTY
107
+ this._lineLength = (<NodeJS.WriteStream> output).columns
108
+ this._notifyListeners()
109
+ }
110
+
111
+ get level(): LogLevel {
112
+ return this._level
113
+ }
114
+
115
+ set level(level: LogLevel) {
116
+ this._level = level
117
+ this._notifyListeners()
118
+ }
119
+
120
+ get colors(): boolean {
121
+ return this._colors
122
+ }
123
+
124
+ set colors(color: boolean) {
125
+ this._colors = color
126
+ this._notifyListeners()
127
+ }
128
+
129
+ get spinner(): boolean {
130
+ return this._spinner
131
+ }
132
+
133
+ set spinner(spinner: boolean) {
134
+ this._spinner = spinner
135
+ this._notifyListeners()
136
+ }
137
+
138
+ get lineLength(): number {
139
+ return this._lineLength
140
+ }
141
+
142
+ set lineLength(lineLength: number) {
143
+ this._lineLength = lineLength
144
+ this._notifyListeners()
145
+ }
146
+
147
+ get taskLength(): number {
148
+ return this._taskLength
149
+ }
150
+
151
+ set taskLength(taskLength: number) {
152
+ this._taskLength = taskLength
153
+ this._notifyListeners()
154
+ }
155
+
156
+ get indentSize(): number {
157
+ return this._indentSize
158
+ }
159
+
160
+ set indentSize(indentSize: number) {
161
+ this._indentSize = indentSize
162
+ if (this._indentSize < 1) this._indentSize = 1
163
+ this._notifyListeners()
164
+ }
165
+
166
+ get showSources(): boolean {
167
+ return this._showSources
168
+ }
169
+
170
+ set showSources(showSources: boolean) {
171
+ this._showSources = showSources
172
+ this._notifyListeners()
173
+ }
174
+
175
+ get defaultTaskName(): string {
176
+ return this._defaultTaskName
177
+ }
178
+
179
+ set defaultTaskName(defaultTaskName: string) {
180
+ this._defaultTaskName = defaultTaskName
181
+ this._notifyListeners()
182
+ }
183
+
184
+ get inspectOptions(): InspectOptions {
185
+ return {
186
+ colors: this._colors,
187
+ breakLength: this._lineLength,
188
+ ...this._inspectOptions,
189
+ }
190
+ }
191
+
192
+ setInspectOption<K extends keyof InspectOptions>(key: K, value: InspectOptions[K]): void {
193
+ this._inspectOptions[key] = value
194
+ this._notifyListeners()
195
+ }
196
+ }
197
+
198
+ /** Shared instance of our {@link LogOptions}. */
199
+ export const logOptions: LogOptions = new LogOptionsImpl()