@visulima/task-runner 1.0.0-alpha.5 → 1.0.0-alpha.6
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/CHANGELOG.md +31 -0
- package/dist/archive.d.ts +38 -0
- package/dist/cache.d.ts +31 -3
- package/dist/chrome-trace.d.ts +53 -0
- package/dist/file-access-tracker.d.ts +7 -1
- package/dist/fingerprint.d.ts +9 -0
- package/dist/incremental-hasher.d.ts +18 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.js +22 -19
- package/dist/life-cycle.d.ts +2 -0
- package/dist/log-reporter.d.ts +34 -0
- package/dist/output-resolver.d.ts +20 -0
- package/dist/packem_shared/{Cache-iAjRMV2d.js → Cache-CWaX_c8U.js} +135 -45
- package/dist/packem_shared/{CompositeLifeCycle-7AtYw1dv.js → CompositeLifeCycle-CSVbRC_5.js} +10 -0
- package/dist/packem_shared/{FileAccessTracker-CrtBAt5D.js → FileAccessTracker-CQ5Ot7Hd.js} +68 -16
- package/dist/packem_shared/{FingerprintManager-Cu-ta9ee.js → FingerprintManager-CV7U4f4f.js} +22 -1
- package/dist/packem_shared/{IncrementalFileHasher-Cm_kJY5V.js → IncrementalFileHasher-BRS76-mb.js} +26 -0
- package/dist/packem_shared/LogReporter-BDt52HLu.js +44 -0
- package/dist/packem_shared/{RemoteCache-BFceSe4a.js → RemoteCache-DSU3lc87.js} +77 -37
- package/dist/packem_shared/{TaskOrchestrator-lLn-PH1m.js → TaskOrchestrator-rf45vW5c.js} +94 -15
- package/dist/packem_shared/{TerminalBuffer-CnPyFgPB.js → TerminalBuffer-qVJvbRQZ.js} +1 -1
- package/dist/packem_shared/{TrackedTaskExecutor-BGUKFE-7.js → TrackedTaskExecutor-CFPpQfXF.js} +1 -1
- package/dist/packem_shared/archive-UQHAnZUa.js +102 -0
- package/dist/packem_shared/{buildForwardDependencyMap-0BJFMMPv.js → buildForwardDependencyMap-DLPgKEto.js} +2 -1
- package/dist/packem_shared/{computeTaskHash-B2SVZqgp.js → computeTaskHash-DYqfrDGq.js} +122 -6
- package/dist/packem_shared/{createTaskGraph-CcsFaSrz.js → createTaskGraph-B7nH0kY_.js} +2 -2
- package/dist/packem_shared/{defaultTaskRunner-BdFTifsh.js → defaultTaskRunner-Cp7jCmIl.js} +28 -6
- package/dist/packem_shared/{extractPackageName-CbVNW-dr.js → extractPackageName-BllKetnz.js} +2 -1
- package/dist/packem_shared/{generateRunSummary-qn-_jKwt.js → generateRunSummary-BE1jnQ3H.js} +19 -1
- package/dist/packem_shared/{parsePartition-C4-P5RjK.js → parsePartition-BfLbHGAx.js} +18 -0
- package/dist/packem_shared/{projectGraphToDot-C8uYeaPo.js → projectGraphToDot-DU1lSe-c.js} +1 -1
- package/dist/packem_shared/resolveOutputs-n6MCKoTe.js +111 -0
- package/dist/packem_shared/{runConcurrentFallback-CGHz_f-Q.js → runConcurrentFallback-BTmgGV1H.js} +1 -1
- package/dist/packem_shared/{runConcurrently-qrkWyzXW.js → runConcurrently-CmfC4r-f.js} +1 -1
- package/dist/packem_shared/toChromeTrace-B2tZoJ-7.js +121 -0
- package/dist/remote-cache.d.ts +45 -0
- package/dist/run-summary.d.ts +26 -4
- package/dist/task-hasher.d.ts +37 -0
- package/dist/task-orchestrator.d.ts +2 -2
- package/dist/types.d.ts +137 -3
- package/index.js +52 -52
- package/package.json +12 -12
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
+
|
|
7
|
+
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
+
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
+
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
+
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
+
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
+
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
+
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Fallback to createRequire
|
|
17
|
+
return __cjs_require(module);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
execFile
|
|
22
|
+
} = __cjs_getBuiltinModule("node:child_process");
|
|
23
|
+
const {
|
|
24
|
+
createReadStream,
|
|
25
|
+
createWriteStream
|
|
26
|
+
} = __cjs_getBuiltinModule("node:fs");
|
|
27
|
+
const {
|
|
28
|
+
rm
|
|
29
|
+
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
30
|
+
const {
|
|
31
|
+
pipeline
|
|
32
|
+
} = __cjs_getBuiltinModule("node:stream/promises");
|
|
33
|
+
const {
|
|
34
|
+
createBrotliDecompress,
|
|
35
|
+
createBrotliCompress,
|
|
36
|
+
constants
|
|
37
|
+
} = __cjs_getBuiltinModule("node:zlib");
|
|
38
|
+
|
|
39
|
+
const BROTLI_COMPRESS_OPTIONS = {
|
|
40
|
+
params: {
|
|
41
|
+
[constants.BROTLI_PARAM_MODE]: constants.BROTLI_MODE_TEXT,
|
|
42
|
+
[constants.BROTLI_PARAM_QUALITY]: 4
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const createTar = (sourceDirectory, outputPath) => new Promise((resolve, reject) => {
|
|
46
|
+
execFile("tar", ["-cf", outputPath, "-C", sourceDirectory, "."], (error) => {
|
|
47
|
+
if (error) {
|
|
48
|
+
reject(error);
|
|
49
|
+
} else {
|
|
50
|
+
resolve();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
const extractTar = (archivePath, destinationDirectory) => new Promise((resolve, reject) => {
|
|
55
|
+
execFile("tar", ["-xf", archivePath, "-C", destinationDirectory], (error) => {
|
|
56
|
+
if (error) {
|
|
57
|
+
reject(error);
|
|
58
|
+
} else {
|
|
59
|
+
resolve();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
const createTarGz = (sourceDirectory, outputPath) => new Promise((resolve, reject) => {
|
|
64
|
+
execFile("tar", ["-czf", outputPath, "-C", sourceDirectory, "."], (error) => {
|
|
65
|
+
if (error) {
|
|
66
|
+
reject(error);
|
|
67
|
+
} else {
|
|
68
|
+
resolve();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
const extractTarGz = (archivePath, destinationDirectory) => new Promise((resolve, reject) => {
|
|
73
|
+
execFile("tar", ["-xzf", archivePath, "-C", destinationDirectory], (error) => {
|
|
74
|
+
if (error) {
|
|
75
|
+
reject(error);
|
|
76
|
+
} else {
|
|
77
|
+
resolve();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
const createTarBrotli = async (sourceDirectory, outputPath) => {
|
|
82
|
+
const tarPath = `${outputPath}.tar`;
|
|
83
|
+
try {
|
|
84
|
+
await createTar(sourceDirectory, tarPath);
|
|
85
|
+
await pipeline(createReadStream(tarPath), createBrotliCompress(BROTLI_COMPRESS_OPTIONS), createWriteStream(outputPath));
|
|
86
|
+
} finally {
|
|
87
|
+
await rm(tarPath, { force: true }).catch(() => {
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const extractTarBrotli = async (archivePath, destinationDirectory) => {
|
|
92
|
+
const tarPath = `${archivePath}.tar`;
|
|
93
|
+
try {
|
|
94
|
+
await pipeline(createReadStream(archivePath), createBrotliDecompress(), createWriteStream(tarPath));
|
|
95
|
+
await extractTar(tarPath, destinationDirectory);
|
|
96
|
+
} finally {
|
|
97
|
+
await rm(tarPath, { force: true }).catch(() => {
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export { extractTarGz as a, createTarGz as b, createTarBrotli as c, extractTarBrotli as e };
|
|
@@ -33,7 +33,8 @@ const findProjectForFile = (filePath, projects) => {
|
|
|
33
33
|
let bestLength = 0;
|
|
34
34
|
for (const [name, config] of Object.entries(projects)) {
|
|
35
35
|
const { root } = config;
|
|
36
|
-
if ((filePath.startsWith(`${root}/`) || filePath === root) &&
|
|
36
|
+
if ((filePath.startsWith(`${root}/`) || filePath === root) && // Prefer the most specific (longest) match
|
|
37
|
+
root.length > bestLength) {
|
|
37
38
|
bestMatch = name;
|
|
38
39
|
bestLength = root.length;
|
|
39
40
|
}
|
|
@@ -21,12 +21,13 @@ const {
|
|
|
21
21
|
execFile
|
|
22
22
|
} = __cjs_getBuiltinModule("node:child_process");
|
|
23
23
|
const {
|
|
24
|
+
stat,
|
|
24
25
|
readFile
|
|
25
26
|
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
26
27
|
import { b as hashStrings, s as sortObjectKeys, c as collectFiles, x as xxh3Hash, e as createXxh3Hasher } from './utils-zO0ZRgtf.js';
|
|
27
28
|
import { join, resolve, relative } from '@visulima/path';
|
|
28
29
|
import { getFrameworkEnvVariables } from './detectFrameworks-CeFzKE6J.js';
|
|
29
|
-
import { LockfileHasher } from './extractPackageName-
|
|
30
|
+
import { LockfileHasher } from './extractPackageName-BllKetnz.js';
|
|
30
31
|
import { loadNativeBindings } from './isNativeAvailable-BpD28A6Z.js';
|
|
31
32
|
|
|
32
33
|
const DEFAULT_GLOBAL_INPUTS = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "tsconfig.base.json", "tsconfig.json", ".env"];
|
|
@@ -73,7 +74,38 @@ const hashRuntimeValue = async (runtime) => {
|
|
|
73
74
|
}
|
|
74
75
|
return hashStrings(runtime, output);
|
|
75
76
|
};
|
|
77
|
+
const SHELL_SPECIAL_VARS = /* @__PURE__ */ new Set(["0", "!", "#", "$", "*", "-", "?", "@", "_"]);
|
|
78
|
+
const extractReferencedEnvVars = (command) => {
|
|
79
|
+
const found = /* @__PURE__ */ new Set();
|
|
80
|
+
const pattern = /\$(?:\{([^}]+)\}|([A-Z_]\w*))/gi;
|
|
81
|
+
let match;
|
|
82
|
+
while ((match = pattern.exec(command)) !== null) {
|
|
83
|
+
const raw = match[1] ?? match[2];
|
|
84
|
+
if (!raw) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const name = raw.split(/[#%:/?+\-=,^]/)[0]?.trim();
|
|
88
|
+
if (!name || SHELL_SPECIAL_VARS.has(name) || /^\d+$/.test(name)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!/^[A-Z_]\w*$/i.test(name)) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
found.add(name);
|
|
95
|
+
}
|
|
96
|
+
return [...found];
|
|
97
|
+
};
|
|
76
98
|
const isFileSetInput = (input) => "fileset" in input;
|
|
99
|
+
const normalizeFileset = (fileset) => {
|
|
100
|
+
if (typeof fileset === "string") {
|
|
101
|
+
return fileset;
|
|
102
|
+
}
|
|
103
|
+
const token = fileset.base === "workspace" ? "{workspaceRoot}" : "{projectRoot}";
|
|
104
|
+
if (fileset.pattern.startsWith("!")) {
|
|
105
|
+
return `!${token}/${fileset.pattern.slice(1)}`;
|
|
106
|
+
}
|
|
107
|
+
return `${token}/${fileset.pattern}`;
|
|
108
|
+
};
|
|
77
109
|
const isRuntimeInput = (input) => "runtime" in input;
|
|
78
110
|
const isEnvironmentInput = (input) => "env" in input;
|
|
79
111
|
const isExternalDependencyInput = (input) => "externalDependencies" in input;
|
|
@@ -90,6 +122,8 @@ class InProcessTaskHasher {
|
|
|
90
122
|
#smartLockfileHashing;
|
|
91
123
|
#lockfileHasher;
|
|
92
124
|
#frameworkInference;
|
|
125
|
+
#autoEnvVars;
|
|
126
|
+
#incrementalHasher;
|
|
93
127
|
#globalHash = void 0;
|
|
94
128
|
constructor(options) {
|
|
95
129
|
this.#workspaceRoot = options.workspaceRoot;
|
|
@@ -103,6 +137,8 @@ class InProcessTaskHasher {
|
|
|
103
137
|
this.#smartLockfileHashing = options.smartLockfileHashing ?? false;
|
|
104
138
|
this.#lockfileHasher = this.#smartLockfileHashing ? new LockfileHasher(options.workspaceRoot) : void 0;
|
|
105
139
|
this.#frameworkInference = options.frameworkInference ?? false;
|
|
140
|
+
this.#autoEnvVars = options.autoEnvVars ?? false;
|
|
141
|
+
this.#incrementalHasher = options.incrementalHasher;
|
|
106
142
|
}
|
|
107
143
|
async hashTask(task) {
|
|
108
144
|
const commandHash = this.#hashCommand(task);
|
|
@@ -117,7 +153,7 @@ class InProcessTaskHasher {
|
|
|
117
153
|
const negationPatterns = this.#collectNegationPatterns(inputs, task.target.project);
|
|
118
154
|
for (const input of inputs) {
|
|
119
155
|
if (isFileSetInput(input)) {
|
|
120
|
-
const fileHashes = await this.#hashFileSet(task, input.fileset, negationPatterns);
|
|
156
|
+
const fileHashes = await this.#hashFileSet(task, normalizeFileset(input.fileset), negationPatterns);
|
|
121
157
|
for (const [filePath, hash] of Object.entries(fileHashes)) {
|
|
122
158
|
nodes[filePath] = hash;
|
|
123
159
|
}
|
|
@@ -135,6 +171,17 @@ class InProcessTaskHasher {
|
|
|
135
171
|
for (const envVariable of this.#envVars) {
|
|
136
172
|
runtime[`env:${envVariable}`] = hashStrings(envVariable, process.env[envVariable] ?? "");
|
|
137
173
|
}
|
|
174
|
+
if (this.#autoEnvVars) {
|
|
175
|
+
const command = this.#resolveCommandText(task);
|
|
176
|
+
if (command) {
|
|
177
|
+
for (const name of extractReferencedEnvVars(command)) {
|
|
178
|
+
const key = `env:${name}`;
|
|
179
|
+
if (runtime[key] === void 0) {
|
|
180
|
+
runtime[key] = hashStrings(name, process.env[name] ?? "");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
138
185
|
const project = this.#projects[task.target.project];
|
|
139
186
|
if (project) {
|
|
140
187
|
if (this.#lockfileHasher) {
|
|
@@ -177,6 +224,21 @@ class InProcessTaskHasher {
|
|
|
177
224
|
hash.update(overridesJson);
|
|
178
225
|
return hash.digest();
|
|
179
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Looks up the command string associated with a task so referenced
|
|
229
|
+
* env vars can be extracted. Checks `task.overrides.command` first
|
|
230
|
+
* (consumers like vis stash the resolved command there), then falls
|
|
231
|
+
* back to the project's target config.
|
|
232
|
+
*/
|
|
233
|
+
#resolveCommandText(task) {
|
|
234
|
+
const override = task.overrides["command"];
|
|
235
|
+
if (typeof override === "string") {
|
|
236
|
+
return override;
|
|
237
|
+
}
|
|
238
|
+
const project = this.#projects[task.target.project];
|
|
239
|
+
const command = project?.targets?.[task.target.target]?.command ?? this.#targetDefaults[task.target.target]?.command;
|
|
240
|
+
return typeof command === "string" ? command : void 0;
|
|
241
|
+
}
|
|
180
242
|
#resolveInputs(task) {
|
|
181
243
|
const project = this.#projects[task.target.project];
|
|
182
244
|
const targetConfig = project?.targets?.[task.target.target];
|
|
@@ -216,7 +278,7 @@ class InProcessTaskHasher {
|
|
|
216
278
|
if (!isFileSetInput(input)) {
|
|
217
279
|
continue;
|
|
218
280
|
}
|
|
219
|
-
const resolved = input.fileset.replace("{projectRoot}", projectRoot).replace("{workspaceRoot}", ".");
|
|
281
|
+
const resolved = normalizeFileset(input.fileset).replace("{projectRoot}", projectRoot).replace("{workspaceRoot}", ".");
|
|
220
282
|
if (resolved.startsWith("!")) {
|
|
221
283
|
patterns.push(
|
|
222
284
|
resolved.slice(1).replace(/\/\*\*\/\*$/, "").replace(/\/\*$/, "")
|
|
@@ -243,13 +305,30 @@ class InProcessTaskHasher {
|
|
|
243
305
|
try {
|
|
244
306
|
if (this.#native) {
|
|
245
307
|
const fileHashes = this.#native.hashFilesInDirectory(absoluteBase, this.#workspaceRoot);
|
|
308
|
+
const incremental = this.#incrementalHasher;
|
|
309
|
+
const recordPromises = [];
|
|
246
310
|
for (const { hash, path } of fileHashes) {
|
|
247
311
|
const absPath = resolve(this.#workspaceRoot, path);
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
312
|
+
if (isExcluded(absPath)) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
result[path] = hash;
|
|
316
|
+
this.#fileHashCache.set(absPath, hash);
|
|
317
|
+
if (incremental) {
|
|
318
|
+
recordPromises.push(
|
|
319
|
+
stat(absPath).then((s) => {
|
|
320
|
+
if (s.isFile()) {
|
|
321
|
+
incremental.recordSnapshot(absPath, hash, s.mtimeMs, s.size);
|
|
322
|
+
}
|
|
323
|
+
return void 0;
|
|
324
|
+
}).catch(() => {
|
|
325
|
+
})
|
|
326
|
+
);
|
|
251
327
|
}
|
|
252
328
|
}
|
|
329
|
+
if (recordPromises.length > 0) {
|
|
330
|
+
await Promise.all(recordPromises);
|
|
331
|
+
}
|
|
253
332
|
return result;
|
|
254
333
|
}
|
|
255
334
|
const files = await collectFiles(absoluteBase, IGNORED_DIRS);
|
|
@@ -272,6 +351,24 @@ class InProcessTaskHasher {
|
|
|
272
351
|
if (cached) {
|
|
273
352
|
return cached;
|
|
274
353
|
}
|
|
354
|
+
if (this.#incrementalHasher) {
|
|
355
|
+
try {
|
|
356
|
+
const fileStat = await stat(filePath);
|
|
357
|
+
if (fileStat.isFile()) {
|
|
358
|
+
const snapshotHit = this.#incrementalHasher.getSnapshotHash(filePath, fileStat.mtimeMs, fileStat.size);
|
|
359
|
+
if (snapshotHit) {
|
|
360
|
+
this.#fileHashCache.set(filePath, snapshotHit);
|
|
361
|
+
return snapshotHit;
|
|
362
|
+
}
|
|
363
|
+
const content = await readFile(filePath);
|
|
364
|
+
const hash = xxh3Hash(content);
|
|
365
|
+
this.#incrementalHasher.recordSnapshot(filePath, hash, fileStat.mtimeMs, fileStat.size);
|
|
366
|
+
this.#fileHashCache.set(filePath, hash);
|
|
367
|
+
return hash;
|
|
368
|
+
}
|
|
369
|
+
} catch {
|
|
370
|
+
}
|
|
371
|
+
}
|
|
275
372
|
try {
|
|
276
373
|
const content = await readFile(filePath);
|
|
277
374
|
const hash = xxh3Hash(content);
|
|
@@ -326,6 +423,25 @@ class InProcessTaskHasher {
|
|
|
326
423
|
this.#globalHash = void 0;
|
|
327
424
|
this.#lockfileHasher?.clearCache();
|
|
328
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Reads `filePath` fresh and returns its content hash, bypassing the
|
|
428
|
+
* in-memory cache used during the initial `hashTask` pass.
|
|
429
|
+
*
|
|
430
|
+
* Used to detect tasks that modify their own tracked inputs: compare
|
|
431
|
+
* a pre-execution hash (from `task.hashDetails.nodes`) against the
|
|
432
|
+
* post-execution result of this method.
|
|
433
|
+
* @param filePath Absolute path to the file.
|
|
434
|
+
* @returns The fresh xxh3 hash, or `undefined` if the file cannot be read.
|
|
435
|
+
*/
|
|
436
|
+
// eslint-disable-next-line class-methods-use-this
|
|
437
|
+
async rehashFile(filePath) {
|
|
438
|
+
try {
|
|
439
|
+
const content = await readFile(filePath);
|
|
440
|
+
return xxh3Hash(content);
|
|
441
|
+
} catch {
|
|
442
|
+
return void 0;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
329
445
|
}
|
|
330
446
|
const computeTaskHash = (hashDetails) => {
|
|
331
447
|
const native = getNativeBindings();
|
|
@@ -78,9 +78,9 @@ const getDependencyProjectTasks = (projectName, targetName, overrides, workspace
|
|
|
78
78
|
const resolveStringDependency = (task, dep, workspace, projectGraph, targetDefaults) => {
|
|
79
79
|
if (dep.startsWith("^")) {
|
|
80
80
|
const targetName = dep.slice(1);
|
|
81
|
-
return getDependencyProjectTasks(task.target.project, targetName,
|
|
81
|
+
return getDependencyProjectTasks(task.target.project, targetName, {}, workspace, projectGraph, targetDefaults);
|
|
82
82
|
}
|
|
83
|
-
return getSameProjectTask(task.target.project, dep,
|
|
83
|
+
return getSameProjectTask(task.target.project, dep, {}, workspace, targetDefaults);
|
|
84
84
|
};
|
|
85
85
|
const resolveConfigDependency = (task, dep, workspace, projectGraph, targetDefaults) => {
|
|
86
86
|
const tasks = [];
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { x as xxh3Hash } from './utils-zO0ZRgtf.js';
|
|
2
|
+
import { Cache } from './Cache-CWaX_c8U.js';
|
|
2
3
|
import { inferFrameworkEnvPatterns } from './detectFrameworks-CeFzKE6J.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { IncrementalFileHasher } from './IncrementalFileHasher-BRS76-mb.js';
|
|
5
|
+
import { RemoteCache } from './RemoteCache-DSU3lc87.js';
|
|
6
|
+
import { InProcessTaskHasher } from './computeTaskHash-DYqfrDGq.js';
|
|
7
|
+
import { TaskOrchestrator } from './TaskOrchestrator-rf45vW5c.js';
|
|
8
|
+
import { TaskScheduler } from './parsePartition-BfLbHGAx.js';
|
|
7
9
|
|
|
10
|
+
const computeGlobalEnvNamespace = (globalEnv) => {
|
|
11
|
+
if (!globalEnv || globalEnv.length === 0) {
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
const payload = [...globalEnv].sort().map((name) => `${name}=${process.env[name] ?? ""}`).join("\n");
|
|
15
|
+
return xxh3Hash(Buffer.from(payload)).slice(0, 16);
|
|
16
|
+
};
|
|
8
17
|
const resolveParallel = (parallel) => {
|
|
9
18
|
if (typeof parallel === "number") {
|
|
10
19
|
return Math.max(1, parallel);
|
|
@@ -16,8 +25,10 @@ const resolveParallel = (parallel) => {
|
|
|
16
25
|
};
|
|
17
26
|
const defaultTaskRunner = async (_tasks, options, context) => {
|
|
18
27
|
const { lifeCycle, projectGraph, taskExecutor, taskGraph, workspaceRoot } = context;
|
|
28
|
+
const cacheNamespace = options.namespaceByGlobalEnv ? computeGlobalEnvNamespace(options.globalEnv) : void 0;
|
|
19
29
|
const cache = new Cache({
|
|
20
30
|
cacheDirectory: options.cacheDirectory,
|
|
31
|
+
cacheNamespace,
|
|
21
32
|
maxCacheAge: options.maxCacheAge,
|
|
22
33
|
maxCacheSize: options.maxCacheSize,
|
|
23
34
|
workspaceRoot
|
|
@@ -28,11 +39,17 @@ const defaultTaskRunner = async (_tasks, options, context) => {
|
|
|
28
39
|
for (const [name, node] of Object.entries(projectGraph.nodes)) {
|
|
29
40
|
projects[name] = node.data;
|
|
30
41
|
}
|
|
42
|
+
const incrementalHasher = options.incrementalFileHashing ? new IncrementalFileHasher({ workspaceRoot }) : void 0;
|
|
43
|
+
if (incrementalHasher) {
|
|
44
|
+
await incrementalHasher.load();
|
|
45
|
+
}
|
|
31
46
|
const taskHasher = new InProcessTaskHasher({
|
|
47
|
+
autoEnvVars: options.autoEnvVars,
|
|
32
48
|
envVars: options.envVars,
|
|
33
49
|
frameworkInference: options.frameworkInference,
|
|
34
50
|
globalEnv: options.globalEnv,
|
|
35
51
|
globalInputs: options.globalInputs,
|
|
52
|
+
incrementalHasher,
|
|
36
53
|
namedInputs: options.namedInputs,
|
|
37
54
|
projects,
|
|
38
55
|
smartLockfileHashing: options.smartLockfileHashing,
|
|
@@ -72,7 +89,12 @@ const defaultTaskRunner = async (_tasks, options, context) => {
|
|
|
72
89
|
untrackedEnvVars: options.untrackedEnvVars,
|
|
73
90
|
workspaceRoot
|
|
74
91
|
});
|
|
75
|
-
|
|
92
|
+
const results = await orchestrator.run();
|
|
93
|
+
if (incrementalHasher) {
|
|
94
|
+
await incrementalHasher.save().catch(() => {
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return results;
|
|
76
98
|
};
|
|
77
99
|
|
|
78
100
|
export { defaultTaskRunner };
|
package/dist/packem_shared/{extractPackageName-CbVNW-dr.js → extractPackageName-BllKetnz.js}
RENAMED
|
@@ -44,7 +44,8 @@ const parseNpmLockfile = (content) => {
|
|
|
44
44
|
continue;
|
|
45
45
|
}
|
|
46
46
|
const name = extractPackageName(path);
|
|
47
|
-
if (name &&
|
|
47
|
+
if (name && // Use the first (top-level) occurrence
|
|
48
|
+
!versions.has(name)) {
|
|
48
49
|
versions.set(name, entry.version);
|
|
49
50
|
}
|
|
50
51
|
}
|
package/dist/packem_shared/{generateRunSummary-qn-_jKwt.js → generateRunSummary-BE1jnQ3H.js}
RENAMED
|
@@ -18,6 +18,7 @@ const __cjs_getBuiltinModule = (module) => {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const {
|
|
21
|
+
readFile,
|
|
21
22
|
mkdir,
|
|
22
23
|
writeFile
|
|
23
24
|
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
@@ -130,5 +131,22 @@ const writeRunSummary = async (summary, workspaceRoot) => {
|
|
|
130
131
|
await writeFile(filePath, JSON.stringify(summary, void 0, 2));
|
|
131
132
|
return filePath;
|
|
132
133
|
};
|
|
134
|
+
const LAST_SUMMARY_FILE = "last-summary.json";
|
|
135
|
+
const getLastRunSummaryPath = (workspaceRoot) => join(workspaceRoot, ".task-runner", LAST_SUMMARY_FILE);
|
|
136
|
+
const writeLastRunSummary = async (summary, workspaceRoot) => {
|
|
137
|
+
const cacheDirectory = join(workspaceRoot, ".task-runner");
|
|
138
|
+
await mkdir(cacheDirectory, { recursive: true });
|
|
139
|
+
const filePath = getLastRunSummaryPath(workspaceRoot);
|
|
140
|
+
await writeFile(filePath, JSON.stringify(summary, void 0, 2));
|
|
141
|
+
return filePath;
|
|
142
|
+
};
|
|
143
|
+
const readLastRunSummary = async (workspaceRoot) => {
|
|
144
|
+
try {
|
|
145
|
+
const content = await readFile(getLastRunSummaryPath(workspaceRoot), "utf8");
|
|
146
|
+
return JSON.parse(content);
|
|
147
|
+
} catch {
|
|
148
|
+
return void 0;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
133
151
|
|
|
134
|
-
export { generateRunSummary, writeRunSummary };
|
|
152
|
+
export { generateRunSummary, getLastRunSummaryPath, readLastRunSummary, writeLastRunSummary, writeRunSummary };
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
const taskPriorityWeight = (priority) => {
|
|
2
|
+
switch (priority) {
|
|
3
|
+
case "high": {
|
|
4
|
+
return 2;
|
|
5
|
+
}
|
|
6
|
+
case "low": {
|
|
7
|
+
return 0;
|
|
8
|
+
}
|
|
9
|
+
default: {
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
1
14
|
const parsePartition = (value) => {
|
|
2
15
|
const raw = value ?? process.env.VIS_PARTITION;
|
|
3
16
|
if (!raw) {
|
|
@@ -124,6 +137,11 @@ class TaskScheduler {
|
|
|
124
137
|
}
|
|
125
138
|
#sortByPriority(tasks) {
|
|
126
139
|
return [...tasks].toSorted((a, b) => {
|
|
140
|
+
const aPriority = taskPriorityWeight(a.priority);
|
|
141
|
+
const bPriority = taskPriorityWeight(b.priority);
|
|
142
|
+
if (aPriority !== bPriority) {
|
|
143
|
+
return bPriority - aPriority;
|
|
144
|
+
}
|
|
127
145
|
const aDeps = this.#dependentCounts.get(a.id) ?? 0;
|
|
128
146
|
const bDeps = this.#dependentCounts.get(b.id) ?? 0;
|
|
129
147
|
if (aDeps !== bDeps) {
|
|
@@ -153,7 +153,7 @@ const toGraphHtml = (taskGraph, options = {}) => {
|
|
|
153
153
|
graphData.edges = graphData.edges.filter((e) => focused.has(e.source) && focused.has(e.target));
|
|
154
154
|
graphData.roots = graphData.roots.filter((r) => focused.has(r));
|
|
155
155
|
}
|
|
156
|
-
return String.raw(_a || (_a = __template(['<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<title>Task Graph</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, -apple-system, sans-serif; background: #1a1a2e; color: #eee; }\n svg { width: 100vw; height: 100vh; }\n .node rect { rx: 6; ry: 6; stroke: #555; stroke-width: 1.5; cursor: pointer; }\n .node text { font-size: 11px; fill: #1a1a2e; font-weight: 600; pointer-events: none; }\n .node:hover rect { stroke: #fff; stroke-width: 2; }\n .edge { stroke: #444; stroke-width: 1.5; fill: none; marker-end: url(#arrow); }\n .label { font-size: 10px; fill: #888; }\n #info { position: fixed; top: 12px; right: 12px; background: #16213e; padding: 12px 16px; border-radius: 8px; font-size: 13px; }\n #info b { color: #e94560; }\n</style>\n</head>\n<body>\n<div id="info">\n <b>', "</b> tasks · <b>", "</b> dependencies · <b>", '</b> roots\n</div>\n<svg id="graph">\n <defs>\n <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">\n <path d="M 0 0 L 10 5 L 0 10 z" fill="#666"/>\n </marker>\n </defs>\n</svg>\n<script>\nconst data = ', ";\nconst svg = document.getElementById('graph');\nconst W = window.innerWidth, H = window.innerHeight;\nconst statusColors = {\n success: '#2ecc71', 'local-cache': '#3498db', 'remote-cache': '#9b59b6',\n failure: '#e74c3c', running: '#f39c12', skipped: '#95a5a6', pending: '#ecf0f1'\n};\nconst projectColors = {};\nconst palette = ['#e94560','#0f3460','#533483','#16c79a','#f39c12','#2ecc71','#3498db','#e67e22','#9b59b6','#1abc9c'];\nlet ci = 0;\ndata.nodes.forEach(n => {\n if (!projectColors[n.project]) projectColors[n.project] = palette[ci++ % palette.length];\n});\n\n// Simple force-directed layout\nconst nodes = data.nodes.map((n, i) => ({\n ...n, x: W/2 + (Math.random()-0.5)*400, y: H/2 + (Math.random()-0.5)*300, vx: 0, vy: 0\n}));\nconst nodeMap = new Map(nodes.map(n => [n.id, n]));\nconst edges = data.edges.map(e => ({ source: nodeMap.get(e.source), target: nodeMap.get(e.target) }));\n\nfunction simulate() {\n for (let iter = 0; iter < 300; iter++) {\n // Repulsion\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i+1; j < nodes.length; j++) {\n let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = 8000 / (d * d);\n nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;\n nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;\n }\n }\n // Attraction (edges)\n edges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = (d - 150) * 0.01;\n e.source.vx += dx/d * f; e.source.vy += dy/d * f;\n e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;\n });\n // Gravity\n nodes.forEach(n => {\n n.vx += (W/2 - n.x) * 0.001;\n n.vy += (H/2 - n.y) * 0.001;\n n.x += n.vx * 0.3; n.y += n.vy * 0.3;\n n.vx *= 0.8; n.vy *= 0.8;\n n.x = Math.max(60, Math.min(W-60, n.x));\n n.y = Math.max(30, Math.min(H-30, n.y));\n });\n }\n}\nsimulate();\n\n// Render\nedges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n const line = document.createElementNS('http://www.w3.org/2000/svg','line');\n line.setAttribute('x1', e.source.x); line.setAttribute('y1', e.source.y);\n line.setAttribute('x2', e.target.x); line.setAttribute('y2', e.target.y);\n line.setAttribute('class','edge');\n svg.appendChild(line);\n});\nnodes.forEach(n => {\n const g = document.createElementNS('http://www.w3.org/2000/svg','g');\n g.setAttribute('class','node');\n g.setAttribute('transform','translate('+(n.x-50)+','+(n.y-14)+')');\n const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');\n rect.setAttribute('width','100'); rect.setAttribute('height','28');\n rect.setAttribute('fill', n.status ? (statusColors[n.status]||'#ecf0f1') : projectColors[n.project]);\n g.appendChild(rect);\n const text = document.createElementNS('http://www.w3.org/2000/svg','text');\n text.setAttribute('x','50'); text.setAttribute('y','18'); text.setAttribute('text-anchor','middle');\n text.textContent = n.id.length > 14 ? n.target : n.id;\n g.appendChild(text);\n g.addEventListener('click', () => {\n const deps = data.edges.filter(e => e.source === n.id).map(e => e.target);\n const rdeps = data.edges.filter(e => e.target === n.id).map(e => e.source);\n alert(n.id + '\n\nDepends on: ' + (deps.join(', ')||'none') + '\nRequired by: ' + (rdeps.join(', ')||'none'));\n });\n svg.appendChild(g);\n});\n<\/script>\n</body>\n</html>"], ['<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<title>Task Graph</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, -apple-system, sans-serif; background: #1a1a2e; color: #eee; }\n svg { width: 100vw; height: 100vh; }\n .node rect { rx: 6; ry: 6; stroke: #555; stroke-width: 1.5; cursor: pointer; }\n .node text { font-size: 11px; fill: #1a1a2e; font-weight: 600; pointer-events: none; }\n .node:hover rect { stroke: #fff; stroke-width: 2; }\n .edge { stroke: #444; stroke-width: 1.5; fill: none; marker-end: url(#arrow); }\n .label { font-size: 10px; fill: #888; }\n #info { position: fixed; top: 12px; right: 12px; background: #16213e; padding: 12px 16px; border-radius: 8px; font-size: 13px; }\n #info b { color: #e94560; }\n</style>\n</head>\n<body>\n<div id="info">\n <b>', "</b> tasks · <b>", "</b> dependencies · <b>", '</b> roots\n</div>\n<svg id="graph">\n <defs>\n <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">\n <path d="M 0 0 L 10 5 L 0 10 z" fill="#666"/>\n </marker>\n </defs>\n</svg>\n<script>\nconst data = ', ";\nconst svg = document.getElementById('graph');\nconst W = window.innerWidth, H = window.innerHeight;\nconst statusColors = {\n success: '#2ecc71', 'local-cache': '#3498db', 'remote-cache': '#9b59b6',\n failure: '#e74c3c', running: '#f39c12', skipped: '#95a5a6', pending: '#ecf0f1'\n};\nconst projectColors = {};\nconst palette = ['#e94560','#0f3460','#533483','#16c79a','#f39c12','#2ecc71','#3498db','#e67e22','#9b59b6','#1abc9c'];\nlet ci = 0;\ndata.nodes.forEach(n => {\n if (!projectColors[n.project]) projectColors[n.project] = palette[ci++ % palette.length];\n});\n\n// Simple force-directed layout\nconst nodes = data.nodes.map((n, i) => ({\n ...n, x: W/2 + (Math.random()-0.5)*400, y: H/2 + (Math.random()-0.5)*300, vx: 0, vy: 0\n}));\nconst nodeMap = new Map(nodes.map(n => [n.id, n]));\nconst edges = data.edges.map(e => ({ source: nodeMap.get(e.source), target: nodeMap.get(e.target) }));\n\nfunction simulate() {\n for (let iter = 0; iter < 300; iter++) {\n // Repulsion\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i+1; j < nodes.length; j++) {\n let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = 8000 / (d * d);\n nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;\n nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;\n }\n }\n // Attraction (edges)\n edges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = (d - 150) * 0.01;\n e.source.vx += dx/d * f; e.source.vy += dy/d * f;\n e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;\n });\n // Gravity\n nodes.forEach(n => {\n n.vx += (W/2 - n.x) * 0.001;\n n.vy += (H/2 - n.y) * 0.001;\n n.x += n.vx * 0.3; n.y += n.vy * 0.3;\n n.vx *= 0.8; n.vy *= 0.8;\n n.x = Math.max(60, Math.min(W-60, n.x));\n n.y = Math.max(30, Math.min(H-30, n.y));\n });\n }\n}\nsimulate();\n\n// Render\nedges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n const line = document.createElementNS('http://www.w3.org/2000/svg','line');\n line.setAttribute('x1', e.source.x); line.setAttribute('y1', e.source.y);\n line.setAttribute('x2', e.target.x); line.setAttribute('y2', e.target.y);\n line.setAttribute('class','edge');\n svg.appendChild(line);\n});\nnodes.forEach(n => {\n const g = document.createElementNS('http://www.w3.org/2000/svg','g');\n g.setAttribute('class','node');\n g.setAttribute('transform','translate('+(n.x-50)+','+(n.y-14)+')');\n const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');\n rect.setAttribute('width','100'); rect.setAttribute('height','28');\n rect.setAttribute('fill', n.status ? (statusColors[n.status]||'#ecf0f1') : projectColors[n.project]);\n g.appendChild(rect);\n const text = document.createElementNS('http://www.w3.org/2000/svg','text');\n text.setAttribute('x','50'); text.setAttribute('y','18'); text.setAttribute('text-anchor','middle');\n text.textContent = n.id.length > 14 ? n.target : n.id;\n g.appendChild(text);\n g.addEventListener('click', () => {\n const deps = data.edges.filter(e => e.source === n.id).map(e => e.target);\n const rdeps = data.edges.filter(e => e.target === n.id).map(e => e.source);\n alert(n.id + '\\n\\nDepends on: ' + (deps.join(', ')||'none') + '\\nRequired by: ' + (rdeps.join(', ')||'none'));\n });\n svg.appendChild(g);\n});\n<\/script>\n</body>\n</html>"])), graphData.nodes.length, graphData.edges.length, graphData.roots.length, JSON.stringify(graphData).replaceAll("</", String.raw`<\/`));
|
|
156
|
+
return String.raw(_a || (_a = __template(['<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<title>Task Graph</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, -apple-system, sans-serif; background: #1a1a2e; color: #eee; }\n svg { width: 100vw; height: 100vh; }\n .node rect { rx: 6; ry: 6; stroke: #555; stroke-width: 1.5; cursor: pointer; }\n .node text { font-size: 11px; fill: #1a1a2e; font-weight: 600; pointer-events: none; }\n .node:hover rect { stroke: #fff; stroke-width: 2; }\n .edge { stroke: #444; stroke-width: 1.5; fill: none; marker-end: url(#arrow); }\n .label { font-size: 10px; fill: #888; }\n #info { position: fixed; top: 12px; right: 12px; background: #16213e; padding: 12px 16px; border-radius: 8px; font-size: 13px; }\n #info b { color: #e94560; }\n</style>\n</head>\n<body>\n<div id="info">\n <b>', "</b> tasks · <b>", "</b> dependencies · <b>", '</b> roots\n</div>\n<svg id="graph">\n <defs>\n <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">\n <path d="M 0 0 L 10 5 L 0 10 z" fill="#666"/>\n </marker>\n </defs>\n</svg>\n<script>\nconst data = ', ";\nconst svg = document.getElementById('graph');\nconst W = window.innerWidth, H = window.innerHeight;\nconst statusColors = {\n success: '#2ecc71', 'local-cache': '#3498db', 'remote-cache': '#9b59b6',\n failure: '#e74c3c', running: '#f39c12', skipped: '#95a5a6', pending: '#ecf0f1'\n};\nconst projectColors = {};\nconst palette = ['#e94560','#0f3460','#533483','#16c79a','#f39c12','#2ecc71','#3498db','#e67e22','#9b59b6','#1abc9c'];\nlet ci = 0;\ndata.nodes.forEach(n => {\n if (!projectColors[n.project]) {\n projectColors[n.project] = palette[ci++ % palette.length];\n }\n});\n\n// Simple force-directed layout\nconst nodes = data.nodes.map((n, i) => ({\n ...n, x: W/2 + (Math.random()-0.5)*400, y: H/2 + (Math.random()-0.5)*300, vx: 0, vy: 0\n}));\nconst nodeMap = new Map(nodes.map(n => [n.id, n]));\nconst edges = data.edges.map(e => ({ source: nodeMap.get(e.source), target: nodeMap.get(e.target) }));\n\nfunction simulate() {\n for (let iter = 0; iter < 300; iter++) {\n // Repulsion\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i+1; j < nodes.length; j++) {\n let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = 8000 / (d * d);\n nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;\n nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;\n }\n }\n // Attraction (edges)\n edges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = (d - 150) * 0.01;\n e.source.vx += dx/d * f; e.source.vy += dy/d * f;\n e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;\n });\n // Gravity\n nodes.forEach(n => {\n n.vx += (W/2 - n.x) * 0.001;\n n.vy += (H/2 - n.y) * 0.001;\n n.x += n.vx * 0.3; n.y += n.vy * 0.3;\n n.vx *= 0.8; n.vy *= 0.8;\n n.x = Math.max(60, Math.min(W-60, n.x));\n n.y = Math.max(30, Math.min(H-30, n.y));\n });\n }\n}\nsimulate();\n\n// Render\nedges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n const line = document.createElementNS('http://www.w3.org/2000/svg','line');\n line.setAttribute('x1', e.source.x); line.setAttribute('y1', e.source.y);\n line.setAttribute('x2', e.target.x); line.setAttribute('y2', e.target.y);\n line.setAttribute('class','edge');\n svg.appendChild(line);\n});\nnodes.forEach(n => {\n const g = document.createElementNS('http://www.w3.org/2000/svg','g');\n g.setAttribute('class','node');\n g.setAttribute('transform','translate('+(n.x-50)+','+(n.y-14)+')');\n const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');\n rect.setAttribute('width','100'); rect.setAttribute('height','28');\n rect.setAttribute('fill', n.status ? (statusColors[n.status]||'#ecf0f1') : projectColors[n.project]);\n g.appendChild(rect);\n const text = document.createElementNS('http://www.w3.org/2000/svg','text');\n text.setAttribute('x','50'); text.setAttribute('y','18'); text.setAttribute('text-anchor','middle');\n text.textContent = n.id.length > 14 ? n.target : n.id;\n g.appendChild(text);\n g.addEventListener('click', () => {\n const deps = data.edges.filter(e => e.source === n.id).map(e => e.target);\n const rdeps = data.edges.filter(e => e.target === n.id).map(e => e.source);\n alert(n.id + '\n\nDepends on: ' + (deps.join(', ')||'none') + '\nRequired by: ' + (rdeps.join(', ')||'none'));\n });\n svg.appendChild(g);\n});\n<\/script>\n</body>\n</html>"], ['<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<title>Task Graph</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, -apple-system, sans-serif; background: #1a1a2e; color: #eee; }\n svg { width: 100vw; height: 100vh; }\n .node rect { rx: 6; ry: 6; stroke: #555; stroke-width: 1.5; cursor: pointer; }\n .node text { font-size: 11px; fill: #1a1a2e; font-weight: 600; pointer-events: none; }\n .node:hover rect { stroke: #fff; stroke-width: 2; }\n .edge { stroke: #444; stroke-width: 1.5; fill: none; marker-end: url(#arrow); }\n .label { font-size: 10px; fill: #888; }\n #info { position: fixed; top: 12px; right: 12px; background: #16213e; padding: 12px 16px; border-radius: 8px; font-size: 13px; }\n #info b { color: #e94560; }\n</style>\n</head>\n<body>\n<div id="info">\n <b>', "</b> tasks · <b>", "</b> dependencies · <b>", '</b> roots\n</div>\n<svg id="graph">\n <defs>\n <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">\n <path d="M 0 0 L 10 5 L 0 10 z" fill="#666"/>\n </marker>\n </defs>\n</svg>\n<script>\nconst data = ', ";\nconst svg = document.getElementById('graph');\nconst W = window.innerWidth, H = window.innerHeight;\nconst statusColors = {\n success: '#2ecc71', 'local-cache': '#3498db', 'remote-cache': '#9b59b6',\n failure: '#e74c3c', running: '#f39c12', skipped: '#95a5a6', pending: '#ecf0f1'\n};\nconst projectColors = {};\nconst palette = ['#e94560','#0f3460','#533483','#16c79a','#f39c12','#2ecc71','#3498db','#e67e22','#9b59b6','#1abc9c'];\nlet ci = 0;\ndata.nodes.forEach(n => {\n if (!projectColors[n.project]) {\n projectColors[n.project] = palette[ci++ % palette.length];\n }\n});\n\n// Simple force-directed layout\nconst nodes = data.nodes.map((n, i) => ({\n ...n, x: W/2 + (Math.random()-0.5)*400, y: H/2 + (Math.random()-0.5)*300, vx: 0, vy: 0\n}));\nconst nodeMap = new Map(nodes.map(n => [n.id, n]));\nconst edges = data.edges.map(e => ({ source: nodeMap.get(e.source), target: nodeMap.get(e.target) }));\n\nfunction simulate() {\n for (let iter = 0; iter < 300; iter++) {\n // Repulsion\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i+1; j < nodes.length; j++) {\n let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = 8000 / (d * d);\n nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;\n nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;\n }\n }\n // Attraction (edges)\n edges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = (d - 150) * 0.01;\n e.source.vx += dx/d * f; e.source.vy += dy/d * f;\n e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;\n });\n // Gravity\n nodes.forEach(n => {\n n.vx += (W/2 - n.x) * 0.001;\n n.vy += (H/2 - n.y) * 0.001;\n n.x += n.vx * 0.3; n.y += n.vy * 0.3;\n n.vx *= 0.8; n.vy *= 0.8;\n n.x = Math.max(60, Math.min(W-60, n.x));\n n.y = Math.max(30, Math.min(H-30, n.y));\n });\n }\n}\nsimulate();\n\n// Render\nedges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n const line = document.createElementNS('http://www.w3.org/2000/svg','line');\n line.setAttribute('x1', e.source.x); line.setAttribute('y1', e.source.y);\n line.setAttribute('x2', e.target.x); line.setAttribute('y2', e.target.y);\n line.setAttribute('class','edge');\n svg.appendChild(line);\n});\nnodes.forEach(n => {\n const g = document.createElementNS('http://www.w3.org/2000/svg','g');\n g.setAttribute('class','node');\n g.setAttribute('transform','translate('+(n.x-50)+','+(n.y-14)+')');\n const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');\n rect.setAttribute('width','100'); rect.setAttribute('height','28');\n rect.setAttribute('fill', n.status ? (statusColors[n.status]||'#ecf0f1') : projectColors[n.project]);\n g.appendChild(rect);\n const text = document.createElementNS('http://www.w3.org/2000/svg','text');\n text.setAttribute('x','50'); text.setAttribute('y','18'); text.setAttribute('text-anchor','middle');\n text.textContent = n.id.length > 14 ? n.target : n.id;\n g.appendChild(text);\n g.addEventListener('click', () => {\n const deps = data.edges.filter(e => e.source === n.id).map(e => e.target);\n const rdeps = data.edges.filter(e => e.target === n.id).map(e => e.source);\n alert(n.id + '\\n\\nDepends on: ' + (deps.join(', ')||'none') + '\\nRequired by: ' + (rdeps.join(', ')||'none'));\n });\n svg.appendChild(g);\n});\n<\/script>\n</body>\n</html>"])), graphData.nodes.length, graphData.edges.length, graphData.roots.length, JSON.stringify(graphData).replaceAll("</", String.raw`<\/`));
|
|
157
157
|
};
|
|
158
158
|
const toGraphAscii = (taskGraph, options = {}) => {
|
|
159
159
|
const { taskStatuses } = options;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
+
|
|
7
|
+
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
+
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
+
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
+
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
+
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
+
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
+
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Fallback to createRequire
|
|
17
|
+
return __cjs_require(module);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
stat,
|
|
22
|
+
glob
|
|
23
|
+
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
24
|
+
const {
|
|
25
|
+
matchesGlob
|
|
26
|
+
} = __cjs_getBuiltinModule("node:path");
|
|
27
|
+
import { isAbsolute, resolve, relative, join } from '@visulima/path';
|
|
28
|
+
|
|
29
|
+
const GLOB_METACHARS = /[*?[\]{}]/;
|
|
30
|
+
const toWorkspaceRelative = (workspaceRoot, absolutePath) => {
|
|
31
|
+
const rel = relative(workspaceRoot, absolutePath);
|
|
32
|
+
if (rel.length === 0 || rel.startsWith("..")) {
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
return rel;
|
|
36
|
+
};
|
|
37
|
+
const resolveOutputs = async (workspaceRoot, outputs, autoWrites) => {
|
|
38
|
+
if (!outputs || outputs.length === 0) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
const positives = [];
|
|
42
|
+
const negatives = [];
|
|
43
|
+
let wantsAuto = false;
|
|
44
|
+
for (const entry of outputs) {
|
|
45
|
+
if (typeof entry !== "string") {
|
|
46
|
+
if (entry.auto) {
|
|
47
|
+
wantsAuto = true;
|
|
48
|
+
}
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (entry.length === 0) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (entry.startsWith("!")) {
|
|
55
|
+
const pattern = entry.slice(1);
|
|
56
|
+
if (pattern.length > 0) {
|
|
57
|
+
negatives.push(pattern);
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
positives.push(entry);
|
|
62
|
+
}
|
|
63
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
64
|
+
if (wantsAuto && autoWrites) {
|
|
65
|
+
for (const write of autoWrites) {
|
|
66
|
+
const absolute = isAbsolute(write) ? write : resolve(workspaceRoot, write);
|
|
67
|
+
const rel = toWorkspaceRelative(workspaceRoot, absolute);
|
|
68
|
+
if (rel) {
|
|
69
|
+
resolved.add(rel);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const patternResults = await Promise.all(positives.map((pattern) => expandPattern(workspaceRoot, pattern)));
|
|
74
|
+
for (const paths of patternResults) {
|
|
75
|
+
for (const rel of paths) {
|
|
76
|
+
resolved.add(rel);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (negatives.length === 0) {
|
|
80
|
+
return [...resolved].sort();
|
|
81
|
+
}
|
|
82
|
+
return [...resolved].filter((candidate) => !negatives.some((pattern) => matchesGlob(candidate, pattern))).sort();
|
|
83
|
+
};
|
|
84
|
+
const expandPattern = async (workspaceRoot, pattern) => {
|
|
85
|
+
if (!GLOB_METACHARS.test(pattern)) {
|
|
86
|
+
const absolute = resolve(workspaceRoot, pattern);
|
|
87
|
+
const rel = toWorkspaceRelative(workspaceRoot, absolute);
|
|
88
|
+
if (!rel) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
await stat(absolute);
|
|
93
|
+
return [rel];
|
|
94
|
+
} catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const paths = [];
|
|
99
|
+
for await (const entry of glob(pattern, { cwd: workspaceRoot, withFileTypes: true })) {
|
|
100
|
+
if (!entry.isFile()) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const rel = toWorkspaceRelative(workspaceRoot, join(entry.parentPath, entry.name));
|
|
104
|
+
if (rel) {
|
|
105
|
+
paths.push(rel);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return paths;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export { resolveOutputs };
|
package/dist/packem_shared/{runConcurrentFallback-CGHz_f-Q.js → runConcurrentFallback-BTmgGV1H.js}
RENAMED
|
@@ -226,7 +226,7 @@ const spawnCommandPty = (index, config, shellPath, onEvent, onComplete) => {
|
|
|
226
226
|
cwd: config.cwd ?? process.cwd(),
|
|
227
227
|
env: Object.fromEntries(
|
|
228
228
|
Object.entries({ ...process.env, ...config.env, FORCE_COLOR: process.env["FORCE_COLOR"] ?? "1", TERM: "xterm-256color" }).filter(
|
|
229
|
-
(entry) => entry[1]
|
|
229
|
+
(entry) => typeof entry[1] === "string"
|
|
230
230
|
)
|
|
231
231
|
),
|
|
232
232
|
name: "xterm-256color",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { runConcurrentFallback } from './runConcurrentFallback-
|
|
1
|
+
import { runConcurrentFallback } from './runConcurrentFallback-BTmgGV1H.js';
|
|
2
2
|
import { detectScriptShell } from './detectScriptShell-CR-xXKA4.js';
|
|
3
3
|
import { logTimings } from './formatTimingTable-3qtCM552.js';
|
|
4
4
|
import { withRestart } from './withRestart-BREjRJa4.js';
|