@plugjs/plug 0.4.0 → 0.4.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 +1 -1
- package/dist/asserts.cjs.map +1 -1
- package/dist/asserts.mjs +1 -1
- 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 +95 -70
- package/dist/build.cjs.map +2 -2
- package/dist/build.d.ts +6 -8
- package/dist/build.mjs +92 -67
- package/dist/build.mjs.map +2 -2
- package/dist/cli.mjs +1 -1
- 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/helpers.cjs +3 -4
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.mjs +3 -4
- package/dist/helpers.mjs.map +1 -1
- package/dist/logging/options.cjs +3 -10
- package/dist/logging/options.cjs.map +1 -1
- package/dist/logging/options.d.ts +44 -1
- package/dist/logging/options.mjs +3 -10
- package/dist/logging/options.mjs.map +1 -1
- package/dist/plugs/build.cjs +4 -7
- package/dist/plugs/build.cjs.map +1 -1
- package/dist/plugs/build.mjs +2 -5
- package/dist/plugs/build.mjs.map +1 -1
- 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 +33 -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/{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 +1 -1
- package/src/asserts.ts +1 -1
- package/src/async.ts +6 -29
- package/src/build.ts +151 -117
- package/src/files.ts +14 -6
- package/src/helpers.ts +3 -4
- package/src/logging/options.ts +4 -13
- package/src/plugs/build.ts +2 -15
- package/src/plugs/debug.ts +10 -9
- package/src/types.ts +52 -23
- package/src/utils/diff.ts +1 -6
- package/src/utils/singleton.ts +19 -0
- package/src/utils.ts +1 -1
- 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/src/utils/types.ts +0 -11
package/src/build.ts
CHANGED
|
@@ -3,77 +3,124 @@ import { runAsync } from './async'
|
|
|
3
3
|
import { $gry, $ms, $p, $t, getLogger, logOptions } from './logging'
|
|
4
4
|
import { Context, ContextPromises, PipeImpl } from './pipe'
|
|
5
5
|
import { findCaller } from './utils/caller'
|
|
6
|
+
import { getSingleton } from './utils/singleton'
|
|
7
|
+
import { buildMarker } from './types'
|
|
6
8
|
|
|
7
9
|
import type { Pipe } from './index'
|
|
8
10
|
import type { AbsolutePath } from './paths'
|
|
9
11
|
import type {
|
|
10
12
|
Build,
|
|
13
|
+
BuildProps,
|
|
11
14
|
BuildDef,
|
|
12
|
-
Props,
|
|
13
15
|
Result,
|
|
14
16
|
State,
|
|
15
17
|
Task,
|
|
16
18
|
TaskDef,
|
|
17
|
-
Tasks,
|
|
18
19
|
ThisBuild,
|
|
20
|
+
Props,
|
|
21
|
+
TaskCall,
|
|
22
|
+
BuildTasks,
|
|
23
|
+
Tasks,
|
|
19
24
|
} from './types'
|
|
20
25
|
|
|
21
26
|
/* ========================================================================== *
|
|
22
|
-
*
|
|
27
|
+
* INTERNAL UTILITIES *
|
|
23
28
|
* ========================================================================== */
|
|
24
29
|
|
|
25
|
-
/** Symbol indicating that an object is a {@link
|
|
26
|
-
const
|
|
30
|
+
/** Symbol indicating that an object is a {@link TaskCall} */
|
|
31
|
+
const taskCallMarker = Symbol.for('plugjs:plug:types:TaskCall')
|
|
32
|
+
|
|
33
|
+
/** Type guard for {@link TaskCall}s */
|
|
34
|
+
function isTaskCall(something: any): something is TaskCall {
|
|
35
|
+
return something[taskCallMarker] === taskCallMarker
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Shallow merge two records */
|
|
39
|
+
function merge<A, B>(a: A, b: B): A & B {
|
|
40
|
+
return Object.assign(Object.create(null), a, b)
|
|
41
|
+
}
|
|
27
42
|
|
|
28
|
-
/**
|
|
29
|
-
function
|
|
30
|
-
|
|
43
|
+
/** Create a {@link State} from its components */
|
|
44
|
+
function makeState(state: {
|
|
45
|
+
cache?: Map<Task, Promise<Result>>
|
|
46
|
+
stack?: Task[],
|
|
47
|
+
tasks?: Record<string, Task>
|
|
48
|
+
props?: Record<string, string>
|
|
49
|
+
fails?: Set<Task>
|
|
50
|
+
}): State {
|
|
51
|
+
const {
|
|
52
|
+
cache = new Map(),
|
|
53
|
+
fails = new Set(),
|
|
54
|
+
stack = [],
|
|
55
|
+
tasks = {},
|
|
56
|
+
props = {},
|
|
57
|
+
} = state
|
|
58
|
+
|
|
59
|
+
return { cache, fails, stack, tasks, props } as State
|
|
31
60
|
}
|
|
32
61
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
62
|
+
/* ========================================================================== *
|
|
63
|
+
* TASK IMPLEMENTATION *
|
|
64
|
+
* ========================================================================== */
|
|
65
|
+
|
|
66
|
+
const lastIdKey = Symbol.for('plugjs:plug:singleton:taskId')
|
|
67
|
+
const taskId = getSingleton(lastIdKey, () => ({ id: 0 }))
|
|
68
|
+
|
|
69
|
+
class TaskImpl<R extends Result> implements Task<R> {
|
|
70
|
+
public readonly before: Task<Result>[] = []
|
|
71
|
+
public readonly after: Task<Result>[] = []
|
|
72
|
+
public readonly id: number = ++ taskId.id
|
|
73
|
+
|
|
74
|
+
props: Props<BuildDef>
|
|
75
|
+
tasks: Tasks<BuildDef>
|
|
76
|
+
|
|
77
|
+
constructor(
|
|
78
|
+
public readonly name: string,
|
|
79
|
+
public readonly buildFile: AbsolutePath,
|
|
80
|
+
private readonly _def: TaskDef,
|
|
81
|
+
_tasks: Record<string, Task>,
|
|
82
|
+
_props: Record<string, string>,
|
|
83
|
+
) {
|
|
84
|
+
this.tasks = _tasks as Tasks
|
|
85
|
+
this.props = _props as Props
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async invoke(state: State, taskName: string): Promise<R> {
|
|
89
|
+
assert(! state.stack.includes(this), `Recursion detected calling ${$t(taskName)}`)
|
|
44
90
|
|
|
45
91
|
/* Check cache */
|
|
46
|
-
const cached = state.cache.get(
|
|
47
|
-
if (cached) return cached
|
|
92
|
+
const cached = state.cache.get(this)
|
|
93
|
+
if (cached) return cached as Promise<R>
|
|
48
94
|
|
|
49
95
|
/* Create new substate merging sibling tasks/props and adding this to the stack */
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
96
|
+
state = makeState({
|
|
97
|
+
props: merge(this.props, state.props),
|
|
98
|
+
tasks: merge(this.tasks, state.tasks),
|
|
99
|
+
stack: [ ...state.stack, this ],
|
|
100
|
+
cache: state.cache,
|
|
101
|
+
fails: state.fails,
|
|
102
|
+
})
|
|
56
103
|
|
|
57
104
|
/* Create run context and build */
|
|
58
|
-
const context = new Context(
|
|
105
|
+
const context = new Context(this.buildFile, taskName)
|
|
59
106
|
|
|
60
107
|
/* The build (the `this` value calling the definition) is a proxy */
|
|
61
108
|
const build = new Proxy({}, {
|
|
62
|
-
get(_: any, name: string): void | string | (() => Pipe) {
|
|
109
|
+
get: (_: any, name: string): void | string | (() => Pipe) => {
|
|
63
110
|
// Tasks first, props might come also from environment
|
|
64
|
-
if (name in tasks) {
|
|
111
|
+
if (name in state.tasks) {
|
|
65
112
|
return (): Pipe => {
|
|
66
|
-
const promise = tasks[name]!.invoke(state, name)
|
|
113
|
+
const promise = (state as any).tasks[name]!.invoke(state, name)
|
|
67
114
|
return new PipeImpl(context, promise)
|
|
68
115
|
}
|
|
69
|
-
} else if (name in props) {
|
|
70
|
-
return props[name]
|
|
116
|
+
} else if (name in state.props) {
|
|
117
|
+
return (state as any).props[name]
|
|
71
118
|
}
|
|
72
119
|
},
|
|
73
120
|
})
|
|
74
121
|
|
|
75
122
|
/* Run all tasks hooked _before_ this one */
|
|
76
|
-
for (const before of
|
|
123
|
+
for (const before of this.before) await before.invoke(state, before.name)
|
|
77
124
|
|
|
78
125
|
/* Some logging */
|
|
79
126
|
context.log.info('Running...')
|
|
@@ -81,56 +128,31 @@ function makeTask(
|
|
|
81
128
|
|
|
82
129
|
/* Run asynchronously in an asynchronous context */
|
|
83
130
|
const promise = runAsync(context, taskName, async () => {
|
|
84
|
-
return await _def.call(build) || undefined
|
|
131
|
+
return await this._def.call(build) || undefined
|
|
85
132
|
}).then(async (result) => {
|
|
86
133
|
const level = taskName.startsWith('_') ? 'info' : 'notice'
|
|
87
134
|
context.log[level](`Success ${$ms(Date.now() - now)}`)
|
|
88
135
|
return result
|
|
89
136
|
}).catch((error) => {
|
|
90
|
-
fails.add(
|
|
137
|
+
state.fails.add(this)
|
|
91
138
|
throw context.log.fail(`Failure ${$ms(Date.now() - now)}`, error)
|
|
92
139
|
}).finally(async () => {
|
|
93
140
|
await ContextPromises.wait(context)
|
|
94
141
|
}).then(async (result) => {
|
|
95
|
-
for (const after of
|
|
142
|
+
for (const after of this.after) await after.invoke(state, after.name)
|
|
96
143
|
return result
|
|
97
144
|
})
|
|
98
145
|
|
|
99
146
|
/* Cache the resulting promise and return it */
|
|
100
|
-
cache.set(
|
|
101
|
-
return promise
|
|
147
|
+
state.cache.set(this, promise)
|
|
148
|
+
return promise as Promise<R>
|
|
102
149
|
}
|
|
103
|
-
|
|
104
|
-
/* Create the new Task. The function will simply create an empty state */
|
|
105
|
-
const task: Task = Object.assign((overrideProps: Props = {}) => {
|
|
106
|
-
const state: State = {
|
|
107
|
-
cache: new Map<Task, Promise<Result>>(),
|
|
108
|
-
stack: [] as Task[],
|
|
109
|
-
props: Object.assign({}, props, overrideProps),
|
|
110
|
-
fails: new Set<Task>,
|
|
111
|
-
tasks: tasks,
|
|
112
|
-
}
|
|
113
|
-
return invoke(state, _name)
|
|
114
|
-
}, { buildFile, tasks, props, invoke, before: [], after: [] })
|
|
115
|
-
|
|
116
|
-
/* Assign the task's marker and name and return it */
|
|
117
|
-
Object.defineProperty(task, taskMarker, { value: true })
|
|
118
|
-
Object.defineProperty(task, 'name', { value: _name })
|
|
119
|
-
return task
|
|
120
150
|
}
|
|
121
151
|
|
|
122
152
|
/* ========================================================================== *
|
|
123
153
|
* BUILD COMPILER *
|
|
124
154
|
* ========================================================================== */
|
|
125
155
|
|
|
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
|
-
const buildMarker = Symbol.for('plugjs:isBuild')
|
|
133
|
-
|
|
134
156
|
/** Compile a {@link BuildDef | build definition} into a {@link Build} */
|
|
135
157
|
export function build<
|
|
136
158
|
D extends BuildDef, B extends ThisBuild<D>
|
|
@@ -142,13 +164,14 @@ export function build<
|
|
|
142
164
|
/* Iterate through all definition extracting properties and tasks */
|
|
143
165
|
for (const [ key, val ] of Object.entries(def)) {
|
|
144
166
|
let len = 0
|
|
145
|
-
if (
|
|
146
|
-
tasks[key] = val
|
|
167
|
+
if (isTaskCall(val)) { // this goes first, tasks calls _are_ functions!
|
|
168
|
+
tasks[key] = val.task
|
|
147
169
|
len = key.length
|
|
148
170
|
} else if (typeof val === 'string') {
|
|
149
171
|
props[key] = val
|
|
150
172
|
} else if (typeof val === 'function') {
|
|
151
|
-
tasks[key] =
|
|
173
|
+
tasks[key] = new TaskImpl(key, buildFile, val, tasks, props)
|
|
174
|
+
// tasks[key] = makeTask(buildFile, tasks, props, val, key)
|
|
152
175
|
len = key.length
|
|
153
176
|
}
|
|
154
177
|
|
|
@@ -157,33 +180,21 @@ export function build<
|
|
|
157
180
|
if (len > logOptions.taskLength) logOptions.taskLength = len
|
|
158
181
|
}
|
|
159
182
|
|
|
160
|
-
/*
|
|
161
|
-
const
|
|
162
|
-
|
|
183
|
+
/* A function _starting_ a build */
|
|
184
|
+
const start = async function start<R>(
|
|
185
|
+
callback: (state: State) => Promise<R>,
|
|
163
186
|
overrideProps: Record<string, string | undefined> = {},
|
|
164
|
-
): Promise<
|
|
165
|
-
/* Our "root" logger and initial (empty) state */
|
|
166
|
-
const logger = getLogger()
|
|
167
|
-
const state = {
|
|
168
|
-
cache: new Map<Task, Promise<Result>>(),
|
|
169
|
-
props: Object.assign({}, props, overrideProps),
|
|
170
|
-
fails: new Set<Task>(),
|
|
171
|
-
stack: [] as Task[],
|
|
172
|
-
tasks: tasks,
|
|
173
|
-
}
|
|
174
|
-
|
|
187
|
+
): Promise<R> {
|
|
175
188
|
/* Let's go down to business */
|
|
189
|
+
const state = makeState({ tasks, props: merge(props, overrideProps) })
|
|
190
|
+
const logger = getLogger()
|
|
176
191
|
logger.notice('Starting...')
|
|
177
192
|
const now = Date.now()
|
|
178
193
|
|
|
179
194
|
try {
|
|
180
|
-
|
|
181
|
-
for (const name of taskNames) {
|
|
182
|
-
const task = tasks[name]
|
|
183
|
-
assert(task, `Task ${$t(name)} not found in build ${$p(buildFile)}`)
|
|
184
|
-
await task.invoke(state, name)
|
|
185
|
-
}
|
|
195
|
+
const result = await callback(state)
|
|
186
196
|
logger.notice(`Build successful ${$ms(Date.now() - now)}`)
|
|
197
|
+
return result
|
|
187
198
|
} catch (error) {
|
|
188
199
|
if (state.fails.size) {
|
|
189
200
|
logger.error('')
|
|
@@ -195,14 +206,40 @@ export function build<
|
|
|
195
206
|
}
|
|
196
207
|
}
|
|
197
208
|
|
|
198
|
-
/* Create
|
|
199
|
-
const
|
|
209
|
+
/* Create the "invoke" function for this build */
|
|
210
|
+
const invoke = async function invoke(
|
|
211
|
+
taskNames: readonly string[],
|
|
212
|
+
overrideProps: Record<string, string | undefined> = {},
|
|
213
|
+
): Promise<void> {
|
|
214
|
+
await start(async (state: State): Promise<void> => {
|
|
215
|
+
for (const name of taskNames) {
|
|
216
|
+
const task = tasks[name]
|
|
217
|
+
assert(task, `Task ${$t(name)} not found in build ${$p(buildFile)}`)
|
|
218
|
+
await task.invoke(state, name)
|
|
219
|
+
}
|
|
220
|
+
}, overrideProps)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* Convert our Tasks into TaskCalls */
|
|
224
|
+
const callables: Record<string, TaskCall> = {}
|
|
225
|
+
for (const [ name, task ] of Object.entries(tasks)) {
|
|
226
|
+
/** The callable function, using "start" */
|
|
227
|
+
const callable = async (overrideProps?: Record<string, string>): Promise<Result> =>
|
|
228
|
+
start(async (state: State): Promise<Result> =>
|
|
229
|
+
task.invoke(state, name), overrideProps)
|
|
230
|
+
|
|
231
|
+
/* Extra properties for our callable: marker, task and name */
|
|
232
|
+
callables[name] = Object.defineProperties(callable, {
|
|
233
|
+
[taskCallMarker]: { value: taskCallMarker },
|
|
234
|
+
'task': { value: task },
|
|
235
|
+
'name': { value: name },
|
|
236
|
+
}) as TaskCall
|
|
237
|
+
}
|
|
200
238
|
|
|
201
|
-
/*
|
|
239
|
+
/* Create and return our build */
|
|
240
|
+
const compiled = merge(props, callables)
|
|
202
241
|
Object.defineProperty(compiled, buildMarker, { value: invoke })
|
|
203
|
-
|
|
204
|
-
/* All done! */
|
|
205
|
-
return compiled
|
|
242
|
+
return compiled as Build<D>
|
|
206
243
|
}
|
|
207
244
|
|
|
208
245
|
/** Check if the specified build is actually a {@link Build} */
|
|
@@ -211,13 +248,12 @@ export function isBuild(build: any): build is Build<Record<string, any>> {
|
|
|
211
248
|
}
|
|
212
249
|
|
|
213
250
|
/** Invoke a number of tasks in a {@link Build} */
|
|
214
|
-
export function invokeTasks(
|
|
215
|
-
build:
|
|
216
|
-
tasks:
|
|
217
|
-
props?:
|
|
251
|
+
export function invokeTasks<B extends Build>(
|
|
252
|
+
build: B,
|
|
253
|
+
tasks: readonly BuildTasks<B>[],
|
|
254
|
+
props?: BuildProps<B>,
|
|
218
255
|
): Promise<void> {
|
|
219
|
-
if (
|
|
220
|
-
(buildMarker in build) && (typeof build[buildMarker] === 'function')) {
|
|
256
|
+
if (isBuild(build)) {
|
|
221
257
|
return build[buildMarker](tasks, props)
|
|
222
258
|
} else {
|
|
223
259
|
throw new TypeError('Invalid build instance')
|
|
@@ -228,38 +264,36 @@ export function invokeTasks(
|
|
|
228
264
|
* HOOKS *
|
|
229
265
|
* ========================================================================== */
|
|
230
266
|
|
|
231
|
-
|
|
232
|
-
[ name in keyof B as B[name] extends Task ? name : never ] : any
|
|
233
|
-
}
|
|
234
|
-
|
|
267
|
+
/** Make sure that the specified hooks run _before_ the given tasks */
|
|
235
268
|
export function hookBefore<B extends Build, T extends keyof B>(
|
|
236
269
|
build: B,
|
|
237
|
-
taskName: string & T &
|
|
238
|
-
hooks: (string & Exclude<
|
|
270
|
+
taskName: string & T & BuildTasks<B>,
|
|
271
|
+
hooks: readonly (string & Exclude<BuildTasks<B>, T>)[],
|
|
239
272
|
): void {
|
|
240
|
-
const
|
|
241
|
-
assert(
|
|
273
|
+
const taskCall = build[taskName]
|
|
274
|
+
assert(isTaskCall(taskCall), `Task "${$t(taskName)}" not found in build`)
|
|
242
275
|
|
|
243
276
|
for (const hook of hooks) {
|
|
244
277
|
const beforeHook = build[hook]
|
|
245
|
-
assert(
|
|
246
|
-
if (task.before.includes(beforeHook)) continue
|
|
247
|
-
task.before.push(beforeHook)
|
|
278
|
+
assert(isTaskCall(beforeHook), `Task "${$t(hook)}" to hook before "${$t(taskName)}" not found in build`)
|
|
279
|
+
if (taskCall.task.before.includes(beforeHook.task)) continue
|
|
280
|
+
taskCall.task.before.push(beforeHook.task)
|
|
248
281
|
}
|
|
249
282
|
}
|
|
250
283
|
|
|
284
|
+
/** Make sure that the specified hooks run _after_ the given tasks */
|
|
251
285
|
export function hookAfter<B extends Build, T extends keyof B>(
|
|
252
286
|
build: B,
|
|
253
|
-
taskName: string & T &
|
|
254
|
-
hooks: (string & Exclude<
|
|
287
|
+
taskName: string & T & BuildTasks<B>,
|
|
288
|
+
hooks: readonly (string & Exclude<BuildTasks<B>, T>)[],
|
|
255
289
|
): void {
|
|
256
|
-
const
|
|
257
|
-
assert(
|
|
290
|
+
const taskCall = build[taskName]
|
|
291
|
+
assert(isTaskCall(taskCall), `Task "${$t(taskName)}" not found in build`)
|
|
258
292
|
|
|
259
293
|
for (const hook of hooks) {
|
|
260
294
|
const afterHook = build[hook]
|
|
261
|
-
assert(
|
|
262
|
-
if (task.after.includes(afterHook)) continue
|
|
263
|
-
task.after.push(afterHook)
|
|
295
|
+
assert(isTaskCall(afterHook), `Task "${$t(hook)}" to hook after "${$t(taskName)}" not found in build`)
|
|
296
|
+
if (taskCall.task.after.includes(afterHook.task)) continue
|
|
297
|
+
taskCall.task.after.push(afterHook.task)
|
|
264
298
|
}
|
|
265
299
|
}
|
package/src/files.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import { inspect } from 'node:util'
|
|
2
|
+
|
|
1
3
|
import { assert } from './asserts'
|
|
2
4
|
import { mkdir, writeFile } from './fs'
|
|
3
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
assertRelativeChildPath,
|
|
7
|
+
getAbsoluteParent,
|
|
8
|
+
getCurrentWorkingDirectory,
|
|
9
|
+
resolveAbsolutePath,
|
|
10
|
+
} from './paths'
|
|
4
11
|
|
|
5
12
|
import type { AbsolutePath } from './paths'
|
|
6
13
|
|
|
@@ -38,13 +45,12 @@ export class Files {
|
|
|
38
45
|
* Create a new {@link Files} instance rooted in the specified `directory`
|
|
39
46
|
* relative to the specified {@link Run}'s directory.
|
|
40
47
|
*/
|
|
41
|
-
constructor(directory
|
|
42
|
-
this._directory = directory
|
|
48
|
+
constructor(directory?: AbsolutePath) {
|
|
49
|
+
this._directory = directory || getCurrentWorkingDirectory()
|
|
43
50
|
this._files = []
|
|
44
51
|
|
|
45
52
|
// Nicety for "console.log" / "util.inspect"...
|
|
46
|
-
|
|
47
|
-
Object.defineProperty(this, inspect, { value: () => ({
|
|
53
|
+
Object.defineProperty(this, inspect.custom, { value: () => ({
|
|
48
54
|
directory: this._directory,
|
|
49
55
|
files: [ ...this._files ],
|
|
50
56
|
}) })
|
|
@@ -76,9 +82,11 @@ export class Files {
|
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
/** Create a new {@link FilesBuilder} creating {@link Files} instances. */
|
|
85
|
+
static builder(): FilesBuilder
|
|
79
86
|
static builder(files: Files): FilesBuilder
|
|
80
87
|
static builder(directory: AbsolutePath): FilesBuilder
|
|
81
|
-
static builder(arg
|
|
88
|
+
static builder(arg?: Files | AbsolutePath): FilesBuilder {
|
|
89
|
+
if (! arg) arg = getCurrentWorkingDirectory()
|
|
82
90
|
const directory = typeof arg === 'string' ? arg : arg.directory
|
|
83
91
|
const set = typeof arg === 'string' ? new Set<string>() : new Set(arg._files)
|
|
84
92
|
|
package/src/helpers.ts
CHANGED
|
@@ -150,14 +150,14 @@ export function merge(pipes: (Pipe | Files | Promise<Files>)[]): Pipe {
|
|
|
150
150
|
const context = requireContext()
|
|
151
151
|
return new PipeImpl(context, Promise.resolve().then(async () => {
|
|
152
152
|
// No pipes? Just send off an empty pipe...
|
|
153
|
-
if (pipes.length === 0) return Files
|
|
153
|
+
if (pipes.length === 0) return new Files()
|
|
154
154
|
|
|
155
155
|
// Await for all pipes / files / files promises
|
|
156
156
|
const awaited = await assertPromises<Files>(pipes)
|
|
157
157
|
const results = awaited.filter((result) => result.length)
|
|
158
158
|
|
|
159
159
|
// No files in anything to be merged? Again send off an empty pipe...
|
|
160
|
-
if (results.length === 0) return Files
|
|
160
|
+
if (results.length === 0) return new Files()
|
|
161
161
|
|
|
162
162
|
// Find the common directory between all the Files instances
|
|
163
163
|
const [ firstDir, ...otherDirs ] = results.map((f) => f.directory)
|
|
@@ -184,8 +184,7 @@ export function merge(pipes: (Pipe | Files | Promise<Files>)[]): Pipe {
|
|
|
184
184
|
*/
|
|
185
185
|
export function noop(): Pipe {
|
|
186
186
|
const context = requireContext()
|
|
187
|
-
|
|
188
|
-
return new PipeImpl(context, Promise.resolve(files))
|
|
187
|
+
return new PipeImpl(context, Promise.resolve(new Files()))
|
|
189
188
|
}
|
|
190
189
|
|
|
191
190
|
/**
|
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'
|
|
@@ -232,18 +233,8 @@ class LogOptionsImpl extends EventEmitter implements LogOptions {
|
|
|
232
233
|
}
|
|
233
234
|
}
|
|
234
235
|
|
|
235
|
-
|
|
236
|
-
const optionsKey = Symbol.for('plugjs
|
|
237
|
-
|
|
238
|
-
/** Get the shared _per process_ instance of our {@link LogOptions}. */
|
|
239
|
-
function getLogOptions(): LogOptions {
|
|
240
|
-
let options: LogOptions = (<any> globalThis)[optionsKey]
|
|
241
|
-
if (! options) {
|
|
242
|
-
options = new LogOptionsImpl()
|
|
243
|
-
;(<any> globalThis)[optionsKey] = options
|
|
244
|
-
}
|
|
245
|
-
return options
|
|
246
|
-
}
|
|
236
|
+
/** Singleton key for {@link LogOptions} instance. */
|
|
237
|
+
const optionsKey = Symbol.for('plugjs:plug:types:LogOptions')
|
|
247
238
|
|
|
248
239
|
/** Shared instance of our {@link LogOptions}. */
|
|
249
|
-
export const logOptions =
|
|
240
|
+
export const logOptions = getSingleton(optionsKey, () => new LogOptionsImpl())
|
package/src/plugs/build.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { $p } from '../logging/colors'
|
|
2
2
|
import { ForkingPlug, type ForkOptions } from '../fork'
|
|
3
3
|
import { requireFilename } from '../paths'
|
|
4
|
+
import { invokeTasks, isBuild } from '../build'
|
|
4
5
|
|
|
5
6
|
import type { Files } from '../files'
|
|
6
7
|
import type { Context, Plug } from '../pipe'
|
|
7
|
-
import type { Build } from '../types'
|
|
8
8
|
|
|
9
9
|
/** Writes some info about the current {@link Files} being passed around. */
|
|
10
10
|
export class RunBuildInternal implements Plug<void> {
|
|
@@ -28,25 +28,12 @@ export class RunBuildInternal implements Plug<void> {
|
|
|
28
28
|
if (! isBuild(maybeBuild)) {
|
|
29
29
|
context.log.fail(`File ${$p(file)} did not export a proper build`)
|
|
30
30
|
} else {
|
|
31
|
-
await maybeBuild
|
|
31
|
+
await invokeTasks(maybeBuild, tasks, this._props)
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** Symbol indicating that an object is a {@link Build}. */
|
|
38
|
-
const buildMarker = Symbol.for('plugjs:isBuild')
|
|
39
|
-
|
|
40
|
-
/** Check if the specified build is actually a {@link Build} */
|
|
41
|
-
function isBuild(build: any): build is Build<Record<string, any>> & {
|
|
42
|
-
[buildMarker]: (
|
|
43
|
-
tasks: readonly string[],
|
|
44
|
-
props?: Record<string, string | undefined>,
|
|
45
|
-
) => Promise<void>
|
|
46
|
-
} {
|
|
47
|
-
return build && typeof build[buildMarker] === 'function'
|
|
48
|
-
}
|
|
49
|
-
|
|
50
37
|
export class RunBuild extends ForkingPlug {
|
|
51
38
|
constructor(
|
|
52
39
|
tasks: readonly string[],
|
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
|
})
|