@plugjs/plug 0.3.5 → 0.4.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/asserts.cjs +10 -12
- package/dist/asserts.cjs.map +1 -1
- package/dist/asserts.d.ts +1 -2
- package/dist/asserts.mjs +9 -10
- package/dist/asserts.mjs.map +1 -1
- package/dist/async.cjs +5 -20
- package/dist/async.cjs.map +2 -2
- package/dist/async.mjs +5 -20
- package/dist/async.mjs.map +2 -2
- package/dist/build.cjs +113 -64
- package/dist/build.cjs.map +2 -2
- package/dist/build.d.ts +9 -7
- package/dist/build.mjs +110 -63
- package/dist/build.mjs.map +2 -2
- package/dist/cli.d.mts +12 -0
- package/dist/cli.mjs +266 -0
- package/dist/cli.mjs.map +6 -0
- package/dist/files.cjs +5 -3
- package/dist/files.cjs.map +1 -1
- package/dist/files.d.ts +2 -1
- package/dist/files.mjs +11 -4
- package/dist/files.mjs.map +1 -1
- package/dist/fork.cjs +30 -12
- package/dist/fork.cjs.map +1 -1
- package/dist/fork.d.ts +10 -0
- package/dist/fork.mjs +31 -13
- package/dist/fork.mjs.map +1 -1
- package/dist/helpers.cjs +32 -13
- package/dist/helpers.cjs.map +2 -2
- package/dist/helpers.d.ts +12 -0
- package/dist/helpers.mjs +37 -14
- package/dist/helpers.mjs.map +2 -2
- package/dist/index.cjs +5 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.mjs +4 -1
- package/dist/index.mjs.map +1 -1
- package/dist/logging/emit.cjs +4 -4
- package/dist/logging/emit.cjs.map +1 -1
- package/dist/logging/emit.mjs +4 -4
- package/dist/logging/emit.mjs.map +1 -1
- package/dist/logging/logger.cjs +43 -2
- package/dist/logging/logger.cjs.map +1 -1
- package/dist/logging/logger.d.ts +36 -3
- package/dist/logging/logger.mjs +43 -3
- package/dist/logging/logger.mjs.map +1 -1
- package/dist/logging/options.cjs +8 -12
- package/dist/logging/options.cjs.map +1 -1
- package/dist/logging/options.d.ts +44 -1
- package/dist/logging/options.mjs +8 -12
- package/dist/logging/options.mjs.map +1 -1
- package/dist/logging.cjs +14 -3
- package/dist/logging.cjs.map +1 -1
- package/dist/logging.d.ts +2 -0
- package/dist/logging.mjs +13 -3
- package/dist/logging.mjs.map +1 -1
- package/dist/plugs/build.cjs +63 -0
- package/dist/plugs/build.cjs.map +6 -0
- package/dist/plugs/build.d.ts +13 -0
- package/dist/plugs/build.mjs +37 -0
- package/dist/plugs/build.mjs.map +6 -0
- package/dist/plugs/debug.cjs +7 -9
- package/dist/plugs/debug.cjs.map +1 -1
- package/dist/plugs/debug.mjs +8 -10
- package/dist/plugs/debug.mjs.map +1 -1
- package/dist/types.cjs +12 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.ts +35 -9
- package/dist/types.mjs +5 -0
- package/dist/types.mjs.map +2 -2
- package/dist/utils/diff.cjs +1 -4
- package/dist/utils/diff.cjs.map +1 -1
- package/dist/utils/diff.mjs +1 -4
- package/dist/utils/diff.mjs.map +1 -1
- package/dist/utils/exec.cjs +5 -12
- package/dist/utils/exec.cjs.map +2 -2
- package/dist/utils/exec.d.ts +0 -2
- package/dist/utils/exec.mjs +6 -13
- package/dist/utils/exec.mjs.map +1 -1
- package/dist/utils/{types.cjs → singleton.cjs} +14 -13
- package/dist/utils/singleton.cjs.map +6 -0
- package/dist/utils/singleton.d.ts +12 -0
- package/dist/utils/singleton.mjs +13 -0
- package/dist/utils/singleton.mjs.map +6 -0
- package/dist/utils.cjs +2 -2
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.mjs +1 -1
- package/package.json +7 -9
- package/src/asserts.ts +10 -12
- package/src/async.ts +6 -29
- package/src/build.ts +169 -106
- package/{extra/plug.mts → src/cli.mts} +115 -141
- package/src/files.ts +14 -6
- package/src/fork.ts +42 -16
- package/src/helpers.ts +56 -5
- package/src/index.ts +2 -1
- package/src/logging/emit.ts +4 -4
- package/src/logging/logger.ts +60 -7
- package/src/logging/options.ts +9 -14
- package/src/logging.ts +20 -5
- package/src/plugs/build.ts +45 -0
- package/src/plugs/debug.ts +10 -9
- package/src/types.ts +54 -23
- package/src/utils/diff.ts +1 -6
- package/src/utils/exec.ts +6 -20
- package/src/utils/singleton.ts +19 -0
- package/src/utils.ts +1 -1
- package/cli/plug.mjs +0 -1385
- package/cli/ts-loader.mjs +0 -275
- package/cli/tsrun.mjs +0 -1204
- package/dist/utils/types.cjs.map +0 -6
- package/dist/utils/types.d.ts +0 -4
- package/dist/utils/types.mjs +0 -12
- package/dist/utils/types.mjs.map +0 -6
- package/extra/ts-loader.mts +0 -546
- package/extra/tsrun.mts +0 -127
- package/extra/utils.ts +0 -150
- package/src/utils/types.ts +0 -11
package/src/helpers.ts
CHANGED
|
@@ -7,14 +7,23 @@ import { requireContext } from './async'
|
|
|
7
7
|
import { Files } from './files'
|
|
8
8
|
import { rm } from './fs'
|
|
9
9
|
import { $p, log } from './logging'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
commonPath,
|
|
12
|
+
getAbsoluteParent,
|
|
13
|
+
getCurrentWorkingDirectory,
|
|
14
|
+
resolveDirectory,
|
|
15
|
+
resolveFile,
|
|
16
|
+
} from './paths'
|
|
11
17
|
import { PipeImpl } from './pipe'
|
|
18
|
+
import { RunBuild } from './plugs/build'
|
|
12
19
|
import { execChild } from './utils/exec'
|
|
13
20
|
import { parseOptions } from './utils/options'
|
|
14
21
|
import { walk } from './utils/walk'
|
|
15
22
|
|
|
23
|
+
import type { ForkOptions } from './fork'
|
|
16
24
|
import type { Pipe } from './index'
|
|
17
25
|
import type { AbsolutePath } from './paths'
|
|
26
|
+
import type { Context } from './pipe'
|
|
18
27
|
import type { ExecChildOptions } from './utils/exec'
|
|
19
28
|
import type { ParseOptions } from './utils/options'
|
|
20
29
|
import type { WalkOptions } from './utils/walk'
|
|
@@ -29,6 +38,11 @@ export interface FindOptions extends WalkOptions {
|
|
|
29
38
|
directory?: string
|
|
30
39
|
}
|
|
31
40
|
|
|
41
|
+
/** Return the current execution {@link Context} */
|
|
42
|
+
export function context(): Context {
|
|
43
|
+
return requireContext()
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
/** Find files in the current directory using the specified _glob_. */
|
|
33
47
|
export function find(glob: string): Pipe
|
|
34
48
|
/** Find files in the current directory using the specified _globs_. */
|
|
@@ -56,6 +70,44 @@ export function find(...args: ParseOptions<FindOptions>): Pipe {
|
|
|
56
70
|
}))
|
|
57
71
|
}
|
|
58
72
|
|
|
73
|
+
export type InvokeBuildOptions = ForkOptions & Record<string, string>
|
|
74
|
+
export type InvokeBuildTasks = string | [ string, ...string[] ]
|
|
75
|
+
|
|
76
|
+
export function invokeBuild(buildFile: string): Promise<void>
|
|
77
|
+
export function invokeBuild(buildFile: string, task: string): Promise<void>
|
|
78
|
+
export function invokeBuild(buildFile: string, task: string, options: InvokeBuildOptions): Promise<void>
|
|
79
|
+
export function invokeBuild(buildFile: string, tasks: [ string, ...string[] ]): Promise<void>
|
|
80
|
+
export function invokeBuild(buildFile: string, tasks: [ string, ...string[] ], options: InvokeBuildOptions): Promise<void>
|
|
81
|
+
export function invokeBuild(buildFile: string, options: InvokeBuildOptions): Promise<void>
|
|
82
|
+
export async function invokeBuild(
|
|
83
|
+
buildFile: string,
|
|
84
|
+
tasksOrOptions?: string | [ string, ...string[] ] | InvokeBuildOptions,
|
|
85
|
+
maybeOptions?: InvokeBuildOptions,
|
|
86
|
+
): Promise<void> {
|
|
87
|
+
const [ tasks, options = {} ] =
|
|
88
|
+
typeof tasksOrOptions === 'string' ?
|
|
89
|
+
[ [ tasksOrOptions ], maybeOptions ] :
|
|
90
|
+
Array.isArray(tasksOrOptions) ?
|
|
91
|
+
[ tasksOrOptions, maybeOptions ] :
|
|
92
|
+
typeof tasksOrOptions === 'object' ?
|
|
93
|
+
[ [ 'default' ], tasksOrOptions ] :
|
|
94
|
+
[ [ 'default' ], {} ]
|
|
95
|
+
|
|
96
|
+
if (tasks.length === 0) tasks.push('default')
|
|
97
|
+
|
|
98
|
+
const { coverageDir, forceModule, ...props } = options
|
|
99
|
+
const forkOptions = { coverageDir, forceModule }
|
|
100
|
+
|
|
101
|
+
const context = requireContext()
|
|
102
|
+
const file = context.resolve(buildFile)
|
|
103
|
+
const dir = getAbsoluteParent(file)
|
|
104
|
+
const files = Files.builder(dir).add(file).build()
|
|
105
|
+
|
|
106
|
+
return new RunBuild(tasks, props, forkOptions)
|
|
107
|
+
.pipe(files, context)
|
|
108
|
+
.then(() => void 0)
|
|
109
|
+
}
|
|
110
|
+
|
|
59
111
|
/**
|
|
60
112
|
* Recursively remove the specified directory _**(use with care)**_.
|
|
61
113
|
*/
|
|
@@ -98,14 +150,14 @@ export function merge(pipes: (Pipe | Files | Promise<Files>)[]): Pipe {
|
|
|
98
150
|
const context = requireContext()
|
|
99
151
|
return new PipeImpl(context, Promise.resolve().then(async () => {
|
|
100
152
|
// No pipes? Just send off an empty pipe...
|
|
101
|
-
if (pipes.length === 0) return Files
|
|
153
|
+
if (pipes.length === 0) return new Files()
|
|
102
154
|
|
|
103
155
|
// Await for all pipes / files / files promises
|
|
104
156
|
const awaited = await assertPromises<Files>(pipes)
|
|
105
157
|
const results = awaited.filter((result) => result.length)
|
|
106
158
|
|
|
107
159
|
// No files in anything to be merged? Again send off an empty pipe...
|
|
108
|
-
if (results.length === 0) return Files
|
|
160
|
+
if (results.length === 0) return new Files()
|
|
109
161
|
|
|
110
162
|
// Find the common directory between all the Files instances
|
|
111
163
|
const [ firstDir, ...otherDirs ] = results.map((f) => f.directory)
|
|
@@ -132,8 +184,7 @@ export function merge(pipes: (Pipe | Files | Promise<Files>)[]): Pipe {
|
|
|
132
184
|
*/
|
|
133
185
|
export function noop(): Pipe {
|
|
134
186
|
const context = requireContext()
|
|
135
|
-
|
|
136
|
-
return new PipeImpl(context, Promise.resolve(files))
|
|
187
|
+
return new PipeImpl(context, Promise.resolve(new Files()))
|
|
137
188
|
}
|
|
138
189
|
|
|
139
190
|
/**
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface Pipe extends Promise<Files> {
|
|
|
28
28
|
|
|
29
29
|
// Submodule exports (our package.json exports)
|
|
30
30
|
export * as asserts from './asserts'
|
|
31
|
+
export * as async from './async'
|
|
31
32
|
export * as files from './files'
|
|
32
33
|
export * as fork from './fork'
|
|
33
34
|
export * as fs from './fs'
|
|
@@ -37,7 +38,7 @@ export * as pipe from './pipe'
|
|
|
37
38
|
export * as utils from './utils'
|
|
38
39
|
|
|
39
40
|
// Individual utilities
|
|
40
|
-
export { log, $ms, $p, $t, $blu, $cyn, $grn, $gry, $mgt, $red, $und, $wht, $ylw } from './logging'
|
|
41
|
+
export { banner, log, $ms, $p, $t, $blu, $cyn, $grn, $gry, $mgt, $red, $und, $wht, $ylw } from './logging'
|
|
41
42
|
export { assert, fail, BuildFailure } from './asserts'
|
|
42
43
|
|
|
43
44
|
// Our minimal exports
|
package/src/logging/emit.ts
CHANGED
|
@@ -42,8 +42,8 @@ export type LogEmitter = (options: LogEmitterOptions, args: any[]) => void
|
|
|
42
42
|
|
|
43
43
|
/** Emit in full colors! */
|
|
44
44
|
export const emitColor: LogEmitter = (options: LogEmitterOptions, args: any[]): void => {
|
|
45
|
-
const { taskName, level, prefix, indent } = options
|
|
46
|
-
const logPrefix =
|
|
45
|
+
const { taskName, level, prefix = '', indent = 0 } = options
|
|
46
|
+
const logPrefix = ''.padStart(indent * _indentSize) + prefix
|
|
47
47
|
|
|
48
48
|
/* Prefixes, to prepend at the beginning of each line */
|
|
49
49
|
const prefixes: string[] = []
|
|
@@ -85,8 +85,8 @@ export const emitColor: LogEmitter = (options: LogEmitterOptions, args: any[]):
|
|
|
85
85
|
|
|
86
86
|
/** Emit in plain text! (no colors) */
|
|
87
87
|
export const emitPlain: LogEmitter = (options: LogEmitterOptions, args: any[]): void => {
|
|
88
|
-
const { taskName, level, prefix, indent } = options
|
|
89
|
-
const logPrefix =
|
|
88
|
+
const { taskName, level, prefix = '', indent = 0 } = options
|
|
89
|
+
const logPrefix = ''.padStart(indent * _indentSize) + prefix
|
|
90
90
|
|
|
91
91
|
const prefixes: string[] = []
|
|
92
92
|
|
package/src/logging/logger.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatWithOptions } from 'node:util'
|
|
2
|
+
|
|
3
|
+
import { BuildFailure } from '../asserts'
|
|
2
4
|
import { emitColor, emitPlain } from './emit'
|
|
3
5
|
import { DEBUG, ERROR, INFO, NOTICE, TRACE, WARN } from './levels'
|
|
4
6
|
import { logOptions } from './options'
|
|
5
7
|
import { ReportImpl } from './report'
|
|
8
|
+
import { $gry } from './colors'
|
|
6
9
|
|
|
7
|
-
import type { LogEmitter } from './emit'
|
|
10
|
+
import type { LogEmitter, LogEmitterOptions } from './emit'
|
|
8
11
|
import type { LogLevel } from './levels'
|
|
9
12
|
import type { Report } from './report'
|
|
10
13
|
|
|
14
|
+
|
|
11
15
|
/* ========================================================================== */
|
|
12
16
|
|
|
13
17
|
/* Initial value of log colors, and subscribe to changes */
|
|
@@ -26,8 +30,6 @@ logOptions.on('changed', ({ defaultTaskName, colors, level }) => {
|
|
|
26
30
|
|
|
27
31
|
/** The basic interface giving access to log facilities. */
|
|
28
32
|
export interface Log {
|
|
29
|
-
/** The current {@link Logger} */
|
|
30
|
-
readonly logger: Logger
|
|
31
33
|
/** Log a `TRACE` message */
|
|
32
34
|
trace(...args: [ any, ...any ]): void
|
|
33
35
|
/** Log a `DEBUG` message */
|
|
@@ -45,7 +47,7 @@ export interface Log {
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
/** A {@link Logger} extends the basic {@link Log} adding some state. */
|
|
48
|
-
export interface Logger extends
|
|
50
|
+
export interface Logger extends Log {
|
|
49
51
|
/** The current level for logging. */
|
|
50
52
|
level: LogLevel,
|
|
51
53
|
|
|
@@ -95,7 +97,7 @@ class LoggerImpl implements Logger {
|
|
|
95
97
|
|
|
96
98
|
// The `BuildFailure` is a bit special case
|
|
97
99
|
const params = args.filter((arg) => {
|
|
98
|
-
if (
|
|
100
|
+
if (arg instanceof BuildFailure) {
|
|
99
101
|
// Filter out any previously logged build failure and mark
|
|
100
102
|
if (_loggedFailures.has(arg)) return false
|
|
101
103
|
_loggedFailures.add(arg)
|
|
@@ -198,6 +200,57 @@ class LoggerImpl implements Logger {
|
|
|
198
200
|
}
|
|
199
201
|
|
|
200
202
|
report(title: string): Report {
|
|
201
|
-
|
|
203
|
+
const emitter: LogEmitter = (options: LogEmitterOptions, args: any) => {
|
|
204
|
+
if (this._stack.length) {
|
|
205
|
+
for (const { message, ...extras } of this._stack) {
|
|
206
|
+
this._emitter({ ...options, ...extras }, [ message ])
|
|
207
|
+
}
|
|
208
|
+
this._stack.splice(0)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let { indent = 0, prefix = '' } = options
|
|
212
|
+
prefix = this._indent ? $gry('| ') + prefix : prefix
|
|
213
|
+
indent += this._indent
|
|
214
|
+
this._emitter({ ...options, indent, prefix }, args)
|
|
215
|
+
}
|
|
216
|
+
return new ReportImpl(title, this._task, emitter)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* ========================================================================== */
|
|
221
|
+
|
|
222
|
+
/** Pattern to match ANSI expressions */
|
|
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 */
|
|
228
|
+
export class TestLogger extends LoggerImpl {
|
|
229
|
+
private _lines: string[] = []
|
|
230
|
+
|
|
231
|
+
constructor() {
|
|
232
|
+
super('', (options: LogEmitterOptions, args: any[]): void => {
|
|
233
|
+
const { prefix = '', indent = 0 } = options
|
|
234
|
+
const linePrefix = ''.padStart(indent * 2) + prefix
|
|
235
|
+
|
|
236
|
+
/* Now for the normal logging of all our parameters */
|
|
237
|
+
formatWithOptions({ colors: false, breakLength: 120 }, ...args)
|
|
238
|
+
.split('\n').forEach((line) => {
|
|
239
|
+
const stripped = line.replaceAll(ansiRegExp, '')
|
|
240
|
+
this._lines.push(`${linePrefix}${stripped}`)
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Return the _current_ buffer for this instance */
|
|
246
|
+
get buffer(): string {
|
|
247
|
+
return this._lines.join('\n')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Reset the buffer and return any previously buffered text */
|
|
251
|
+
reset(): string {
|
|
252
|
+
const buffer = this.buffer
|
|
253
|
+
this._lines = []
|
|
254
|
+
return buffer
|
|
202
255
|
}
|
|
203
256
|
}
|
package/src/logging/options.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { EventEmitter } from 'node:events'
|
|
|
2
2
|
import { Socket } from 'node:net'
|
|
3
3
|
|
|
4
4
|
import { getLevelNumber, NOTICE } from './levels'
|
|
5
|
+
import { getSingleton } from '../utils/singleton'
|
|
5
6
|
|
|
6
7
|
import type { Writable } from 'node:stream'
|
|
7
8
|
import type { InspectOptions } from 'node:util'
|
|
@@ -96,7 +97,11 @@ class LogOptionsImpl extends EventEmitter implements LogOptions {
|
|
|
96
97
|
* and consumed by the `Exec` plug (which has no other way of communicating)
|
|
97
98
|
*/
|
|
98
99
|
const { fd, ...options } = JSON.parse(process.env.__LOG_OPTIONS || '{}')
|
|
99
|
-
if (fd)
|
|
100
|
+
if (fd) {
|
|
101
|
+
const output = new Socket({ fd, readable: false, writable: true }).unref()
|
|
102
|
+
process.on('beforeExit', () => this._output.end())
|
|
103
|
+
this._output = output
|
|
104
|
+
}
|
|
100
105
|
Object.assign(this, options)
|
|
101
106
|
}
|
|
102
107
|
|
|
@@ -228,18 +233,8 @@ class LogOptionsImpl extends EventEmitter implements LogOptions {
|
|
|
228
233
|
}
|
|
229
234
|
}
|
|
230
235
|
|
|
231
|
-
|
|
232
|
-
const optionsKey = Symbol.for('plugjs
|
|
233
|
-
|
|
234
|
-
/** Get the shared _per process_ instance of our {@link LogOptions}. */
|
|
235
|
-
function getLogOptions(): LogOptions {
|
|
236
|
-
let options: LogOptions = (<any> globalThis)[optionsKey]
|
|
237
|
-
if (! options) {
|
|
238
|
-
options = new LogOptionsImpl()
|
|
239
|
-
;(<any> globalThis)[optionsKey] = options
|
|
240
|
-
}
|
|
241
|
-
return options
|
|
242
|
-
}
|
|
236
|
+
/** Singleton key for {@link LogOptions} instance. */
|
|
237
|
+
const optionsKey = Symbol.for('plugjs:plug:types:LogOptions')
|
|
243
238
|
|
|
244
239
|
/** Shared instance of our {@link LogOptions}. */
|
|
245
|
-
export const logOptions =
|
|
240
|
+
export const logOptions = getSingleton(optionsKey, () => new LogOptionsImpl())
|
package/src/logging.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { currentContext } from './async'
|
|
2
|
+
import { $gry, $wht } from './logging/colors'
|
|
2
3
|
import { getLogger } from './logging/logger'
|
|
3
4
|
import { setupSpinner } from './logging/spinner'
|
|
4
5
|
|
|
5
|
-
import type {
|
|
6
|
+
import type { Log, Logger } from './logging/logger'
|
|
6
7
|
|
|
7
8
|
export * from './logging/colors'
|
|
8
9
|
export * from './logging/github'
|
|
@@ -32,10 +33,6 @@ export const log: LogFunction = ((): LogFunction => {
|
|
|
32
33
|
|
|
33
34
|
/* Create a Logger wrapping the current logger */
|
|
34
35
|
const wrapper: Log = {
|
|
35
|
-
get logger() {
|
|
36
|
-
return logger()
|
|
37
|
-
},
|
|
38
|
-
|
|
39
36
|
trace(...args: [ any, ...any ]): void {
|
|
40
37
|
logger().trace(...args)
|
|
41
38
|
},
|
|
@@ -71,3 +68,21 @@ export const log: LogFunction = ((): LogFunction => {
|
|
|
71
68
|
/* Return our function, with added Logger implementation */
|
|
72
69
|
return Object.assign(log, wrapper)
|
|
73
70
|
})()
|
|
71
|
+
|
|
72
|
+
/* ========================================================================== *
|
|
73
|
+
* BANNER *
|
|
74
|
+
* ========================================================================== */
|
|
75
|
+
|
|
76
|
+
/** Print a nice _banner_ message on the log */
|
|
77
|
+
export function banner(message: string): void {
|
|
78
|
+
const padMessage = message.length > 60 ? message.length : 60
|
|
79
|
+
const padLines = padMessage + 2
|
|
80
|
+
|
|
81
|
+
log.notice([
|
|
82
|
+
'',
|
|
83
|
+
$gry(`\u2554${''.padStart(padLines, '\u2550')}\u2557`),
|
|
84
|
+
`${$gry('\u2551')} ${$wht(message.padEnd(padMessage, ' '))} ${$gry('\u2551')}`,
|
|
85
|
+
$gry(`\u255A${''.padStart(padLines, '\u2550')}\u255D`),
|
|
86
|
+
'',
|
|
87
|
+
].join('\n'))
|
|
88
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { $p } from '../logging/colors'
|
|
2
|
+
import { ForkingPlug, type ForkOptions } from '../fork'
|
|
3
|
+
import { requireFilename } from '../paths'
|
|
4
|
+
import { invokeTasks, isBuild } from '../build'
|
|
5
|
+
|
|
6
|
+
import type { Files } from '../files'
|
|
7
|
+
import type { Context, Plug } from '../pipe'
|
|
8
|
+
|
|
9
|
+
/** Writes some info about the current {@link Files} being passed around. */
|
|
10
|
+
export class RunBuildInternal implements Plug<void> {
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly _tasks: readonly string[],
|
|
13
|
+
private readonly _props: Readonly<Record<string, string>>,
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
async pipe(files: Files, context: Context): Promise<void> {
|
|
17
|
+
const tasks = this._tasks.length === 0 ? [ 'default' ] : this._tasks
|
|
18
|
+
|
|
19
|
+
for (const file of files.absolutePaths()) {
|
|
20
|
+
// Import and check build file
|
|
21
|
+
let maybeBuild = await import(file)
|
|
22
|
+
while (maybeBuild) {
|
|
23
|
+
if (isBuild(maybeBuild)) break
|
|
24
|
+
maybeBuild = maybeBuild.default
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// We _need_ a build
|
|
28
|
+
if (! isBuild(maybeBuild)) {
|
|
29
|
+
context.log.fail(`File ${$p(file)} did not export a proper build`)
|
|
30
|
+
} else {
|
|
31
|
+
await invokeTasks(maybeBuild, tasks, this._props)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class RunBuild extends ForkingPlug {
|
|
38
|
+
constructor(
|
|
39
|
+
tasks: readonly string[],
|
|
40
|
+
props: Readonly<Record<string, string>>,
|
|
41
|
+
options: ForkOptions,
|
|
42
|
+
) {
|
|
43
|
+
super(requireFilename(__fileurl), [ tasks, props, options ], RunBuildInternal.name)
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/plugs/debug.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $gry, $
|
|
1
|
+
import { $gry, $und, $ylw } from '../logging'
|
|
2
2
|
import { install } from '../pipe'
|
|
3
3
|
|
|
4
4
|
import type { Files } from '../files'
|
|
@@ -26,14 +26,15 @@ install('debug', class Debug implements Plug<Files> {
|
|
|
26
26
|
|
|
27
27
|
async pipe(files: Files, context: Context): Promise<Files> {
|
|
28
28
|
context.log.notice(this._title, `${$gry('(')}${$ylw(files.length)} ${$gry('files)')}`)
|
|
29
|
-
context.log.notice('-
|
|
30
|
-
context.log.notice('-
|
|
31
|
-
context.log.notice('-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
context.log.notice('- build file dir:', $gry($und(context.resolve('@'))))
|
|
30
|
+
context.log.notice('- current dir:', $gry($und(context.resolve('.'))))
|
|
31
|
+
context.log.notice('- files dir:', $gry($und(files.directory)))
|
|
32
|
+
|
|
33
|
+
const paths = [ ...files ]
|
|
34
|
+
const path = paths.shift()
|
|
35
|
+
context.log.notice('- relative paths:', $und($gry(path)))
|
|
36
|
+
paths.forEach((p) => context.log.notice('- :', $und($gry(p))))
|
|
37
|
+
|
|
37
38
|
return files
|
|
38
39
|
}
|
|
39
40
|
})
|
package/src/types.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface State {
|
|
|
26
26
|
readonly tasks: Tasks
|
|
27
27
|
/** All _properties_ available in this {@link State} */
|
|
28
28
|
readonly props: Props
|
|
29
|
+
/** All _tasks_ that have failed in this {@link State} */
|
|
30
|
+
readonly fails: Set<Task>
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/* ========================================================================== *
|
|
@@ -36,21 +38,23 @@ export interface State {
|
|
|
36
38
|
* The {@link Task} interface normalizes a task definition, associating it with
|
|
37
39
|
* its build file, its sibling {@link Task}s and available _properties_.
|
|
38
40
|
*/
|
|
39
|
-
export interface Task<T extends Result = Result
|
|
40
|
-
/**
|
|
41
|
-
|
|
41
|
+
export interface Task<T extends Result = Result> {
|
|
42
|
+
/** The unique ID of this {@link Task} */
|
|
43
|
+
readonly id: number,
|
|
44
|
+
/** The _original_ name of this task */
|
|
45
|
+
readonly name: string
|
|
42
46
|
/** All _properties_ siblings to this {@link Task} */
|
|
43
47
|
readonly props: Props
|
|
44
48
|
/** All {@link Tasks} sibling to this {@link Task} */
|
|
45
49
|
readonly tasks: Tasks
|
|
46
50
|
/** The absolute file name where this {@link Task} was defined */
|
|
47
51
|
readonly buildFile: AbsolutePath,
|
|
48
|
-
/** Invoke a task from in the context of a {@link Build} */
|
|
49
|
-
invoke(state: State, taskName: string): Promise<T>
|
|
50
52
|
/** Other {@link Task}s hooked _before_ this one */
|
|
51
53
|
readonly before: Task[]
|
|
52
54
|
/** Other {@link Task}s hooked _after_ this one */
|
|
53
55
|
readonly after: Task[]
|
|
56
|
+
/** Invoke a task from in the context of a {@link Build} */
|
|
57
|
+
invoke(state: State, taskName: string): Promise<T>
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
/**
|
|
@@ -62,6 +66,12 @@ export type TaskResult = Pipe | Files | void | undefined
|
|
|
62
66
|
/** The {@link TaskDef} type identifies the _definition_ of a task. */
|
|
63
67
|
export type TaskDef<R extends TaskResult = TaskResult> = () => R | Promise<R>
|
|
64
68
|
|
|
69
|
+
/** A callable, compiled {@link Task} from a {@link TaskDef} */
|
|
70
|
+
export type TaskCall<D extends BuildDef = BuildDef, R extends Result = Result> = {
|
|
71
|
+
(props?: Partial<Props<D>>): Promise<R>
|
|
72
|
+
task: Task<R>
|
|
73
|
+
}
|
|
74
|
+
|
|
65
75
|
/* ========================================================================== *
|
|
66
76
|
* TASKS AND PROPERTIES *
|
|
67
77
|
* ========================================================================== */
|
|
@@ -73,12 +83,12 @@ export type Props<D extends BuildDef = BuildDef> = {
|
|
|
73
83
|
|
|
74
84
|
/** A type identifying all _tasks_ in a {@link Build} */
|
|
75
85
|
export type Tasks<D extends BuildDef = BuildDef> = {
|
|
76
|
-
readonly [ k in string & keyof D as D[k] extends TaskDef |
|
|
86
|
+
readonly [ k in string & keyof D as D[k] extends TaskDef | TaskCall ? k : never ] :
|
|
77
87
|
D[k] extends TaskDef<infer R> ?
|
|
78
|
-
R extends void | undefined ?
|
|
79
|
-
R extends Pipe | Files ?
|
|
88
|
+
R extends void | undefined ? TaskCall<D, undefined> :
|
|
89
|
+
R extends Pipe | Files ? TaskCall<D, Files> :
|
|
80
90
|
never :
|
|
81
|
-
D[k] extends
|
|
91
|
+
D[k] extends TaskCall ? D[k] :
|
|
82
92
|
never
|
|
83
93
|
}
|
|
84
94
|
|
|
@@ -91,7 +101,7 @@ export type Tasks<D extends BuildDef = BuildDef> = {
|
|
|
91
101
|
* all its properties and tasks.
|
|
92
102
|
*/
|
|
93
103
|
export interface BuildDef {
|
|
94
|
-
[ k : string ] : string | TaskDef |
|
|
104
|
+
[ k : string ] : string | TaskDef | TaskCall
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
/**
|
|
@@ -99,24 +109,45 @@ export interface BuildDef {
|
|
|
99
109
|
* {@link TaskDef | task definitions }.
|
|
100
110
|
*/
|
|
101
111
|
export type ThisBuild<D extends BuildDef> = {
|
|
102
|
-
readonly [ k in keyof D ] :
|
|
103
|
-
k extends
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
R extends Files ? () => Pipe :
|
|
111
|
-
never :
|
|
112
|
-
D[k] extends string ?
|
|
113
|
-
string :
|
|
112
|
+
readonly [ k in keyof D as k extends string ? k : never ] :
|
|
113
|
+
D[k] extends TaskDef<infer R> ?
|
|
114
|
+
R extends Promise<undefined> | void | undefined ? () => Promise<undefined> :
|
|
115
|
+
R extends Pipe | Files ? () => Pipe :
|
|
116
|
+
never :
|
|
117
|
+
D[k] extends TaskCall<any, infer R> ?
|
|
118
|
+
R extends undefined ? () => Promise<undefined> :
|
|
119
|
+
R extends Files ? () => Pipe :
|
|
114
120
|
never :
|
|
121
|
+
D[k] extends string ?
|
|
122
|
+
string :
|
|
115
123
|
never
|
|
116
124
|
}
|
|
117
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Symbol indicating that an object is a {@link Build}.
|
|
128
|
+
*
|
|
129
|
+
* In a compiled {@link Build} this symbol will be associated with a function
|
|
130
|
+
* taking an array of strings (task names) and record of props to override
|
|
131
|
+
*/
|
|
132
|
+
export const buildMarker = Symbol.for('plugjs:plug:types:Build')
|
|
133
|
+
|
|
118
134
|
/**
|
|
119
135
|
* The {@link Build} type represents the collection of {@link Task}s
|
|
120
136
|
* and _properties_ compiled from a {@link BuildDef | build definition}.
|
|
121
137
|
*/
|
|
122
|
-
export type Build<D extends BuildDef = BuildDef> =
|
|
138
|
+
export type Build<D extends BuildDef = BuildDef> = Props<D> & Tasks<D> & {
|
|
139
|
+
readonly [buildMarker]: (
|
|
140
|
+
tasks: readonly string[],
|
|
141
|
+
props?: Record<string, string | undefined>,
|
|
142
|
+
) => Promise<void>
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** A type identifying all _task names_ in a {@link Build}. */
|
|
146
|
+
export type BuildTasks<B extends Build> = string & keyof {
|
|
147
|
+
[ name in keyof B as B[name] extends Function ? name : never ] : any
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** A type identifying a subset of _properties_ for a {@link Build}. */
|
|
151
|
+
export type BuildProps<B extends Build> = {
|
|
152
|
+
[ name in keyof B as B[name] extends string ? name : never ]? : string
|
|
153
|
+
}
|
package/src/utils/diff.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { inspect, isDeepStrictEqual } from 'node:util'
|
|
|
3
3
|
|
|
4
4
|
import { assert } from '../asserts'
|
|
5
5
|
import { $grn, $red, logOptions } from '../logging'
|
|
6
|
-
import { getTypeOf } from './types'
|
|
7
6
|
|
|
8
7
|
import type { InspectOptions } from 'node:util'
|
|
9
8
|
|
|
@@ -326,13 +325,9 @@ export function textDiff(
|
|
|
326
325
|
let lhsLines: string[]
|
|
327
326
|
let rhsLines: string[]
|
|
328
327
|
|
|
329
|
-
// Get the _real_ types of both arguments
|
|
330
|
-
const lhsType = getTypeOf(lhs)
|
|
331
|
-
const rhsType = getTypeOf(rhs)
|
|
332
|
-
|
|
333
328
|
// If _both_ arguments are strings, then just split and compare, otherwise
|
|
334
329
|
// we nuse NodeJS' inspect to prep their string version
|
|
335
|
-
if ((
|
|
330
|
+
if ((typeof lhs === 'string') && (typeof rhs === 'string')) {
|
|
336
331
|
lhsLines = lhs.split('\n')
|
|
337
332
|
rhsLines = rhs.split('\n')
|
|
338
333
|
} else {
|
package/src/utils/exec.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
1
2
|
import path from 'node:path'
|
|
2
3
|
import readline from 'node:readline'
|
|
3
|
-
import { fork as forkProcess, spawn as spawnProcess } from 'node:child_process'
|
|
4
4
|
|
|
5
5
|
import { assert, BuildFailure } from '../asserts'
|
|
6
|
-
import { $p
|
|
6
|
+
import { $p } from '../logging'
|
|
7
7
|
import { getCurrentWorkingDirectory, resolveDirectory } from '../paths'
|
|
8
8
|
|
|
9
9
|
import type { SpawnOptions } from 'node:child_process'
|
|
@@ -16,8 +16,6 @@ export interface ExecChildOptions {
|
|
|
16
16
|
coverageDir?: string,
|
|
17
17
|
/** Extra environment variables, or overrides for existing ones */
|
|
18
18
|
env?: Record<string, any>,
|
|
19
|
-
/** Whether to _fork_ the process (argument is a javascript file) or not */
|
|
20
|
-
fork?: boolean,
|
|
21
19
|
/** Whether to run the command in a shell (optionally name the shell) */
|
|
22
20
|
shell?: string | boolean,
|
|
23
21
|
/** The current working directory of the process to execute. */
|
|
@@ -32,7 +30,6 @@ export async function execChild(
|
|
|
32
30
|
): Promise<void> {
|
|
33
31
|
const {
|
|
34
32
|
env = {}, // default empty environment
|
|
35
|
-
fork = false, // by default do not fork
|
|
36
33
|
shell = false, // by default do not use a shell
|
|
37
34
|
cwd = undefined, // by default use "process.cwd()"
|
|
38
35
|
coverageDir, // default "undefined" (pass throug from env)
|
|
@@ -42,9 +39,6 @@ export async function execChild(
|
|
|
42
39
|
const childCwd = cwd ? context.resolve(cwd) : getCurrentWorkingDirectory()
|
|
43
40
|
assert(resolveDirectory(childCwd), `Current working directory ${$p(childCwd)} does not exist`)
|
|
44
41
|
|
|
45
|
-
// Check for wrong fork/shell combination
|
|
46
|
-
assert(!(fork && shell), 'Options "fork" and "shell" can not coexist')
|
|
47
|
-
|
|
48
42
|
// Figure out the PATH environment variable
|
|
49
43
|
const childPaths: AbsolutePath[] = []
|
|
50
44
|
|
|
@@ -62,8 +56,7 @@ export async function execChild(
|
|
|
62
56
|
|
|
63
57
|
// Build our environment variables record
|
|
64
58
|
const PATH = childPaths.join(path.delimiter)
|
|
65
|
-
const
|
|
66
|
-
const childEnv: Record<string, string> = { ...process.env, ...env, ...logForkEnv, PATH }
|
|
59
|
+
const childEnv: Record<string, string> = { ...process.env, ...env, PATH }
|
|
67
60
|
|
|
68
61
|
// Instrument coverage directory if needed
|
|
69
62
|
if (coverageDir) childEnv.NODE_V8_COVERAGE = context.resolve(coverageDir)
|
|
@@ -71,19 +64,17 @@ export async function execChild(
|
|
|
71
64
|
// Prepare the options for calling `spawn`
|
|
72
65
|
const childOptions: SpawnOptions = {
|
|
73
66
|
...extraOptions,
|
|
74
|
-
stdio: [ 'ignore', 'pipe', 'pipe'
|
|
67
|
+
stdio: [ 'ignore', 'pipe', 'pipe' ],
|
|
75
68
|
cwd: childCwd,
|
|
76
69
|
env: childEnv,
|
|
77
70
|
shell,
|
|
78
71
|
}
|
|
79
72
|
|
|
80
73
|
// Spawn our subprocess and monitor its stdout/stderr
|
|
81
|
-
context.log.info(
|
|
74
|
+
context.log.info('Executing', [ cmd, ...args ])
|
|
82
75
|
context.log.debug('Child process options', childOptions)
|
|
83
76
|
|
|
84
|
-
const child =
|
|
85
|
-
forkProcess(cmd, args, childOptions) :
|
|
86
|
-
spawnProcess(cmd, args, childOptions)
|
|
77
|
+
const child = spawn(cmd, args, childOptions)
|
|
87
78
|
|
|
88
79
|
try {
|
|
89
80
|
context.log.info('Child process PID', child.pid)
|
|
@@ -99,11 +90,6 @@ export async function execChild(
|
|
|
99
90
|
const err = readline.createInterface(child.stderr)
|
|
100
91
|
err.on('line', (line) => context.log.warn(line ||'\u00a0'))
|
|
101
92
|
}
|
|
102
|
-
|
|
103
|
-
// Log output bypass
|
|
104
|
-
if (child.stdio[4]) {
|
|
105
|
-
child.stdio[4].on('data', (data) => logOptions.output.write(data))
|
|
106
|
-
}
|
|
107
93
|
} catch (error) {
|
|
108
94
|
// If something happens before returning our promise, kill the child...
|
|
109
95
|
child.kill()
|