@plugjs/plug 0.0.15 → 0.0.17
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/assert.cjs +18 -38
- package/dist/assert.cjs.map +1 -1
- package/dist/assert.d.ts +2 -16
- package/dist/assert.mjs +17 -32
- package/dist/assert.mjs.map +1 -1
- package/dist/async.cjs +2 -2
- package/dist/async.cjs.map +1 -1
- package/dist/async.d.ts +1 -1
- package/dist/async.mjs +2 -2
- package/dist/async.mjs.map +1 -1
- package/dist/build.cjs +20 -72
- package/dist/build.cjs.map +2 -2
- package/dist/build.d.ts +5 -3
- package/dist/build.mjs +22 -74
- package/dist/build.mjs.map +2 -2
- package/dist/failure.cjs +48 -0
- package/dist/failure.cjs.map +6 -0
- package/dist/failure.d.ts +13 -0
- package/dist/failure.mjs +22 -0
- package/dist/failure.mjs.map +6 -0
- package/dist/fork.cjs +21 -10
- package/dist/fork.cjs.map +1 -1
- package/dist/fork.d.ts +19 -1
- package/dist/fork.mjs +21 -11
- package/dist/fork.mjs.map +1 -1
- package/dist/helpers.cjs +8 -40
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.ts +23 -3
- package/dist/helpers.mjs +8 -40
- package/dist/helpers.mjs.map +1 -1
- package/dist/index.cjs +16 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +1 -1
- package/dist/log/logger.cjs +15 -12
- package/dist/log/logger.cjs.map +1 -1
- package/dist/log/logger.d.ts +2 -0
- package/dist/log/logger.mjs +15 -12
- package/dist/log/logger.mjs.map +1 -1
- package/dist/log/report.cjs +7 -4
- package/dist/log/report.cjs.map +1 -1
- package/dist/log/report.mjs +7 -4
- package/dist/log/report.mjs.map +1 -1
- package/dist/log.cjs +3 -0
- package/dist/log.cjs.map +1 -1
- package/dist/log.mjs +3 -0
- package/dist/log.mjs.map +1 -1
- package/dist/pipe.cjs +56 -14
- package/dist/pipe.cjs.map +1 -1
- package/dist/pipe.d.ts +42 -28
- package/dist/pipe.mjs +60 -13
- package/dist/pipe.mjs.map +1 -1
- package/dist/plugs/copy.cjs +1 -1
- package/dist/plugs/copy.cjs.map +1 -1
- package/dist/plugs/copy.mjs +1 -1
- package/dist/plugs/copy.mjs.map +1 -1
- package/dist/plugs/coverage.cjs +1 -2
- package/dist/plugs/coverage.cjs.map +1 -1
- package/dist/plugs/coverage.mjs +1 -2
- package/dist/plugs/coverage.mjs.map +1 -1
- package/dist/plugs/debug.cjs +6 -1
- package/dist/plugs/debug.cjs.map +1 -1
- package/dist/plugs/debug.d.ts +1 -1
- package/dist/plugs/debug.mjs +7 -2
- package/dist/plugs/debug.mjs.map +1 -1
- package/dist/plugs/esbuild.mjs.map +1 -1
- package/dist/plugs/eslint/runner.cjs +2 -1
- package/dist/plugs/eslint/runner.cjs.map +1 -1
- package/dist/plugs/eslint/runner.d.ts +1 -1
- package/dist/plugs/eslint/runner.mjs +3 -2
- package/dist/plugs/eslint/runner.mjs.map +1 -1
- package/dist/plugs/eslint.cjs +2 -2
- package/dist/plugs/eslint.cjs.map +1 -1
- package/dist/plugs/eslint.mjs +1 -1
- package/dist/plugs/exec.cjs +1 -1
- package/dist/plugs/exec.cjs.map +1 -1
- package/dist/plugs/exec.mjs +1 -1
- package/dist/plugs/exec.mjs.map +1 -1
- package/dist/plugs/mocha/runner.cjs +2 -2
- package/dist/plugs/mocha/runner.cjs.map +1 -1
- package/dist/plugs/mocha/runner.d.ts +1 -1
- package/dist/plugs/mocha/runner.mjs +2 -2
- package/dist/plugs/mocha/runner.mjs.map +1 -1
- package/dist/plugs/mocha.cjs +2 -2
- package/dist/plugs/mocha.cjs.map +1 -1
- package/dist/plugs/mocha.mjs +1 -1
- package/dist/plugs/rmf.mjs.map +1 -1
- package/dist/plugs/tsc/report.cjs.map +1 -1
- package/dist/plugs/tsc/report.mjs.map +1 -1
- package/dist/plugs/tsc/runner.cjs +3 -6
- package/dist/plugs/tsc/runner.cjs.map +2 -2
- package/dist/plugs/tsc/runner.mjs +4 -7
- package/dist/plugs/tsc/runner.mjs.map +2 -2
- package/dist/plugs/tsc.cjs +2 -2
- package/dist/plugs/tsc.cjs.map +1 -1
- package/dist/plugs/tsc.mjs +1 -1
- package/dist/types.d.ts +7 -8
- package/dist/utils/asyncfs.cjs.map +1 -1
- package/dist/utils/asyncfs.mjs.map +1 -1
- package/extra/cli.mjs +1 -1
- package/package.json +14 -13
- package/src/assert.ts +20 -34
- package/src/async.ts +3 -3
- package/src/failure.ts +31 -0
- package/src/fork.ts +43 -10
- package/src/helpers.ts +35 -56
- package/src/index.ts +4 -3
- package/src/log/logger.ts +22 -16
- package/src/log/report.ts +7 -3
- package/src/log.ts +4 -0
- package/src/pipe.ts +147 -44
- package/src/plugs/copy.ts +2 -2
- package/src/plugs/coverage/report.ts +1 -1
- package/src/plugs/coverage.ts +2 -3
- package/src/plugs/debug.ts +11 -4
- package/src/plugs/esbuild.ts +1 -1
- package/src/plugs/eslint/runner.ts +4 -3
- package/src/plugs/eslint.ts +1 -1
- package/src/plugs/exec.ts +1 -1
- package/src/plugs/mocha/runner.ts +3 -3
- package/src/plugs/mocha.ts +1 -1
- package/src/plugs/rmf.ts +1 -1
- package/src/plugs/tsc/runner.ts +5 -8
- package/src/plugs/tsc.ts +1 -1
- package/src/types.ts +8 -15
- package/src/utils/asyncfs.ts +1 -0
package/src/helpers.ts
CHANGED
|
@@ -2,37 +2,15 @@ import { assert } from './assert'
|
|
|
2
2
|
import { requireContext } from './async'
|
|
3
3
|
import { Files } from './files'
|
|
4
4
|
import { $p, log } from './log'
|
|
5
|
-
import { AbsolutePath,
|
|
6
|
-
import {
|
|
5
|
+
import { AbsolutePath, getCurrentWorkingDirectory, resolveDirectory, resolveFile } from './paths'
|
|
6
|
+
import { Pipe } from './pipe'
|
|
7
7
|
import { rm } from './utils/asyncfs'
|
|
8
8
|
import { ParseOptions, parseOptions } from './utils/options'
|
|
9
9
|
import { walk, WalkOptions } from './utils/walk'
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
plug(plug: Plug<Files>): Pipe
|
|
17
|
-
plug(plug: PlugFunction<Files>): Pipe
|
|
18
|
-
plug(plug: Plug<void | undefined>): Promise<undefined>
|
|
19
|
-
plug(plug: PlugFunction<void | undefined>): Promise<undefined>
|
|
20
|
-
plug(arg: Plug<PlugResult> | PlugFunction<PlugResult>): Pipe | Promise<undefined> {
|
|
21
|
-
const plug = typeof arg === 'function' ? { pipe: arg } : arg
|
|
22
|
-
|
|
23
|
-
const parent = this
|
|
24
|
-
return new PipeImpl(async (context: Context): Promise<Files> => {
|
|
25
|
-
const files = await parent._start(context)
|
|
26
|
-
const files2 = await plug.pipe(files, context)
|
|
27
|
-
assert(files2, 'Unable to extend pipe (part tres)')
|
|
28
|
-
return files2
|
|
29
|
-
})
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async run(): Promise<Files> {
|
|
33
|
-
return this._start(requireContext())
|
|
34
|
-
}
|
|
35
|
-
}
|
|
11
|
+
/* ========================================================================== *
|
|
12
|
+
* EXTERNAL HELPERS *
|
|
13
|
+
* ========================================================================== */
|
|
36
14
|
|
|
37
15
|
/** The {@link FindOptions} interface defines the options for finding files. */
|
|
38
16
|
export interface FindOptions extends WalkOptions {
|
|
@@ -40,16 +18,20 @@ export interface FindOptions extends WalkOptions {
|
|
|
40
18
|
directory?: string
|
|
41
19
|
}
|
|
42
20
|
|
|
43
|
-
|
|
21
|
+
/** Find files in the current directory using the specified _glob_. */
|
|
44
22
|
export function find(glob: string): Pipe
|
|
23
|
+
/** Find files in the current directory using the specified _globs_. */
|
|
45
24
|
export function find(glob: string, ...globs: string[]): Pipe
|
|
25
|
+
/** Find files using the specified _glob_ and {@link FindOptions | options}. */
|
|
46
26
|
export function find(glob: string, options: FindOptions): Pipe
|
|
27
|
+
/** Find files using the specified _globs_ and {@link FindOptions | options}. */
|
|
47
28
|
export function find(glob: string, ...extra: [...globs: string[], options: FindOptions]): Pipe
|
|
48
|
-
|
|
29
|
+
/* Overload */
|
|
49
30
|
export function find(...args: ParseOptions<FindOptions>): Pipe {
|
|
50
31
|
const { params: globs, options } = parseOptions(args, {})
|
|
51
32
|
|
|
52
|
-
|
|
33
|
+
const context = requireContext()
|
|
34
|
+
return new Pipe(context, Promise.resolve().then(async () => {
|
|
53
35
|
const directory = options.directory ?
|
|
54
36
|
context.resolve(options.directory) :
|
|
55
37
|
getCurrentWorkingDirectory()
|
|
@@ -60,30 +42,7 @@ export function find(...args: ParseOptions<FindOptions>): Pipe {
|
|
|
60
42
|
}
|
|
61
43
|
|
|
62
44
|
return builder.build()
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function merge(...pipes: Pipe[]): Pipe {
|
|
67
|
-
return new PipeImpl(async (): Promise<Files> => {
|
|
68
|
-
if (pipes.length === 0) return Files.builder(getCurrentWorkingDirectory()).build()
|
|
69
|
-
|
|
70
|
-
const results: Files[] = []
|
|
71
|
-
|
|
72
|
-
for (const pipe of pipes) {
|
|
73
|
-
const result = await pipe.run()
|
|
74
|
-
assert(result, 'Pipe did not return a Files result')
|
|
75
|
-
results.push(result)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const [ first, ...others ] = results
|
|
79
|
-
|
|
80
|
-
const firstDir = first.directory
|
|
81
|
-
const otherDirs = others.map((f) => f.directory)
|
|
82
|
-
|
|
83
|
-
const directory = commonPath(firstDir, ...otherDirs)
|
|
84
|
-
|
|
85
|
-
return Files.builder(directory).merge(first, ...others).build()
|
|
86
|
-
})
|
|
45
|
+
}))
|
|
87
46
|
}
|
|
88
47
|
|
|
89
48
|
/**
|
|
@@ -108,13 +67,33 @@ export async function rmrf(directory: string): Promise<void> {
|
|
|
108
67
|
await rm(dir, { recursive: true })
|
|
109
68
|
}
|
|
110
69
|
|
|
111
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a (set of) path(s) into an {@link AbsolutePath}.
|
|
72
|
+
*
|
|
73
|
+
* If the path (or first component thereof) starts with `@...`, then the
|
|
74
|
+
* resolved path will be relative to the directory containing the build file
|
|
75
|
+
* where the current task was defined, otherwise it will be relative to the
|
|
76
|
+
* current working directory.
|
|
77
|
+
*/
|
|
78
|
+
export function resolve(...paths: [ string, ...string[] ]): AbsolutePath {
|
|
79
|
+
return requireContext().resolve(...paths)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Return an absolute path of the file if it exist on disk.
|
|
84
|
+
*
|
|
85
|
+
* See the comments on {@link resolve} to understand how paths are resolved.
|
|
86
|
+
*/
|
|
112
87
|
export function isFile(...paths: [ string, ...string[] ]): AbsolutePath | undefined {
|
|
113
88
|
const path = requireContext().resolve(...paths)
|
|
114
89
|
return resolveFile(path)
|
|
115
90
|
}
|
|
116
91
|
|
|
117
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Return an absolute path of the directory if it exist on disk.
|
|
94
|
+
*
|
|
95
|
+
* See the comments on {@link resolve} to understand how paths are resolved.
|
|
96
|
+
*/
|
|
118
97
|
export function isDirectory(...paths: [ string, ...string[] ]): AbsolutePath | undefined {
|
|
119
98
|
const path = requireContext().resolve(...paths)
|
|
120
99
|
return resolveDirectory(path)
|
package/src/index.ts
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
export type { AbsolutePath } from './paths'
|
|
4
4
|
export type { Files, FilesBuilder } from './files'
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
export { BuildFailure } from './failure'
|
|
7
|
+
export { Pipe } from './pipe'
|
|
6
8
|
|
|
7
9
|
// Our minimal exports
|
|
8
10
|
export * from './assert'
|
|
9
11
|
export * from './build'
|
|
12
|
+
export * from './fork'
|
|
10
13
|
export * from './helpers'
|
|
11
14
|
export * from './log'
|
|
12
|
-
|
|
13
|
-
// PlugJS types
|
|
14
15
|
export * from './plugs'
|
|
15
16
|
export * from './types'
|
package/src/log/logger.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BuildFailure, isBuildFailure } from '../failure'
|
|
2
2
|
import { emitColor, emitPlain, LogEmitter } from './emit'
|
|
3
3
|
import { DEBUG, ERROR, INFO, LogLevel, NOTICE, TRACE, WARN } from './levels'
|
|
4
4
|
import { logOptions } from './options'
|
|
@@ -34,6 +34,8 @@ export interface Log {
|
|
|
34
34
|
warn(...args: [ any, ...any ]): this
|
|
35
35
|
/** Log an `ERROR` message */
|
|
36
36
|
error(...args: [ any, ...any ]): this
|
|
37
|
+
/** Log an `ERROR` message and fail the build */
|
|
38
|
+
fail(...args: [ any, ...any ]): never
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/** A {@link Logger} extends the basic {@link Log} adding some state. */
|
|
@@ -83,9 +85,18 @@ class LoggerImpl implements Logger {
|
|
|
83
85
|
private _emit(level: LogLevel, args: [ any, ...any ]): this {
|
|
84
86
|
if (this._level > level) return this
|
|
85
87
|
|
|
86
|
-
// Filter out build failures
|
|
87
|
-
const params = args.filter((arg) =>
|
|
88
|
-
|
|
88
|
+
// Filter out build failures that were already logged
|
|
89
|
+
const params = args.filter((arg) => {
|
|
90
|
+
if (isBuildFailure(arg)) {
|
|
91
|
+
if (arg.logged) return false // already logged? skip!
|
|
92
|
+
return arg.logged = true // set logged to true and log!
|
|
93
|
+
} else {
|
|
94
|
+
return true
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// If there's nothing left to log, then we're done
|
|
99
|
+
if (params.length === 0) return this
|
|
89
100
|
|
|
90
101
|
// Prepare our options for logging
|
|
91
102
|
const options = { level, taskName: this._task, indent: this._indent }
|
|
@@ -98,18 +109,8 @@ class LoggerImpl implements Logger {
|
|
|
98
109
|
this._stack.splice(0)
|
|
99
110
|
}
|
|
100
111
|
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
if (isBuildError(arg)) {
|
|
104
|
-
this._emitter({ ...options, level: ERROR }, [ arg.message ])
|
|
105
|
-
return false
|
|
106
|
-
} else {
|
|
107
|
-
return true
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
// If we have any leftovers, dump them out, too!
|
|
112
|
-
if (remaining) this._emitter(options, remaining)
|
|
112
|
+
// Emit our log lines and return
|
|
113
|
+
this._emitter(options, params)
|
|
113
114
|
return this
|
|
114
115
|
}
|
|
115
116
|
|
|
@@ -145,6 +146,11 @@ class LoggerImpl implements Logger {
|
|
|
145
146
|
return this._emit(ERROR, args)
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
fail(...args: [ any, ...any ]): never {
|
|
150
|
+
this._emit(ERROR, args)
|
|
151
|
+
throw new BuildFailure({ logged: true })
|
|
152
|
+
}
|
|
153
|
+
|
|
148
154
|
enter(): this
|
|
149
155
|
enter(level: LogLevel, message: string): this
|
|
150
156
|
enter(...args: [] | [ level: LogLevel, message: string ]): this {
|
package/src/log/report.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BuildFailure } from '../failure'
|
|
2
2
|
import { AbsolutePath } from '../paths'
|
|
3
3
|
import { readFile } from '../utils/asyncfs'
|
|
4
4
|
import { $blu, $cyn, $gry, $red, $und, $wht, $ylw } from './colors'
|
|
@@ -191,7 +191,11 @@ export class ReportImpl implements Report {
|
|
|
191
191
|
[ ...record.message.map((msg) => msg.split('\n')).flat(1) ] :
|
|
192
192
|
record.message.split('\n')
|
|
193
193
|
messages = messages.filter((message) => !! message)
|
|
194
|
-
if (! messages.length)
|
|
194
|
+
if (! messages.length) {
|
|
195
|
+
const options = { taskName: this._task, level: ERROR }
|
|
196
|
+
this._emitter(options, [ 'No message for report record' ])
|
|
197
|
+
throw new BuildFailure({ logged: true })
|
|
198
|
+
}
|
|
195
199
|
|
|
196
200
|
const level = record.level
|
|
197
201
|
const file = record.file
|
|
@@ -263,7 +267,7 @@ export class ReportImpl implements Report {
|
|
|
263
267
|
done(showSources?: boolean | undefined): void {
|
|
264
268
|
if (showSources == null) showSources = logOptions.showSources
|
|
265
269
|
if (! this.empty) this._emit(showSources)
|
|
266
|
-
if (this.errors) throw
|
|
270
|
+
if (this.errors) throw new BuildFailure({ logged: true })
|
|
267
271
|
}
|
|
268
272
|
|
|
269
273
|
private _emit(showSources: boolean): this {
|
package/src/log.ts
CHANGED
|
@@ -58,6 +58,10 @@ export const log: LogFunction = ((): LogFunction => {
|
|
|
58
58
|
logger().error(...args)
|
|
59
59
|
return wrapper
|
|
60
60
|
},
|
|
61
|
+
|
|
62
|
+
fail(...args: [ any, ...any ]): never {
|
|
63
|
+
throw logger().fail(...args) // fail() returns never but ?!?!?!?!?
|
|
64
|
+
},
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
/* Create a function that will default logging to "NOTICE" */
|
package/src/pipe.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
AbsolutePath,
|
|
3
|
+
commonPath,
|
|
4
|
+
getAbsoluteParent,
|
|
5
|
+
getCurrentWorkingDirectory,
|
|
6
|
+
resolveAbsolutePath,
|
|
7
|
+
} from './paths'
|
|
4
8
|
|
|
5
|
-
import { ForkingPlug } from './fork'
|
|
6
9
|
import { sep } from 'path'
|
|
10
|
+
import { assert, assertPromises } from './assert'
|
|
11
|
+
import { requireContext } from './async'
|
|
12
|
+
import { Files } from './files'
|
|
13
|
+
import { getLogger, Logger } from './log'
|
|
14
|
+
import { Result } from './types'
|
|
7
15
|
|
|
8
16
|
/* ========================================================================== *
|
|
9
17
|
* PLUGS *
|
|
@@ -50,6 +58,14 @@ export class Context {
|
|
|
50
58
|
this.log = getLogger(taskName)
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Resolve a (set of) path(s) in this {@link Context}.
|
|
63
|
+
*
|
|
64
|
+
* If the path (or first component thereof) starts with `@...`, then the
|
|
65
|
+
* resolved path will be relative to the directory containing the build file
|
|
66
|
+
* where the current task was defined, otherwise it will be relative to the
|
|
67
|
+
* current working directory.
|
|
68
|
+
*/
|
|
53
69
|
resolve(path: string, ...paths: string[]): AbsolutePath {
|
|
54
70
|
// Paths starting with "@" are relative to the build file directory
|
|
55
71
|
if (path && path.startsWith('@')) {
|
|
@@ -70,6 +86,25 @@ export class Context {
|
|
|
70
86
|
* PIPES *
|
|
71
87
|
* ========================================================================== */
|
|
72
88
|
|
|
89
|
+
/**
|
|
90
|
+
* In pipe chains, we want to keep track of the _leaf_ `Files` promises (that
|
|
91
|
+
* is, when a derived pipe is created calling `plug` we want to track only the
|
|
92
|
+
* new, derived, promise).
|
|
93
|
+
*
|
|
94
|
+
* We key these _leaf_ promises by _context_ (with a WeakMap), and those will
|
|
95
|
+
* be awaited at the end of the task.
|
|
96
|
+
*/
|
|
97
|
+
const contextPromises = new WeakMap<Context, Set<Promise<Result>>>()
|
|
98
|
+
|
|
99
|
+
export function getContextPromises(context: Context): Set<Promise<Result>> {
|
|
100
|
+
let promises = contextPromises.get(context)
|
|
101
|
+
if (! promises) {
|
|
102
|
+
promises = new Set<Promise<Files>>()
|
|
103
|
+
contextPromises.set(context, promises)
|
|
104
|
+
}
|
|
105
|
+
return promises
|
|
106
|
+
}
|
|
107
|
+
|
|
73
108
|
/**
|
|
74
109
|
* A class that will be extended by {@link Pipe} where {@link install} will
|
|
75
110
|
* add prototype properties from installed {@link Plug}s
|
|
@@ -79,16 +114,116 @@ abstract class PipeProto {
|
|
|
79
114
|
}
|
|
80
115
|
|
|
81
116
|
/**
|
|
82
|
-
* The {@link Pipe}
|
|
117
|
+
* The {@link Pipe} class defines processing pipeline where multiple
|
|
83
118
|
* {@link Plug}s can transform lists of {@link Files}.
|
|
84
119
|
*/
|
|
85
|
-
export
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
120
|
+
export class Pipe extends PipeProto implements Promise<Files> {
|
|
121
|
+
readonly [Symbol.toStringTag] = 'Pipe'
|
|
122
|
+
|
|
123
|
+
constructor(
|
|
124
|
+
private readonly _context: Context,
|
|
125
|
+
private readonly _promise: Promise<Result>,
|
|
126
|
+
) {
|
|
127
|
+
super()
|
|
128
|
+
|
|
129
|
+
// New "Pipe", remember the promise!
|
|
130
|
+
getContextPromises(_context).add(_promise)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ------------------------------------------------------------------------ *
|
|
134
|
+
* Promise implementation *
|
|
135
|
+
* ------------------------------------------------------------------------ *
|
|
136
|
+
* From a _types_ point of view, the `Pipe` implements a `Promise<Files>` *
|
|
137
|
+
* (because only when plugging the correct `Plug` the correct value are *
|
|
138
|
+
* returned). *
|
|
139
|
+
* *
|
|
140
|
+
* Whether to return (as a type) another `Pipe` or a `Promise<undefined>` *
|
|
141
|
+
* is determined by the type of the `plug` parameter below. *
|
|
142
|
+
* *
|
|
143
|
+
* That said, in practice, a `Pipe` implements `Promise<Files | undefined>` *
|
|
144
|
+
* because the result of the plug is _eventually_ computed asynchronously *
|
|
145
|
+
* while `plug` returns immediately.
|
|
146
|
+
* *
|
|
147
|
+
* So, all those "as whatever" below are kind-of-legit... *
|
|
148
|
+
* ------------------------------------------------------------------------ */
|
|
149
|
+
|
|
150
|
+
then<R1 = Files, R2 = never>(
|
|
151
|
+
onfulfilled?: ((value: Files) => R1 | PromiseLike<R1>) | null | undefined,
|
|
152
|
+
onrejected?: ((reason: any) => R2 | PromiseLike<R2>) | null | undefined,
|
|
153
|
+
): Promise<R1 | R2> {
|
|
154
|
+
// We are delegating the handling of this promise to the caller
|
|
155
|
+
getContextPromises(this._context).delete(this._promise)
|
|
156
|
+
return this._promise.then(onfulfilled as (value: Result) => R1 | PromiseLike<R1>, onrejected)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
catch<R = never>(
|
|
160
|
+
onrejected?: ((reason: any) => R | PromiseLike<R>) | null | undefined,
|
|
161
|
+
): Promise<Files | R> {
|
|
162
|
+
// We are delegating the handling of this promise to the caller
|
|
163
|
+
getContextPromises(this._context).delete(this._promise)
|
|
164
|
+
return this._promise.catch(onrejected) as Promise<Files | R>
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
finally(onfinally?: (() => void) | null | undefined): Promise<Files> {
|
|
168
|
+
// We are delegating the handling of this promise to the caller
|
|
169
|
+
getContextPromises(this._context).delete(this._promise)
|
|
170
|
+
return this._promise.finally(onfinally) as Promise<Files>
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* ------------------------------------------------------------------------ *
|
|
174
|
+
* Pipe implementation *
|
|
175
|
+
* ------------------------------------------------------------------------ */
|
|
176
|
+
|
|
177
|
+
plug(plug: Plug<Files>): Pipe
|
|
178
|
+
plug(plug: PlugFunction<Files>): Pipe
|
|
179
|
+
plug(plug: Plug<void | undefined>): Promise<undefined>
|
|
180
|
+
plug(plug: PlugFunction<void | undefined>): Promise<undefined>
|
|
181
|
+
plug(arg: Plug<PlugResult> | PlugFunction<PlugResult>): Pipe | Promise<undefined> {
|
|
182
|
+
const plug = typeof arg === 'function' ? { pipe: arg } : arg
|
|
183
|
+
|
|
184
|
+
// We are creating a new "leaf" Pipe, we can forget our promise
|
|
185
|
+
getContextPromises(this._context).delete(this._promise)
|
|
186
|
+
|
|
187
|
+
// Create and return the new Pipe
|
|
188
|
+
return new Pipe(this._context, this._promise.then(async (result) => {
|
|
189
|
+
assert(result, 'Unable to extend pipe')
|
|
190
|
+
const result2 = await plug.pipe(result, this._context)
|
|
191
|
+
return result2 || undefined
|
|
192
|
+
}))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Merge the results of several {@link Pipe}s into a single one.
|
|
197
|
+
*
|
|
198
|
+
* Merging is performed _in parallel_. When serial execution is to be desired,
|
|
199
|
+
* we can merge the awaited _result_ of the {@link Pipe}.
|
|
200
|
+
*
|
|
201
|
+
* For example:
|
|
202
|
+
*
|
|
203
|
+
* ```
|
|
204
|
+
* const pipe: Pipe = merge([
|
|
205
|
+
* await this.anotherTask1(),
|
|
206
|
+
* await this.anotherTask2(),
|
|
207
|
+
* ])
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
static merge(pipes: (Pipe | Files | Promise<Files>)[]): Pipe {
|
|
211
|
+
const context = requireContext()
|
|
212
|
+
return new Pipe(context, Promise.resolve().then(async () => {
|
|
213
|
+
// No pipes? Just send off an empty pipe...
|
|
214
|
+
if (pipes.length === 0) return Files.builder(getCurrentWorkingDirectory()).build()
|
|
215
|
+
|
|
216
|
+
// Await for all pipes / files / files promises
|
|
217
|
+
const results = await assertPromises<Files>(pipes, 'Error in pipe while merging')
|
|
90
218
|
|
|
91
|
-
|
|
219
|
+
// Find the common directory between all the Files instances
|
|
220
|
+
const [ firstDir, ...otherDirs ] = results.map((f) => f.directory)
|
|
221
|
+
const directory = commonPath(firstDir, ...otherDirs)
|
|
222
|
+
|
|
223
|
+
// Build our new files instance merging all the results
|
|
224
|
+
return Files.builder(directory).merge(...results).build()
|
|
225
|
+
}))
|
|
226
|
+
}
|
|
92
227
|
}
|
|
93
228
|
|
|
94
229
|
/* ========================================================================== *
|
|
@@ -96,7 +231,7 @@ export abstract class Pipe extends PipeProto {
|
|
|
96
231
|
* ========================================================================== */
|
|
97
232
|
|
|
98
233
|
/** The names which can be installed as direct plugs. */
|
|
99
|
-
export type PlugName = string & Exclude<keyof Pipe, 'plug' |
|
|
234
|
+
export type PlugName = string & Exclude<keyof Pipe, 'plug' | keyof Promise<any>>
|
|
100
235
|
|
|
101
236
|
/** The parameters of the plug extension with the given name */
|
|
102
237
|
export type PipeParameters<Name extends PlugName> = PipeOverloads<Name>['args']
|
|
@@ -210,35 +345,3 @@ export function install<
|
|
|
210
345
|
/* Inject the create function in the Pipe's prototype */
|
|
211
346
|
Object.defineProperty(PipeProto.prototype, name, { value: plug })
|
|
212
347
|
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Install a _forking_ {@link Plug} in the {@link Pipe}, in other words
|
|
216
|
-
* execute the plug in a separate process.
|
|
217
|
-
*
|
|
218
|
-
* As a contract, if the _last non-null_ parameter of the constructor is an
|
|
219
|
-
* object and contains the key `coverageDir`, the process will be forked with
|
|
220
|
-
* the approptiately resolved `NODE_V8_COVERAGE` environment variable.
|
|
221
|
-
*
|
|
222
|
-
* Also, forking plugs require some special attention:
|
|
223
|
-
*
|
|
224
|
-
* * plug functions are not supported, only classes implementing the
|
|
225
|
-
* {@link Plug} interface can be used with this.
|
|
226
|
-
*
|
|
227
|
-
* * the class itself _MUST_ be exported as the _default_ export for the
|
|
228
|
-
* `scriptFile` specified below. This is to simplify interoperability between
|
|
229
|
-
* CommonJS and ESM modules as we use dynamic `import(...)` statements.
|
|
230
|
-
*/
|
|
231
|
-
export function installForking<Name extends PlugName>(
|
|
232
|
-
plugName: Name,
|
|
233
|
-
scriptFile: AbsolutePath,
|
|
234
|
-
): void {
|
|
235
|
-
/** Extend out our ForkingPlug below */
|
|
236
|
-
const ctor = class extends ForkingPlug {
|
|
237
|
-
constructor(...args: any[]) {
|
|
238
|
-
super(scriptFile, args)
|
|
239
|
-
}
|
|
240
|
-
} as unknown as PlugConstructor<Name>
|
|
241
|
-
|
|
242
|
-
/** Install the plug in */
|
|
243
|
-
install(plugName, ctor)
|
|
244
|
-
}
|
package/src/plugs/copy.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { assert } from '../assert'
|
|
|
2
2
|
import { Files } from '../files'
|
|
3
3
|
import { $p } from '../log'
|
|
4
4
|
import { assertAbsolutePath, getAbsoluteParent, resolveAbsolutePath } from '../paths'
|
|
5
|
-
import { install, PipeParameters, Plug
|
|
5
|
+
import { Context, install, PipeParameters, Plug } from '../pipe'
|
|
6
6
|
import { chmod, copyFile, fsConstants, mkdir } from '../utils/asyncfs'
|
|
7
7
|
|
|
8
8
|
/** Options for copying files */
|
|
@@ -95,7 +95,7 @@ install('copy', class Copy implements Plug<Files> {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/* Record this file */
|
|
98
|
-
builder.add(
|
|
98
|
+
builder.add(target)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const result = builder.build()
|
package/src/plugs/coverage.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { html, initFunction } from '@plugjs/cov8-html'
|
|
2
2
|
import { sep } from 'node:path'
|
|
3
|
-
import { fail } from '../assert'
|
|
4
3
|
import { Files } from '../files'
|
|
5
4
|
import { $gry, $ms, $p, $red, $ylw, ERROR, NOTICE, WARN } from '../log'
|
|
6
5
|
import { AbsolutePath, resolveAbsolutePath } from '../paths'
|
|
7
|
-
import { install, PipeParameters, Plug
|
|
6
|
+
import { Context, install, PipeParameters, Plug } from '../pipe'
|
|
8
7
|
import { walk } from '../utils/walk'
|
|
9
8
|
import { createAnalyser, SourceMapBias } from './coverage/analysis'
|
|
10
9
|
import { coverageReport, CoverageResult } from './coverage/report'
|
|
@@ -79,7 +78,7 @@ install('coverage', class Coverage implements Plug<Files | undefined> {
|
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
if (coverageFiles.length === 0) {
|
|
82
|
-
fail(`No coverage files found in ${$p(coverageDir)}`)
|
|
81
|
+
throw context.log.fail(`No coverage files found in ${$p(coverageDir)}`)
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
const sourceFiles = [ ...files.absolutePaths() ]
|
package/src/plugs/debug.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Files } from '../files'
|
|
2
|
-
import { $gry, $p, $und } from '../log'
|
|
3
|
-
import { install,
|
|
2
|
+
import { $gry, $p, $und, $ylw } from '../log'
|
|
3
|
+
import { Context, install, PipeParameters, Plug } from '../pipe'
|
|
4
4
|
|
|
5
5
|
declare module '../pipe' {
|
|
6
6
|
export interface Pipe {
|
|
7
7
|
/** Log some info about the current {@link Files} being passed around. */
|
|
8
|
-
debug(): Pipe
|
|
8
|
+
debug(title?: string): Pipe
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -15,8 +15,15 @@ declare module '../pipe' {
|
|
|
15
15
|
|
|
16
16
|
/** Writes some info about the current {@link Files} being passed around. */
|
|
17
17
|
install('debug', class Debug implements Plug<Files> {
|
|
18
|
+
private readonly _title: string
|
|
19
|
+
|
|
20
|
+
constructor(...args: PipeParameters<'debug'>) {
|
|
21
|
+
const [ title = 'Debugging' ] = args
|
|
22
|
+
this._title = title
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
async pipe(files: Files, context: Context): Promise<Files> {
|
|
19
|
-
context.log.notice('
|
|
26
|
+
context.log.notice(this._title, `${$gry('(')}${$ylw(files.length)} ${$gry('files)')}`)
|
|
20
27
|
context.log.notice('- base dir:', $p(context.resolve('@')))
|
|
21
28
|
context.log.notice('- build file dir:', $p(context.resolve('.')))
|
|
22
29
|
context.log.notice('- files dir:', $p(files.directory))
|
package/src/plugs/esbuild.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { assert } from '../assert'
|
|
|
4
4
|
import { Files, FilesBuilder } from '../files'
|
|
5
5
|
import { $p, ERROR, Logger, ReportLevel, ReportRecord, WARN } from '../log'
|
|
6
6
|
import { AbsolutePath, getAbsoluteParent, resolveAbsolutePath } from '../paths'
|
|
7
|
-
import { install, PipeParameters, Plug
|
|
7
|
+
import { Context, install, PipeParameters, Plug } from '../pipe'
|
|
8
8
|
import { readFile } from '../utils/asyncfs'
|
|
9
9
|
|
|
10
10
|
export type ESBuildOptions = Omit<BuildOptions, 'absWorkingDir' | 'entryPoints' | 'watch'>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { ESLint as RealESLint } from 'eslint'
|
|
2
|
-
import { assert
|
|
2
|
+
import { assert } from '../../assert'
|
|
3
|
+
import { BuildFailure } from '../../failure'
|
|
3
4
|
import { Files } from '../../files'
|
|
4
5
|
import { $p, ERROR, NOTICE, WARN } from '../../log'
|
|
5
6
|
import { getCurrentWorkingDirectory, resolveAbsolutePath, resolveDirectory, resolveFile } from '../../paths'
|
|
6
|
-
import { PipeParameters, Plug
|
|
7
|
+
import { Context, PipeParameters, Plug } from '../../pipe'
|
|
7
8
|
import { readFile } from '../../utils/asyncfs'
|
|
8
9
|
import { ESLintOptions } from '../eslint'
|
|
9
10
|
|
|
@@ -59,7 +60,7 @@ export default class ESLint implements Plug<void> {
|
|
|
59
60
|
|
|
60
61
|
/* In case of failures from promises, fail! */
|
|
61
62
|
const { results, failures } = summary
|
|
62
|
-
if (failures) throw
|
|
63
|
+
if (failures) throw new BuildFailure({ logged: true })
|
|
63
64
|
|
|
64
65
|
/* Create our report */
|
|
65
66
|
const report = context.log.report('ESLint Report')
|
package/src/plugs/eslint.ts
CHANGED
package/src/plugs/exec.ts
CHANGED
|
@@ -3,12 +3,12 @@ import reaadline from 'node:readline'
|
|
|
3
3
|
|
|
4
4
|
import { spawn, SpawnOptions } from 'node:child_process'
|
|
5
5
|
import { assert } from '../assert'
|
|
6
|
+
import { requireContext } from '../async'
|
|
6
7
|
import { Files } from '../files'
|
|
7
8
|
import { $p, logOptions } from '../log'
|
|
8
9
|
import { AbsolutePath, getCurrentWorkingDirectory, resolveDirectory } from '../paths'
|
|
9
10
|
import { Context, install, PipeParameters, Plug } from '../pipe'
|
|
10
11
|
import { parseOptions } from '../utils/options'
|
|
11
|
-
import { requireContext } from '../async'
|
|
12
12
|
|
|
13
13
|
/** Options for executing scripts */
|
|
14
14
|
export interface ExecOptions {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import RealMocha from 'mocha' // Mocha types pollute the global scope!
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { BuildFailure } from '../../failure'
|
|
4
4
|
import { Files } from '../../files'
|
|
5
5
|
import { $wht, NOTICE } from '../../log'
|
|
6
|
-
import { PipeParameters, Plug
|
|
6
|
+
import { Context, PipeParameters, Plug } from '../../pipe'
|
|
7
7
|
import { MochaOptions } from '../mocha'
|
|
8
8
|
import { logSymbol, PlugReporter } from './reporter'
|
|
9
9
|
|
|
@@ -40,7 +40,7 @@ export default class Mocha implements Plug<void> {
|
|
|
40
40
|
return new Promise((resolve, reject) => {
|
|
41
41
|
try {
|
|
42
42
|
mocha.run((failures) => {
|
|
43
|
-
if (failures) reject(
|
|
43
|
+
if (failures) reject(new BuildFailure({ logged: true }))
|
|
44
44
|
resolve(undefined)
|
|
45
45
|
})
|
|
46
46
|
} catch (error) {
|
package/src/plugs/mocha.ts
CHANGED
package/src/plugs/rmf.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Files } from '../files'
|
|
2
2
|
import { $gry, $p } from '../log'
|
|
3
|
-
import { install, PipeParameters, Plug
|
|
3
|
+
import { Context, install, PipeParameters, Plug } from '../pipe'
|
|
4
4
|
import { rm } from '../utils/asyncfs'
|
|
5
5
|
|
|
6
6
|
declare module '../pipe' {
|