@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,433 @@
1
+ import { fail, failure } from '../assert.js'
2
+ import { AbsolutePath } from '../paths.js'
3
+ import { readFile } from '../utils/asyncfs.js'
4
+ import { $blu, $cyn, $gry, $red, $und, $wht, $ylw } from './colors.js'
5
+ import { emitColor, emitPlain, LogEmitter } from './emit.js'
6
+ import { ERROR, INFO, LogLevels, NOTICE, WARN } from './levels.js'
7
+ import { logOptions } from './options.js'
8
+
9
+ /* ========================================================================== */
10
+
11
+ /* Track changes for colors */
12
+ let _colors = logOptions.colors
13
+ logOptions.on('changed', ({ colors }) => {
14
+ _colors = colors
15
+ })
16
+
17
+ /* ========================================================================== *
18
+
19
+ /** Levels used in a {@link Report} */
20
+ export type ReportLevel = LogLevels['NOTICE' | 'WARN' | 'ERROR']
21
+
22
+ /** A record for a {@link Report} */
23
+ export interface ReportRecord {
24
+ /** The _level_ (or _severity_) of this {@link ReportRecord}. */
25
+ level: ReportLevel,
26
+ /** A detail message to associate with this {@link ReportRecord}. */
27
+ message: string | string[]
28
+
29
+ /**
30
+ * Tags to associate with this{@link ReportRecord}.
31
+ *
32
+ * Those are error categories, or error codes and are directly related with
33
+ * whatever produced the {@link Report}.
34
+ */
35
+ tags?: string [] | string | null | undefined
36
+
37
+ /** Line number in the source code (starting at `1`) */
38
+ line?: number | null | undefined
39
+ /** Column number in the source code (starting at `1`) */
40
+ column?: number | null | undefined
41
+ /** Number of characters involved (`-1` means until the end of the line ) */
42
+ length?: number | null | undefined
43
+
44
+ /** The {@link AbsolutePath} of the file associated with this. */
45
+ file?: AbsolutePath | null | undefined,
46
+ /** The _real source code_ associated with this (for error higlighting). */
47
+ source?: string | null | undefined
48
+ }
49
+
50
+ /** A {@link Report} that will standardise the way we output information. */
51
+ export interface Report {
52
+ /** The number of `notice` records _and_ annotations in this {@link Report}. */
53
+ readonly notices: number
54
+ /** The number of `warning` records _and_ annotations in this {@link Report}. */
55
+ readonly warnings: number
56
+ /** The number of `error` records _and_ annotations in this {@link Report}. */
57
+ readonly errors: number
58
+
59
+ /** The number of `notice` records in this {@link Report}. */
60
+ readonly noticeRecords: number
61
+ /** The number of `warning` records in this {@link Report}. */
62
+ readonly warningRecords: number
63
+ /** The number of `error` records in this {@link Report}. */
64
+ readonly errorRecords: number
65
+
66
+ /** The number of `notice` annotations in this {@link Report}. */
67
+ readonly noticeAnnotations: number
68
+ /** The number of `warning` annotations in this {@link Report}. */
69
+ readonly warningAnnotations: number
70
+ /** The number of `error` annotations in this {@link Report}. */
71
+ readonly errorAnnotations: number
72
+
73
+ /** The number of _all_ records in this {@link Report} */
74
+ readonly records: number
75
+ /** The number of _all_ annotations in this {@link Report} */
76
+ readonly annotations: number
77
+
78
+ /** Checks whether this {@link Report} contains records or annotations */
79
+ readonly empty: boolean
80
+
81
+ /** Add a new {@link ReportRecord | record} to this {@link Report}. */
82
+ add(...records: ReportRecord[]): this
83
+
84
+ /** Add an annotation (small note) for a file in this report */
85
+ annotate(level: ReportLevel, file: AbsolutePath, note: string): this
86
+
87
+ /** Attempt to load any source file missing from the reports */
88
+ loadSources(): Promise<void>
89
+
90
+ /** Emit this {@link Report} and throw a build failure on error. */
91
+ done(showSources?: boolean | undefined): void
92
+ }
93
+
94
+ /** Create a new {@link Report} with the given title */
95
+ export function createReport(title: string, taskName: string): Report {
96
+ const emitter = _colors ? emitColor : emitPlain
97
+ return new ReportImpl(title, taskName, emitter)
98
+ }
99
+
100
+ /* ========================================================================== *
101
+ * REPORT IMPLEMENTATION *
102
+ * ========================================================================== */
103
+
104
+ const nul = '\u2400' // null, yep, as a character, always gets sorted last
105
+ type Null = typeof nul
106
+
107
+ interface ReportInternalRecord {
108
+ readonly level: ReportLevel
109
+ readonly messages: readonly string[]
110
+ readonly tags: readonly string[]
111
+ readonly line: number
112
+ readonly column: number
113
+ readonly length: number
114
+ }
115
+
116
+ interface ReportInternalAnnotation {
117
+ readonly level: ReportLevel
118
+ readonly note: string
119
+ }
120
+
121
+ class ReportImpl implements Report {
122
+ private readonly _sources = new Map<AbsolutePath, string[]>()
123
+ private readonly _annotations = new Map<AbsolutePath, ReportInternalAnnotation>()
124
+ private readonly _records = new Map<AbsolutePath | Null, Set<ReportInternalRecord>>()
125
+ private _noticeRecords = 0
126
+ private _warningRecords = 0
127
+ private _errorRecords = 0
128
+ private _noticeAnnotations = 0
129
+ private _warningAnnotations = 0
130
+ private _errorAnnotations = 0
131
+
132
+ constructor(
133
+ private readonly _title: string,
134
+ private readonly _task: string,
135
+ private readonly _emitter: LogEmitter,
136
+ ) {}
137
+
138
+ get notices(): number {
139
+ return this._noticeRecords + this._noticeAnnotations
140
+ }
141
+
142
+ get warnings(): number {
143
+ return this._warningRecords + this._warningAnnotations
144
+ }
145
+
146
+ get errors(): number {
147
+ return this._errorRecords + this._errorAnnotations
148
+ }
149
+
150
+ get noticeRecords(): number {
151
+ return this._noticeRecords
152
+ }
153
+
154
+ get warningRecords(): number {
155
+ return this._warningRecords
156
+ }
157
+
158
+ get errorRecords(): number {
159
+ return this._errorRecords
160
+ }
161
+
162
+ get noticeAnnotations(): number {
163
+ return this._noticeAnnotations
164
+ }
165
+
166
+ get warningAnnotations(): number {
167
+ return this._warningAnnotations
168
+ }
169
+
170
+ get errorAnnotations(): number {
171
+ return this._errorAnnotations
172
+ }
173
+
174
+ get records(): number {
175
+ return this._noticeRecords + this._warningRecords + this._errorRecords
176
+ }
177
+
178
+ get annotations(): number {
179
+ return this._noticeAnnotations + this._warningAnnotations + this._errorAnnotations
180
+ }
181
+
182
+
183
+ get empty(): boolean {
184
+ return ! (this.records + this.annotations)
185
+ }
186
+
187
+ annotate(annotationLevel: ReportLevel, file: AbsolutePath, note: string): this {
188
+ if (note) {
189
+ const level = annotationLevel
190
+ this._annotations.set(file, { level, note })
191
+ switch (level) {
192
+ case NOTICE: this._noticeRecords ++; break
193
+ case WARN: this._warningRecords ++; break
194
+ case ERROR: this._errorRecords ++; break
195
+ }
196
+ }
197
+ return this
198
+ }
199
+
200
+ add(...records: ReportRecord[]): this {
201
+ for (const record of records) {
202
+ /* Normalize the basic entries in this message */
203
+ let messages =
204
+ Array.isArray(record.message) ?
205
+ [ ...record.message.map((msg) => msg.split('\n')).flat(1) ] :
206
+ record.message.split('\n')
207
+ messages = messages.filter((message) => !! message)
208
+ if (! messages.length) fail('No message for report record')
209
+
210
+ const level = record.level
211
+ const file = record.file
212
+ const source = record.source || undefined
213
+ const tags = record.tags ?
214
+ Array.isArray(record.tags) ?
215
+ [ ...record.tags ] :
216
+ [ record.tags ] :
217
+ []
218
+
219
+ switch (level) {
220
+ case NOTICE: this._noticeRecords ++; break
221
+ case WARN: this._warningRecords ++; break
222
+ case ERROR: this._errorRecords ++; break
223
+ }
224
+
225
+ /* Line, column and characters are a bit more complicated */
226
+ let line: number = 0
227
+ let column: number = 0
228
+ let length: number = 1
229
+
230
+ if (file && record.line) {
231
+ line = record.line
232
+ if (record.column) {
233
+ column = record.column
234
+ if (record.length) {
235
+ length = record.length
236
+ if (length < 0) {
237
+ length = Number.MAX_SAFE_INTEGER
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ /* Remember our source code, line by line */
244
+ if ((file && source) && (! this._sources.has(file))) {
245
+ this._sources.set(file, source.split('\n'))
246
+ }
247
+
248
+ /* Remember this normalized report */
249
+ let reports = this._records.get(file || nul)
250
+ if (! reports) this._records.set(file || nul, reports = new Set())
251
+ reports.add({ level, messages, tags, line, column, length: length })
252
+ }
253
+
254
+ /* All done */
255
+ return this
256
+ }
257
+
258
+ async loadSources(): Promise<void> {
259
+ // Read files in parallel
260
+ const promises: Promise<any>[] = []
261
+
262
+ // Iterate through all the files having records
263
+ for (const file of this._records.keys()) {
264
+ if (! file) continue
265
+ if (file === nul) continue
266
+ if (this._sources.has(file)) continue
267
+ promises.push(readFile(file, 'utf-8')
268
+ .then((source) => source.split('\n'))
269
+ .then((lines) => this._sources.set(file, lines)))
270
+ }
271
+
272
+ // Await _all_ promise, ignore errors
273
+ await Promise.allSettled(promises)
274
+ }
275
+
276
+
277
+ done(showSources?: boolean | undefined): void {
278
+ if (showSources == null) showSources = logOptions.showSources
279
+ if (! this.empty) this._emit(showSources)
280
+ if (this.errors) throw failure()
281
+ }
282
+
283
+ private _emit(showSources: boolean): this {
284
+ /* Counters for all we need to print nicely */
285
+ let fPad = 0
286
+ let aPad = 0
287
+ let mPad = 0
288
+ let lPad = 0
289
+ let cPad = 0
290
+
291
+ /* This is GIANT: sort and convert our data for easy reporting */
292
+ const entries = [ ...this._annotations.keys(), ...this._records.keys() ]
293
+ // dedupe
294
+ .filter((file, i, a) => a.indexOf(file) === i) // dedupe
295
+
296
+ // sort ("null" files first - remember, "undefined" never gets sorted)
297
+ .sort((a, b) => {
298
+ return ((a || '') < (b || '')) ? -1 : ((a || '') > (b || '')) ? 1 : 0
299
+ })
300
+
301
+ // map to a [ file, record[], annotation? ]
302
+ .map((file) => {
303
+ // Get our annotation for the file
304
+ const ann = file && file !== nul && this._annotations.get(file)
305
+
306
+ // Get the records (or an empty record array)
307
+ const records = [ ...(this._records.get(file) || []) ]
308
+ // Sort records by line / column
309
+ .sort(({ line: al, column: ac }, { line: bl, column: bc }) =>
310
+ ((al || Number.MAX_SAFE_INTEGER) - (bl || Number.MAX_SAFE_INTEGER)) ||
311
+ ((ac || Number.MAX_SAFE_INTEGER) - (bc || Number.MAX_SAFE_INTEGER)) )
312
+
313
+ // Update our record padding length
314
+ .map((record) => {
315
+ if (record.line && (record.line > lPad)) lPad = record.line
316
+ if (record.column && (record.column > cPad)) cPad = record.column
317
+ for (const message of record.messages) {
318
+ if (message.length > mPad) mPad = message.length
319
+ }
320
+ return record
321
+ })
322
+
323
+ // Update our file and annotation padding lengths
324
+ if (file && (file.length > fPad)) fPad = file.length
325
+ if (ann && (ann.note.length > aPad)) aPad = ann.note.length
326
+
327
+ // Return our entry
328
+ return { file, records, annotation: ann }
329
+ })
330
+
331
+ /* Adjust paddings... */
332
+ mPad = mPad <= 100 ? mPad : 0 // limit length of padding for breakaway lines
333
+ lPad = lPad.toString().length
334
+ cPad = cPad.toString().length
335
+
336
+ /* Basic emit options */
337
+ const options = { taskName: this._task, level: INFO }
338
+
339
+ this._emitter(options, [ '' ])
340
+ this._emitter(options, [ $und($wht(this._title)) ])
341
+
342
+
343
+ /* Iterate through all our [file,reports] tuple */
344
+ for (let f = 0; f < entries.length; f ++) {
345
+ const { file, records, annotation } = entries[f]
346
+ const source = file && file != nul && this._sources.get(file)
347
+
348
+ if ((f === 0) || entries[f - 1]?.records.length) {
349
+ this._emitter(options, [ '' ])
350
+ }
351
+
352
+ if (file && file !== nul && annotation) {
353
+ const { level, note } = annotation
354
+ const $col = level === NOTICE ? $blu : level === WARN ? $ylw : $red
355
+ const ann = `${$gry('[')}${$col(note.padStart(aPad))}${$gry(']')}`
356
+ const pad = ''.padStart(fPad - file.length) // file is underlined
357
+
358
+ this._emitter({ ...options, level }, [ $wht($und(file)), pad, ann ])
359
+ } else if (file !== nul ) {
360
+ this._emitter(options, [ $wht($und(file)) ])
361
+ } else if (f > 0) {
362
+ this._emitter(options, [ '' ]) // white line for the last
363
+ }
364
+
365
+ /* Now get each message and do our magic */
366
+ for (let r = 0; r < records.length; r ++) {
367
+ const { level, messages, tags, line, column, length = 1 } = records[r]
368
+
369
+ /* Prefix includes line and column */
370
+ let pfx: string
371
+ if (file && line) {
372
+ if (column) {
373
+ pfx = ` ${line.toString().padStart(lPad)}:${column.toString().padEnd(cPad)} `
374
+ } else {
375
+ pfx = ` ${line.toString().padStart(lPad)}:${'-'.padEnd(cPad)} `
376
+ }
377
+ } else if (file != nul) {
378
+ pfx = ` ${'-'.padStart(lPad)}:${'-'.padEnd(cPad)} `
379
+ } else {
380
+ pfx = ' ~ '
381
+ }
382
+
383
+ const prefix = ''.padStart(pfx.length + 1)
384
+
385
+ /* Nice tags */
386
+ const tag = tags.length == 0 ? '' :
387
+ `${$gry('[')}${tags.map((tag) => $cyn(tag)).join($gry('|'))}${$gry(']')}`
388
+
389
+ /* Print out our messages, one by one */
390
+ if (messages.length === 1) {
391
+ this._emitter({ ...options, level }, [ $gry(pfx), messages[0].padEnd(mPad), tag ])
392
+ } else {
393
+ for (let m = 0; m < messages.length; m ++) {
394
+ if (! m) { // first line
395
+ this._emitter({ ...options, level }, [ $gry(pfx), messages[m] ])
396
+ } else if (m === messages.length - 1) { // last line
397
+ this._emitter({ ...options, level, prefix }, [ messages[m].padEnd(mPad), tag ])
398
+ } else { // in between lines
399
+ this._emitter({ ...options, level, prefix }, [ messages[m] ])
400
+ }
401
+ }
402
+ }
403
+
404
+ /* See if we have to / can print out the source */
405
+ if (showSources && source && source[line - 1]) {
406
+ if (column) {
407
+ const $col = level === NOTICE ? $blu : level === WARN ? $ylw : $red
408
+ const offset = column - 1
409
+ const head = $gry(source[line - 1].substring(0, offset))
410
+ const body = $und($col(source[line - 1].substring(offset, offset + length)))
411
+ const tail = $gry(source[line - 1].substring(offset + length))
412
+
413
+ this._emitter({ ...options, level, prefix }, [ $gry(`| ${head}${body}${tail}`) ])
414
+ } else {
415
+ this._emitter({ ...options, level, prefix }, [ $gry(`| ${source[line - 1]}`) ])
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ /* Our totals */
422
+ const eLabel = this.errors === 1 ? 'error' : 'errors'
423
+ const wLabel = this.warnings === 1 ? 'warning' : 'warnings'
424
+ const eNumber = this.errors ? $red(this.errors) : 'no'
425
+ const wNumber = this.warnings ? $ylw(this.warnings) : 'no'
426
+
427
+ this._emitter(options, [ '' ])
428
+ this._emitter(options, [ 'Found', eNumber, eLabel, 'and', wNumber, wLabel ])
429
+ this._emitter(options, [ '' ])
430
+
431
+ return this
432
+ }
433
+ }
@@ -0,0 +1,70 @@
1
+ import { runningTasks } from '../async.js'
2
+ import { $cyn, $gry, $t } from './colors.js'
3
+ import { logOptions } from './options.js'
4
+
5
+ /* ========================================================================== */
6
+
7
+ /* Clear the current line and set column to zero */
8
+ export const zapSpinner = '\u001b[0G\u001b[2K'
9
+
10
+ /* ========================================================================== */
11
+
12
+ /* Initial value of log colors, and subscribe to changes */
13
+ let _output = logOptions.output
14
+ let _colors = logOptions.colors
15
+ let _spinner = logOptions.spinner
16
+ let _taskLength = logOptions.taskLength
17
+ logOptions.on('changed', ({ output, colors, spinner, taskLength }) => {
18
+ _output = output
19
+ _colors = colors
20
+ _spinner = spinner
21
+ _taskLength = taskLength
22
+ setupSpinner()
23
+ })
24
+
25
+ /* ========================================================================== */
26
+
27
+ /* Spinner characters */
28
+ const _spins = [
29
+ $cyn('\u2809'), // ⠉ - 14
30
+ $cyn('\u2819'), // ⠙ - 145
31
+ $cyn('\u2818'), // ⠘ - 45
32
+ $cyn('\u2838'), // ⠸ - 456
33
+ $cyn('\u2830'), // ⠰ - 56
34
+ $cyn('\u2834'), // ⠴ - 356
35
+ $cyn('\u2824'), // ⠤ - 36
36
+ $cyn('\u2826'), // ⠦ - 236
37
+ $cyn('\u2806'), // ⠆ - 23
38
+ $cyn('\u2807'), // ⠇ - 123
39
+ $cyn('\u2803'), // ⠃ - 12
40
+ $cyn('\u280b'), // ⠋ - 124
41
+ ]
42
+
43
+ /* The index in our `_spins` */
44
+ let _nextSpin = 0
45
+ /* The interval running the spinner */
46
+ let _interval: NodeJS.Timer | undefined
47
+
48
+ /* Spin the spinner! */
49
+ function spin(): void {
50
+ if (! _colors) return clearInterval(_interval)
51
+ if (! _spinner) return clearInterval(_interval)
52
+
53
+ const tasks = runningTasks()
54
+ if (! tasks.length) return
55
+
56
+ const pad = ''.padStart(_taskLength, ' ')
57
+ const names = tasks.map((task) => $t(task)).join($gry(', '))
58
+
59
+ const task = tasks.length > 1 ? 'tasks' : 'task'
60
+
61
+ _nextSpin = (++ _nextSpin) % _spins.length
62
+
63
+ _output.write(`${zapSpinner}${pad} ${_spins[_nextSpin]} Running ${tasks.length} ${task}: ${$gry(names)}`)
64
+ }
65
+
66
+ /* Start or stop the spinner */
67
+ export function setupSpinner(): void {
68
+ if (_interval) clearInterval(_interval)
69
+ if (_colors && _spinner) _interval = setInterval(spin, 150).unref()
70
+ }
package/src/log.ts ADDED
@@ -0,0 +1,68 @@
1
+ import { currentRun } from './async.js'
2
+ import { getLogger, Log } from './log/logger.js'
3
+ import { setupSpinner } from './log/spinner.js'
4
+
5
+ export * from './log/colors.js'
6
+ export * from './log/levels.js'
7
+ export * from './log/logger.js'
8
+ export * from './log/options.js'
9
+ export * from './log/report.js'
10
+
11
+ /* ========================================================================== *
12
+ * INITIALIZATION *
13
+ * ========================================================================== */
14
+
15
+ /* Remember to setup the spinner */
16
+ setupSpinner()
17
+
18
+ /* ========================================================================== *
19
+ * LOGGER *
20
+ * ========================================================================== */
21
+
22
+ /** The generic, shared `log` function type. */
23
+ export type LogFunction = ((...args: [ any, ...any ]) => void) & Log
24
+
25
+ /** Our logging function (defaulting to the `NOTICE` level) */
26
+ export const log: LogFunction = ((): LogFunction => {
27
+ /* Return either the current run's log, or the default task's logger */
28
+ const logger = (): Log => (currentRun()?.log || getLogger())
29
+
30
+ /* Create a Logger wrapping the current logger */
31
+ const wrapper: Log = {
32
+ trace(...args: [ any, ...any ]): Log {
33
+ logger().trace(...args)
34
+ return wrapper
35
+ },
36
+
37
+ debug(...args: [ any, ...any ]): Log {
38
+ logger().debug(...args)
39
+ return wrapper
40
+ },
41
+
42
+ info(...args: [ any, ...any ]): Log {
43
+ logger().info(...args)
44
+ return wrapper
45
+ },
46
+
47
+ notice(...args: [ any, ...any ]): Log {
48
+ logger().notice(...args)
49
+ return wrapper
50
+ },
51
+
52
+ warn(...args: [ any, ...any ]): Log {
53
+ logger().warn(...args)
54
+ return wrapper
55
+ },
56
+
57
+ error(...args: [ any, ...any ]): Log {
58
+ logger().error(...args)
59
+ return wrapper
60
+ },
61
+ }
62
+
63
+ /* Create a function that will default logging to "NOTICE" */
64
+ const log = (...args: [ any, ...any ]): void => void wrapper.notice(...args)
65
+
66
+ /* Return our function, with added Logger implementation */
67
+ return Object.assign(log, wrapper)
68
+ })()