@visulima/task-runner 0.0.1 → 1.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +170 -36
  4. package/binding.js +204 -0
  5. package/dist/affected.d.ts +48 -0
  6. package/dist/cache.d.ts +103 -0
  7. package/dist/default-task-runner.d.ts +44 -0
  8. package/dist/file-access-tracker.d.ts +53 -0
  9. package/dist/fingerprint.d.ts +45 -0
  10. package/dist/framework-inference.d.ts +35 -0
  11. package/dist/graph-visualizer.d.ts +74 -0
  12. package/dist/incremental-hasher.d.ts +58 -0
  13. package/dist/index.d.ts +34 -0
  14. package/dist/index.js +20 -0
  15. package/dist/life-cycle.d.ts +36 -0
  16. package/dist/lockfile-hasher.d.ts +73 -0
  17. package/dist/native-binding.d.ts +64 -0
  18. package/dist/packem_shared/Cache-IYpTYVUC.js +298 -0
  19. package/dist/packem_shared/CompositeLifeCycle-7AtYw1dv.js +112 -0
  20. package/dist/packem_shared/FileAccessTracker-CrtBAt5D.js +239 -0
  21. package/dist/packem_shared/FingerprintManager-D6Y0erg-.js +227 -0
  22. package/dist/packem_shared/IncrementalFileHasher-Ds3J6dgb.js +151 -0
  23. package/dist/packem_shared/RemoteCache-BDqrnDEi.js +179 -0
  24. package/dist/packem_shared/TaskOrchestrator-BvYs3ONw.js +342 -0
  25. package/dist/packem_shared/TaskScheduler-CJilHDta.js +111 -0
  26. package/dist/packem_shared/TrackedTaskExecutor-BGUKFE-7.js +164 -0
  27. package/dist/packem_shared/collectFiles-ClXHnHhg.js +22 -0
  28. package/dist/packem_shared/computeTaskHash-BoCnnvIJ.js +356 -0
  29. package/dist/packem_shared/createTaskGraph-CcsFaSrz.js +164 -0
  30. package/dist/packem_shared/defaultTaskRunner-CrW4v5Ye.js +79 -0
  31. package/dist/packem_shared/detectFrameworks-CeFzKE6J.js +101 -0
  32. package/dist/packem_shared/extractPackageName-CbVNW-dr.js +189 -0
  33. package/dist/packem_shared/filterAffectedTasks-I-18zPg6.js +135 -0
  34. package/dist/packem_shared/findCycle-DF4_BRdO.js +212 -0
  35. package/dist/packem_shared/generateRunSummary-qn-_jKwt.js +134 -0
  36. package/dist/packem_shared/isNativeAvailable-BWhnZ4ES.js +19 -0
  37. package/dist/packem_shared/projectGraphToDot-VdTjHcVp.js +202 -0
  38. package/dist/packem_shared/utils-zO0ZRgtf.js +390 -0
  39. package/dist/remote-cache.d.ts +55 -0
  40. package/dist/run-summary.d.ts +89 -0
  41. package/dist/task-graph-utils.d.ts +39 -0
  42. package/dist/task-graph.d.ts +22 -0
  43. package/dist/task-hasher.d.ts +67 -0
  44. package/dist/task-orchestrator.d.ts +38 -0
  45. package/dist/task-scheduler.d.ts +18 -0
  46. package/dist/tracked-executor.d.ts +46 -0
  47. package/dist/types.d.ts +385 -0
  48. package/dist/utils.d.ts +39 -0
  49. package/package.json +72 -7
