@plugjs/plug 0.4.35 → 0.5.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/dist/build.cjs +12 -5
- package/dist/build.cjs.map +1 -1
- package/dist/build.d.ts +3 -1
- package/dist/build.mjs +12 -6
- package/dist/build.mjs.map +1 -1
- package/dist/cli.mjs +12 -7
- package/dist/cli.mjs.map +2 -2
- package/dist/fork.cjs +20 -13
- package/dist/fork.cjs.map +1 -1
- package/dist/fork.d.ts +1 -0
- package/dist/fork.mjs +20 -13
- package/dist/fork.mjs.map +1 -1
- package/dist/logging/emit.cjs +31 -2
- package/dist/logging/emit.cjs.map +2 -2
- package/dist/logging/emit.d.ts +14 -0
- package/dist/logging/emit.mjs +29 -2
- package/dist/logging/emit.mjs.map +2 -2
- package/dist/logging/github.cjs +4 -7
- package/dist/logging/github.cjs.map +1 -1
- package/dist/logging/github.mjs +4 -7
- package/dist/logging/github.mjs.map +1 -1
- package/dist/logging/logger.cjs +23 -37
- package/dist/logging/logger.cjs.map +1 -1
- package/dist/logging/logger.d.ts +7 -7
- package/dist/logging/logger.mjs +24 -38
- package/dist/logging/logger.mjs.map +1 -1
- package/dist/logging/options.cjs +30 -29
- package/dist/logging/options.cjs.map +1 -1
- package/dist/logging/options.d.ts +2 -11
- package/dist/logging/options.mjs +30 -29
- package/dist/logging/options.mjs.map +1 -1
- package/dist/logging.cjs +5 -3
- package/dist/logging.cjs.map +1 -1
- package/dist/logging.mjs +5 -3
- package/dist/logging.mjs.map +1 -1
- package/dist/plugs/exports.cjs +3 -5
- package/dist/plugs/exports.cjs.map +1 -1
- package/dist/plugs/exports.mjs +3 -5
- package/dist/plugs/exports.mjs.map +1 -1
- package/dist/utils/ansi.cjs +39 -0
- package/dist/utils/ansi.cjs.map +6 -0
- package/dist/utils/ansi.d.ts +4 -0
- package/dist/utils/ansi.mjs +13 -0
- package/dist/utils/ansi.mjs.map +6 -0
- package/dist/utils/exec.cjs +10 -1
- package/dist/utils/exec.cjs.map +1 -1
- package/dist/utils/exec.mjs +11 -2
- package/dist/utils/exec.mjs.map +1 -1
- package/dist/utils.cjs +2 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.mjs +1 -0
- package/dist/utils.mjs.map +1 -1
- package/package.json +1 -1
- package/src/build.ts +14 -5
- package/src/cli.mts +11 -5
- package/src/fork.ts +26 -15
- package/src/logging/emit.ts +65 -2
- package/src/logging/github.ts +4 -11
- package/src/logging/logger.ts +25 -43
- package/src/logging/options.ts +29 -37
- package/src/logging.ts +5 -3
- package/src/plugs/exports.ts +3 -4
- package/src/utils/ansi.ts +10 -0
- package/src/utils/exec.ts +7 -2
- package/src/utils.ts +1 -0
package/src/cli.mts
CHANGED
|
@@ -7,13 +7,16 @@ import _fs from 'node:fs'
|
|
|
7
7
|
import { main, yargsParser } from '@plugjs/tsrun'
|
|
8
8
|
|
|
9
9
|
import { BuildFailure } from './asserts'
|
|
10
|
+
import { runAsync } from './async'
|
|
10
11
|
import { invokeTasks, isBuild } from './build'
|
|
11
12
|
import { $blu, $gry, $p, $red, $t, $und, $wht } from './logging/colors'
|
|
12
13
|
import { logLevels } from './logging/levels'
|
|
13
14
|
import { logOptions } from './logging/options'
|
|
14
15
|
import { getCurrentWorkingDirectory, resolveAbsolutePath, resolveDirectory, resolveFile } from './paths'
|
|
16
|
+
import { Context } from './pipe'
|
|
15
17
|
|
|
16
18
|
import type { AbsolutePath } from './paths'
|
|
19
|
+
import type { Build } from './types'
|
|
17
20
|
|
|
18
21
|
/* Log levels */
|
|
19
22
|
const { TRACE, DEBUG, INFO, NOTICE, WARN, ERROR, OFF } = logLevels
|
|
@@ -258,11 +261,14 @@ main(import.meta.url, async (args: string[]): Promise<void> => {
|
|
|
258
261
|
if (tasks.length === 0) tasks.push('default')
|
|
259
262
|
|
|
260
263
|
// Import and check build file
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
maybeBuild
|
|
265
|
-
|
|
264
|
+
const initialContext = new Context(buildFile, '')
|
|
265
|
+
const maybeBuild = await runAsync(initialContext, async (): Promise<Build | void> => {
|
|
266
|
+
let maybeBuild = await import(buildFile)
|
|
267
|
+
while (maybeBuild) {
|
|
268
|
+
if (isBuild(maybeBuild)) return maybeBuild
|
|
269
|
+
maybeBuild = maybeBuild.default
|
|
270
|
+
}
|
|
271
|
+
})
|
|
266
272
|
|
|
267
273
|
// We _need_ a build
|
|
268
274
|
if (! isBuild(maybeBuild)) {
|
package/src/fork.ts
CHANGED
|
@@ -4,9 +4,11 @@ import { assert, BuildFailure } from './asserts'
|
|
|
4
4
|
import { runAsync } from './async'
|
|
5
5
|
import { Files } from './files'
|
|
6
6
|
import { $gry, $p, $red, logOptions } from './logging'
|
|
7
|
+
import { emit, emitForked } from './logging/emit'
|
|
7
8
|
import { requireFilename, resolveFile } from './paths'
|
|
8
9
|
import { Context, install } from './pipe'
|
|
9
10
|
|
|
11
|
+
import type { ForkedLogMessage } from './logging/emit'
|
|
10
12
|
import type { AbsolutePath } from './paths'
|
|
11
13
|
import type { Plug, PlugName, PlugResult } from './pipe'
|
|
12
14
|
|
|
@@ -37,6 +39,7 @@ export interface ForkData {
|
|
|
37
39
|
filesDir: AbsolutePath,
|
|
38
40
|
/** All files to pipe */
|
|
39
41
|
filesList: AbsolutePath[],
|
|
42
|
+
logIndent: number,
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
/** Fork result, from child to parent process */
|
|
@@ -69,6 +72,7 @@ export abstract class ForkingPlug implements Plug<PlugResult> {
|
|
|
69
72
|
buildFile: context.buildFile,
|
|
70
73
|
filesDir: files.directory,
|
|
71
74
|
filesList: [ ...files.absolutePaths() ],
|
|
75
|
+
logIndent: context.log.indent,
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
/* Get _this_ filename to spawn */
|
|
@@ -76,7 +80,7 @@ export abstract class ForkingPlug implements Plug<PlugResult> {
|
|
|
76
80
|
context.log.debug('About to fork plug from', $p(this._scriptFile))
|
|
77
81
|
|
|
78
82
|
/* Environment variables */
|
|
79
|
-
const env = { ...process.env, ...logOptions.forkEnv(context.taskName
|
|
83
|
+
const env = { ...process.env, ...logOptions.forkEnv(context.taskName) }
|
|
80
84
|
|
|
81
85
|
/* Check our args (reversed) to see if the last specifies `coverageDir` */
|
|
82
86
|
for (let i = this._arguments.length - 1; i >= 0; i --) {
|
|
@@ -95,15 +99,11 @@ export abstract class ForkingPlug implements Plug<PlugResult> {
|
|
|
95
99
|
|
|
96
100
|
/* Run our script in a _separate_ process */
|
|
97
101
|
const child = fork(script, {
|
|
98
|
-
stdio: [ 'ignore', 'inherit', 'inherit', 'ipc'
|
|
102
|
+
stdio: [ 'ignore', 'inherit', 'inherit', 'ipc' ],
|
|
99
103
|
env,
|
|
100
104
|
})
|
|
101
105
|
|
|
102
|
-
/*
|
|
103
|
-
if (child.stdio[4]) {
|
|
104
|
-
child.stdio[4].on('data', (data) => logOptions.output.write(data))
|
|
105
|
-
}
|
|
106
|
-
|
|
106
|
+
/* Do some logging... */
|
|
107
107
|
context.log.info('Running', $p(script), $gry(`(pid=${child.pid})`))
|
|
108
108
|
|
|
109
109
|
/* Return a promise from the child process events */
|
|
@@ -116,9 +116,18 @@ export abstract class ForkingPlug implements Plug<PlugResult> {
|
|
|
116
116
|
return done || reject(BuildFailure.fail())
|
|
117
117
|
})
|
|
118
118
|
|
|
119
|
-
child.on('message', (message: ForkResult) => {
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
child.on('message', (message: ForkResult | ForkedLogMessage) => {
|
|
120
|
+
if ('logLevel' in message) {
|
|
121
|
+
const { logLevel, taskName, lines } = message
|
|
122
|
+
lines.forEach((line) => {
|
|
123
|
+
// this is _a hack_, as we want to reuse the indent and prefix of
|
|
124
|
+
// the current log, but the task name from the forked plug!!!
|
|
125
|
+
(context.log as any)._emit(logLevel, [ line ], taskName)
|
|
126
|
+
})
|
|
127
|
+
} else {
|
|
128
|
+
context.log.debug('Message from forked plug process with PID', child.pid, message)
|
|
129
|
+
response = message
|
|
130
|
+
}
|
|
122
131
|
})
|
|
123
132
|
|
|
124
133
|
child.on('exit', (code, signal) => {
|
|
@@ -204,10 +213,15 @@ if ((process.argv[1] === requireFilename(__fileurl)) && (process.send)) {
|
|
|
204
213
|
buildFile,
|
|
205
214
|
filesDir,
|
|
206
215
|
filesList,
|
|
216
|
+
logIndent,
|
|
207
217
|
} = message
|
|
208
218
|
|
|
219
|
+
/* Before anything else, capture logs! */
|
|
220
|
+
emit.emitter = emitForked // replace the log emitter...
|
|
221
|
+
|
|
209
222
|
/* First of all, our plug context */
|
|
210
223
|
const context = new Context(buildFile, taskName)
|
|
224
|
+
context.log.indent = logIndent
|
|
211
225
|
context.log.debug('Message from parent process for PID', process.pid, message)
|
|
212
226
|
|
|
213
227
|
/* Contextualize this run, and go! */
|
|
@@ -260,11 +274,8 @@ if ((process.argv[1] === requireFilename(__fileurl)) && (process.send)) {
|
|
|
260
274
|
console.error('\n\nError sending message back to parent process', error)
|
|
261
275
|
process.exitCode = 1
|
|
262
276
|
}).finally(() => {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
process.disconnect()
|
|
266
|
-
process.exit(process.exitCode)
|
|
267
|
-
})
|
|
277
|
+
process.disconnect()
|
|
278
|
+
process.exit(process.exitCode)
|
|
268
279
|
})
|
|
269
280
|
})
|
|
270
281
|
}
|
package/src/logging/emit.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { formatWithOptions } from 'node:util'
|
|
2
2
|
|
|
3
|
+
import { fail } from '../asserts'
|
|
3
4
|
import { $blu, $grn, $gry, $red, $t, $ylw } from './colors'
|
|
4
5
|
import { DEBUG, INFO, NOTICE, TRACE, WARN } from './levels'
|
|
5
6
|
import { logOptions } from './options'
|
|
@@ -14,13 +15,17 @@ let _output = logOptions.output
|
|
|
14
15
|
let _indentSize = logOptions.indentSize
|
|
15
16
|
let _taskLength = logOptions.taskLength
|
|
16
17
|
let _lineLength = logOptions.lineLength
|
|
17
|
-
let _inspectOptions = logOptions.inspectOptions
|
|
18
|
+
let _inspectOptions = { ...logOptions.inspectOptions }
|
|
18
19
|
logOptions.on('changed', (options) => {
|
|
19
20
|
_output = options.output
|
|
20
21
|
_indentSize = options.indentSize
|
|
21
22
|
_taskLength = options.taskLength
|
|
22
23
|
_lineLength = options.lineLength
|
|
23
|
-
_inspectOptions = options.inspectOptions
|
|
24
|
+
_inspectOptions = { ...options.inspectOptions } // proxy
|
|
25
|
+
_defaultEmitter =
|
|
26
|
+
options.format === 'fancy' ? emitFancy :
|
|
27
|
+
options.format === 'plain' ? emitPlain :
|
|
28
|
+
fail(`Invalid log format "${logOptions.format}"`)
|
|
24
29
|
})
|
|
25
30
|
|
|
26
31
|
/* ========================================================================== *
|
|
@@ -38,6 +43,12 @@ export interface LogEmitterOptions {
|
|
|
38
43
|
/** Emit a line (or multiple lines) of text to the log */
|
|
39
44
|
export type LogEmitter = (options: LogEmitterOptions, args: any[]) => void
|
|
40
45
|
|
|
46
|
+
/** A {@link LogEmitter} function configurable with a specific emitter */
|
|
47
|
+
export interface ConfigurableLogEmitter extends LogEmitter {
|
|
48
|
+
get emitter(): LogEmitter
|
|
49
|
+
set emitter(emitter: LogEmitter | undefined)
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
/* ========================================================================== */
|
|
42
53
|
|
|
43
54
|
/** Emit in full colors with spinner support and whatnot! */
|
|
@@ -120,3 +131,55 @@ export const emitPlain: LogEmitter = (options: LogEmitterOptions, args: any[]):
|
|
|
120
131
|
_output.write(`${linePrefix}${line}\n`)
|
|
121
132
|
}
|
|
122
133
|
}
|
|
134
|
+
|
|
135
|
+
/* ========================================================================== */
|
|
136
|
+
|
|
137
|
+
export interface ForkedLogMessage {
|
|
138
|
+
logLevel: LogLevel,
|
|
139
|
+
taskName: string,
|
|
140
|
+
lines: string[],
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Emit to the parent process of a forked child, or to the default emitter */
|
|
144
|
+
export const emitForked: LogEmitter = (options: LogEmitterOptions, args: any[]): void => {
|
|
145
|
+
if (process.connected && process.send) {
|
|
146
|
+
const { taskName, level, prefix = '', indent = 0 } = options
|
|
147
|
+
const linePrefix = ''.padStart(indent * _indentSize) + prefix
|
|
148
|
+
|
|
149
|
+
/* Now for the normal logging of all our parameters */
|
|
150
|
+
const breakLength = _lineLength - _taskLength - linePrefix.length - 20
|
|
151
|
+
const message = formatWithOptions({ ..._inspectOptions, breakLength }, ...args)
|
|
152
|
+
|
|
153
|
+
/* Format each individual line */
|
|
154
|
+
const lines = message.split('\n').map((line) => `${linePrefix}${line}`)
|
|
155
|
+
|
|
156
|
+
/* Send the message to the parent process */
|
|
157
|
+
process.send({ logLevel: level, taskName, lines })
|
|
158
|
+
} else {
|
|
159
|
+
_defaultEmitter(options, args)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* ========================================================================== */
|
|
164
|
+
|
|
165
|
+
/** The _default_ emitter (from `format`) */
|
|
166
|
+
let _defaultEmitter =
|
|
167
|
+
logOptions.format === 'fancy' ? emitFancy :
|
|
168
|
+
logOptions.format === 'plain' ? emitPlain :
|
|
169
|
+
fail(`Invalid log format "${logOptions.format}"`)
|
|
170
|
+
|
|
171
|
+
/** The _actual_ emitter (either default or configured) */
|
|
172
|
+
let _emitter = _defaultEmitter
|
|
173
|
+
|
|
174
|
+
/** Our `emit` wrapper function to export */
|
|
175
|
+
const wrapper: LogEmitter = function emit(options: LogEmitterOptions, args: any[]): void {
|
|
176
|
+
_defaultEmitter(options, args)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** A _configurable_ {@link LogEmitter} where log should be emitted to */
|
|
180
|
+
export const emit = Object.defineProperty(wrapper, 'emitter', {
|
|
181
|
+
get: () => _emitter,
|
|
182
|
+
set: (emitter: LogEmitter | undefined) => {
|
|
183
|
+
_emitter = emitter || _defaultEmitter
|
|
184
|
+
},
|
|
185
|
+
}) as ConfigurableLogEmitter
|
package/src/logging/github.ts
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import { EOL } from 'node:os'
|
|
2
2
|
import { formatWithOptions } from 'node:util'
|
|
3
3
|
|
|
4
|
+
import { stripAnsi } from '../utils/ansi'
|
|
4
5
|
import { logOptions } from './options'
|
|
5
6
|
|
|
6
7
|
import type { AbsolutePath } from '../paths'
|
|
7
8
|
|
|
8
|
-
/* Strip ANSI from strings */
|
|
9
|
-
const ansiRegExp = new RegExp([
|
|
10
|
-
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
|
11
|
-
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))',
|
|
12
|
-
].join('|'), 'g')
|
|
13
|
-
|
|
14
9
|
/* Initial values, and subscribe to changes */
|
|
15
10
|
let _output = logOptions.output
|
|
16
|
-
let _inspectOptions = logOptions.inspectOptions
|
|
17
11
|
let _githubAnnotations = logOptions.githubAnnotations
|
|
12
|
+
let _inspectOptions = { ...logOptions.inspectOptions, breakLength: Infinity }
|
|
18
13
|
logOptions.on('changed', (options) => {
|
|
19
14
|
_output = options.output
|
|
20
15
|
_githubAnnotations = options.githubAnnotations
|
|
@@ -23,16 +18,14 @@ logOptions.on('changed', (options) => {
|
|
|
23
18
|
|
|
24
19
|
|
|
25
20
|
function escapeData(data: string): string {
|
|
26
|
-
return data
|
|
27
|
-
.replace(ansiRegExp, '')
|
|
21
|
+
return stripAnsi(data)
|
|
28
22
|
.replace(/%/g, '%25')
|
|
29
23
|
.replace(/\r/g, '%0D')
|
|
30
24
|
.replace(/\n/g, '%0A')
|
|
31
25
|
}
|
|
32
26
|
|
|
33
27
|
function escapeProp(prop: string | number): string {
|
|
34
|
-
return `${prop}`
|
|
35
|
-
.replace(ansiRegExp, '')
|
|
28
|
+
return stripAnsi(`${prop}`)
|
|
36
29
|
.replace(/%/g, '%25')
|
|
37
30
|
.replace(/\r/g, '%0D')
|
|
38
31
|
.replace(/\n/g, '%0A')
|
package/src/logging/logger.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { formatWithOptions } from 'node:util'
|
|
2
2
|
|
|
3
3
|
import { BuildFailure } from '../asserts'
|
|
4
|
+
import { currentContext } from '../async'
|
|
5
|
+
import { stripAnsi } from '../utils/ansi'
|
|
4
6
|
import { $gry } from './colors'
|
|
5
|
-
import {
|
|
7
|
+
import { emit } from './emit'
|
|
6
8
|
import { DEBUG, ERROR, INFO, NOTICE, TRACE, WARN } from './levels'
|
|
7
9
|
import { logOptions } from './options'
|
|
8
10
|
import { ReportImpl } from './report'
|
|
@@ -16,11 +18,7 @@ import type { Report } from './report'
|
|
|
16
18
|
|
|
17
19
|
/* Initial value of log colors, and subscribe to changes */
|
|
18
20
|
let _level = logOptions.level
|
|
19
|
-
|
|
20
|
-
let _defaultTaskName = logOptions.defaultTaskName
|
|
21
|
-
logOptions.on('changed', ({ defaultTaskName, format, level }) => {
|
|
22
|
-
_defaultTaskName = defaultTaskName
|
|
23
|
-
_format = format
|
|
21
|
+
logOptions.on('changed', ({ level }) => {
|
|
24
22
|
_level = level
|
|
25
23
|
})
|
|
26
24
|
|
|
@@ -50,6 +48,8 @@ export interface Log {
|
|
|
50
48
|
export interface Logger extends Log {
|
|
51
49
|
/** The current level for logging. */
|
|
52
50
|
level: LogLevel,
|
|
51
|
+
/** The current indent level for logging. */
|
|
52
|
+
indent: number,
|
|
53
53
|
|
|
54
54
|
/** Enter a sub-level of logging, increasing indent */
|
|
55
55
|
enter(): void
|
|
@@ -64,36 +64,31 @@ export interface Logger extends Log {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/** Return a {@link Logger} associated with the specified task name. */
|
|
67
|
-
export function getLogger(task
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
_loggers.set(task, logger)
|
|
73
|
-
}
|
|
74
|
-
return logger
|
|
67
|
+
export function getLogger(task?: string, indent?: number): Logger {
|
|
68
|
+
const context = currentContext()
|
|
69
|
+
const taskName = task === undefined ? (context?.taskName || '') : task
|
|
70
|
+
const indentLevel = indent === undefined ? (context?.log.indent || 0) : 0
|
|
71
|
+
return new LoggerImpl(taskName, emit, indentLevel)
|
|
75
72
|
}
|
|
76
73
|
|
|
77
74
|
/* ========================================================================== */
|
|
78
75
|
|
|
79
|
-
/** Cache of loggers by task-name. */
|
|
80
|
-
const _loggers = new Map<string, Logger>()
|
|
81
76
|
/** Weak set of already logged build failures */
|
|
82
77
|
const _loggedFailures = new WeakSet<BuildFailure>()
|
|
83
78
|
|
|
84
79
|
/** Default implementation of the {@link Logger} interface. */
|
|
85
80
|
class LoggerImpl implements Logger {
|
|
86
81
|
private readonly _stack: { level: LogLevel, message: string, indent: number }[] = []
|
|
87
|
-
|
|
88
|
-
private _indent = 0
|
|
82
|
+
public level = _level
|
|
89
83
|
|
|
90
84
|
constructor(
|
|
91
85
|
private readonly _task: string,
|
|
92
86
|
private readonly _emitter: LogEmitter,
|
|
87
|
+
public indent: number,
|
|
93
88
|
) {}
|
|
94
89
|
|
|
95
|
-
private _emit(level: LogLevel, args: [ any, ...any ]): void {
|
|
96
|
-
if (this.
|
|
90
|
+
private _emit(level: LogLevel, args: [ any, ...any ], taskName = this._task): void {
|
|
91
|
+
if (this.level > level) return
|
|
97
92
|
|
|
98
93
|
// The `BuildFailure` is a bit special case
|
|
99
94
|
const params = args.filter((arg) => {
|
|
@@ -123,7 +118,7 @@ class LoggerImpl implements Logger {
|
|
|
123
118
|
if (params.length === 0) return
|
|
124
119
|
|
|
125
120
|
// Prepare our options for logging
|
|
126
|
-
const options = { level, taskName
|
|
121
|
+
const options = { level, taskName, indent: this.indent }
|
|
127
122
|
|
|
128
123
|
// Dump any existing stack entry
|
|
129
124
|
if (this._stack.length) {
|
|
@@ -137,14 +132,6 @@ class LoggerImpl implements Logger {
|
|
|
137
132
|
this._emitter(options, params)
|
|
138
133
|
}
|
|
139
134
|
|
|
140
|
-
get level(): LogLevel {
|
|
141
|
-
return this._level
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
set level(level: LogLevel) {
|
|
145
|
-
this._level = level
|
|
146
|
-
}
|
|
147
|
-
|
|
148
135
|
trace(...args: [ any, ...any ]): void {
|
|
149
136
|
this._emit(TRACE, args)
|
|
150
137
|
}
|
|
@@ -179,19 +166,19 @@ class LoggerImpl implements Logger {
|
|
|
179
166
|
enter(...args: [] | [ level: LogLevel, message: string ]): void {
|
|
180
167
|
if (args.length) {
|
|
181
168
|
const [ level, message ] = args
|
|
182
|
-
this._stack.push({ level, message, indent: this.
|
|
169
|
+
this._stack.push({ level, message, indent: this.indent })
|
|
183
170
|
}
|
|
184
171
|
|
|
185
|
-
this.
|
|
172
|
+
this.indent ++
|
|
186
173
|
}
|
|
187
174
|
|
|
188
175
|
leave(): void
|
|
189
176
|
leave(level: LogLevel, message: string): void
|
|
190
177
|
leave(...args: [] | [ level: LogLevel, message: string ]): void {
|
|
191
178
|
this._stack.pop()
|
|
192
|
-
this.
|
|
179
|
+
this.indent --
|
|
193
180
|
|
|
194
|
-
if (this.
|
|
181
|
+
if (this.indent < 0) this.indent = 0
|
|
195
182
|
|
|
196
183
|
if (args.length) {
|
|
197
184
|
const [ level, message ] = args
|
|
@@ -209,8 +196,8 @@ class LoggerImpl implements Logger {
|
|
|
209
196
|
}
|
|
210
197
|
|
|
211
198
|
let { indent = 0, prefix = '' } = options
|
|
212
|
-
prefix = this.
|
|
213
|
-
indent += this.
|
|
199
|
+
prefix = this.indent ? $gry('| ') + prefix : prefix
|
|
200
|
+
indent += this.indent
|
|
214
201
|
this._emitter({ ...options, indent, prefix }, args)
|
|
215
202
|
}
|
|
216
203
|
return new ReportImpl(title, this._task, emitter)
|
|
@@ -219,12 +206,7 @@ class LoggerImpl implements Logger {
|
|
|
219
206
|
|
|
220
207
|
/* ========================================================================== */
|
|
221
208
|
|
|
222
|
-
/**
|
|
223
|
-
const ansiPattern = '[\\u001b\\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]'
|
|
224
|
-
/** Regular expression matching ANSI */
|
|
225
|
-
const ansiRegExp = new RegExp(ansiPattern, 'g')
|
|
226
|
-
|
|
227
|
-
/** A test logger, writing to a buffer always _without_ colors */
|
|
209
|
+
/** A test logger, writing to a buffer always _without_ colors/indent */
|
|
228
210
|
export class TestLogger extends LoggerImpl {
|
|
229
211
|
private _lines: string[] = []
|
|
230
212
|
|
|
@@ -236,10 +218,10 @@ export class TestLogger extends LoggerImpl {
|
|
|
236
218
|
/* Now for the normal logging of all our parameters */
|
|
237
219
|
formatWithOptions({ colors: false, breakLength: 120 }, ...args)
|
|
238
220
|
.split('\n').forEach((line) => {
|
|
239
|
-
const stripped = line
|
|
221
|
+
const stripped = stripAnsi(line)
|
|
240
222
|
this._lines.push(`${linePrefix}${stripped}`)
|
|
241
223
|
})
|
|
242
|
-
})
|
|
224
|
+
}, 0)
|
|
243
225
|
}
|
|
244
226
|
|
|
245
227
|
/** Return the _current_ buffer for this instance */
|
package/src/logging/options.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events'
|
|
2
|
-
import { Socket } from 'node:net'
|
|
3
2
|
|
|
4
3
|
import { getSingleton } from '../utils/singleton'
|
|
5
4
|
import { getLevelNumber, NOTICE } from './levels'
|
|
@@ -30,16 +29,12 @@ export interface LogOptions {
|
|
|
30
29
|
indentSize: number,
|
|
31
30
|
/** Whether to show sources in reports or not. */
|
|
32
31
|
showSources: boolean,
|
|
33
|
-
/** The task name to be used by default if a task is not contextualized. */
|
|
34
|
-
defaultTaskName: string,
|
|
35
32
|
/** Whether GitHub annotations are enabled or not. */
|
|
36
33
|
githubAnnotations: boolean,
|
|
34
|
+
|
|
37
35
|
/** The options used by NodeJS for object inspection. */
|
|
38
36
|
readonly inspectOptions: InspectOptions,
|
|
39
37
|
|
|
40
|
-
/** Set an inspect option in {@link LogOptions.inspectOptions}). */
|
|
41
|
-
setInspectOption<K extends keyof InspectOptions>(key: K, value: InspectOptions[K]): void
|
|
42
|
-
|
|
43
38
|
/** Add an event listener for the specified event. */
|
|
44
39
|
on(eventName: 'changed', listener: (logOptions: this) => void): this;
|
|
45
40
|
/** Add an event listener for the specified event triggering only once. */
|
|
@@ -51,9 +46,8 @@ export interface LogOptions {
|
|
|
51
46
|
* Return a record of environment variables for forking.
|
|
52
47
|
*
|
|
53
48
|
* @param taskName The default task name of the forked process
|
|
54
|
-
* @param logFd A file descriptor where logs should be sinked to
|
|
55
49
|
*/
|
|
56
|
-
forkEnv(taskName?: string
|
|
50
|
+
forkEnv(taskName?: string): Record<string, string>
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
/* ========================================================================== *
|
|
@@ -72,7 +66,6 @@ class LogOptionsImpl extends EventEmitter implements LogOptions {
|
|
|
72
66
|
private _showSources = true // by default, always show source snippets
|
|
73
67
|
private _githubAnnotations = false // ultimately set by the constructor
|
|
74
68
|
private _inspectOptions: InspectOptions = {}
|
|
75
|
-
private _defaultTaskName = ''
|
|
76
69
|
private _taskLength = 0
|
|
77
70
|
private _indentSize = 2
|
|
78
71
|
|
|
@@ -104,12 +97,7 @@ class LogOptionsImpl extends EventEmitter implements LogOptions {
|
|
|
104
97
|
* and it's processed _last_ as it's normally only created by fork below
|
|
105
98
|
* and consumed by the `Exec` plug (which has no other way of communicating)
|
|
106
99
|
*/
|
|
107
|
-
const
|
|
108
|
-
if (fd) {
|
|
109
|
-
const output = new Socket({ fd, readable: false, writable: true }).unref()
|
|
110
|
-
process.on('beforeExit', () => output.end())
|
|
111
|
-
this._output = output
|
|
112
|
-
}
|
|
100
|
+
const options = JSON.parse(process.env.__LOG_OPTIONS || '{}')
|
|
113
101
|
Object.assign(this, options)
|
|
114
102
|
}
|
|
115
103
|
|
|
@@ -117,17 +105,19 @@ class LogOptionsImpl extends EventEmitter implements LogOptions {
|
|
|
117
105
|
super.emit('changed', this)
|
|
118
106
|
}
|
|
119
107
|
|
|
120
|
-
forkEnv(taskName?: string
|
|
108
|
+
forkEnv(taskName?: string): Record<string, string> {
|
|
109
|
+
void taskName
|
|
121
110
|
return {
|
|
122
111
|
__LOG_OPTIONS: JSON.stringify({
|
|
123
112
|
level: this._level,
|
|
124
113
|
colors: this._colors,
|
|
114
|
+
format: this._format,
|
|
125
115
|
lineLength: this._lineLength,
|
|
126
116
|
taskLength: this._taskLength,
|
|
117
|
+
showSources: this._showSources,
|
|
127
118
|
githubAnnotations: this.githubAnnotations,
|
|
128
|
-
|
|
119
|
+
indentSize: this.indentSize,
|
|
129
120
|
spinner: false, // forked spinner is always false
|
|
130
|
-
fd, // file descriptor for logs
|
|
131
121
|
}),
|
|
132
122
|
}
|
|
133
123
|
}
|
|
@@ -159,6 +149,7 @@ class LogOptionsImpl extends EventEmitter implements LogOptions {
|
|
|
159
149
|
set colors(color: boolean) {
|
|
160
150
|
this._colors = color
|
|
161
151
|
this._colorsSet = true
|
|
152
|
+
this._inspectOptions.colors = color
|
|
162
153
|
this._notifyListeners()
|
|
163
154
|
}
|
|
164
155
|
|
|
@@ -218,15 +209,6 @@ class LogOptionsImpl extends EventEmitter implements LogOptions {
|
|
|
218
209
|
this._notifyListeners()
|
|
219
210
|
}
|
|
220
211
|
|
|
221
|
-
get defaultTaskName(): string {
|
|
222
|
-
return this._defaultTaskName
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
set defaultTaskName(defaultTaskName: string) {
|
|
226
|
-
this._defaultTaskName = defaultTaskName
|
|
227
|
-
this._notifyListeners()
|
|
228
|
-
}
|
|
229
|
-
|
|
230
212
|
get githubAnnotations(): boolean {
|
|
231
213
|
return this._githubAnnotations
|
|
232
214
|
}
|
|
@@ -237,16 +219,26 @@ class LogOptionsImpl extends EventEmitter implements LogOptions {
|
|
|
237
219
|
}
|
|
238
220
|
|
|
239
221
|
get inspectOptions(): InspectOptions {
|
|
240
|
-
return {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
222
|
+
return new Proxy(this._inspectOptions, {
|
|
223
|
+
get: (target, prop): any => {
|
|
224
|
+
if (prop === 'colors') return this.colors
|
|
225
|
+
if (prop === 'breakLength') return this._lineLength
|
|
226
|
+
return (target as any)[prop]
|
|
227
|
+
},
|
|
228
|
+
set: (target, prop, value): boolean => {
|
|
229
|
+
if (prop === 'colors') {
|
|
230
|
+
this.colors = !! value
|
|
231
|
+
} else if (prop === 'breakLength') {
|
|
232
|
+
const length = parseInt(value)
|
|
233
|
+
if (isNaN(length)) return false
|
|
234
|
+
this.lineLength = length
|
|
235
|
+
} else {
|
|
236
|
+
(target as any)[prop] = value
|
|
237
|
+
}
|
|
238
|
+
this._notifyListeners()
|
|
239
|
+
return true
|
|
240
|
+
},
|
|
241
|
+
})
|
|
250
242
|
}
|
|
251
243
|
}
|
|
252
244
|
|
package/src/logging.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { currentContext } from './async'
|
|
|
2
2
|
import { $gry, $wht } from './logging/colors'
|
|
3
3
|
import { getLogger } from './logging/logger'
|
|
4
4
|
import { setupSpinner } from './logging/spinner'
|
|
5
|
+
import { stripAnsi } from './utils/ansi'
|
|
5
6
|
|
|
6
7
|
import type { Log, Logger } from './logging/logger'
|
|
7
8
|
|
|
@@ -75,13 +76,14 @@ export const log: LogFunction = ((): LogFunction => {
|
|
|
75
76
|
|
|
76
77
|
/** Print a nice _banner_ message on the log */
|
|
77
78
|
export function banner(message: string): void {
|
|
78
|
-
const
|
|
79
|
-
const padLines =
|
|
79
|
+
const length = stripAnsi(message).length
|
|
80
|
+
const padLines = length > 60 ? length + 2 : 62
|
|
81
|
+
const padBlank = length > 60 ? 0 : 60 - length
|
|
80
82
|
|
|
81
83
|
log.notice([
|
|
82
84
|
'',
|
|
83
85
|
$gry(`\u2554${''.padStart(padLines, '\u2550')}\u2557`),
|
|
84
|
-
`${$gry('\u2551')} ${$wht(message.padEnd(
|
|
86
|
+
`${$gry('\u2551')} ${$wht(message)}${''.padEnd(padBlank, ' ')} ${$gry('\u2551')}`,
|
|
85
87
|
$gry(`\u255A${''.padStart(padLines, '\u2550')}\u255D`),
|
|
86
88
|
'',
|
|
87
89
|
].join('\n'))
|
package/src/plugs/exports.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EOL } from 'node:os'
|
|
2
2
|
import { sep } from 'node:path'
|
|
3
3
|
|
|
4
|
+
import { assert } from '../asserts'
|
|
4
5
|
import { Files } from '../files'
|
|
5
6
|
import { readFile, writeFile } from '../fs'
|
|
6
7
|
import { $p } from '../logging'
|
|
@@ -76,7 +77,7 @@ install('exports', class Exports implements Plug<Files> {
|
|
|
76
77
|
packageData.type === 'commonjs' ? 'commonjs' :
|
|
77
78
|
packageData.type == null ? 'commonjs' :
|
|
78
79
|
undefined
|
|
79
|
-
|
|
80
|
+
assert(type, `Unknown module type "${packageData.type}" in ${$p(incomingFile)}`)
|
|
80
81
|
|
|
81
82
|
context.log.debug(`Package file ${$p(incomingFile)} declares module type "${type}"`)
|
|
82
83
|
|
|
@@ -84,9 +85,7 @@ install('exports', class Exports implements Plug<Files> {
|
|
|
84
85
|
const esmExtension = this._esmExtension || (type === 'module' ? '.js' : '.mjs')
|
|
85
86
|
|
|
86
87
|
// reject when commonjs and ecmascript modules have the same extension
|
|
87
|
-
|
|
88
|
-
context.log.fail(`CommonJS and EcmaScript modules both resolve to same extension "${cjsExtension}"`)
|
|
89
|
-
}
|
|
88
|
+
assert(cjsExtension !== esmExtension, `CommonJS and EcmaScript modules both resolve to same extension "${cjsExtension}"`)
|
|
90
89
|
|
|
91
90
|
const exports: ExportsDeclaration = {}
|
|
92
91
|
function addExport(
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A {@link RegExp} matching ANSI escapes */
|
|
2
|
+
export const ansiRegExp = new RegExp([
|
|
3
|
+
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
|
4
|
+
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))',
|
|
5
|
+
].join('|'), 'g')
|
|
6
|
+
|
|
7
|
+
/** Strip ANSI characters (colors) from a string */
|
|
8
|
+
export function stripAnsi(string: string): string {
|
|
9
|
+
return string && string.replace(ansiRegExp, '')
|
|
10
|
+
}
|
package/src/utils/exec.ts
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'node:path'
|
|
|
3
3
|
import readline from 'node:readline'
|
|
4
4
|
|
|
5
5
|
import { assert, BuildFailure } from '../asserts'
|
|
6
|
-
import { $p } from '../logging'
|
|
6
|
+
import { $p, logOptions } from '../logging'
|
|
7
7
|
import { getCurrentWorkingDirectory, resolveDirectory } from '../paths'
|
|
8
8
|
|
|
9
9
|
import type { SpawnOptions } from 'node:child_process'
|
|
@@ -56,7 +56,12 @@ export async function execChild(
|
|
|
56
56
|
|
|
57
57
|
// Build our environment variables record
|
|
58
58
|
const PATH = childPaths.join(path.delimiter)
|
|
59
|
-
const childEnv: Record<string, string> = {
|
|
59
|
+
const childEnv: Record<string, string> = {
|
|
60
|
+
...process.env, // environment from current running process
|
|
61
|
+
...env, // environment configured from "execChild" arguments
|
|
62
|
+
...logOptions.forkEnv(), // forked log options for child plugjs
|
|
63
|
+
PATH, // path with all ".../node_modules/.bin" directories
|
|
64
|
+
}
|
|
60
65
|
|
|
61
66
|
// Instrument coverage directory if needed
|
|
62
67
|
if (coverageDir) childEnv.NODE_V8_COVERAGE = context.resolve(coverageDir)
|
package/src/utils.ts
CHANGED