@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.
- package/LICENSE.md +211 -0
- package/NOTICE.md +13 -0
- package/README.md +7 -0
- package/dist/assert.cjs +72 -0
- package/dist/assert.cjs.map +6 -0
- package/dist/assert.mjs +41 -0
- package/dist/assert.mjs.map +6 -0
- package/dist/async.cjs +58 -0
- package/dist/async.cjs.map +6 -0
- package/dist/async.mjs +30 -0
- package/dist/async.mjs.map +6 -0
- package/dist/build.cjs +136 -0
- package/dist/build.cjs.map +6 -0
- package/dist/build.mjs +110 -0
- package/dist/build.mjs.map +6 -0
- package/dist/files.cjs +113 -0
- package/dist/files.cjs.map +6 -0
- package/dist/files.mjs +88 -0
- package/dist/files.mjs.map +6 -0
- package/dist/fork.cjs +177 -0
- package/dist/fork.cjs.map +6 -0
- package/dist/fork.mjs +151 -0
- package/dist/fork.mjs.map +6 -0
- package/dist/helpers.cjs +129 -0
- package/dist/helpers.cjs.map +6 -0
- package/dist/helpers.mjs +97 -0
- package/dist/helpers.mjs.map +6 -0
- package/dist/index.cjs +25 -0
- package/dist/index.cjs.map +6 -0
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +6 -0
- package/dist/log/colors.cjs +129 -0
- package/dist/log/colors.cjs.map +6 -0
- package/dist/log/colors.mjs +93 -0
- package/dist/log/colors.mjs.map +6 -0
- package/dist/log/emit.cjs +109 -0
- package/dist/log/emit.cjs.map +6 -0
- package/dist/log/emit.mjs +83 -0
- package/dist/log/emit.mjs.map +6 -0
- package/dist/log/levels.cjs +75 -0
- package/dist/log/levels.cjs.map +6 -0
- package/dist/log/levels.mjs +41 -0
- package/dist/log/levels.mjs.map +6 -0
- package/dist/log/logger.cjs +129 -0
- package/dist/log/logger.cjs.map +6 -0
- package/dist/log/logger.mjs +104 -0
- package/dist/log/logger.mjs.map +6 -0
- package/dist/log/options.cjs +149 -0
- package/dist/log/options.cjs.map +6 -0
- package/dist/log/options.mjs +124 -0
- package/dist/log/options.mjs.map +6 -0
- package/dist/log/report.cjs +284 -0
- package/dist/log/report.cjs.map +6 -0
- package/dist/log/report.mjs +259 -0
- package/dist/log/report.mjs.map +6 -0
- package/dist/log/spinner.cjs +83 -0
- package/dist/log/spinner.cjs.map +6 -0
- package/dist/log/spinner.mjs +57 -0
- package/dist/log/spinner.mjs.map +6 -0
- package/dist/log.cjs +71 -0
- package/dist/log.cjs.map +6 -0
- package/dist/log.mjs +45 -0
- package/dist/log.mjs.map +6 -0
- package/dist/paths.cjs +158 -0
- package/dist/paths.cjs.map +6 -0
- package/dist/paths.mjs +122 -0
- package/dist/paths.mjs.map +6 -0
- package/dist/pipe.cjs +71 -0
- package/dist/pipe.cjs.map +6 -0
- package/dist/pipe.mjs +44 -0
- package/dist/pipe.mjs.map +6 -0
- package/dist/plugs/copy.cjs +95 -0
- package/dist/plugs/copy.cjs.map +6 -0
- package/dist/plugs/copy.mjs +64 -0
- package/dist/plugs/copy.mjs.map +6 -0
- package/dist/plugs/coverage/analysis.cjs +229 -0
- package/dist/plugs/coverage/analysis.cjs.map +6 -0
- package/dist/plugs/coverage/analysis.mjs +202 -0
- package/dist/plugs/coverage/analysis.mjs.map +6 -0
- package/dist/plugs/coverage/report.cjs +215 -0
- package/dist/plugs/coverage/report.cjs.map +6 -0
- package/dist/plugs/coverage/report.mjs +200 -0
- package/dist/plugs/coverage/report.mjs.map +6 -0
- package/dist/plugs/coverage.cjs +142 -0
- package/dist/plugs/coverage.cjs.map +6 -0
- package/dist/plugs/coverage.mjs +117 -0
- package/dist/plugs/coverage.mjs.map +6 -0
- package/dist/plugs/debug.cjs +50 -0
- package/dist/plugs/debug.cjs.map +6 -0
- package/dist/plugs/debug.mjs +25 -0
- package/dist/plugs/debug.mjs.map +6 -0
- package/dist/plugs/esbuild/bundle-locals.cjs +51 -0
- package/dist/plugs/esbuild/bundle-locals.cjs.map +6 -0
- package/dist/plugs/esbuild/bundle-locals.mjs +26 -0
- package/dist/plugs/esbuild/bundle-locals.mjs.map +6 -0
- package/dist/plugs/esbuild/check-dependencies.cjs +140 -0
- package/dist/plugs/esbuild/check-dependencies.cjs.map +6 -0
- package/dist/plugs/esbuild/check-dependencies.mjs +115 -0
- package/dist/plugs/esbuild/check-dependencies.mjs.map +6 -0
- package/dist/plugs/esbuild/fix-extensions.cjs +91 -0
- package/dist/plugs/esbuild/fix-extensions.cjs.map +6 -0
- package/dist/plugs/esbuild/fix-extensions.mjs +60 -0
- package/dist/plugs/esbuild/fix-extensions.mjs.map +6 -0
- package/dist/plugs/esbuild.cjs +109 -0
- package/dist/plugs/esbuild.cjs.map +6 -0
- package/dist/plugs/esbuild.mjs +83 -0
- package/dist/plugs/esbuild.mjs.map +6 -0
- package/dist/plugs/eslint/runner.cjs +91 -0
- package/dist/plugs/eslint/runner.cjs.map +6 -0
- package/dist/plugs/eslint/runner.mjs +68 -0
- package/dist/plugs/eslint/runner.mjs.map +6 -0
- package/dist/plugs/exec.cjs +128 -0
- package/dist/plugs/exec.cjs.map +6 -0
- package/dist/plugs/exec.mjs +96 -0
- package/dist/plugs/exec.mjs.map +6 -0
- package/dist/plugs/filter.cjs +59 -0
- package/dist/plugs/filter.cjs.map +6 -0
- package/dist/plugs/filter.mjs +34 -0
- package/dist/plugs/filter.mjs.map +6 -0
- package/dist/plugs/mocha/reporter.cjs +140 -0
- package/dist/plugs/mocha/reporter.cjs.map +6 -0
- package/dist/plugs/mocha/reporter.mjs +107 -0
- package/dist/plugs/mocha/reporter.mjs.map +6 -0
- package/dist/plugs/mocha/runner.cjs +73 -0
- package/dist/plugs/mocha/runner.cjs.map +6 -0
- package/dist/plugs/mocha/runner.mjs +44 -0
- package/dist/plugs/mocha/runner.mjs.map +6 -0
- package/dist/plugs/tsc/compiler.cjs +74 -0
- package/dist/plugs/tsc/compiler.cjs.map +6 -0
- package/dist/plugs/tsc/compiler.mjs +43 -0
- package/dist/plugs/tsc/compiler.mjs.map +6 -0
- package/dist/plugs/tsc/options.cjs +82 -0
- package/dist/plugs/tsc/options.cjs.map +6 -0
- package/dist/plugs/tsc/options.mjs +51 -0
- package/dist/plugs/tsc/options.mjs.map +6 -0
- package/dist/plugs/tsc/report.cjs +90 -0
- package/dist/plugs/tsc/report.cjs.map +6 -0
- package/dist/plugs/tsc/report.mjs +59 -0
- package/dist/plugs/tsc/report.mjs.map +6 -0
- package/dist/plugs/tsc/runner.cjs +101 -0
- package/dist/plugs/tsc/runner.cjs.map +6 -0
- package/dist/plugs/tsc/runner.mjs +72 -0
- package/dist/plugs/tsc/runner.mjs.map +6 -0
- package/dist/plugs.cjs +31 -0
- package/dist/plugs.cjs.map +6 -0
- package/dist/plugs.mjs +13 -0
- package/dist/plugs.mjs.map +6 -0
- package/dist/run.cjs +95 -0
- package/dist/run.cjs.map +6 -0
- package/dist/run.mjs +70 -0
- package/dist/run.mjs.map +6 -0
- package/dist/task.cjs +39 -0
- package/dist/task.cjs.map +6 -0
- package/dist/task.mjs +14 -0
- package/dist/task.mjs.map +6 -0
- package/dist/utils/asyncfs.cjs +143 -0
- package/dist/utils/asyncfs.cjs.map +6 -0
- package/dist/utils/asyncfs.mjs +83 -0
- package/dist/utils/asyncfs.mjs.map +6 -0
- package/dist/utils/caller.cjs +59 -0
- package/dist/utils/caller.cjs.map +6 -0
- package/dist/utils/caller.mjs +34 -0
- package/dist/utils/caller.mjs.map +6 -0
- package/dist/utils/match.cjs +69 -0
- package/dist/utils/match.cjs.map +6 -0
- package/dist/utils/match.mjs +38 -0
- package/dist/utils/match.mjs.map +6 -0
- package/dist/utils/options.cjs +41 -0
- package/dist/utils/options.cjs.map +6 -0
- package/dist/utils/options.mjs +16 -0
- package/dist/utils/options.mjs.map +6 -0
- package/dist/utils/walk.cjs +104 -0
- package/dist/utils/walk.cjs.map +6 -0
- package/dist/utils/walk.mjs +79 -0
- package/dist/utils/walk.mjs.map +6 -0
- package/extra/cli.mjs +212 -0
- package/extra/ts-loader.mjs +214 -0
- package/extra/webassembly.d.ts +11 -0
- package/package.json +57 -0
- package/src/assert.ts +47 -0
- package/src/async.ts +50 -0
- package/src/files.ts +129 -0
- package/src/fork.ts +263 -0
- package/src/helpers.ts +145 -0
- package/src/index.ts +20 -0
- package/src/log/colors.ts +119 -0
- package/src/log/emit.ts +125 -0
- package/src/log/levels.ts +65 -0
- package/src/log/logger.ts +171 -0
- package/src/log/options.ts +199 -0
- package/src/log/report.ts +433 -0
- package/src/log/spinner.ts +70 -0
- package/src/log.ts +68 -0
- package/src/paths.ts +213 -0
- package/src/pipe.ts +231 -0
- package/src/plugs/copy.ts +113 -0
- package/src/plugs/coverage/analysis.ts +395 -0
- package/src/plugs/coverage/report.ts +337 -0
- package/src/plugs/coverage.ts +194 -0
- package/src/plugs/debug.ts +35 -0
- package/src/plugs/esbuild/bundle-locals.ts +33 -0
- package/src/plugs/esbuild/check-dependencies.ts +158 -0
- package/src/plugs/esbuild/fix-extensions.ts +108 -0
- package/src/plugs/esbuild.ts +128 -0
- package/src/plugs/eslint/runner.ts +112 -0
- package/src/plugs/exec.ts +215 -0
- package/src/plugs/filter.ts +56 -0
- package/src/plugs/mocha/reporter.ts +152 -0
- package/src/plugs/mocha/runner.ts +77 -0
- package/src/plugs/tsc/compiler.ts +66 -0
- package/src/plugs/tsc/options.ts +97 -0
- package/src/plugs/tsc/report.ts +74 -0
- package/src/plugs/tsc/runner.ts +100 -0
- package/src/plugs.ts +33 -0
- package/src/run.ts +160 -0
- package/src/task.ts +26 -0
- package/src/utils/asyncfs.ts +82 -0
- package/src/utils/caller.ts +45 -0
- package/src/utils/match.ts +286 -0
- package/src/utils/options.ts +22 -0
- package/src/utils/walk.ts +136 -0
- package/types/assert.d.ts +18 -0
- package/types/async.d.ts +20 -0
- package/types/build.d.ts +56 -0
- package/types/files.d.ts +44 -0
- package/types/fork.d.ts +57 -0
- package/types/helpers.d.ts +49 -0
- package/types/index.d.ts +14 -0
- package/types/log/colors.d.ts +25 -0
- package/types/log/emit.d.ts +14 -0
- package/types/log/levels.d.ts +52 -0
- package/types/log/logger.d.ts +31 -0
- package/types/log/options.d.ts +40 -0
- package/types/log/report.d.ts +64 -0
- package/types/log/spinner.d.ts +2 -0
- package/types/log.d.ts +10 -0
- package/types/paths.d.ts +76 -0
- package/types/pipe.d.ts +152 -0
- package/types/plugs/copy.d.ts +27 -0
- package/types/plugs/coverage/analysis.d.ts +104 -0
- package/types/plugs/coverage/report.d.ts +53 -0
- package/types/plugs/coverage.d.ts +46 -0
- package/types/plugs/debug.d.ts +14 -0
- package/types/plugs/esbuild/bundle-locals.d.ts +6 -0
- package/types/plugs/esbuild/check-dependencies.d.ts +12 -0
- package/types/plugs/esbuild/fix-extensions.d.ts +29 -0
- package/types/plugs/esbuild.d.ts +24 -0
- package/types/plugs/eslint/runner.d.ts +22 -0
- package/types/plugs/exec.d.ts +90 -0
- package/types/plugs/filter.d.ts +23 -0
- package/types/plugs/mocha/reporter.d.ts +8 -0
- package/types/plugs/mocha/runner.d.ts +34 -0
- package/types/plugs/tsc/compiler.d.ts +24 -0
- package/types/plugs/tsc/options.d.ts +8 -0
- package/types/plugs/tsc/report.d.ts +5 -0
- package/types/plugs/tsc/runner.d.ts +13 -0
- package/types/plugs.d.ts +16 -0
- package/types/run.d.ts +89 -0
- package/types/task.d.ts +15 -0
- package/types/utils/asyncfs.d.ts +37 -0
- package/types/utils/caller.d.ts +7 -0
- package/types/utils/match.d.ts +216 -0
- package/types/utils/options.d.ts +15 -0
- 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
|
+
})()
|