@@ -0,0 +1,356 @@
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
+ readFile
25
+ } = __cjs_getBuiltinModule("node:fs/promises");
26
+ import { b as hashStrings, s as sortObjectKeys, c as collectFiles, x as xxh3Hash, e as createXxh3Hasher } from './utils-zO0ZRgtf.js';
27
+ import { join, resolve, relative } from '@visulima/path';
28
+ import { getFrameworkEnvVariables } from './detectFrameworks-CeFzKE6J.js';
29
+ import { LockfileHasher } from './extractPackageName-CbVNW-dr.js';
30
+ import { loadNativeBindings } from './isNativeAvailable-BWhnZ4ES.js';
31
+
32
+ const DEFAULT_GLOBAL_INPUTS = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "tsconfig.base.json", "tsconfig.json", ".env"];
33
+ const IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "coverage", "dist", "node_modules"]);
34
+ const LOCKFILE_NAMES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]);
35
+ let cachedNative;
36
+ const getNativeBindings = () => {
37
+ if (cachedNative === void 0) {
38
+ cachedNative = loadNativeBindings();
39
+ }
40
+ return cachedNative;
41
+ };
42
+ const hashSortedEntries = (hash, record, prefix) => {
43
+ for (const key of Object.keys(record).toSorted()) {
44
+ {
45
+ hash.update(`${key}\0`);
46
+ }
47
+ hash.update(record[key]);
48
+ }
49
+ };
50
+ const executeRuntimeCommand = (runtime) => {
51
+ if (runtime === "node -v" || runtime === "node --version") {
52
+ return Promise.resolve(process.version);
53
+ }
54
+ const parts = runtime.split(/\s+/);
55
+ const command = parts[0];
56
+ const args = parts.slice(1);
57
+ return new Promise((resolve2) => {
58
+ execFile(command, args, { timeout: 1e4 }, (error, stdout) => {
59
+ if (error) {
60
+ resolve2(`__runtime_error__:${runtime}`);
61
+ } else {
62
+ resolve2(stdout.trim());
63
+ }
64
+ });
65
+ });
66
+ };
67
+ const runtimeCache = /* @__PURE__ */ new Map();
68
+ const hashRuntimeValue = async (runtime) => {
69
+ let output = runtimeCache.get(runtime);
70
+ if (output === void 0) {
71
+ output = await executeRuntimeCommand(runtime);
72
+ runtimeCache.set(runtime, output);
73
+ }
74
+ return hashStrings(runtime, output);
75
+ };
76
+ const isFileSetInput = (input) => "fileset" in input;
77
+ const isRuntimeInput = (input) => "runtime" in input;
78
+ const isEnvironmentInput = (input) => "env" in input;
79
+ const isExternalDependencyInput = (input) => "externalDependencies" in input;
80
+ class InProcessTaskHasher {
81
+ #workspaceRoot;
82
+ #projects;
83
+ #namedInputs;
84
+ #targetDefaults;
85
+ #envVars;
86
+ #globalInputs;
87
+ #globalEnv;
88
+ #fileHashCache = /* @__PURE__ */ new Map();
89
+ #native;
90
+ #smartLockfileHashing;
91
+ #lockfileHasher;
92
+ #frameworkInference;
93
+ #globalHash = void 0;
94
+ constructor(options) {
95
+ this.#workspaceRoot = options.workspaceRoot;
96
+ this.#projects = options.projects;
97
+ this.#namedInputs = options.namedInputs ?? {};
98
+ this.#targetDefaults = options.targetDefaults ?? {};
99
+ this.#envVars = options.envVars ?? [];
100
+ this.#globalInputs = options.globalInputs ?? DEFAULT_GLOBAL_INPUTS;
101
+ this.#globalEnv = options.globalEnv ?? [];
102
+ this.#native = getNativeBindings();
103
+ this.#smartLockfileHashing = options.smartLockfileHashing ?? false;
104
+ this.#lockfileHasher = this.#smartLockfileHashing ? new LockfileHasher(options.workspaceRoot) : void 0;
105
+ this.#frameworkInference = options.frameworkInference ?? false;
106
+ }
107
+ // eslint-disable-next-line sonarjs/cognitive-complexity
108
+ async hashTask(task) {
109
+ const commandHash = this.#hashCommand(task);
110
+ const nodes = {};
111
+ const implicitDeps = {};
112
+ const runtime = {};
113
+ const globalHash = await this.#computeGlobalHash();
114
+ if (globalHash) {
115
+ implicitDeps["__global__"] = globalHash;
116
+ }
117
+ const inputs = this.#resolveInputs(task);
118
+ const negationPatterns = this.#collectNegationPatterns(inputs, task.target.project);
119
+ for (const input of inputs) {
120
+ if (isFileSetInput(input)) {
121
+ const fileHashes = await this.#hashFileSet(task, input.fileset, negationPatterns);
122
+ for (const [filePath, hash] of Object.entries(fileHashes)) {
123
+ nodes[filePath] = hash;
124
+ }
125
+ } else if (isRuntimeInput(input)) {
126
+ runtime[input.runtime] = await hashRuntimeValue(input.runtime);
127
+ } else if (isEnvironmentInput(input)) {
128
+ runtime[`env:${input.env}`] = hashStrings(input.env, process.env[input.env] ?? "");
129
+ } else if (isExternalDependencyInput(input)) {
130
+ const depHashes = await Promise.all(input.externalDependencies.map(async (dep) => [dep, await this.#hashExternalDependency(dep)]));
131
+ for (const [dep, hash] of depHashes) {
132
+ implicitDeps[dep] = hash;
133
+ }
134
+ }
135
+ }
136
+ for (const envVariable of this.#envVars) {
137
+ runtime[`env:${envVariable}`] = hashStrings(envVariable, process.env[envVariable] ?? "");
138
+ }
139
+ const project = this.#projects[task.target.project];
140
+ if (project) {
141
+ if (this.#lockfileHasher) {
142
+ const lockfileHash = await this.#lockfileHasher.hashForPackage(join(project.root, "package.json"));
143
+ if (lockfileHash) {
144
+ implicitDeps["__lockfile__"] = lockfileHash.hash;
145
+ }
146
+ }
147
+ if (this.#frameworkInference) {
148
+ const packageJsonPath = resolve(this.#workspaceRoot, project.root, "package.json");
149
+ const frameworkEnvVariables = await getFrameworkEnvVariables(packageJsonPath);
150
+ for (const envName of Object.keys(frameworkEnvVariables)) {
151
+ runtime[`framework-env:${envName}`] = hashStrings(envName, process.env[envName] ?? "");
152
+ }
153
+ }
154
+ }
155
+ return {
156
+ command: commandHash,
157
+ implicitDeps: Object.keys(implicitDeps).length > 0 ? implicitDeps : void 0,
158
+ nodes,
159
+ runtime: Object.keys(runtime).length > 0 ? runtime : void 0
160
+ };
161
+ }
162
+ /**
163
+ * Hashes the command identity for a task using xxh3-128.
164
+ * Uses native Rust implementation when available, otherwise pure TS xxh3-ts.
165
+ * Both produce identical xxh3-128 output.
166
+ */
167
+ #hashCommand(task) {
168
+ const overridesJson = JSON.stringify(sortObjectKeys(task.overrides));
169
+ if (this.#native) {
170
+ return this.#native.hashCommand(task.target.project, task.target.target, task.target.configuration ?? void 0, overridesJson);
171
+ }
172
+ const hash = createXxh3Hasher();
173
+ hash.update(task.target.project);
174
+ hash.update(task.target.target);
175
+ if (task.target.configuration) {
176
+ hash.update(task.target.configuration);
177
+ }
178
+ hash.update(overridesJson);
179
+ return hash.digest();
180
+ }
181
+ #resolveInputs(task) {
182
+ const project = this.#projects[task.target.project];
183
+ const targetConfig = project?.targets?.[task.target.target];
184
+ const defaultConfig = this.#targetDefaults[task.target.target];
185
+ const rawInputs = targetConfig?.inputs ?? defaultConfig?.inputs;
186
+ if (!rawInputs) {
187
+ return [{ fileset: "{projectRoot}/**/*" }];
188
+ }
189
+ return this.#expandInputs(rawInputs, task.target.project);
190
+ }
191
+ #expandInputs(inputs, projectName) {
192
+ const result = [];
193
+ const seen = /* @__PURE__ */ new Set();
194
+ for (const input of inputs) {
195
+ if (typeof input === "string") {
196
+ if (input.startsWith("{") || input.startsWith("!{")) {
197
+ result.push({ fileset: input });
198
+ } else if (input.startsWith("^")) {
199
+ continue;
200
+ } else if (this.#namedInputs[input] && !seen.has(input)) {
201
+ seen.add(input);
202
+ result.push(...this.#expandInputs(this.#namedInputs[input], projectName));
203
+ } else {
204
+ result.push({ fileset: input });
205
+ }
206
+ } else {
207
+ result.push(input);
208
+ }
209
+ }
210
+ return result;
211
+ }
212
+ #collectNegationPatterns(inputs, projectName) {
213
+ const project = this.#projects[projectName];
214
+ const projectRoot = project?.root ?? "";
215
+ const patterns = [];
216
+ for (const input of inputs) {
217
+ if (!isFileSetInput(input)) {
218
+ continue;
219
+ }
220
+ const resolved = input.fileset.replace("{projectRoot}", projectRoot).replace("{workspaceRoot}", ".");
221
+ if (resolved.startsWith("!")) {
222
+ patterns.push(
223
+ resolved.slice(1).replace(/\/\*\*\/\*$/, "").replace(/\/\*$/, "")
224
+ );
225
+ }
226
+ }
227
+ return patterns;
228
+ }
229
+ /**
230
+ * Hashes all files matching a fileset pattern.
231
+ * Uses native Rust parallel hashing (via rayon) when available.
232
+ */
233
+ async #hashFileSet(task, pattern, negationPatterns = []) {
234
+ const project = this.#projects[task.target.project];
235
+ const projectRoot = project?.root ?? "";
236
+ const resolvedPattern = pattern.replace("{projectRoot}", projectRoot).replace("{workspaceRoot}", ".");
237
+ if (resolvedPattern.startsWith("!")) {
238
+ return {};
239
+ }
240
+ const absoluteBase = resolve(this.#workspaceRoot, resolvedPattern.replace(/\/\*\*\/\*$/, "").replace(/\/\*$/, ""));
241
+ const absoluteNegations = negationPatterns.map((p) => resolve(this.#workspaceRoot, p));
242
+ const result = {};
243
+ const isExcluded = (filePath) => absoluteNegations.some((neg) => filePath.startsWith(`${neg}/`) || filePath === neg);
244
+ try {
245
+ if (this.#native) {
246
+ const fileHashes = this.#native.hashFilesInDirectory(absoluteBase, this.#workspaceRoot);
247
+ for (const { hash, path } of fileHashes) {
248
+ const absPath = resolve(this.#workspaceRoot, path);
249
+ if (!isExcluded(absPath)) {
250
+ result[path] = hash;
251
+ this.#fileHashCache.set(absPath, hash);
252
+ }
253
+ }
254
+ return result;
255
+ }
256
+ const files = await collectFiles(absoluteBase, IGNORED_DIRS);
257
+ const hashPromises = files.map(async (filePath) => {
258
+ if (isExcluded(filePath)) {
259
+ return;
260
+ }
261
+ const hash = await this.#hashFile(filePath);
262
+ if (hash) {
263
+ result[relative(this.#workspaceRoot, filePath)] = hash;
264
+ }
265
+ });
266
+ await Promise.all(hashPromises);
267
+ } catch {
268
+ }
269
+ return result;
270
+ }
271
+ async #hashFile(filePath) {
272
+ const cached = this.#fileHashCache.get(filePath);
273
+ if (cached) {
274
+ return cached;
275
+ }
276
+ try {
277
+ const content = await readFile(filePath);
278
+ const hash = xxh3Hash(content);
279
+ this.#fileHashCache.set(filePath, hash);
280
+ return hash;
281
+ } catch {
282
+ return void 0;
283
+ }
284
+ }
285
+ async #hashExternalDependency(depName) {
286
+ try {
287
+ const packageJsonPath = join(this.#workspaceRoot, "node_modules", depName, "package.json");
288
+ const packageJsonContent = await readFile(packageJsonPath, "utf8");
289
+ const packageJson = JSON.parse(packageJsonContent);
290
+ return hashStrings(depName, packageJson.version ?? "unknown");
291
+ } catch {
292
+ return hashStrings(depName, "not-installed");
293
+ }
294
+ }
295
+ /**
296
+ * Computes a combined hash of all global inputs and global env vars.
297
+ * Cached after first computation.
298
+ */
299
+ async #computeGlobalHash() {
300
+ if (this.#globalHash !== void 0) {
301
+ return this.#globalHash;
302
+ }
303
+ const hash = createXxh3Hasher();
304
+ let hasContent = false;
305
+ const globalFileHashes = await Promise.all(
306
+ this.#globalInputs.filter((input) => !(this.#smartLockfileHashing && LOCKFILE_NAMES.has(input))).map(async (globalInput) => {
307
+ const fileHash = await this.#hashFile(join(this.#workspaceRoot, globalInput));
308
+ return fileHash ? { hash: fileHash, name: globalInput } : void 0;
309
+ })
310
+ );
311
+ for (const entry of globalFileHashes) {
312
+ if (entry) {
313
+ hash.update(entry.name);
314
+ hash.update(entry.hash);
315
+ hasContent = true;
316
+ }
317
+ }
318
+ for (const envName of this.#globalEnv) {
319
+ hash.update(`globalEnv:${envName}=${process.env[envName] ?? ""}`);
320
+ hasContent = true;
321
+ }
322
+ this.#globalHash = hasContent ? hash.digest() : "";
323
+ return this.#globalHash || void 0;
324
+ }
325
+ clearCache() {
326
+ this.#fileHashCache.clear();
327
+ this.#globalHash = void 0;
328
+ this.#lockfileHasher?.clearCache();
329
+ }
330
+ }
331
+ const computeTaskHash = (hashDetails) => {
332
+ const native = getNativeBindings();
333
+ if (native) {
334
+ const nodes = Object.keys(hashDetails.nodes).toSorted().map((key) => [key, hashDetails.nodes[key]]);
335
+ const implicitDeps = hashDetails.implicitDeps ? Object.keys(hashDetails.implicitDeps).toSorted().map((key) => [key, hashDetails.implicitDeps[key]]) : void 0;
336
+ const runtime = hashDetails.runtime ? Object.keys(hashDetails.runtime).toSorted().map((key) => [key, hashDetails.runtime[key]]) : void 0;
337
+ return native.computeTaskHash({
338
+ command: hashDetails.command,
339
+ implicit_deps: implicitDeps,
340
+ nodes,
341
+ runtime
342
+ });
343
+ }
344
+ const hash = createXxh3Hasher();
345
+ hash.update(hashDetails.command);
346
+ hashSortedEntries(hash, hashDetails.nodes);
347
+ if (hashDetails.implicitDeps) {
348
+ hashSortedEntries(hash, hashDetails.implicitDeps);
349
+ }
350
+ if (hashDetails.runtime) {
351
+ hashSortedEntries(hash, hashDetails.runtime);
352
+ }
353
+ return hash.digest();
354
+ };
355
+
356
+ export { InProcessTaskHasher, computeTaskHash };
@@ -0,0 +1,164 @@
1
+ const getTaskId = (target) => {
2
+ const parts = [target.project, target.target];
3
+ if (target.configuration) {
4
+ parts.push(target.configuration);
5
+ }
6
+ return parts.join(":");
7
+ };
8
+ const parseTaskId = (taskId) => {
9
+ const parts = taskId.split(":");
10
+ if (parts.length < 2) {
11
+ throw new Error(`Invalid task ID: ${taskId}`);
12
+ }
13
+ return {
14
+ configuration: parts[2],
15
+ project: parts[0],
16
+ target: parts[1]
17
+ };
18
+ };
19
+ const getTaskOutputs = (projectName, targetName, workspace, targetDefaults) => {
20
+ const project = workspace.projects[projectName];
21
+ const targetConfig = project?.targets?.[targetName];
22
+ const defaultConfig = targetDefaults?.[targetName];
23
+ const outputs = targetConfig?.outputs ?? defaultConfig?.outputs ?? [];
24
+ return outputs.map((output) => output.replace("{projectRoot}", project?.root ?? "").replace("{projectName}", projectName));
25
+ };
26
+ const getSameProjectTask = (projectName, targetName, overrides, workspace, targetDefaults) => {
27
+ const project = workspace.projects[projectName];
28
+ if (!project) {
29
+ return [];
30
+ }
31
+ const hasTarget = project.targets?.[targetName] !== void 0 || targetDefaults?.[targetName] !== void 0;
32
+ if (!hasTarget) {
33
+ return [];
34
+ }
35
+ const target = {
36
+ project: projectName,
37
+ target: targetName
38
+ };
39
+ return [
40
+ {
41
+ cache: project.targets?.[targetName]?.cache ?? targetDefaults?.[targetName]?.cache,
42
+ id: getTaskId(target),
43
+ outputs: getTaskOutputs(projectName, targetName, workspace, targetDefaults),
44
+ overrides,
45
+ parallelism: project.targets?.[targetName]?.parallelism ?? targetDefaults?.[targetName]?.parallelism,
46
+ projectRoot: project.root,
47
+ target
48
+ }
49
+ ];
50
+ };
51
+ const getDependencyProjectTasks = (projectName, targetName, overrides, workspace, projectGraph, targetDefaults) => {
52
+ const tasks = [];
53
+ const deps = projectGraph.dependencies[projectName] ?? [];
54
+ for (const dep of deps) {
55
+ const depProject = workspace.projects[dep.target];
56
+ if (!depProject) {
57
+ continue;
58
+ }
59
+ const hasTarget = depProject.targets?.[targetName] !== void 0 || targetDefaults?.[targetName] !== void 0;
60
+ if (hasTarget) {
61
+ const target = {
62
+ project: dep.target,
63
+ target: targetName
64
+ };
65
+ tasks.push({
66
+ cache: depProject.targets?.[targetName]?.cache ?? targetDefaults?.[targetName]?.cache,
67
+ id: getTaskId(target),
68
+ outputs: getTaskOutputs(dep.target, targetName, workspace, targetDefaults),
69
+ overrides,
70
+ parallelism: depProject.targets?.[targetName]?.parallelism ?? targetDefaults?.[targetName]?.parallelism,
71
+ projectRoot: depProject.root,
72
+ target
73
+ });
74
+ }
75
+ }
76
+ return tasks;
77
+ };
78
+ const resolveStringDependency = (task, dep, workspace, projectGraph, targetDefaults) => {
79
+ if (dep.startsWith("^")) {
80
+ const targetName = dep.slice(1);
81
+ return getDependencyProjectTasks(task.target.project, targetName, task.overrides, workspace, projectGraph, targetDefaults);
82
+ }
83
+ return getSameProjectTask(task.target.project, dep, task.overrides, workspace, targetDefaults);
84
+ };
85
+ const resolveConfigDependency = (task, dep, workspace, projectGraph, targetDefaults) => {
86
+ const tasks = [];
87
+ if (dep.dependencies) {
88
+ tasks.push(
89
+ ...getDependencyProjectTasks(
90
+ task.target.project,
91
+ dep.target,
92
+ dep.params === "forward" ? task.overrides : {},
93
+ workspace,
94
+ projectGraph,
95
+ targetDefaults
96
+ )
97
+ );
98
+ } else if (dep.projects) {
99
+ const projects = Array.isArray(dep.projects) ? dep.projects : [dep.projects];
100
+ for (const projectName of projects) {
101
+ tasks.push(...getSameProjectTask(projectName, dep.target, dep.params === "forward" ? task.overrides : {}, workspace, targetDefaults));
102
+ }
103
+ } else {
104
+ tasks.push(...getSameProjectTask(task.target.project, dep.target, dep.params === "forward" ? task.overrides : {}, workspace, targetDefaults));
105
+ }
106
+ return tasks;
107
+ };
108
+ const resolveDependency = (task, dep, workspace, projectGraph, targetDefaults) => {
109
+ if (typeof dep === "string") {
110
+ return resolveStringDependency(task, dep, workspace, projectGraph, targetDefaults);
111
+ }
112
+ return resolveConfigDependency(task, dep, workspace, projectGraph, targetDefaults);
113
+ };
114
+ const resolveTaskDependencies = (task, options) => {
115
+ const { projectGraph, targetDefaults, workspace } = options;
116
+ const project = workspace.projects[task.target.project];
117
+ if (!project) {
118
+ return [];
119
+ }
120
+ const targetConfig = project.targets?.[task.target.target];
121
+ const defaultConfig = targetDefaults?.[task.target.target];
122
+ const dependsOn = targetConfig?.dependsOn ?? defaultConfig?.dependsOn ?? [];
123
+ const depTasks = [];
124
+ for (const dep of dependsOn) {
125
+ const resolved = resolveDependency(task, dep, workspace, projectGraph, targetDefaults);
126
+ depTasks.push(...resolved);
127
+ }
128
+ return depTasks;
129
+ };
130
+ const createTaskGraph = (initialTasks, options) => {
131
+ const tasks = {};
132
+ const dependencies = {};
133
+ const visited = /* @__PURE__ */ new Set();
134
+ const queue = [...initialTasks];
135
+ while (queue.length > 0) {
136
+ const task = queue.shift();
137
+ if (!task) {
138
+ break;
139
+ }
140
+ if (visited.has(task.id)) {
141
+ continue;
142
+ }
143
+ visited.add(task.id);
144
+ tasks[task.id] = task;
145
+ dependencies[task.id] = [];
146
+ const deps = resolveTaskDependencies(task, options);
147
+ for (const depTask of deps) {
148
+ dependencies[task.id]?.push(depTask.id);
149
+ if (!visited.has(depTask.id)) {
150
+ queue.push(depTask);
151
+ }
152
+ }
153
+ }
154
+ const allDeps = /* @__PURE__ */ new Set();
155
+ for (const deps of Object.values(dependencies)) {
156
+ for (const dep of deps) {
157
+ allDeps.add(dep);
158
+ }
159
+ }
160
+ const roots = Object.keys(tasks).filter((taskId) => !allDeps.has(taskId));
161
+ return { dependencies, roots, tasks };
162
+ };
163
+
164
+ export { createTaskGraph, getTaskId, parseTaskId };
@@ -0,0 +1,79 @@
1
+ import { Cache } from './Cache-IYpTYVUC.js';
2
+ import { inferFrameworkEnvPatterns } from './detectFrameworks-CeFzKE6J.js';
3
+ import { EmptyLifeCycle } from './CompositeLifeCycle-7AtYw1dv.js';
4
+ import { RemoteCache } from './RemoteCache-BDqrnDEi.js';
5
+ import { InProcessTaskHasher } from './computeTaskHash-BoCnnvIJ.js';
6
+ import { TaskOrchestrator } from './TaskOrchestrator-BvYs3ONw.js';
7
+ import { TaskScheduler } from './TaskScheduler-CJilHDta.js';
8
+
9
+ const resolveParallel = (parallel) => {
10
+ if (typeof parallel === "number") {
11
+ return Math.max(1, parallel);
12
+ }
13
+ if (parallel === false) {
14
+ return 1;
15
+ }
16
+ return 3;
17
+ };
18
+ const defaultTaskRunner = async (_tasks, options, context) => {
19
+ const { lifeCycle = new EmptyLifeCycle(), projectGraph, taskExecutor, taskGraph, workspaceRoot } = context;
20
+ const cache = new Cache({
21
+ cacheDirectory: options.cacheDirectory,
22
+ maxCacheAge: options.maxCacheAge,
23
+ maxCacheSize: options.maxCacheSize,
24
+ workspaceRoot
25
+ });
26
+ cache.removeOldEntries().catch(() => {
27
+ });
28
+ const projects = {};
29
+ for (const [name, node] of Object.entries(projectGraph.nodes)) {
30
+ projects[name] = node.data;
31
+ }
32
+ const taskHasher = new InProcessTaskHasher({
33
+ envVars: options.envVars,
34
+ frameworkInference: options.frameworkInference,
35
+ globalEnv: options.globalEnv,
36
+ globalInputs: options.globalInputs,
37
+ namedInputs: options.namedInputs,
38
+ projects,
39
+ smartLockfileHashing: options.smartLockfileHashing,
40
+ targetDefaults: options.targetDefaults,
41
+ workspaceRoot
42
+ });
43
+ const maxParallel = resolveParallel(options.parallel);
44
+ const scheduler = new TaskScheduler(taskGraph, projectGraph, maxParallel);
45
+ const resolveCommand = (task) => {
46
+ const project = projectGraph.nodes[task.target.project];
47
+ const targetConfig = project?.data.targets?.[task.target.target];
48
+ const defaultConfig = options.targetDefaults?.[task.target.target];
49
+ return targetConfig?.command ?? defaultConfig?.command;
50
+ };
51
+ const remoteCache = options.remoteCache ? new RemoteCache(options.remoteCache) : void 0;
52
+ let fingerprintEnvPatterns = options.fingerprintEnvPatterns ?? [];
53
+ if (options.frameworkInference && options.autoFingerprint) {
54
+ const inferredPatterns = await inferFrameworkEnvPatterns(workspaceRoot, projects);
55
+ fingerprintEnvPatterns = [.../* @__PURE__ */ new Set([...fingerprintEnvPatterns, ...inferredPatterns])];
56
+ }
57
+ const orchestrator = new TaskOrchestrator({
58
+ autoFingerprint: options.autoFingerprint,
59
+ cache,
60
+ cacheDiagnostics: options.cacheDiagnostics,
61
+ captureOutput: true,
62
+ dryRun: options.dryRun,
63
+ fingerprintEnvPatterns,
64
+ lifeCycle,
65
+ remoteCache,
66
+ resolveCommand: options.autoFingerprint ? resolveCommand : void 0,
67
+ scheduler,
68
+ skipCache: options.skipNxCache,
69
+ summarize: options.summarize,
70
+ taskExecutor,
71
+ taskGraph,
72
+ taskHasher,
73
+ untrackedEnvVars: options.untrackedEnvVars,
74
+ workspaceRoot
75
+ });
76
+ return orchestrator.run();
77
+ };
78
+
79
+ export { defaultTaskRunner };
@@ -0,0 +1,101 @@
1
+ import { join } from '@visulima/path';
2
+ import { r as readPackageDeps } from './utils-zO0ZRgtf.js';
3
+
4
+ const FRAMEWORK_DEFINITIONS = [
5
+ {
6
+ envPrefixes: ["NEXT_PUBLIC_"],
7
+ name: "Next.js",
8
+ packages: ["next"]
9
+ },
10
+ {
11
+ envPrefixes: ["VITE_"],
12
+ name: "Vite",
13
+ packages: ["vite"]
14
+ },
15
+ {
16
+ envPrefixes: ["REACT_APP_"],
17
+ name: "Create React App",
18
+ packages: ["react-scripts"]
19
+ },
20
+ {
21
+ envPrefixes: ["GATSBY_"],
22
+ name: "Gatsby",
23
+ packages: ["gatsby"]
24
+ },
25
+ {
26
+ envPrefixes: ["NUXT_PUBLIC_"],
27
+ name: "Nuxt",
28
+ packages: ["nuxt", "nuxt3"]
29
+ },
30
+ {
31
+ envPrefixes: ["EXPO_PUBLIC_"],
32
+ name: "Expo",
33
+ packages: ["expo"]
34
+ },
35
+ {
36
+ envPrefixes: ["REMIX_PUBLIC_"],
37
+ name: "Remix",
38
+ packages: ["@remix-run/react", "@remix-run/node"]
39
+ },
40
+ {
41
+ envPrefixes: ["PUBLIC_"],
42
+ name: "Astro",
43
+ packages: ["astro"]
44
+ },
45
+ {
46
+ envPrefixes: ["PUBLIC_"],
47
+ name: "SvelteKit",
48
+ packages: ["@sveltejs/kit"]
49
+ },
50
+ {
51
+ envPrefixes: ["VITE_"],
52
+ name: "Solid Start",
53
+ packages: ["@solidjs/start", "solid-start"]
54
+ }
55
+ ];
56
+ const detectFrameworks = async (packageJsonPath) => {
57
+ const allDeps = await readPackageDeps(packageJsonPath, { optional: false, peer: false });
58
+ if (!allDeps) {
59
+ return [];
60
+ }
61
+ const detected = [];
62
+ for (const framework of FRAMEWORK_DEFINITIONS) {
63
+ if (framework.packages.some((pkg) => allDeps.has(pkg))) {
64
+ detected.push({
65
+ envPrefixes: framework.envPrefixes,
66
+ name: framework.name
67
+ });
68
+ }
69
+ }
70
+ return detected;
71
+ };
72
+ const inferFrameworkEnvPatterns = async (workspaceRoot, projects) => {
73
+ const allPrefixes = /* @__PURE__ */ new Set();
74
+ const detectionPromises = Object.values(projects).map(async (project) => {
75
+ const packageJsonPath = join(workspaceRoot, project.root, "package.json");
76
+ const frameworks = await detectFrameworks(packageJsonPath);
77
+ for (const fw of frameworks) {
78
+ for (const prefix of fw.envPrefixes) {
79
+ allPrefixes.add(prefix);
80
+ }
81
+ }
82
+ });
83
+ await Promise.all(detectionPromises);
84
+ return [...allPrefixes].toSorted().map((prefix) => `${prefix}*`);
85
+ };
86
+ const getFrameworkEnvVariables = async (packageJsonPath, env = process.env) => {
87
+ const frameworks = await detectFrameworks(packageJsonPath);
88
+ const result = {};
89
+ for (const framework of frameworks) {
90
+ for (const prefix of framework.envPrefixes) {
91
+ for (const [key, value] of Object.entries(env)) {
92
+ if (key.startsWith(prefix) && value !== void 0) {
93
+ result[key] = value;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ return result;
99
+ };
100
+
101
+ export { detectFrameworks, getFrameworkEnvVariables, inferFrameworkEnvPatterns };