@jujulego/jill 3.0.0-alpha.10 → 3.0.0-alpha.11
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/flat-job-plan.js +71 -0
- package/dist/flat-job-plan.js.map +1 -0
- package/dist/flat-job-tree.js +2 -1
- package/dist/flat-job-tree.js.map +1 -1
- package/dist/inked.js +1 -1
- package/dist/instrument.js +1 -1
- package/dist/job-command-execute.ink.js +9 -9
- package/dist/job-command-execute.ink.js.map +1 -1
- package/dist/job-plan.js +10 -76
- package/dist/job-plan.js.map +1 -1
- package/dist/job-plan.json.js +59 -0
- package/dist/job-plan.json.js.map +1 -0
- package/dist/list.ink.js +8 -8
- package/dist/main.js +873 -901
- package/dist/main.js.map +1 -1
- package/dist/planner.service.js +39 -9
- package/dist/planner.service.js.map +1 -1
- package/dist/tree.ink.js +7 -7
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +13 -18
package/dist/main.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
;{try{(function(){var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
;{try{(function(){var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1635b077-95c7-4391-8a7c-36d52dbe9982",e._sentryDebugIdIdentifier="sentry-dbid-1635b077-95c7-4391-8a7c-36d52dbe9982");})();}catch(e){}};!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};e.SENTRY_RELEASE={id:"3.0.0-alpha.11"};}catch(e){}}();import { token$, inject$, asyncScope$ } from '@kyrielle/injector';
|
|
2
|
+
import { startSpan, startInactiveSpan, getActiveSpan, updateSpanName, getRootSpan, captureException } from '@sentry/node';
|
|
3
|
+
import { pipe$, waitFor$, var$, filter$, collect$, map$, asyncIterator$, flow$ } from 'kyrielle';
|
|
4
4
|
import process$1 from 'node:process';
|
|
5
5
|
import { hideBin } from 'yargs/helpers';
|
|
6
|
-
import { scheduler$, isWorkloadEnded, WorkloadState, spawn$, parallelFlow$, fallbackFlow
|
|
7
|
-
import
|
|
6
|
+
import { scheduler$, isWorkloadEnded, WorkloadState, spawn$, sequenceFlow$, parallelFlow$, fallbackFlow$ } from '@kyrielle/workload';
|
|
7
|
+
import path from 'node:path';
|
|
8
8
|
import { _ } from '@swc/helpers/_/_apply_decs_2203_r';
|
|
9
|
-
import {
|
|
9
|
+
import { logger$, withTimestamp, LogLevel, withLabel, qLogDelay, LogGateway, logDelay$, toStderr } from '@kyrielle/logger';
|
|
10
10
|
import fs from 'node:fs';
|
|
11
11
|
import { PathScurry } from 'path-scurry';
|
|
12
|
-
import assert from 'assert';
|
|
13
|
-
import { Writable } from 'node:stream';
|
|
14
|
-
import moo from 'moo';
|
|
15
|
-
import path from 'node:path';
|
|
16
12
|
import { Glob } from 'glob';
|
|
17
13
|
import normalize from 'normalize-package-data';
|
|
18
14
|
import { satisfies, compare, parse } from 'semver';
|
|
15
|
+
import assert from 'assert';
|
|
16
|
+
import { Writable } from 'node:stream';
|
|
17
|
+
import { text } from 'node:stream/consumers';
|
|
18
|
+
import moo from 'moo';
|
|
19
19
|
import { spawn } from 'node:child_process';
|
|
20
20
|
import chalk from 'chalk';
|
|
21
21
|
import slugify from 'slugify';
|
|
@@ -26,27 +26,37 @@ import os from 'node:os';
|
|
|
26
26
|
import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
|
|
27
27
|
import { chalkTemplateStderr } from 'chalk-template';
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
function trace(fun, opts) {
|
|
30
|
+
const name = typeof opts === 'string' ? opts : opts.name;
|
|
31
|
+
const op = typeof opts === 'object' ? opts.op : undefined;
|
|
32
|
+
const use = typeof opts === 'object' && opts.use || ((_, r)=>r);
|
|
33
|
+
return function(...args) {
|
|
34
|
+
return pipe$(startSpan({
|
|
35
|
+
name,
|
|
36
|
+
op
|
|
37
|
+
}, ()=>fun.call(this, ...args)), (r)=>use(name, r));
|
|
38
|
+
};
|
|
37
39
|
}
|
|
38
|
-
function
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
function instrument(opts) {
|
|
41
|
+
return (target, context)=>{
|
|
42
|
+
const name = typeof opts === 'string' ? opts : opts?.name ?? context.name.toString();
|
|
43
|
+
const use = typeof opts === 'object' ? opts.use : (_, r)=>r;
|
|
44
|
+
return trace(target, {
|
|
45
|
+
name,
|
|
46
|
+
use
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function traceImport(name, loader) {
|
|
51
|
+
return startSpan({
|
|
52
|
+
name: name,
|
|
53
|
+
op: 'resource.script'
|
|
54
|
+
}, loader);
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
// Tokens
|
|
48
58
|
const CONFIG = token$('Config', async ()=>{
|
|
49
|
-
const { ConfigService } = await Promise.resolve().then(function () { return config_service; });
|
|
59
|
+
const { ConfigService } = await traceImport('ConfigService', ()=>Promise.resolve().then(function () { return config_service; }));
|
|
50
60
|
return waitFor$(inject$(ConfigService, asyncScope$()).config$);
|
|
51
61
|
});
|
|
52
62
|
const CWD = token$('cwd', ()=>process$1.cwd());
|
|
@@ -107,46 +117,32 @@ const SCHEDULER = token$('Scheduler', async ()=>{
|
|
|
107
117
|
return scheduler;
|
|
108
118
|
});
|
|
109
119
|
|
|
110
|
-
function
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
name,
|
|
127
|
-
use
|
|
128
|
-
});
|
|
120
|
+
function mutex$() {
|
|
121
|
+
const count$ = var$(0);
|
|
122
|
+
return {
|
|
123
|
+
async acquire () {
|
|
124
|
+
let cnt = count$.defer();
|
|
125
|
+
while(cnt > 0){
|
|
126
|
+
cnt = await waitFor$(count$);
|
|
127
|
+
}
|
|
128
|
+
count$.mutate(cnt + 1);
|
|
129
|
+
},
|
|
130
|
+
release () {
|
|
131
|
+
const cnt = count$.defer();
|
|
132
|
+
if (cnt > 0) {
|
|
133
|
+
count$.mutate(cnt - 1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
129
136
|
};
|
|
130
137
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
class ClientError extends Error {
|
|
139
|
-
name = 'ClientError';
|
|
140
|
-
constructor(message){
|
|
141
|
-
super(message);
|
|
138
|
+
async function with$(lock, fn) {
|
|
139
|
+
try {
|
|
140
|
+
await lock.acquire();
|
|
141
|
+
return await fn();
|
|
142
|
+
} finally{
|
|
143
|
+
lock.release();
|
|
142
144
|
}
|
|
143
145
|
}
|
|
144
|
-
class TaskExpressionError extends ClientError {
|
|
145
|
-
name = 'TaskExpressionError';
|
|
146
|
-
}
|
|
147
|
-
class TaskSyntaxError extends ClientError {
|
|
148
|
-
name = 'TaskSyntaxError';
|
|
149
|
-
}
|
|
150
146
|
|
|
151
147
|
function logStreamedLines(logger, level) {
|
|
152
148
|
let leftover = '';
|
|
@@ -170,6 +166,140 @@ function logStreamedLines(logger, level) {
|
|
|
170
166
|
});
|
|
171
167
|
}
|
|
172
168
|
|
|
169
|
+
function command$(workspace, cmd, opts = {}) {
|
|
170
|
+
const { superCommand, logger = inject$(LOGGER), ...rest } = opts;
|
|
171
|
+
// Apply super command
|
|
172
|
+
if (superCommand) {
|
|
173
|
+
cmd = superCommand + ' ' + cmd;
|
|
174
|
+
}
|
|
175
|
+
// Prepare job
|
|
176
|
+
const job = spawn$(cmd, {
|
|
177
|
+
...rest,
|
|
178
|
+
cwd: workspace.root,
|
|
179
|
+
env: {
|
|
180
|
+
FORCE_COLOR: '1',
|
|
181
|
+
...rest.env
|
|
182
|
+
},
|
|
183
|
+
shell: true
|
|
184
|
+
});
|
|
185
|
+
job.stdout.pipe(logStreamedLines(logger, LogLevel.info));
|
|
186
|
+
job.stderr.pipe(logStreamedLines(logger, LogLevel.info));
|
|
187
|
+
return job;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
class ClientError extends Error {
|
|
191
|
+
name = 'ClientError';
|
|
192
|
+
constructor(message){
|
|
193
|
+
super(message);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
class TaskExpressionError extends ClientError {
|
|
197
|
+
name = 'TaskExpressionError';
|
|
198
|
+
}
|
|
199
|
+
class TaskSyntaxError extends ClientError {
|
|
200
|
+
name = 'TaskSyntaxError';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Utils
|
|
204
|
+
function capitalize(txt) {
|
|
205
|
+
return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase();
|
|
206
|
+
}
|
|
207
|
+
function splitCommandLine(line) {
|
|
208
|
+
line = line.trim();
|
|
209
|
+
const parts = [];
|
|
210
|
+
let current_cote = '';
|
|
211
|
+
let last = 0;
|
|
212
|
+
for(let i = 1; i < line.length; ++i){
|
|
213
|
+
const c = line[i];
|
|
214
|
+
if (current_cote) {
|
|
215
|
+
if (c === current_cote) {
|
|
216
|
+
current_cote = '';
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
if ([
|
|
220
|
+
'"',
|
|
221
|
+
'\''
|
|
222
|
+
].includes(c)) {
|
|
223
|
+
current_cote = c;
|
|
224
|
+
} else if (c === ' ') {
|
|
225
|
+
parts.push(line.slice(last, i));
|
|
226
|
+
last = i + 1;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
parts.push(line.slice(last));
|
|
231
|
+
return parts;
|
|
232
|
+
}
|
|
233
|
+
function escapeCommandLineArg(arg) {
|
|
234
|
+
if (arg.match(/[ ()]/)) {
|
|
235
|
+
return `"${arg.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
236
|
+
}
|
|
237
|
+
return arg;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function runScript$(workspace, script, args, opts = {}) {
|
|
241
|
+
// Run script itself
|
|
242
|
+
const jobs = [
|
|
243
|
+
await planScript$(workspace, script, args, opts)
|
|
244
|
+
];
|
|
245
|
+
if (!jobs[0]) {
|
|
246
|
+
throw new ScriptNotFound(`No script ${script} in ${workspace.name}`);
|
|
247
|
+
}
|
|
248
|
+
// Run hooks
|
|
249
|
+
if (opts.runHooks) {
|
|
250
|
+
jobs.unshift(await planScript$(workspace, `pre${script}`, [], opts));
|
|
251
|
+
jobs.push(await planScript$(workspace, `post${script}`, [], opts));
|
|
252
|
+
}
|
|
253
|
+
// Prepare workflow
|
|
254
|
+
const flow = pipe$(jobs, filter$((job)=>job !== null), collect$(sequenceFlow$({
|
|
255
|
+
label: script,
|
|
256
|
+
type: 'script'
|
|
257
|
+
})));
|
|
258
|
+
return {
|
|
259
|
+
...flow,
|
|
260
|
+
script,
|
|
261
|
+
workspace
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
// Utils
|
|
265
|
+
async function planScript$(workspace, script, args, opts) {
|
|
266
|
+
// Load script
|
|
267
|
+
let line = workspace?.getScript(script);
|
|
268
|
+
if (!line) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
// Parse script
|
|
272
|
+
const [command, ...commandArgs] = splitCommandLine(line);
|
|
273
|
+
if (!command) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
if (command === 'jill') {
|
|
277
|
+
const argv = [
|
|
278
|
+
...commandArgs,
|
|
279
|
+
...args
|
|
280
|
+
].map((arg)=>arg.replace(/^["'](.+)["']$/, '$1'));
|
|
281
|
+
const { PlannerService } = await traceImport('PlannerService', ()=>import('./planner.service.js'));
|
|
282
|
+
const plannerService = inject$(PlannerService);
|
|
283
|
+
const job = await plannerService.plan(argv, workspace.root);
|
|
284
|
+
if (job) {
|
|
285
|
+
return job;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Run command
|
|
289
|
+
const pm = await workspace.project.packageManager();
|
|
290
|
+
line = [
|
|
291
|
+
line,
|
|
292
|
+
args.map(escapeCommandLineArg)
|
|
293
|
+
].join(' ').trim();
|
|
294
|
+
return command$(workspace, line, {
|
|
295
|
+
logger: opts.logger,
|
|
296
|
+
superCommand: pm === 'yarn' && command !== 'yarn' ? 'yarn exec' : undefined
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
class ScriptNotFound extends ClientError {
|
|
300
|
+
name = 'ScriptNotFound';
|
|
301
|
+
}
|
|
302
|
+
|
|
173
303
|
var _dec$2, _dec1$1, _dec2, _initProto$2;
|
|
174
304
|
_dec$2 = instrument('GitService.isAffected'), _dec1$1 = instrument('GitService.listBranches'), _dec2 = instrument('GitService.listTags');
|
|
175
305
|
class GitService {
|
|
@@ -198,13 +328,13 @@ class GitService {
|
|
|
198
328
|
// Methods
|
|
199
329
|
/**
|
|
200
330
|
* Runs a git command inside
|
|
201
|
-
*/ async command(cmd,
|
|
331
|
+
*/ async command(cmd, opts = {}) {
|
|
202
332
|
const { logger = this._logger, ...props } = opts;
|
|
203
333
|
// Create job
|
|
204
|
-
const job = spawn$(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
334
|
+
const job = spawn$(`git ${cmd}`, {
|
|
335
|
+
...props,
|
|
336
|
+
shell: true
|
|
337
|
+
});
|
|
208
338
|
job.stdout.pipe(logStreamedLines(logger, LogLevel.debug));
|
|
209
339
|
job.stderr.pipe(logStreamedLines(logger, LogLevel.warning));
|
|
210
340
|
(await this._scheduler).register(job);
|
|
@@ -213,17 +343,17 @@ class GitService {
|
|
|
213
343
|
/**
|
|
214
344
|
* Runs git branch
|
|
215
345
|
*/ branch(args, opts) {
|
|
216
|
-
return this.command(
|
|
346
|
+
return this.command(`branch ${args.join(' ')}`, opts);
|
|
217
347
|
}
|
|
218
348
|
/**
|
|
219
349
|
* Runs git diff
|
|
220
350
|
*/ diff(args, opts) {
|
|
221
|
-
return this.command(
|
|
351
|
+
return this.command(`diff ${args.join(' ')}`, opts);
|
|
222
352
|
}
|
|
223
353
|
/**
|
|
224
354
|
* Runs git tag
|
|
225
355
|
*/ tag(args, opts) {
|
|
226
|
-
return this.command(
|
|
356
|
+
return this.command(`tag ${args.join(' ')}`, opts);
|
|
227
357
|
}
|
|
228
358
|
/**
|
|
229
359
|
* Uses git diff to detect if given files have been affected since given reference
|
|
@@ -265,873 +395,728 @@ class GitService {
|
|
|
265
395
|
}
|
|
266
396
|
}
|
|
267
397
|
|
|
268
|
-
//
|
|
269
|
-
function
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const revision = await formatRevision(opts, wks);
|
|
274
|
-
if (await wks.isAffected(revision)) {
|
|
275
|
-
yield wks;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}());
|
|
279
|
-
};
|
|
398
|
+
// Utils
|
|
399
|
+
async function* combine(...generators) {
|
|
400
|
+
for (const gen of generators){
|
|
401
|
+
yield* gen;
|
|
402
|
+
}
|
|
280
403
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
404
|
+
|
|
405
|
+
class Workspace {
|
|
406
|
+
manifest;
|
|
407
|
+
project;
|
|
408
|
+
// Attributes
|
|
409
|
+
_affectedCache = new Map();
|
|
410
|
+
_logger;
|
|
411
|
+
_git = inject$(GitService);
|
|
412
|
+
_root;
|
|
413
|
+
_jobs = new Map();
|
|
414
|
+
// Constructor
|
|
415
|
+
constructor(root, manifest, project){
|
|
416
|
+
this.manifest = manifest;
|
|
417
|
+
this.project = project;
|
|
418
|
+
this._root = root;
|
|
419
|
+
this._logger = inject$(LOGGER).child(withLabel(manifest.name));
|
|
420
|
+
}
|
|
421
|
+
// Methods
|
|
422
|
+
async _buildDependencies(job, opts) {
|
|
423
|
+
const generators = [];
|
|
424
|
+
switch(opts.buildDeps ?? 'all'){
|
|
425
|
+
case 'all':
|
|
426
|
+
generators.unshift(this.devDependencies());
|
|
427
|
+
// eslint-disable-next no-fallthrough
|
|
428
|
+
case 'prod':
|
|
429
|
+
generators.unshift(this.dependencies());
|
|
430
|
+
}
|
|
431
|
+
// Build deps
|
|
432
|
+
for await (const dep of combine(...generators)){
|
|
433
|
+
const build = await dep.build(opts);
|
|
434
|
+
if (build) {
|
|
435
|
+
job.dependsOn(build);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async *_loadDependencies(dependencies, kind) {
|
|
440
|
+
for (const [dep, range] of Object.entries(dependencies)){
|
|
441
|
+
const ws = await this.project.workspace(dep);
|
|
442
|
+
if (ws) {
|
|
443
|
+
if (ws._satisfies(this, range)) {
|
|
444
|
+
yield ws;
|
|
445
|
+
} else {
|
|
446
|
+
this._logger.warning(`ignoring ${kind} ${ws.reference} as it does not match requirement ${range}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
async _isAffected(reference) {
|
|
452
|
+
const isAffected = await this._git.isAffected(reference, [
|
|
453
|
+
this.root
|
|
298
454
|
], {
|
|
299
|
-
cwd:
|
|
300
|
-
logger:
|
|
455
|
+
cwd: this.project.root,
|
|
456
|
+
logger: this._logger
|
|
301
457
|
});
|
|
302
|
-
if (
|
|
303
|
-
|
|
458
|
+
if (isAffected) {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
// Test dependencies
|
|
462
|
+
const proms = [];
|
|
463
|
+
for await (const dep of combine(this.dependencies(), this.devDependencies())){
|
|
464
|
+
proms.push(dep.isAffected(reference));
|
|
304
465
|
}
|
|
466
|
+
const results = await Promise.all(proms);
|
|
467
|
+
return results.some((r)=>r);
|
|
305
468
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
469
|
+
_satisfies(from, range) {
|
|
470
|
+
if (range.startsWith('file:')) {
|
|
471
|
+
return path.resolve(from.root, range.substring(5)) === this.root;
|
|
472
|
+
}
|
|
473
|
+
if (range.startsWith('workspace:')) {
|
|
474
|
+
range = range.substring(10);
|
|
475
|
+
}
|
|
476
|
+
return !this.version || satisfies(this.version, range);
|
|
477
|
+
}
|
|
478
|
+
async isAffected(reference) {
|
|
479
|
+
let isAffected = this._affectedCache.get(reference);
|
|
480
|
+
if (!isAffected) {
|
|
481
|
+
isAffected = this._isAffected(reference);
|
|
482
|
+
this._affectedCache.set(reference, isAffected);
|
|
483
|
+
}
|
|
484
|
+
return await isAffected;
|
|
485
|
+
}
|
|
486
|
+
async *dependencies() {
|
|
487
|
+
if (!this.manifest.dependencies) return;
|
|
488
|
+
for await (const ws of this._loadDependencies(this.manifest.dependencies, 'dependency')){
|
|
489
|
+
yield ws;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async *devDependencies() {
|
|
493
|
+
if (!this.manifest.devDependencies) return;
|
|
494
|
+
for await (const ws of this._loadDependencies(this.manifest.devDependencies, 'devDependency')){
|
|
495
|
+
yield ws;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
async build(opts = {}) {
|
|
499
|
+
const script = opts.buildScript ?? 'build';
|
|
500
|
+
const job = await this.run(script, [], opts);
|
|
501
|
+
if (!job) {
|
|
502
|
+
this._logger.warning(`will not be built (no "${script}" script found)`);
|
|
503
|
+
}
|
|
504
|
+
return job;
|
|
505
|
+
}
|
|
506
|
+
async exec(command, opts = {}) {
|
|
507
|
+
const pm = await this.project.packageManager();
|
|
508
|
+
const job = command$(this, command, {
|
|
509
|
+
...opts,
|
|
510
|
+
logger: this._logger.child(withLabel(`${this.name}$${command}`)),
|
|
511
|
+
superCommand: pm === 'yarn' ? 'yarn exec' : undefined
|
|
314
512
|
});
|
|
315
|
-
|
|
316
|
-
|
|
513
|
+
await this._buildDependencies(job, opts);
|
|
514
|
+
return job;
|
|
515
|
+
}
|
|
516
|
+
getScript(script) {
|
|
517
|
+
const { scripts = {} } = this.manifest;
|
|
518
|
+
return scripts[script] || null;
|
|
519
|
+
}
|
|
520
|
+
async run(script, args = [], opts = {}) {
|
|
521
|
+
// Script not found
|
|
522
|
+
if (!this.getScript(script)) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
// Create task if it doesn't exist yet
|
|
526
|
+
let job = this._jobs.get(script);
|
|
527
|
+
if (!job) {
|
|
528
|
+
const config = await inject$(CONFIG, asyncScope$());
|
|
529
|
+
job = await runScript$(this, script, args, {
|
|
530
|
+
...opts,
|
|
531
|
+
logger: this._logger.child(withLabel(`${this.name}#${script}`)),
|
|
532
|
+
runHooks: config.hooks
|
|
533
|
+
});
|
|
534
|
+
await this._buildDependencies(job, opts);
|
|
535
|
+
this._jobs.set(script, job);
|
|
317
536
|
}
|
|
537
|
+
return job;
|
|
318
538
|
}
|
|
319
|
-
|
|
320
|
-
|
|
539
|
+
// Properties
|
|
540
|
+
get name() {
|
|
541
|
+
return this.manifest.name;
|
|
321
542
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
543
|
+
get reference() {
|
|
544
|
+
return this.version ? `${this.name}@${this.version}` : this.name;
|
|
545
|
+
}
|
|
546
|
+
get root() {
|
|
547
|
+
return path.resolve(this.project.root, this._root);
|
|
548
|
+
}
|
|
549
|
+
get version() {
|
|
550
|
+
return this.manifest.version;
|
|
325
551
|
}
|
|
326
|
-
return result;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Filter
|
|
330
|
-
function isPrivate$(value) {
|
|
331
|
-
return filter$((wks)=>(wks.manifest.private ?? false) === value);
|
|
332
552
|
}
|
|
333
553
|
|
|
334
|
-
|
|
335
|
-
_dec$1 = instrument('TaskParserService.parse'), _dec1 = instrument('TaskParserService.buildJob');
|
|
336
|
-
// Service
|
|
337
|
-
class TaskParserService {
|
|
338
|
-
static{
|
|
339
|
-
({ e: [_initProto$1] } = _(this, [
|
|
340
|
-
[
|
|
341
|
-
_dec$1,
|
|
342
|
-
2,
|
|
343
|
-
"parse"
|
|
344
|
-
],
|
|
345
|
-
[
|
|
346
|
-
_dec1,
|
|
347
|
-
2,
|
|
348
|
-
"buildJob"
|
|
349
|
-
]
|
|
350
|
-
], []));
|
|
351
|
-
}
|
|
554
|
+
class Project {
|
|
352
555
|
// Attributes
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
556
|
+
_isFullyLoaded = false;
|
|
557
|
+
_lock = mutex$();
|
|
558
|
+
_mainWorkspace;
|
|
559
|
+
_packageManager;
|
|
560
|
+
_workspaceGlob;
|
|
561
|
+
_names = new Map();
|
|
562
|
+
_logger = inject$(LOGGER).child(withLabel('project'));
|
|
563
|
+
_root;
|
|
564
|
+
_scurry = inject$(PATH_SCURRY);
|
|
565
|
+
_workspaces = new Map();
|
|
566
|
+
// Constructor
|
|
567
|
+
constructor(root, opts = {}){
|
|
568
|
+
this._root = root;
|
|
569
|
+
if (opts.packageManager) {
|
|
570
|
+
this._logger.verbose`Forced use of ${opts.packageManager} in ${root}`;
|
|
571
|
+
this._packageManager = opts.packageManager;
|
|
572
|
+
}
|
|
357
573
|
}
|
|
358
|
-
static _sequenceOperatorWarn = true;
|
|
359
574
|
// Methods
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
value: (x)=>x.slice(1, -1).replace(/\\(["\\])/g, '$1')
|
|
379
|
-
}
|
|
380
|
-
]
|
|
381
|
-
},
|
|
382
|
-
operator: {
|
|
383
|
-
rparen: ')',
|
|
384
|
-
whitespace: /[ \t]+/,
|
|
385
|
-
operator: {
|
|
386
|
-
match: [
|
|
387
|
-
'->',
|
|
388
|
-
'&&',
|
|
389
|
-
'//',
|
|
390
|
-
'||'
|
|
391
|
-
],
|
|
392
|
-
pop: 1
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
operatorOrArgument: {
|
|
396
|
-
rparen: ')',
|
|
397
|
-
whitespace: /[ \t]+/,
|
|
398
|
-
operator: {
|
|
399
|
-
match: [
|
|
400
|
-
'->',
|
|
401
|
-
'&&',
|
|
402
|
-
'//',
|
|
403
|
-
'||'
|
|
404
|
-
],
|
|
405
|
-
pop: 1
|
|
406
|
-
},
|
|
407
|
-
argument: [
|
|
408
|
-
{
|
|
409
|
-
match: /[-_:a-zA-Z0-9]+/
|
|
410
|
-
},
|
|
411
|
-
{
|
|
412
|
-
match: /'(?:\\['\\]|[^\r\n'\\])+'/,
|
|
413
|
-
value: (x)=>x.slice(1, -1).replace(/\\(['\\])/g, '$1')
|
|
414
|
-
},
|
|
415
|
-
{
|
|
416
|
-
match: /"(?:\\["\\]|[^\r\n"\\])+"/,
|
|
417
|
-
value: (x)=>x.slice(1, -1).replace(/\\(["\\])/g, '$1')
|
|
418
|
-
}
|
|
419
|
-
]
|
|
575
|
+
async _loadManifest(dir) {
|
|
576
|
+
const file = path.resolve(this.root, dir, 'package.json');
|
|
577
|
+
const relative = path.relative(this.root, path.dirname(file));
|
|
578
|
+
const logger = this._logger.child(withLabel(relative ? `project@${relative}` : 'project'));
|
|
579
|
+
logger.debug('loading package.json ...');
|
|
580
|
+
const data = await fs.promises.readFile(file, 'utf-8');
|
|
581
|
+
const mnf = JSON.parse(data);
|
|
582
|
+
normalize(mnf, (msg)=>logger.verbose(msg));
|
|
583
|
+
return mnf;
|
|
584
|
+
}
|
|
585
|
+
_loadWorkspace(dir) {
|
|
586
|
+
return with$(this._lock, async ()=>{
|
|
587
|
+
let wks = this._workspaces.get(dir);
|
|
588
|
+
if (!wks) {
|
|
589
|
+
const manifest = await this._loadManifest(dir);
|
|
590
|
+
wks = new Workspace(dir, manifest, this);
|
|
591
|
+
this._workspaces.set(dir, wks);
|
|
592
|
+
this._names.set(wks.name, wks);
|
|
420
593
|
}
|
|
594
|
+
return wks;
|
|
421
595
|
});
|
|
422
596
|
}
|
|
423
|
-
|
|
424
|
-
let
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if (
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
// rparen = end of group
|
|
431
|
-
if (token.type === 'rparen') {
|
|
432
|
-
break;
|
|
433
|
-
}
|
|
434
|
-
// Handle argument
|
|
435
|
-
if (token.type === 'argument') {
|
|
436
|
-
if (!node) {
|
|
437
|
-
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected argument'));
|
|
438
|
-
} else if (TaskParserService.isTaskNode(node)) {
|
|
439
|
-
node.args.push(token.value);
|
|
440
|
-
} else {
|
|
441
|
-
const lastTask = node.tasks[node.tasks.length - 1];
|
|
442
|
-
if (!lastTask || !TaskParserService.isTaskNode(lastTask)) {
|
|
443
|
-
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected argument'));
|
|
444
|
-
} else {
|
|
445
|
-
lastTask.args.push(token.value);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
// Handle operator
|
|
451
|
-
if (token.type === 'operator') {
|
|
452
|
-
const operator = token.value;
|
|
453
|
-
if (!node) {
|
|
454
|
-
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected operator'));
|
|
455
|
-
} else if (TaskParserService.isTaskNode(node)) {
|
|
456
|
-
node = {
|
|
457
|
-
operator,
|
|
458
|
-
tasks: [
|
|
459
|
-
node
|
|
460
|
-
]
|
|
461
|
-
};
|
|
462
|
-
continue;
|
|
463
|
-
} else {
|
|
464
|
-
if (node.operator !== operator) {
|
|
465
|
-
node = {
|
|
466
|
-
operator,
|
|
467
|
-
tasks: [
|
|
468
|
-
node
|
|
469
|
-
]
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
continue;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
// Build "child"
|
|
476
|
-
let child;
|
|
477
|
-
if (token.type === 'script') {
|
|
478
|
-
child = {
|
|
479
|
-
script: token.value,
|
|
480
|
-
args: []
|
|
481
|
-
};
|
|
482
|
-
} else if (token.type === 'string') {
|
|
483
|
-
const [script, ...args] = token.value.split(/ +/);
|
|
484
|
-
child = {
|
|
485
|
-
script,
|
|
486
|
-
args
|
|
487
|
-
};
|
|
488
|
-
} else if (token.type === 'lparen') {
|
|
489
|
-
const res = this._nextNode(lexer, i + 1);
|
|
490
|
-
if (!res) {
|
|
491
|
-
throw new TaskSyntaxError(lexer.formatError(token, 'Empty group found'));
|
|
492
|
-
}
|
|
493
|
-
child = res;
|
|
494
|
-
} else {
|
|
495
|
-
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected token'));
|
|
496
|
-
}
|
|
497
|
-
if (!node) {
|
|
498
|
-
node = child;
|
|
499
|
-
} else if (TaskParserService.isTaskNode(node)) {
|
|
500
|
-
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected token, expected an operator'));
|
|
501
|
-
} else {
|
|
502
|
-
node.tasks.push(child);
|
|
597
|
+
async currentWorkspace(cwd = inject$(CWD, asyncScope$())) {
|
|
598
|
+
let workspace = null;
|
|
599
|
+
cwd = path.resolve(cwd);
|
|
600
|
+
for await (const wks of this.workspaces()){
|
|
601
|
+
if (cwd.startsWith(wks.root)) {
|
|
602
|
+
workspace = wks;
|
|
603
|
+
if (wks.root !== this.root) return wks;
|
|
503
604
|
}
|
|
504
605
|
}
|
|
505
|
-
return
|
|
606
|
+
return workspace;
|
|
506
607
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
608
|
+
async mainWorkspace() {
|
|
609
|
+
if (!this._mainWorkspace) {
|
|
610
|
+
const manifest = await this._loadManifest('.');
|
|
611
|
+
this._mainWorkspace = new Workspace('.', manifest, this);
|
|
612
|
+
this._names.set(this._mainWorkspace.name, this._mainWorkspace);
|
|
613
|
+
}
|
|
614
|
+
return this._mainWorkspace;
|
|
615
|
+
}
|
|
616
|
+
async packageManager() {
|
|
617
|
+
if (!this._packageManager) {
|
|
618
|
+
this._logger.debug`searching lockfile in ${this.root}`;
|
|
619
|
+
const files = await this._scurry.readdir(this.root, {
|
|
620
|
+
withFileTypes: false
|
|
621
|
+
});
|
|
622
|
+
if (files.includes('yarn.lock')) {
|
|
623
|
+
this._logger.debug`detected yarn in ${this.root}`;
|
|
624
|
+
this._packageManager = 'yarn';
|
|
625
|
+
} else if (files.includes('package-lock.json')) {
|
|
626
|
+
this._logger.debug`detected npm in ${this.root}`;
|
|
627
|
+
this._packageManager = 'npm';
|
|
516
628
|
} else {
|
|
517
|
-
|
|
629
|
+
this._logger.debug`no package manager recognized in ${this.root}, defaults to npm`;
|
|
630
|
+
this._packageManager = 'npm';
|
|
518
631
|
}
|
|
519
632
|
}
|
|
520
|
-
return
|
|
633
|
+
return this._packageManager;
|
|
521
634
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
635
|
+
async workspace(name) {
|
|
636
|
+
// With current directory
|
|
637
|
+
if (!name) {
|
|
638
|
+
const dir = path.relative(this.root, inject$(CWD, asyncScope$()));
|
|
639
|
+
return this._loadWorkspace(dir);
|
|
640
|
+
}
|
|
641
|
+
// Try name index
|
|
642
|
+
const wks = this._names.get(name);
|
|
643
|
+
if (wks) {
|
|
644
|
+
return wks;
|
|
645
|
+
}
|
|
646
|
+
// Load workspaces
|
|
647
|
+
if (!this._isFullyLoaded) {
|
|
648
|
+
for await (const ws of this.workspaces()){
|
|
649
|
+
if (ws.name === name) {
|
|
650
|
+
return ws;
|
|
651
|
+
}
|
|
532
652
|
}
|
|
653
|
+
this._isFullyLoaded = true;
|
|
533
654
|
}
|
|
655
|
+
return null;
|
|
534
656
|
}
|
|
535
|
-
async
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
657
|
+
async *workspaces() {
|
|
658
|
+
const main = await this.mainWorkspace();
|
|
659
|
+
yield main;
|
|
660
|
+
if (this._isFullyLoaded) {
|
|
661
|
+
for (const wks of this._names.values()){
|
|
662
|
+
if (wks.name !== main.name) yield wks;
|
|
540
663
|
}
|
|
541
|
-
return job;
|
|
542
664
|
} else {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
665
|
+
// Load child workspaces
|
|
666
|
+
const patterns = main.manifest.workspaces ?? [];
|
|
667
|
+
this._scurry.chdir(this.root);
|
|
668
|
+
this._workspaceGlob ??= new Glob(patterns, {
|
|
669
|
+
scurry: this._scurry,
|
|
670
|
+
withFileTypes: true
|
|
671
|
+
});
|
|
672
|
+
for await (const dir of this._workspaceGlob){
|
|
673
|
+
try {
|
|
674
|
+
// Check if dir is a directory
|
|
675
|
+
if (dir.isDirectory()) {
|
|
676
|
+
yield await this._loadWorkspace(dir.fullpath());
|
|
677
|
+
}
|
|
678
|
+
} catch (error) {
|
|
679
|
+
if (error.code === 'ENOENT') {
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
throw error;
|
|
556
683
|
}
|
|
557
|
-
flow = sequenceFlow$({
|
|
558
|
-
label: 'In sequence'
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
for (const child of node.tasks){
|
|
562
|
-
flow.push(await this.buildJob(child, workspace, opts));
|
|
563
684
|
}
|
|
564
|
-
|
|
685
|
+
this._isFullyLoaded = true;
|
|
565
686
|
}
|
|
566
687
|
}
|
|
688
|
+
// Properties
|
|
689
|
+
get root() {
|
|
690
|
+
return path.resolve(this._root);
|
|
691
|
+
}
|
|
567
692
|
}
|
|
568
693
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
count$.mutate(cnt - 1);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
async function with$(lock, fn) {
|
|
588
|
-
try {
|
|
589
|
-
await lock.acquire();
|
|
590
|
-
return await fn();
|
|
591
|
-
} finally{
|
|
592
|
-
lock.release();
|
|
694
|
+
var _dec$1, _initProto$1;
|
|
695
|
+
_dec$1 = instrument();
|
|
696
|
+
/**
|
|
697
|
+
* Helps detecting projects folders
|
|
698
|
+
*/ class ProjectsRepository {
|
|
699
|
+
static{
|
|
700
|
+
({ e: [_initProto$1] } = _(this, [
|
|
701
|
+
[
|
|
702
|
+
_dec$1,
|
|
703
|
+
2,
|
|
704
|
+
"searchProjectRoot"
|
|
705
|
+
]
|
|
706
|
+
], []));
|
|
593
707
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
//
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
708
|
+
// Attributes
|
|
709
|
+
_cache = (_initProto$1(this), new Map());
|
|
710
|
+
_logger = inject$(LOGGER).child(withLabel('projects'));
|
|
711
|
+
_scurry = inject$(PATH_SCURRY);
|
|
712
|
+
// Methods
|
|
713
|
+
async isProjectRoot(dir) {
|
|
714
|
+
this._logger.debug`testing ${dir}`;
|
|
715
|
+
const files = await this._scurry.readdir(dir, {
|
|
716
|
+
withFileTypes: false
|
|
717
|
+
});
|
|
718
|
+
return {
|
|
719
|
+
hasManifest: files.includes(MANIFEST),
|
|
720
|
+
hasLockFile: LOCK_FILES.some((lock)=>files.includes(lock))
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
async searchProjectRoot(directory) {
|
|
724
|
+
directory = path.resolve(directory);
|
|
725
|
+
// Test all ancestors
|
|
726
|
+
let foundManifest = false;
|
|
727
|
+
let projectRoot = directory;
|
|
728
|
+
let dir = directory;
|
|
729
|
+
let prev = dir;
|
|
730
|
+
do {
|
|
731
|
+
// Look for files
|
|
732
|
+
const { hasManifest, hasLockFile } = await this.isProjectRoot(dir);
|
|
733
|
+
if (hasManifest) {
|
|
734
|
+
projectRoot = dir;
|
|
735
|
+
foundManifest = true;
|
|
736
|
+
}
|
|
737
|
+
if (hasLockFile) {
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
prev = dir;
|
|
741
|
+
dir = path.dirname(dir);
|
|
742
|
+
}while (prev !== dir);
|
|
743
|
+
// Log it
|
|
744
|
+
if (foundManifest) {
|
|
745
|
+
this._logger.verbose`project root found at ${projectRoot}`;
|
|
746
|
+
} else {
|
|
747
|
+
this._logger.verbose`project root not found, keeping ${projectRoot}`;
|
|
613
748
|
}
|
|
749
|
+
return projectRoot;
|
|
614
750
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
FORCE_COLOR: '1',
|
|
621
|
-
...rest.env
|
|
751
|
+
getProject(root, opts) {
|
|
752
|
+
let project = this._cache.get(root);
|
|
753
|
+
if (!project) {
|
|
754
|
+
project = new Project(root, opts);
|
|
755
|
+
this._cache.set(root, project);
|
|
622
756
|
}
|
|
757
|
+
return project;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
// Constants
|
|
761
|
+
const MANIFEST = 'package.json';
|
|
762
|
+
const LOCK_FILES = [
|
|
763
|
+
'package-lock.json',
|
|
764
|
+
'yarn.lock'
|
|
765
|
+
];
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Adds arguments to load a project.
|
|
769
|
+
*/ function withProject(parser) {
|
|
770
|
+
return parser.option('project', {
|
|
771
|
+
alias: 'p',
|
|
772
|
+
type: 'string',
|
|
773
|
+
default: '',
|
|
774
|
+
defaultDescription: 'current working directory',
|
|
775
|
+
description: 'Project root directory',
|
|
776
|
+
normalize: true
|
|
777
|
+
}).option('package-manager', {
|
|
778
|
+
choices: [
|
|
779
|
+
'yarn',
|
|
780
|
+
'npm'
|
|
781
|
+
],
|
|
782
|
+
type: 'string',
|
|
783
|
+
description: 'Force package manager'
|
|
784
|
+
}).middleware((args)=>startSpan({
|
|
785
|
+
name: 'project',
|
|
786
|
+
op: 'cli.middleware'
|
|
787
|
+
}, async ()=>{
|
|
788
|
+
const repository = inject$(ProjectsRepository);
|
|
789
|
+
const directory = path.resolve(inject$(CWD, asyncScope$()), args.project);
|
|
790
|
+
args.project = await repository.searchProjectRoot(directory);
|
|
791
|
+
}));
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Loads a project, based on arguments.
|
|
795
|
+
*/ function loadProject(args) {
|
|
796
|
+
const repository = inject$(ProjectsRepository);
|
|
797
|
+
return repository.getProject(args.project, {
|
|
798
|
+
packageManager: args.packageManager
|
|
623
799
|
});
|
|
624
|
-
job.stdout.pipe(logStreamedLines(logger, LogLevel.info));
|
|
625
|
-
job.stderr.pipe(logStreamedLines(logger, LogLevel.info));
|
|
626
|
-
return job;
|
|
627
800
|
}
|
|
628
801
|
|
|
629
|
-
//
|
|
630
|
-
function
|
|
631
|
-
return
|
|
802
|
+
// Filter
|
|
803
|
+
function hasSomeScript$(scripts) {
|
|
804
|
+
return filter$((wks)=>{
|
|
805
|
+
if (!wks.manifest.scripts) {
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
return scripts.some((script)=>script in wks.manifest.scripts);
|
|
809
|
+
});
|
|
632
810
|
}
|
|
633
|
-
function
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
let last = 0;
|
|
638
|
-
for(let i = 1; i < line.length; ++i){
|
|
639
|
-
const c = line[i];
|
|
640
|
-
if (current_cote) {
|
|
641
|
-
if (c === current_cote) {
|
|
642
|
-
current_cote = '';
|
|
643
|
-
}
|
|
644
|
-
} else {
|
|
645
|
-
if ([
|
|
646
|
-
'"',
|
|
647
|
-
'\''
|
|
648
|
-
].includes(c)) {
|
|
649
|
-
current_cote = c;
|
|
650
|
-
} else if (c === ' ') {
|
|
651
|
-
parts.push(line.slice(last, i));
|
|
652
|
-
last = i + 1;
|
|
653
|
-
}
|
|
811
|
+
function hasEveryScript$(scripts) {
|
|
812
|
+
return filter$((wks)=>{
|
|
813
|
+
if (!wks.manifest.scripts) {
|
|
814
|
+
return false;
|
|
654
815
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
return parts;
|
|
816
|
+
return scripts.every((script)=>script in wks.manifest.scripts);
|
|
817
|
+
});
|
|
658
818
|
}
|
|
659
819
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
jobs.push(await planScript$(workspace, `post${script}`, [], opts));
|
|
672
|
-
}
|
|
673
|
-
// Prepare workflow
|
|
674
|
-
const flow = pipe$(jobs, filter$((job)=>job !== null), collect$(sequenceFlow$({
|
|
675
|
-
label: script,
|
|
676
|
-
type: 'script'
|
|
677
|
-
})));
|
|
678
|
-
return {
|
|
679
|
-
...flow,
|
|
680
|
-
script,
|
|
681
|
-
workspace
|
|
820
|
+
// Filter
|
|
821
|
+
function isAffected$(opts) {
|
|
822
|
+
return (origin)=>{
|
|
823
|
+
return asyncIterator$(async function*() {
|
|
824
|
+
for await (const wks of origin){
|
|
825
|
+
const revision = await formatRevision(opts, wks);
|
|
826
|
+
if (await wks.isAffected(revision)) {
|
|
827
|
+
yield wks;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}());
|
|
682
831
|
};
|
|
683
832
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
//
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
833
|
+
async function formatRevision(opts, workspace) {
|
|
834
|
+
const git = inject$(GitService);
|
|
835
|
+
const logger = inject$(LOGGER).child(withLabel(workspace.name));
|
|
836
|
+
// Format revision
|
|
837
|
+
let result = opts.format;
|
|
838
|
+
result = result.replace(/(?<!\\)((?:\\\\)*)%name/g, `$1${workspace.name}`);
|
|
839
|
+
result = result.replace(/\\(.)/g, '$1');
|
|
840
|
+
// Ask git to complete it
|
|
841
|
+
const sortArgs = opts.sort ? [
|
|
842
|
+
'--sort',
|
|
843
|
+
opts.sort
|
|
844
|
+
] : [];
|
|
845
|
+
// - search in branches
|
|
846
|
+
if (result.includes('*')) {
|
|
847
|
+
const branches = await git.listBranches([
|
|
848
|
+
...sortArgs,
|
|
849
|
+
result
|
|
850
|
+
], {
|
|
851
|
+
cwd: workspace.root,
|
|
852
|
+
logger: logger
|
|
853
|
+
});
|
|
854
|
+
if (branches.length > 0) {
|
|
855
|
+
result = branches[branches.length - 1];
|
|
856
|
+
}
|
|
695
857
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
858
|
+
// - search in tags
|
|
859
|
+
if (result.includes('*')) {
|
|
860
|
+
const tags = await git.listTags([
|
|
861
|
+
...sortArgs,
|
|
862
|
+
result
|
|
863
|
+
], {
|
|
864
|
+
cwd: workspace.root,
|
|
865
|
+
logger: logger
|
|
866
|
+
});
|
|
867
|
+
if (tags.length > 0) {
|
|
868
|
+
result = tags[tags.length - 1];
|
|
703
869
|
}
|
|
704
870
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
'yarn',
|
|
714
|
-
'exec'
|
|
715
|
-
] : undefined
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
class ScriptNotFound extends ClientError {
|
|
719
|
-
name = 'ScriptNotFound';
|
|
871
|
+
if (result !== opts.format) {
|
|
872
|
+
logger.verbose(`resolved ${opts.format} into ${result}`);
|
|
873
|
+
}
|
|
874
|
+
if (result.includes('*')) {
|
|
875
|
+
logger.warning(`no revision found matching ${result}, using fallback ${opts.fallback}`);
|
|
876
|
+
return opts.fallback;
|
|
877
|
+
}
|
|
878
|
+
return result;
|
|
720
879
|
}
|
|
721
880
|
|
|
722
|
-
//
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
yield* gen;
|
|
726
|
-
}
|
|
881
|
+
// Filter
|
|
882
|
+
function isPrivate$(value) {
|
|
883
|
+
return filter$((wks)=>(wks.manifest.private ?? false) === value);
|
|
727
884
|
}
|
|
728
885
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
886
|
+
var _dec, _dec1, _initProto;
|
|
887
|
+
_dec = instrument('TaskParserService.parse'), _dec1 = instrument('TaskParserService.buildJob');
|
|
888
|
+
// Service
|
|
889
|
+
class TaskParserService {
|
|
890
|
+
static{
|
|
891
|
+
({ e: [_initProto] } = _(this, [
|
|
892
|
+
[
|
|
893
|
+
_dec,
|
|
894
|
+
2,
|
|
895
|
+
"parse"
|
|
896
|
+
],
|
|
897
|
+
[
|
|
898
|
+
_dec1,
|
|
899
|
+
2,
|
|
900
|
+
"buildJob"
|
|
901
|
+
]
|
|
902
|
+
], []));
|
|
903
|
+
}
|
|
732
904
|
// Attributes
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
_jobs = new Map();
|
|
738
|
-
// Constructor
|
|
739
|
-
constructor(root, manifest, project){
|
|
740
|
-
this.manifest = manifest;
|
|
741
|
-
this.project = project;
|
|
742
|
-
this._root = root;
|
|
743
|
-
this._logger = inject$(LOGGER).child(withLabel(manifest.name));
|
|
905
|
+
_logger = (_initProto(this), inject$(LOGGER).child(withLabel('task-parser')));
|
|
906
|
+
// Statics
|
|
907
|
+
static isTaskNode(node) {
|
|
908
|
+
return 'script' in node;
|
|
744
909
|
}
|
|
910
|
+
static _sequenceOperatorWarn = true;
|
|
745
911
|
// Methods
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
912
|
+
_lexer() {
|
|
913
|
+
return moo.states({
|
|
914
|
+
task: {
|
|
915
|
+
lparen: '(',
|
|
916
|
+
whitespace: /[ \t]+/,
|
|
917
|
+
script: {
|
|
918
|
+
match: /[-_:a-zA-Z0-9]+/,
|
|
919
|
+
push: 'operatorOrArgument'
|
|
920
|
+
},
|
|
921
|
+
string: [
|
|
922
|
+
{
|
|
923
|
+
match: /'(?:\\['\\]|[^\r\n'\\])+'/,
|
|
924
|
+
push: 'operator',
|
|
925
|
+
value: (x)=>x.slice(1, -1).replace(/\\(['\\])/g, '$1')
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
match: /"(?:\\["\\]|[^\r\n"\\])+"/,
|
|
929
|
+
push: 'operator',
|
|
930
|
+
value: (x)=>x.slice(1, -1).replace(/\\(["\\])/g, '$1')
|
|
931
|
+
}
|
|
932
|
+
]
|
|
933
|
+
},
|
|
934
|
+
operator: {
|
|
935
|
+
rparen: ')',
|
|
936
|
+
whitespace: /[ \t]+/,
|
|
937
|
+
operator: {
|
|
938
|
+
match: [
|
|
939
|
+
'->',
|
|
940
|
+
'&&',
|
|
941
|
+
'//',
|
|
942
|
+
'||'
|
|
943
|
+
],
|
|
944
|
+
pop: 1
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
operatorOrArgument: {
|
|
948
|
+
rparen: ')',
|
|
949
|
+
whitespace: /[ \t]+/,
|
|
950
|
+
operator: {
|
|
951
|
+
match: [
|
|
952
|
+
'->',
|
|
953
|
+
'&&',
|
|
954
|
+
'//',
|
|
955
|
+
'||'
|
|
956
|
+
],
|
|
957
|
+
pop: 1
|
|
958
|
+
},
|
|
959
|
+
argument: [
|
|
960
|
+
{
|
|
961
|
+
match: /[-_:a-zA-Z0-9]+/
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
match: /'(?:\\['\\]|[^\r\n'\\])+'/,
|
|
965
|
+
value: (x)=>x.slice(1, -1).replace(/\\(['\\])/g, '$1')
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
match: /"(?:\\["\\]|[^\r\n"\\])+"/,
|
|
969
|
+
value: (x)=>x.slice(1, -1).replace(/\\(["\\])/g, '$1')
|
|
970
|
+
}
|
|
971
|
+
]
|
|
760
972
|
}
|
|
761
|
-
}
|
|
973
|
+
});
|
|
762
974
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
975
|
+
_nextNode(lexer, i = 0) {
|
|
976
|
+
let node = null;
|
|
977
|
+
for (const token of lexer){
|
|
978
|
+
// Ignore whitespaces
|
|
979
|
+
if (token.type === 'whitespace') {
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
// rparen = end of group
|
|
983
|
+
if (token.type === 'rparen') {
|
|
984
|
+
break;
|
|
985
|
+
}
|
|
986
|
+
// Handle argument
|
|
987
|
+
if (token.type === 'argument') {
|
|
988
|
+
if (!node) {
|
|
989
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected argument'));
|
|
990
|
+
} else if (TaskParserService.isTaskNode(node)) {
|
|
991
|
+
node.args.push(token.value);
|
|
769
992
|
} else {
|
|
770
|
-
|
|
993
|
+
const lastTask = node.tasks[node.tasks.length - 1];
|
|
994
|
+
if (!lastTask || !TaskParserService.isTaskNode(lastTask)) {
|
|
995
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected argument'));
|
|
996
|
+
} else {
|
|
997
|
+
lastTask.args.push(token.value);
|
|
998
|
+
}
|
|
771
999
|
}
|
|
1000
|
+
continue;
|
|
772
1001
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
yield ws;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
async build(opts = {}) {
|
|
823
|
-
const script = opts.buildScript ?? 'build';
|
|
824
|
-
const job = await this.run(script, [], opts);
|
|
825
|
-
if (!job) {
|
|
826
|
-
this._logger.warning(`will not be built (no "${script}" script found)`);
|
|
827
|
-
}
|
|
828
|
-
return job;
|
|
829
|
-
}
|
|
830
|
-
async exec(command, args = [], opts = {}) {
|
|
831
|
-
const pm = await this.project.packageManager();
|
|
832
|
-
const job = command$(this, command, args, {
|
|
833
|
-
...opts,
|
|
834
|
-
logger: this._logger.child(withLabel(`${this.name}$${command}`)),
|
|
835
|
-
superCommand: pm === 'yarn' ? [
|
|
836
|
-
'yarn',
|
|
837
|
-
'exec'
|
|
838
|
-
] : undefined
|
|
839
|
-
});
|
|
840
|
-
await this._buildDependencies(job, opts);
|
|
841
|
-
return job;
|
|
842
|
-
}
|
|
843
|
-
getScript(script) {
|
|
844
|
-
const { scripts = {} } = this.manifest;
|
|
845
|
-
return scripts[script] || null;
|
|
846
|
-
}
|
|
847
|
-
async run(script, args = [], opts = {}) {
|
|
848
|
-
// Script not found
|
|
849
|
-
if (!this.getScript(script)) {
|
|
850
|
-
return null;
|
|
851
|
-
}
|
|
852
|
-
// Create task if it doesn't exist yet
|
|
853
|
-
let job = this._jobs.get(script);
|
|
854
|
-
if (!job) {
|
|
855
|
-
const config = await inject$(CONFIG, asyncScope$());
|
|
856
|
-
job = await runScript$(this, script, args, {
|
|
857
|
-
...opts,
|
|
858
|
-
logger: this._logger.child(withLabel(`${this.name}#${script}`)),
|
|
859
|
-
runHooks: config.hooks
|
|
860
|
-
});
|
|
861
|
-
await this._buildDependencies(job, opts);
|
|
862
|
-
this._jobs.set(script, job);
|
|
863
|
-
}
|
|
864
|
-
return job;
|
|
865
|
-
}
|
|
866
|
-
toJSON() {
|
|
867
|
-
return {
|
|
868
|
-
name: this.name,
|
|
869
|
-
version: this.version,
|
|
870
|
-
root: this.root
|
|
871
|
-
};
|
|
872
|
-
}
|
|
873
|
-
// Properties
|
|
874
|
-
get name() {
|
|
875
|
-
return this.manifest.name;
|
|
876
|
-
}
|
|
877
|
-
get reference() {
|
|
878
|
-
return this.version ? `${this.name}@${this.version}` : this.name;
|
|
879
|
-
}
|
|
880
|
-
get root() {
|
|
881
|
-
return path.resolve(this.project.root, this._root);
|
|
882
|
-
}
|
|
883
|
-
get version() {
|
|
884
|
-
return this.manifest.version;
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
class Project {
|
|
889
|
-
// Attributes
|
|
890
|
-
_isFullyLoaded = false;
|
|
891
|
-
_lock = mutex$();
|
|
892
|
-
_mainWorkspace;
|
|
893
|
-
_packageManager;
|
|
894
|
-
_workspaceGlob;
|
|
895
|
-
_names = new Map();
|
|
896
|
-
_logger = inject$(LOGGER).child(withLabel('project'));
|
|
897
|
-
_root;
|
|
898
|
-
_scurry = inject$(PATH_SCURRY);
|
|
899
|
-
_workspaces = new Map();
|
|
900
|
-
// Constructor
|
|
901
|
-
constructor(root, opts = {}){
|
|
902
|
-
this._root = root;
|
|
903
|
-
if (opts.packageManager) {
|
|
904
|
-
this._logger.verbose`Forced use of ${opts.packageManager} in ${root}`;
|
|
905
|
-
this._packageManager = opts.packageManager;
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
// Methods
|
|
909
|
-
async _loadManifest(dir) {
|
|
910
|
-
const file = path.resolve(this.root, dir, 'package.json');
|
|
911
|
-
const relative = path.relative(this.root, path.dirname(file));
|
|
912
|
-
const logger = this._logger.child(withLabel(relative ? `project@${relative}` : 'project'));
|
|
913
|
-
logger.debug('loading package.json ...');
|
|
914
|
-
const data = await fs.promises.readFile(file, 'utf-8');
|
|
915
|
-
const mnf = JSON.parse(data);
|
|
916
|
-
normalize(mnf, (msg)=>logger.verbose(msg));
|
|
917
|
-
return mnf;
|
|
918
|
-
}
|
|
919
|
-
_loadWorkspace(dir) {
|
|
920
|
-
return with$(this._lock, async ()=>{
|
|
921
|
-
let wks = this._workspaces.get(dir);
|
|
922
|
-
if (!wks) {
|
|
923
|
-
const manifest = await this._loadManifest(dir);
|
|
924
|
-
wks = new Workspace(dir, manifest, this);
|
|
925
|
-
this._workspaces.set(dir, wks);
|
|
926
|
-
this._names.set(wks.name, wks);
|
|
1002
|
+
// Handle operator
|
|
1003
|
+
if (token.type === 'operator') {
|
|
1004
|
+
const operator = token.value;
|
|
1005
|
+
if (!node) {
|
|
1006
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected operator'));
|
|
1007
|
+
} else if (TaskParserService.isTaskNode(node)) {
|
|
1008
|
+
node = {
|
|
1009
|
+
operator,
|
|
1010
|
+
tasks: [
|
|
1011
|
+
node
|
|
1012
|
+
]
|
|
1013
|
+
};
|
|
1014
|
+
continue;
|
|
1015
|
+
} else {
|
|
1016
|
+
if (node.operator !== operator) {
|
|
1017
|
+
node = {
|
|
1018
|
+
operator,
|
|
1019
|
+
tasks: [
|
|
1020
|
+
node
|
|
1021
|
+
]
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
// Build "child"
|
|
1028
|
+
let child;
|
|
1029
|
+
if (token.type === 'script') {
|
|
1030
|
+
child = {
|
|
1031
|
+
script: token.value,
|
|
1032
|
+
args: []
|
|
1033
|
+
};
|
|
1034
|
+
} else if (token.type === 'string') {
|
|
1035
|
+
const [script, ...args] = token.value.split(/ +/);
|
|
1036
|
+
child = {
|
|
1037
|
+
script,
|
|
1038
|
+
args
|
|
1039
|
+
};
|
|
1040
|
+
} else if (token.type === 'lparen') {
|
|
1041
|
+
const res = this._nextNode(lexer, i + 1);
|
|
1042
|
+
if (!res) {
|
|
1043
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Empty group found'));
|
|
1044
|
+
}
|
|
1045
|
+
child = res;
|
|
1046
|
+
} else {
|
|
1047
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected token'));
|
|
927
1048
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
for await (const wks of this.workspaces()){
|
|
935
|
-
if (cwd.startsWith(wks.root)) {
|
|
936
|
-
workspace = wks;
|
|
937
|
-
if (wks.root !== this.root) return wks;
|
|
1049
|
+
if (!node) {
|
|
1050
|
+
node = child;
|
|
1051
|
+
} else if (TaskParserService.isTaskNode(node)) {
|
|
1052
|
+
throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected token, expected an operator'));
|
|
1053
|
+
} else {
|
|
1054
|
+
node.tasks.push(child);
|
|
938
1055
|
}
|
|
939
1056
|
}
|
|
940
|
-
return
|
|
941
|
-
}
|
|
942
|
-
async mainWorkspace() {
|
|
943
|
-
if (!this._mainWorkspace) {
|
|
944
|
-
const manifest = await this._loadManifest('.');
|
|
945
|
-
this._mainWorkspace = new Workspace('.', manifest, this);
|
|
946
|
-
this._names.set(this._mainWorkspace.name, this._mainWorkspace);
|
|
947
|
-
}
|
|
948
|
-
return this._mainWorkspace;
|
|
1057
|
+
return node;
|
|
949
1058
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
} else if (files.includes('package-lock.json')) {
|
|
960
|
-
this._logger.debug`detected npm in ${this.root}`;
|
|
961
|
-
this._packageManager = 'npm';
|
|
1059
|
+
parse(expr) {
|
|
1060
|
+
const lexer = this._lexer().reset(expr);
|
|
1061
|
+
const tree = {
|
|
1062
|
+
roots: []
|
|
1063
|
+
};
|
|
1064
|
+
while(true){
|
|
1065
|
+
const node = this._nextNode(lexer);
|
|
1066
|
+
if (node) {
|
|
1067
|
+
tree.roots.push(node);
|
|
962
1068
|
} else {
|
|
963
|
-
|
|
964
|
-
this._packageManager = 'npm';
|
|
1069
|
+
break;
|
|
965
1070
|
}
|
|
966
1071
|
}
|
|
967
|
-
return
|
|
1072
|
+
return tree;
|
|
968
1073
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
}
|
|
980
|
-
// Load workspaces
|
|
981
|
-
if (!this._isFullyLoaded) {
|
|
982
|
-
for await (const ws of this.workspaces()){
|
|
983
|
-
if (ws.name === name) {
|
|
984
|
-
return ws;
|
|
985
|
-
}
|
|
1074
|
+
*extractScripts(node) {
|
|
1075
|
+
if ('roots' in node) {
|
|
1076
|
+
for (const child of node.roots){
|
|
1077
|
+
yield* this.extractScripts(child);
|
|
1078
|
+
}
|
|
1079
|
+
} else if (TaskParserService.isTaskNode(node)) {
|
|
1080
|
+
yield node.script;
|
|
1081
|
+
} else {
|
|
1082
|
+
for (const child of node.tasks){
|
|
1083
|
+
yield* this.extractScripts(child);
|
|
986
1084
|
}
|
|
987
|
-
this._isFullyLoaded = true;
|
|
988
1085
|
}
|
|
989
|
-
return null;
|
|
990
1086
|
}
|
|
991
|
-
async
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
if (wks.name !== main.name) yield wks;
|
|
1087
|
+
async buildJob(node, workspace, opts) {
|
|
1088
|
+
if (TaskParserService.isTaskNode(node)) {
|
|
1089
|
+
const job = await workspace.run(node.script, node.args, opts);
|
|
1090
|
+
if (!job) {
|
|
1091
|
+
throw new TaskExpressionError(`Workspace ${workspace.name} have no ${node.script} script`);
|
|
997
1092
|
}
|
|
1093
|
+
return job;
|
|
998
1094
|
} else {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
} catch (error) {
|
|
1013
|
-
if (error.code === 'ENOENT') {
|
|
1014
|
-
continue;
|
|
1015
|
-
}
|
|
1016
|
-
throw error;
|
|
1095
|
+
let flow;
|
|
1096
|
+
if (node.operator === '//') {
|
|
1097
|
+
flow = parallelFlow$({
|
|
1098
|
+
label: 'In parallel'
|
|
1099
|
+
});
|
|
1100
|
+
} else if (node.operator === '||') {
|
|
1101
|
+
flow = fallbackFlow$({
|
|
1102
|
+
label: 'Fallbacks'
|
|
1103
|
+
});
|
|
1104
|
+
} else {
|
|
1105
|
+
if (node.operator === '->' && TaskParserService._sequenceOperatorWarn) {
|
|
1106
|
+
this._logger.warn('Sequence operator -> is deprecated in favor of &&. It will be removed in a next major release.');
|
|
1107
|
+
TaskParserService._sequenceOperatorWarn = true;
|
|
1017
1108
|
}
|
|
1109
|
+
flow = sequenceFlow$({
|
|
1110
|
+
label: 'In sequence'
|
|
1111
|
+
});
|
|
1018
1112
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
}
|
|
1022
|
-
// Properties
|
|
1023
|
-
get root() {
|
|
1024
|
-
return path.resolve(this._root);
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
var _dec, _initProto;
|
|
1029
|
-
_dec = instrument();
|
|
1030
|
-
/**
|
|
1031
|
-
* Helps detecting projects folders
|
|
1032
|
-
*/ class ProjectsRepository {
|
|
1033
|
-
static{
|
|
1034
|
-
({ e: [_initProto] } = _(this, [
|
|
1035
|
-
[
|
|
1036
|
-
_dec,
|
|
1037
|
-
2,
|
|
1038
|
-
"searchProjectRoot"
|
|
1039
|
-
]
|
|
1040
|
-
], []));
|
|
1041
|
-
}
|
|
1042
|
-
// Attributes
|
|
1043
|
-
_cache = (_initProto(this), new Map());
|
|
1044
|
-
_logger = inject$(LOGGER).child(withLabel('projects'));
|
|
1045
|
-
_scurry = inject$(PATH_SCURRY);
|
|
1046
|
-
// Methods
|
|
1047
|
-
async isProjectRoot(dir) {
|
|
1048
|
-
this._logger.debug`testing ${dir}`;
|
|
1049
|
-
const files = await this._scurry.readdir(dir, {
|
|
1050
|
-
withFileTypes: false
|
|
1051
|
-
});
|
|
1052
|
-
return {
|
|
1053
|
-
hasManifest: files.includes(MANIFEST),
|
|
1054
|
-
hasLockFile: LOCK_FILES.some((lock)=>files.includes(lock))
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
async searchProjectRoot(directory) {
|
|
1058
|
-
directory = path.resolve(directory);
|
|
1059
|
-
// Test all ancestors
|
|
1060
|
-
let foundManifest = false;
|
|
1061
|
-
let projectRoot = directory;
|
|
1062
|
-
let dir = directory;
|
|
1063
|
-
let prev = dir;
|
|
1064
|
-
do {
|
|
1065
|
-
// Look for files
|
|
1066
|
-
const { hasManifest, hasLockFile } = await this.isProjectRoot(dir);
|
|
1067
|
-
if (hasManifest) {
|
|
1068
|
-
projectRoot = dir;
|
|
1069
|
-
foundManifest = true;
|
|
1070
|
-
}
|
|
1071
|
-
if (hasLockFile) {
|
|
1072
|
-
break;
|
|
1113
|
+
for (const child of node.tasks){
|
|
1114
|
+
flow.push(await this.buildJob(child, workspace, opts));
|
|
1073
1115
|
}
|
|
1074
|
-
|
|
1075
|
-
dir = path.dirname(dir);
|
|
1076
|
-
}while (prev !== dir);
|
|
1077
|
-
// Log it
|
|
1078
|
-
if (foundManifest) {
|
|
1079
|
-
this._logger.verbose`project root found at ${projectRoot}`;
|
|
1080
|
-
} else {
|
|
1081
|
-
this._logger.verbose`project root not found, keeping ${projectRoot}`;
|
|
1082
|
-
}
|
|
1083
|
-
return projectRoot;
|
|
1084
|
-
}
|
|
1085
|
-
getProject(root, opts) {
|
|
1086
|
-
let project = this._cache.get(root);
|
|
1087
|
-
if (!project) {
|
|
1088
|
-
project = new Project(root, opts);
|
|
1089
|
-
this._cache.set(root, project);
|
|
1116
|
+
return flow;
|
|
1090
1117
|
}
|
|
1091
|
-
return project;
|
|
1092
1118
|
}
|
|
1093
1119
|
}
|
|
1094
|
-
// Constants
|
|
1095
|
-
const MANIFEST = 'package.json';
|
|
1096
|
-
const LOCK_FILES = [
|
|
1097
|
-
'package-lock.json',
|
|
1098
|
-
'yarn.lock'
|
|
1099
|
-
];
|
|
1100
|
-
|
|
1101
|
-
/**
|
|
1102
|
-
* Adds arguments to load a project.
|
|
1103
|
-
*/ function withProject(parser) {
|
|
1104
|
-
return parser.option('project', {
|
|
1105
|
-
alias: 'p',
|
|
1106
|
-
type: 'string',
|
|
1107
|
-
default: '',
|
|
1108
|
-
defaultDescription: 'current working directory',
|
|
1109
|
-
description: 'Project root directory',
|
|
1110
|
-
normalize: true
|
|
1111
|
-
}).option('package-manager', {
|
|
1112
|
-
choices: [
|
|
1113
|
-
'yarn',
|
|
1114
|
-
'npm'
|
|
1115
|
-
],
|
|
1116
|
-
type: 'string',
|
|
1117
|
-
description: 'Force package manager'
|
|
1118
|
-
}).middleware((args)=>startSpan({
|
|
1119
|
-
name: 'project',
|
|
1120
|
-
op: 'cli.middleware'
|
|
1121
|
-
}, async ()=>{
|
|
1122
|
-
const repository = inject$(ProjectsRepository);
|
|
1123
|
-
const directory = path.resolve(inject$(CWD, asyncScope$()), args.project);
|
|
1124
|
-
args.project = await repository.searchProjectRoot(directory);
|
|
1125
|
-
}));
|
|
1126
|
-
}
|
|
1127
|
-
/**
|
|
1128
|
-
* Loads a project, based on arguments.
|
|
1129
|
-
*/ function loadProject(args) {
|
|
1130
|
-
const repository = inject$(ProjectsRepository);
|
|
1131
|
-
return repository.getProject(args.project, {
|
|
1132
|
-
packageManager: args.packageManager
|
|
1133
|
-
});
|
|
1134
|
-
}
|
|
1135
1120
|
|
|
1136
1121
|
function pipeline$() {
|
|
1137
1122
|
const steps = [];
|
|
@@ -1304,17 +1289,22 @@ const command$4 = {
|
|
|
1304
1289
|
async prepare (args) {
|
|
1305
1290
|
const workspace = await loadWorkspace(args);
|
|
1306
1291
|
// Extract arguments
|
|
1307
|
-
|
|
1292
|
+
let rest = args._;
|
|
1308
1293
|
if (rest[0] === 'exec') {
|
|
1309
1294
|
rest.splice(0, 1);
|
|
1310
1295
|
}
|
|
1311
1296
|
// Run script in workspace
|
|
1312
|
-
|
|
1297
|
+
rest = rest.map((arg)=>escapeCommandLineArg(arg.toString()));
|
|
1298
|
+
return await workspace.exec([
|
|
1299
|
+
args.command,
|
|
1300
|
+
...rest
|
|
1301
|
+
].join(' '), {
|
|
1313
1302
|
buildScript: args.buildScript,
|
|
1314
1303
|
buildDeps: args.depsMode
|
|
1315
1304
|
});
|
|
1316
1305
|
},
|
|
1317
1306
|
async execute (args, arg) {
|
|
1307
|
+
const logger = inject$(LOGGER);
|
|
1318
1308
|
const job = arg;
|
|
1319
1309
|
if (job.dependencies().length > 0) {
|
|
1320
1310
|
const dependencies = pipe$(job.dependencies(), collect$(parallelFlow$({
|
|
@@ -1333,17 +1323,14 @@ const command$4 = {
|
|
|
1333
1323
|
return;
|
|
1334
1324
|
}
|
|
1335
1325
|
} else {
|
|
1336
|
-
const logger = inject$(LOGGER);
|
|
1337
1326
|
logger.verbose('No dependency to build');
|
|
1338
1327
|
}
|
|
1339
1328
|
await startSpan({
|
|
1340
1329
|
op: 'subprocess',
|
|
1341
|
-
name:
|
|
1342
|
-
job.cmd,
|
|
1343
|
-
...job.args
|
|
1344
|
-
].join(' ')
|
|
1330
|
+
name: job.cmd
|
|
1345
1331
|
}, async ()=>{
|
|
1346
|
-
|
|
1332
|
+
logger.verbose(`spawn "${job.cmd}"`);
|
|
1333
|
+
const child = spawn(job.cmd, {
|
|
1347
1334
|
stdio: 'inherit',
|
|
1348
1335
|
cwd: job.cwd,
|
|
1349
1336
|
env: {
|
|
@@ -1627,7 +1614,7 @@ const command$1 = {
|
|
|
1627
1614
|
}
|
|
1628
1615
|
};
|
|
1629
1616
|
|
|
1630
|
-
var version = "3.0.0-alpha.
|
|
1617
|
+
var version = "3.0.0-alpha.11";
|
|
1631
1618
|
|
|
1632
1619
|
// Utils
|
|
1633
1620
|
function dynamicImport(filepath) {
|
|
@@ -1802,7 +1789,7 @@ const logFormat = qwrap(chalkTemplateStderr).fun`#?:${qprop('label')}{grey [${q$
|
|
|
1802
1789
|
const logGateway = inject$(LogGateway);
|
|
1803
1790
|
flow$(inject$(LOGGER), filter$((log)=>log.level >= logLevel), logDelay$(), logGateway);
|
|
1804
1791
|
logGateway.connect('console', toStderr(logFormat));
|
|
1805
|
-
}));
|
|
1792
|
+
}), true);
|
|
1806
1793
|
}
|
|
1807
1794
|
// Utils
|
|
1808
1795
|
const VERBOSITY_LEVEL = {
|
|
@@ -1843,47 +1830,19 @@ function command(module) {
|
|
|
1843
1830
|
});
|
|
1844
1831
|
}
|
|
1845
1832
|
|
|
1846
|
-
function
|
|
1847
|
-
if ('prepare' in module) {
|
|
1848
|
-
const prepare = trace(module.prepare, {
|
|
1849
|
-
name: commandName(module),
|
|
1850
|
-
op: 'cli.prepare'
|
|
1851
|
-
});
|
|
1852
|
-
return command({
|
|
1853
|
-
...module,
|
|
1854
|
-
builder (base) {
|
|
1855
|
-
const parser = withPlanMode(base);
|
|
1856
|
-
if (module.builder) {
|
|
1857
|
-
return module.builder(parser);
|
|
1858
|
-
} else {
|
|
1859
|
-
return parser;
|
|
1860
|
-
}
|
|
1861
|
-
},
|
|
1862
|
-
async handler (args) {
|
|
1863
|
-
job$.mutate(await prepare(args) ?? null);
|
|
1864
|
-
}
|
|
1865
|
-
});
|
|
1866
|
-
} else {
|
|
1867
|
-
return command({
|
|
1868
|
-
...module,
|
|
1869
|
-
handler: ()=>undefined
|
|
1870
|
-
});
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
// Utils
|
|
1874
|
-
function withPlanMode(parser) {
|
|
1833
|
+
function withPlan(parser) {
|
|
1875
1834
|
return parser.option('plan', {
|
|
1876
1835
|
type: 'boolean',
|
|
1877
1836
|
default: false,
|
|
1878
1837
|
describe: 'Only prints tasks to be run'
|
|
1879
|
-
}).option('plan-
|
|
1838
|
+
}).option('plan-format', {
|
|
1880
1839
|
type: 'string',
|
|
1881
|
-
desc: 'Plan output
|
|
1840
|
+
desc: 'Plan output format',
|
|
1882
1841
|
choices: [
|
|
1883
1842
|
'json',
|
|
1884
|
-
'
|
|
1843
|
+
'tree'
|
|
1885
1844
|
],
|
|
1886
|
-
default: '
|
|
1845
|
+
default: 'tree'
|
|
1887
1846
|
});
|
|
1888
1847
|
}
|
|
1889
1848
|
|
|
@@ -1899,7 +1858,7 @@ function jobCommandExecute(module) {
|
|
|
1899
1858
|
return command({
|
|
1900
1859
|
...module,
|
|
1901
1860
|
builder (base) {
|
|
1902
|
-
const parser =
|
|
1861
|
+
const parser = withPlan(base);
|
|
1903
1862
|
if (module.builder) {
|
|
1904
1863
|
return module.builder(parser);
|
|
1905
1864
|
} else {
|
|
@@ -1915,8 +1874,21 @@ function jobCommandExecute(module) {
|
|
|
1915
1874
|
return;
|
|
1916
1875
|
}
|
|
1917
1876
|
if (args.plan) {
|
|
1918
|
-
|
|
1919
|
-
|
|
1877
|
+
switch(args.planFormat){
|
|
1878
|
+
case 'json':
|
|
1879
|
+
{
|
|
1880
|
+
const { jobPlanJson } = await traceImport('printPlanJson', ()=>import('./job-plan.json.js'));
|
|
1881
|
+
jobPlanJson(job);
|
|
1882
|
+
break;
|
|
1883
|
+
}
|
|
1884
|
+
case 'tree':
|
|
1885
|
+
default:
|
|
1886
|
+
{
|
|
1887
|
+
const { jobPlan } = await traceImport('printPlan', ()=>import('./job-plan.js'));
|
|
1888
|
+
jobPlan(job);
|
|
1889
|
+
break;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1920
1892
|
} else {
|
|
1921
1893
|
if (execute) {
|
|
1922
1894
|
await execute(args, job);
|
|
@@ -1973,5 +1945,5 @@ void startSpan({
|
|
|
1973
1945
|
}
|
|
1974
1946
|
});
|
|
1975
1947
|
|
|
1976
|
-
export { CWD as C, LOGGER as L, SCHEDULER as S,
|
|
1948
|
+
export { CWD as C, LOGGER as L, SCHEDULER as S, commandName as a, command as b, capitalize as c, ConfigService as d, cliParser as e, command$5 as f, command$4 as g, command$3 as h, command$2 as i, command$1 as j, instrument as k, logFormat as l, printJson as p, trace as t, withPlan as w };
|
|
1977
1949
|
//# sourceMappingURL=main.js.map
|