@plugjs/plug 0.1.0 → 0.1.2
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 +6 -1
- package/dist/asserts.cjs.map +1 -1
- package/dist/asserts.d.ts +2 -0
- package/dist/asserts.mjs +5 -1
- package/dist/asserts.mjs.map +1 -1
- package/dist/build.cjs +6 -8
- package/dist/build.cjs.map +1 -1
- package/dist/build.mjs +7 -9
- package/dist/build.mjs.map +1 -1
- package/dist/files.cjs +4 -8
- package/dist/files.cjs.map +1 -1
- package/dist/files.mjs +4 -8
- package/dist/files.mjs.map +1 -1
- package/dist/fs.cjs.map +1 -1
- package/dist/fs.mjs.map +1 -1
- package/dist/helpers.cjs +10 -0
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.ts +6 -0
- package/dist/helpers.mjs +9 -0
- package/dist/helpers.mjs.map +1 -1
- package/dist/index.cjs +26 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +15 -2
- package/dist/index.mjs.map +1 -1
- package/dist/logging/emit.d.ts +1 -1
- package/dist/logging/levels.d.ts +6 -6
- package/dist/logging/logger.cjs +8 -11
- package/dist/logging/logger.cjs.map +1 -1
- package/dist/logging/logger.d.ts +10 -10
- package/dist/logging/logger.mjs +8 -11
- package/dist/logging/logger.mjs.map +1 -1
- package/dist/logging/report.cjs +1 -2
- package/dist/logging/report.cjs.map +1 -1
- package/dist/logging/report.d.ts +1 -1
- package/dist/logging/report.mjs +1 -2
- package/dist/logging/report.mjs.map +1 -1
- package/dist/logging.cjs +2 -11
- package/dist/logging.cjs.map +1 -1
- package/dist/logging.d.ts +1 -1
- package/dist/logging.mjs +2 -11
- package/dist/logging.mjs.map +1 -1
- package/dist/paths.cjs +22 -24
- package/dist/paths.cjs.map +1 -1
- package/dist/paths.d.ts +8 -8
- package/dist/paths.mjs +22 -24
- package/dist/paths.mjs.map +1 -1
- package/dist/pipe.d.ts +7 -7
- package/dist/plugs/copy.cjs +11 -3
- package/dist/plugs/copy.cjs.map +1 -1
- package/dist/plugs/copy.d.ts +2 -2
- package/dist/plugs/copy.mjs +11 -3
- package/dist/plugs/copy.mjs.map +1 -1
- package/dist/plugs/esbuild/fix-extensions.cjs +3 -4
- package/dist/plugs/esbuild/fix-extensions.cjs.map +1 -1
- package/dist/plugs/esbuild/fix-extensions.mjs +3 -4
- package/dist/plugs/esbuild/fix-extensions.mjs.map +1 -1
- package/dist/plugs/esbuild.d.ts +1 -1
- package/dist/types.d.ts +7 -7
- package/dist/utils/match.d.ts +1 -1
- package/dist/utils/options.d.ts +3 -3
- package/extra/ts-loader.mjs +3 -3
- package/package.json +20 -14
- package/src/asserts.ts +6 -1
- package/src/build.ts +179 -0
- package/src/files.ts +4 -4
- package/src/fs.ts +6 -5
- package/src/helpers.ts +16 -0
- package/src/index.ts +2 -2
- package/src/logging/logger.ts +31 -35
- package/src/logging.ts +7 -13
- package/src/paths.ts +35 -40
- package/src/plugs/copy.ts +13 -5
package/src/build.ts
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { assert } from './asserts'
|
|
2
|
+
import { runAsync } from './async'
|
|
3
|
+
import { $ms, $p, $t, getLogger, log, logOptions } from './logging'
|
|
4
|
+
import { Context, ContextPromises, PipeImpl } from './pipe'
|
|
5
|
+
import { findCaller } from './utils/caller'
|
|
6
|
+
import { parseOptions } from './utils/options'
|
|
7
|
+
|
|
8
|
+
import type { Pipe } from './index'
|
|
9
|
+
import type { AbsolutePath } from './paths'
|
|
10
|
+
import type {
|
|
11
|
+
Build,
|
|
12
|
+
BuildDef,
|
|
13
|
+
Props,
|
|
14
|
+
Result,
|
|
15
|
+
State,
|
|
16
|
+
Task,
|
|
17
|
+
TaskDef,
|
|
18
|
+
Tasks,
|
|
19
|
+
ThisBuild,
|
|
20
|
+
} from './types'
|
|
21
|
+
|
|
22
|
+
/* ========================================================================== *
|
|
23
|
+
* TASK *
|
|
24
|
+
* ========================================================================== */
|
|
25
|
+
|
|
26
|
+
class TaskImpl implements Task {
|
|
27
|
+
constructor(
|
|
28
|
+
public readonly buildFile: AbsolutePath,
|
|
29
|
+
public readonly tasks: Tasks,
|
|
30
|
+
public readonly props: Props,
|
|
31
|
+
private readonly _def: TaskDef,
|
|
32
|
+
) {}
|
|
33
|
+
|
|
34
|
+
invoke(state: State, taskName: string): Promise<Result> {
|
|
35
|
+
assert(! state.stack.includes(this), `Recursion detected calling ${$t(taskName)}`)
|
|
36
|
+
|
|
37
|
+
/* Check cache */
|
|
38
|
+
const cached = state.cache.get(this)
|
|
39
|
+
if (cached) return cached
|
|
40
|
+
|
|
41
|
+
/* Create new substate merging sibling tasks/props and adding this to the stack */
|
|
42
|
+
const props: Record<string, string> = Object.assign({}, this.props, state.props)
|
|
43
|
+
const tasks: Record<string, Task> = Object.assign({}, this.tasks, state.tasks)
|
|
44
|
+
const stack = [ ...state.stack, this ]
|
|
45
|
+
const cache = state.cache
|
|
46
|
+
|
|
47
|
+
/* Create run context and build */
|
|
48
|
+
const context = new Context(this.buildFile, taskName)
|
|
49
|
+
|
|
50
|
+
const build = new Proxy({}, {
|
|
51
|
+
get(_: any, name: string): void | string | (() => Pipe) {
|
|
52
|
+
// Tasks first, props might come also from environment
|
|
53
|
+
if (name in tasks) {
|
|
54
|
+
return (): Pipe => {
|
|
55
|
+
const state = { stack, cache, tasks, props }
|
|
56
|
+
const promise = tasks[name]!.invoke(state, name)
|
|
57
|
+
return new PipeImpl(context, promise)
|
|
58
|
+
}
|
|
59
|
+
} else if (name in props) {
|
|
60
|
+
return props[name]
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
/* Some logging */
|
|
66
|
+
context.log.info('Running...')
|
|
67
|
+
const now = Date.now()
|
|
68
|
+
|
|
69
|
+
/* Run asynchronously in an asynchronous context */
|
|
70
|
+
const promise = runAsync(context, taskName, async () => {
|
|
71
|
+
return await this._def.call(build) || undefined
|
|
72
|
+
}).then((result) => {
|
|
73
|
+
context.log.notice(`Success ${$ms(Date.now() - now)}`)
|
|
74
|
+
return result
|
|
75
|
+
}).catch((error) => {
|
|
76
|
+
throw context.log.fail(`Failure ${$ms(Date.now() - now)}`, error)
|
|
77
|
+
}).finally(() => ContextPromises.wait(context))
|
|
78
|
+
|
|
79
|
+
/* Cache the resulting promise and return it */
|
|
80
|
+
cache.set(this, promise)
|
|
81
|
+
return promise
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* ========================================================================== *
|
|
86
|
+
* BUILD COMPILER *
|
|
87
|
+
* ========================================================================== */
|
|
88
|
+
|
|
89
|
+
/** Symbol indicating that an object is a {@link Build} */
|
|
90
|
+
const buildMarker = Symbol.for('plugjs:isBuild')
|
|
91
|
+
|
|
92
|
+
/** Compile a {@link BuildDef | build definition} into a {@link Build} */
|
|
93
|
+
export function build<
|
|
94
|
+
D extends BuildDef, B extends ThisBuild<D>
|
|
95
|
+
>(def: D & ThisType<B>): Build<D> {
|
|
96
|
+
const buildFile = findCaller(build)
|
|
97
|
+
const tasks: Record<string, Task> = {}
|
|
98
|
+
const props: Record<string, string> = {}
|
|
99
|
+
|
|
100
|
+
/* Iterate through all definition extracting properties and tasks */
|
|
101
|
+
for (const [ key, val ] of Object.entries(def)) {
|
|
102
|
+
let len = 0
|
|
103
|
+
if (typeof val === 'string') {
|
|
104
|
+
props[key] = val
|
|
105
|
+
} else if (typeof val === 'function') {
|
|
106
|
+
tasks[key] = new TaskImpl(buildFile, tasks, props, val)
|
|
107
|
+
len = key.length
|
|
108
|
+
} else if (val instanceof TaskImpl) {
|
|
109
|
+
tasks[key] = val
|
|
110
|
+
len = key.length
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Update the logger's own "taskLength" for nice printing */
|
|
114
|
+
/* coverage ignore if */
|
|
115
|
+
if (len > logOptions.taskLength) logOptions.taskLength = len
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Create the "call" function for this build */
|
|
119
|
+
const invoke: InvokeBuild = async function invoke(
|
|
120
|
+
taskNames: string[],
|
|
121
|
+
overrideProps: Record<string, string | undefined> = {},
|
|
122
|
+
): Promise<void> {
|
|
123
|
+
/* Our "root" logger and initial (empty) state */
|
|
124
|
+
const logger = getLogger()
|
|
125
|
+
const state = {
|
|
126
|
+
cache: new Map<Task, Promise<Result>>(),
|
|
127
|
+
stack: [] as Task[],
|
|
128
|
+
props: Object.assign({}, props, overrideProps),
|
|
129
|
+
tasks: tasks,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Let's go down to business */
|
|
133
|
+
logger.notice('Starting...')
|
|
134
|
+
const now = Date.now()
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
/* Run tasks _serially_ */
|
|
138
|
+
for (const name of taskNames) {
|
|
139
|
+
const task = tasks[name]
|
|
140
|
+
assert(task, `Task ${$t(name)} not found in build ${$p(buildFile)}`)
|
|
141
|
+
await task.invoke(state, name)
|
|
142
|
+
}
|
|
143
|
+
logger.notice(`Build successful ${$ms(Date.now() - now)}`)
|
|
144
|
+
} catch (error) {
|
|
145
|
+
throw logger.fail(`Build failed ${$ms(Date.now() - now)}`, error)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Create our build, the collection of all props and tasks */
|
|
150
|
+
const compiled = Object.assign({}, props, tasks) as Build<D>
|
|
151
|
+
|
|
152
|
+
/* Sneak our "call" function in the build, for the CLI and "call" below */
|
|
153
|
+
Object.defineProperty(compiled, buildMarker, { value: invoke })
|
|
154
|
+
|
|
155
|
+
/* All done! */
|
|
156
|
+
return compiled
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Internal type describing the build invocation function */
|
|
160
|
+
type InvokeBuild = (tasks: string[], props?: Record<string, string | undefined>) => Promise<void>
|
|
161
|
+
|
|
162
|
+
/** Serially invoke tasks in a {@link Build} optionally overriding properties */
|
|
163
|
+
export async function invoke(
|
|
164
|
+
build: Build,
|
|
165
|
+
...args:
|
|
166
|
+
| [ ...taskNames: [ string, ...string[] ] ]
|
|
167
|
+
| [ ...taskNames: [ string, ...string[] ], options: Record<string, string | undefined> ]
|
|
168
|
+
): Promise<void> {
|
|
169
|
+
const { params: tasks, options: props } = parseOptions(args, {})
|
|
170
|
+
|
|
171
|
+
/* Get the calling function from the sneaked-in property in build */
|
|
172
|
+
const invoke: InvokeBuild = (build as any)[buildMarker]
|
|
173
|
+
|
|
174
|
+
/* Triple check that we actually _have_ a function (no asserts here, log!) */
|
|
175
|
+
if (typeof invoke !== 'function') log.fail('Unknown build type')
|
|
176
|
+
|
|
177
|
+
/* Call everyhin that needs to be called */
|
|
178
|
+
return await invoke(tasks, props)
|
|
179
|
+
}
|
package/src/files.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assert } from './asserts'
|
|
1
2
|
import { mkdir, writeFile } from './fs'
|
|
2
3
|
import { assertRelativeChildPath, getAbsoluteParent, resolveAbsolutePath } from './paths'
|
|
3
4
|
|
|
@@ -88,9 +89,8 @@ export class Files {
|
|
|
88
89
|
directory: instance.directory,
|
|
89
90
|
|
|
90
91
|
add(...files: string[]): FilesBuilder {
|
|
91
|
-
|
|
92
|
+
assert(! built, 'FileBuilder "build()" already called')
|
|
92
93
|
|
|
93
|
-
if (typeof files === 'string') files = [ files ]
|
|
94
94
|
for (const file of files) {
|
|
95
95
|
const relative = assertRelativeChildPath(instance.directory, file)
|
|
96
96
|
set.add(relative)
|
|
@@ -100,7 +100,7 @@ export class Files {
|
|
|
100
100
|
},
|
|
101
101
|
|
|
102
102
|
merge(...args: Files[]): FilesBuilder {
|
|
103
|
-
|
|
103
|
+
assert(! built, 'FileBuilder "build()" already called')
|
|
104
104
|
|
|
105
105
|
for (const files of args) {
|
|
106
106
|
for (const file of files.absolutePaths()) {
|
|
@@ -124,7 +124,7 @@ export class Files {
|
|
|
124
124
|
},
|
|
125
125
|
|
|
126
126
|
build(): Files {
|
|
127
|
-
|
|
127
|
+
assert(! built, 'FileBuilder "build()" already called')
|
|
128
128
|
|
|
129
129
|
built = true
|
|
130
130
|
instance._files.push(...set)
|
package/src/fs.ts
CHANGED
|
@@ -30,11 +30,12 @@ const fs = Object.entries(fsp as any).reduce((fs, [ key, val ]) => {
|
|
|
30
30
|
/* If the value is a function, wrap it! */
|
|
31
31
|
const f = function(...args: any[]): any {
|
|
32
32
|
/* Call the function, and _catch_ any error */
|
|
33
|
-
return val.apply(fsp, args)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
return val.apply(fsp, args)
|
|
34
|
+
.catch(/* coverage ignore next*/ (error: any) => {
|
|
35
|
+
/* For any error caught, we fill in the stack trace */
|
|
36
|
+
Error.captureStackTrace(error)
|
|
37
|
+
throw error
|
|
38
|
+
})
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
/* Make sure that the functions are called correctly */
|
package/src/helpers.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { tmpdir } from 'node:os'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { mkdtempSync } from 'node:fs'
|
|
4
|
+
|
|
1
5
|
import { assert, assertPromises } from './asserts'
|
|
2
6
|
import { requireContext } from './async'
|
|
3
7
|
import { Files } from './files'
|
|
@@ -65,6 +69,7 @@ export async function rmrf(directory: string): Promise<void> {
|
|
|
65
69
|
assert(dir !== context.resolve('@'),
|
|
66
70
|
`Cowardly refusing to wipe build file directory ${$p(dir)}`)
|
|
67
71
|
|
|
72
|
+
/* coverage ignore if */
|
|
68
73
|
if (! resolveDirectory(dir)) {
|
|
69
74
|
log.info('Directory', $p(dir), 'not found')
|
|
70
75
|
return
|
|
@@ -159,6 +164,17 @@ export function isDirectory(...paths: [ string, ...string[] ]): AbsolutePath | u
|
|
|
159
164
|
return resolveDirectory(path)
|
|
160
165
|
}
|
|
161
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Create a temporary directory and return its {@link AbsolutePath}.
|
|
169
|
+
*
|
|
170
|
+
* The directory will be rooted in `/tmp` or wherever `os.tmpdir()` decides.
|
|
171
|
+
*/
|
|
172
|
+
export function mkdtemp(): AbsolutePath {
|
|
173
|
+
const prefix = join(tmpdir(), 'plugjs-')
|
|
174
|
+
const path = mkdtempSync(prefix)
|
|
175
|
+
return resolve(path)
|
|
176
|
+
}
|
|
177
|
+
|
|
162
178
|
/**
|
|
163
179
|
* Execute a command and await for its result from within a task.
|
|
164
180
|
*
|
package/src/index.ts
CHANGED
|
@@ -30,8 +30,8 @@ export * as pipe from './pipe'
|
|
|
30
30
|
export * as utils from './utils'
|
|
31
31
|
|
|
32
32
|
// Individual utilities
|
|
33
|
-
export { log } from './logging'
|
|
34
|
-
export { assert } from './asserts'
|
|
33
|
+
export { log, $ms, $p, $t, $blu, $cyn, $grn, $gry, $mgt, $red, $und, $wht, $ylw } from './logging'
|
|
34
|
+
export { assert, fail } from './asserts'
|
|
35
35
|
|
|
36
36
|
// Our minimal exports
|
|
37
37
|
export * from './build'
|
package/src/logging/logger.ts
CHANGED
|
@@ -27,17 +27,17 @@ logOptions.on('changed', ({ defaultTaskName, colors, level }) => {
|
|
|
27
27
|
/** The basic interface giving access to log facilities. */
|
|
28
28
|
export interface Log {
|
|
29
29
|
/** Log a `TRACE` message */
|
|
30
|
-
trace(...args: [ any, ...any ]):
|
|
30
|
+
trace(...args: [ any, ...any ]): void
|
|
31
31
|
/** Log a `DEBUG` message */
|
|
32
|
-
debug(...args: [ any, ...any ]):
|
|
32
|
+
debug(...args: [ any, ...any ]): void
|
|
33
33
|
/** Log an `INFO` message */
|
|
34
|
-
info(...args: [ any, ...any ]):
|
|
34
|
+
info(...args: [ any, ...any ]): void
|
|
35
35
|
/** Log a `NOTICE` message */
|
|
36
|
-
notice(...args: [ any, ...any ]):
|
|
36
|
+
notice(...args: [ any, ...any ]): void
|
|
37
37
|
/** Log a `WARNING` message */
|
|
38
|
-
warn(...args: [ any, ...any ]):
|
|
38
|
+
warn(...args: [ any, ...any ]): void
|
|
39
39
|
/** Log an `ERROR` message */
|
|
40
|
-
error(...args: [ any, ...any ]):
|
|
40
|
+
error(...args: [ any, ...any ]): void
|
|
41
41
|
/** Log an `ERROR` message and fail the build */
|
|
42
42
|
fail(...args: [ any, ...any ]): never
|
|
43
43
|
}
|
|
@@ -48,13 +48,13 @@ export interface Logger extends Log {
|
|
|
48
48
|
level: LogLevel,
|
|
49
49
|
|
|
50
50
|
/** Enter a sub-level of logging, increasing indent */
|
|
51
|
-
enter():
|
|
51
|
+
enter(): void
|
|
52
52
|
/** Enter a sub-level of logging, increasing indent */
|
|
53
|
-
enter(evel: LogLevel, message: string):
|
|
53
|
+
enter(evel: LogLevel, message: string): void
|
|
54
54
|
/** Leave a sub-level of logging, decreasing indent */
|
|
55
|
-
leave():
|
|
55
|
+
leave(): void
|
|
56
56
|
/** Leave a sub-level of logging, decreasing indent */
|
|
57
|
-
leave(level: LogLevel, message: string):
|
|
57
|
+
leave(level: LogLevel, message: string): void
|
|
58
58
|
/** Create a {@link Report} associated with this instance */
|
|
59
59
|
report(title: string): Report
|
|
60
60
|
}
|
|
@@ -88,8 +88,8 @@ class LoggerImpl implements Logger {
|
|
|
88
88
|
private readonly _emitter: LogEmitter,
|
|
89
89
|
) {}
|
|
90
90
|
|
|
91
|
-
private _emit(level: LogLevel, args: [ any, ...any ]):
|
|
92
|
-
if (this._level > level) return
|
|
91
|
+
private _emit(level: LogLevel, args: [ any, ...any ]): void {
|
|
92
|
+
if (this._level > level) return
|
|
93
93
|
|
|
94
94
|
// The `BuildFailure` is a bit special case
|
|
95
95
|
const params = args.filter((arg) => {
|
|
@@ -116,7 +116,7 @@ class LoggerImpl implements Logger {
|
|
|
116
116
|
})
|
|
117
117
|
|
|
118
118
|
// If there's nothing left to log, then we're done
|
|
119
|
-
if (params.length === 0) return
|
|
119
|
+
if (params.length === 0) return
|
|
120
120
|
|
|
121
121
|
// Prepare our options for logging
|
|
122
122
|
const options = { level, taskName: this._task, indent: this._indent }
|
|
@@ -131,7 +131,6 @@ class LoggerImpl implements Logger {
|
|
|
131
131
|
|
|
132
132
|
// Emit our log lines and return
|
|
133
133
|
this._emitter(options, params)
|
|
134
|
-
return this
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
get level(): LogLevel {
|
|
@@ -142,28 +141,28 @@ class LoggerImpl implements Logger {
|
|
|
142
141
|
this._level = level
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
trace(...args: [ any, ...any ]):
|
|
146
|
-
|
|
144
|
+
trace(...args: [ any, ...any ]): void {
|
|
145
|
+
this._emit(TRACE, args)
|
|
147
146
|
}
|
|
148
147
|
|
|
149
|
-
debug(...args: [ any, ...any ]):
|
|
150
|
-
|
|
148
|
+
debug(...args: [ any, ...any ]): void {
|
|
149
|
+
this._emit(DEBUG, args)
|
|
151
150
|
}
|
|
152
151
|
|
|
153
|
-
info(...args: [ any, ...any ]):
|
|
154
|
-
|
|
152
|
+
info(...args: [ any, ...any ]): void {
|
|
153
|
+
this._emit(INFO, args)
|
|
155
154
|
}
|
|
156
155
|
|
|
157
|
-
notice(...args: [ any, ...any ]):
|
|
158
|
-
|
|
156
|
+
notice(...args: [ any, ...any ]): void {
|
|
157
|
+
this._emit(NOTICE, args)
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
warn(...args: [ any, ...any ]):
|
|
162
|
-
|
|
160
|
+
warn(...args: [ any, ...any ]): void {
|
|
161
|
+
this._emit(WARN, args)
|
|
163
162
|
}
|
|
164
163
|
|
|
165
|
-
error(...args: [ any, ...any ]):
|
|
166
|
-
|
|
164
|
+
error(...args: [ any, ...any ]): void {
|
|
165
|
+
this._emit(ERROR, args)
|
|
167
166
|
}
|
|
168
167
|
|
|
169
168
|
fail(...args: [ any, ...any ]): never {
|
|
@@ -171,21 +170,20 @@ class LoggerImpl implements Logger {
|
|
|
171
170
|
throw BuildFailure.fail()
|
|
172
171
|
}
|
|
173
172
|
|
|
174
|
-
enter():
|
|
175
|
-
enter(level: LogLevel, message: string):
|
|
176
|
-
enter(...args: [] | [ level: LogLevel, message: string ]):
|
|
173
|
+
enter(): void
|
|
174
|
+
enter(level: LogLevel, message: string): void
|
|
175
|
+
enter(...args: [] | [ level: LogLevel, message: string ]): void {
|
|
177
176
|
if (args.length) {
|
|
178
177
|
const [ level, message ] = args
|
|
179
178
|
this._stack.push({ level, message, indent: this._indent })
|
|
180
179
|
}
|
|
181
180
|
|
|
182
181
|
this._indent ++
|
|
183
|
-
return this
|
|
184
182
|
}
|
|
185
183
|
|
|
186
|
-
leave():
|
|
187
|
-
leave(level: LogLevel, message: string):
|
|
188
|
-
leave(...args: [] | [ level: LogLevel, message: string ]):
|
|
184
|
+
leave(): void
|
|
185
|
+
leave(level: LogLevel, message: string): void
|
|
186
|
+
leave(...args: [] | [ level: LogLevel, message: string ]): void {
|
|
189
187
|
this._stack.pop()
|
|
190
188
|
this._indent --
|
|
191
189
|
|
|
@@ -195,8 +193,6 @@ class LoggerImpl implements Logger {
|
|
|
195
193
|
const [ level, message ] = args
|
|
196
194
|
this._emit(level, [ message ])
|
|
197
195
|
}
|
|
198
|
-
|
|
199
|
-
return this
|
|
200
196
|
}
|
|
201
197
|
|
|
202
198
|
report(title: string): Report {
|
package/src/logging.ts
CHANGED
|
@@ -29,38 +29,32 @@ export const log: LogFunction = ((): LogFunction => {
|
|
|
29
29
|
|
|
30
30
|
/* Create a Logger wrapping the current logger */
|
|
31
31
|
const wrapper: Log = {
|
|
32
|
-
trace(...args: [ any, ...any ]):
|
|
32
|
+
trace(...args: [ any, ...any ]): void {
|
|
33
33
|
logger().trace(...args)
|
|
34
|
-
return wrapper
|
|
35
34
|
},
|
|
36
35
|
|
|
37
|
-
debug(...args: [ any, ...any ]):
|
|
36
|
+
debug(...args: [ any, ...any ]): void {
|
|
38
37
|
logger().debug(...args)
|
|
39
|
-
return wrapper
|
|
40
38
|
},
|
|
41
39
|
|
|
42
|
-
info(...args: [ any, ...any ]):
|
|
40
|
+
info(...args: [ any, ...any ]): void {
|
|
43
41
|
logger().info(...args)
|
|
44
|
-
return wrapper
|
|
45
42
|
},
|
|
46
43
|
|
|
47
|
-
notice(...args: [ any, ...any ]):
|
|
44
|
+
notice(...args: [ any, ...any ]): void {
|
|
48
45
|
logger().notice(...args)
|
|
49
|
-
return wrapper
|
|
50
46
|
},
|
|
51
47
|
|
|
52
|
-
warn(...args: [ any, ...any ]):
|
|
48
|
+
warn(...args: [ any, ...any ]): void {
|
|
53
49
|
logger().warn(...args)
|
|
54
|
-
return wrapper
|
|
55
50
|
},
|
|
56
51
|
|
|
57
|
-
error(...args: [ any, ...any ]):
|
|
52
|
+
error(...args: [ any, ...any ]): void {
|
|
58
53
|
logger().error(...args)
|
|
59
|
-
return wrapper
|
|
60
54
|
},
|
|
61
55
|
|
|
62
56
|
fail(...args: [ any, ...any ]): never {
|
|
63
|
-
|
|
57
|
+
return logger().fail(...args)
|
|
64
58
|
},
|
|
65
59
|
}
|
|
66
60
|
|
package/src/paths.ts
CHANGED
|
@@ -14,9 +14,8 @@ export type AbsolutePath = string & { __brand_absolute_path: never }
|
|
|
14
14
|
|
|
15
15
|
/** Resolve a path into an {@link AbsolutePath} */
|
|
16
16
|
export function resolveAbsolutePath(directory: AbsolutePath, ...paths: string[]): AbsolutePath {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return resolved
|
|
17
|
+
assertAbsolutePath(directory)
|
|
18
|
+
return resolve(directory, ...paths) as AbsolutePath
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
/**
|
|
@@ -32,8 +31,6 @@ export function resolveAbsolutePath(directory: AbsolutePath, ...paths: string[])
|
|
|
32
31
|
* ```
|
|
33
32
|
*/
|
|
34
33
|
export function resolveRelativeChildPath(directory: AbsolutePath, ...paths: string[]): string | undefined {
|
|
35
|
-
assertAbsolutePath(directory)
|
|
36
|
-
|
|
37
34
|
const abs = resolveAbsolutePath(directory, ...paths)
|
|
38
35
|
const rel = relative(directory, abs)
|
|
39
36
|
return (isAbsolute(rel) || (rel === '..') || rel.startsWith(`..${sep}`)) ? undefined : rel
|
|
@@ -43,7 +40,6 @@ export function resolveRelativeChildPath(directory: AbsolutePath, ...paths: stri
|
|
|
43
40
|
* Asserts that a path is a relative path to the directory specified, failing
|
|
44
41
|
* the build if it's not (see also {@link resolveRelativeChildPath}).
|
|
45
42
|
*/
|
|
46
|
-
|
|
47
43
|
export function assertRelativeChildPath(directory: AbsolutePath, ...paths: string[]): string {
|
|
48
44
|
const relative = resolveRelativeChildPath(directory, ...paths)
|
|
49
45
|
assert(relative, `Path "${join(...paths)}" not relative to "${directory}"`)
|
|
@@ -76,6 +72,38 @@ export function getCurrentWorkingDirectory(): AbsolutePath {
|
|
|
76
72
|
return cwd
|
|
77
73
|
}
|
|
78
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Return the _common_ path amongst all specified paths.
|
|
77
|
+
*
|
|
78
|
+
* While the first `path` _must_ be an {@link AbsolutePath}, all other `paths`
|
|
79
|
+
* can be _relative_ and will be resolved against the first `path`.
|
|
80
|
+
*/
|
|
81
|
+
export function commonPath(path: AbsolutePath, ...paths: string[]): AbsolutePath {
|
|
82
|
+
assertAbsolutePath(path)
|
|
83
|
+
|
|
84
|
+
// Here the first path will be split into its components
|
|
85
|
+
// on win => [ 'C:', 'Windows', 'System32' ]
|
|
86
|
+
// on unx => [ '', 'usr'
|
|
87
|
+
const components = normalize(path).split(sep)
|
|
88
|
+
|
|
89
|
+
let length = components.length
|
|
90
|
+
for (const current of paths) {
|
|
91
|
+
const absolute = resolveAbsolutePath(path, current)
|
|
92
|
+
const parts = absolute.split(sep)
|
|
93
|
+
for (let i = 0; i < length; i++) {
|
|
94
|
+
if (components[i] !== parts[i]) {
|
|
95
|
+
length = i
|
|
96
|
+
break
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
assert(length, 'No common ancestors amongst paths')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const common = components.slice(0, length).join(sep)
|
|
104
|
+
assertAbsolutePath(common)
|
|
105
|
+
return common
|
|
106
|
+
}
|
|
79
107
|
|
|
80
108
|
/* ========================================================================== *
|
|
81
109
|
* MODULE RESOLUTION FUNCTIONS *
|
|
@@ -130,7 +158,7 @@ export function requireResolve(__fileurl: string, module: string): AbsolutePath
|
|
|
130
158
|
// ... then delegate to the standard "require.resolve(...)"
|
|
131
159
|
const url = pathToFileURL(file)
|
|
132
160
|
const ext = extname(file)
|
|
133
|
-
const checks =
|
|
161
|
+
const checks = [ `${module}`, `${module}${ext}`, `${module}/index${ext}` ]
|
|
134
162
|
|
|
135
163
|
for (const check of checks) {
|
|
136
164
|
const resolved = fileURLToPath(new URL(check, url)) as AbsolutePath
|
|
@@ -147,39 +175,6 @@ export function requireResolve(__fileurl: string, module: string): AbsolutePath
|
|
|
147
175
|
return required
|
|
148
176
|
}
|
|
149
177
|
|
|
150
|
-
/**
|
|
151
|
-
* Return the _common_ path amongst all specified paths.
|
|
152
|
-
*
|
|
153
|
-
* While the first `path` _must_ be an {@link AbsolutePath}, all other `paths`
|
|
154
|
-
* can be _relative_ and will be resolved against the first `path`.
|
|
155
|
-
*/
|
|
156
|
-
export function commonPath(path: AbsolutePath, ...paths: string[]): AbsolutePath {
|
|
157
|
-
assertAbsolutePath(path)
|
|
158
|
-
|
|
159
|
-
// Here the first path will be split into its components
|
|
160
|
-
// on win => [ 'C:', 'Windows', 'System32' ]
|
|
161
|
-
// on unx => [ '', 'usr'
|
|
162
|
-
const components = normalize(path).split(sep)
|
|
163
|
-
|
|
164
|
-
let length = components.length
|
|
165
|
-
for (const current of paths) {
|
|
166
|
-
const absolute = resolveAbsolutePath(path, current)
|
|
167
|
-
const parts = absolute.split(sep)
|
|
168
|
-
for (let i = 0; i < length; i++) {
|
|
169
|
-
if (components[i] !== parts[i]) {
|
|
170
|
-
length = i
|
|
171
|
-
break
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
assert(length, 'No common ancestors amongst paths')
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const common = components.slice(0, length).join(sep)
|
|
179
|
-
assertAbsolutePath(common)
|
|
180
|
-
return common
|
|
181
|
-
}
|
|
182
|
-
|
|
183
178
|
/* ========================================================================== *
|
|
184
179
|
* FILE CHECKING FUNCTIONS *
|
|
185
180
|
* ========================================================================== */
|
package/src/plugs/copy.ts
CHANGED
|
@@ -9,8 +9,8 @@ import type { Context, PipeParameters, Plug } from '../pipe'
|
|
|
9
9
|
|
|
10
10
|
/** Options for copying files */
|
|
11
11
|
export interface CopyOptions {
|
|
12
|
-
/** Whether to allow overwriting or not (default `
|
|
13
|
-
overwrite?:
|
|
12
|
+
/** Whether to allow overwriting or not (default `fail`). */
|
|
13
|
+
overwrite?: 'overwrite' | 'fail' | 'skip',
|
|
14
14
|
/** If specified, use this `mode` (octal string) when creating files. */
|
|
15
15
|
mode?: string | number,
|
|
16
16
|
/** If specified, use this `mode` (octal string) when creating directories. */
|
|
@@ -51,8 +51,8 @@ install('copy', class Copy implements Plug<Files> {
|
|
|
51
51
|
|
|
52
52
|
async pipe(files: Files, context: Context): Promise<Files> {
|
|
53
53
|
/* Destructure our options with some defaults and compute write flags */
|
|
54
|
-
const { mode, dirMode, overwrite, rename = (s): string => s } = this._options
|
|
55
|
-
const flags = overwrite ? fsConstants.COPYFILE_EXCL
|
|
54
|
+
const { mode, dirMode, overwrite = 'fail', rename = (s): string => s } = this._options
|
|
55
|
+
const flags = overwrite === 'overwrite' ? 0 : fsConstants.COPYFILE_EXCL
|
|
56
56
|
const dmode = parseMode(dirMode)
|
|
57
57
|
const fmode = parseMode(mode)
|
|
58
58
|
|
|
@@ -88,7 +88,15 @@ install('copy', class Copy implements Plug<Files> {
|
|
|
88
88
|
|
|
89
89
|
/* Actually _copy_ the file */
|
|
90
90
|
context.log.trace(`Copying "${$p(absolute)}" to "${$p(target)}"`)
|
|
91
|
-
|
|
91
|
+
try {
|
|
92
|
+
await copyFile(absolute, target, flags)
|
|
93
|
+
} catch (error: any) {
|
|
94
|
+
if ((error.code === 'EEXIST') && (overwrite === 'skip')) {
|
|
95
|
+
context.log.warn(`Not overwriting existing file ${$p(target)}`)
|
|
96
|
+
} else {
|
|
97
|
+
throw error
|
|
98
|
+
}
|
|
99
|
+
}
|
|
92
100
|
|
|
93
101
|
/* Set the mode, if we need to */
|
|
94
102
|
if (fmode !== undefined) {
|