@vzn/vx 0.0.288 → 0.0.289
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.289",
|
|
4
4
|
"description": "A fast, correct task runner and build cache for JavaScript monorepos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"optionalDependencies": {
|
|
20
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
21
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
22
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
23
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
20
|
+
"@vzn/vx-linux-x64": "0.0.289",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.289",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.289",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.289"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
import { existsSync, readFileSync } from 'node:fs'
|
|
30
30
|
import path from 'node:path'
|
|
31
31
|
import { taskNameProblem } from './config-schema.js'
|
|
32
|
+
import { buildPackageGraph } from './package-graph.js'
|
|
32
33
|
import type { ProjectMeta } from './workspace.js'
|
|
33
34
|
import {
|
|
34
35
|
foldScriptHooks,
|
|
@@ -377,6 +378,7 @@ export function migrateScripts(
|
|
|
377
378
|
projects.push({ name: meta.name, dir: meta.dir, importLines, tasks })
|
|
378
379
|
}
|
|
379
380
|
}
|
|
381
|
+
breakBuildCycles(projects, metas)
|
|
380
382
|
return {
|
|
381
383
|
headerNotes: [
|
|
382
384
|
// Where to add one is the TODO's to say, when there is one: an Nx repo
|
|
@@ -390,6 +392,95 @@ export function migrateScripts(
|
|
|
390
392
|
}
|
|
391
393
|
}
|
|
392
394
|
|
|
395
|
+
/**
|
|
396
|
+
* pnpm sorts a dependency cycle away and builds anyway; vx's `^build`
|
|
397
|
+
* refuses it, so the config init wrote failed its first run (nuxt:
|
|
398
|
+
* `@nuxt/nitro-server` devDepends on `nuxt`, which depends on it; vitest's
|
|
399
|
+
* browser packages). A package in a cycle of builds waits, instead of on
|
|
400
|
+
* `^build`, on each build outside its cycle that `^build` reaches (through
|
|
401
|
+
* packages with none, as core walks it), with a TODO to order the cycle.
|
|
402
|
+
*/
|
|
403
|
+
function breakBuildCycles(projects: GeneratedProject[], metas: readonly ProjectMeta[]): void {
|
|
404
|
+
const builds = new Set(
|
|
405
|
+
projects
|
|
406
|
+
.filter((p) => p.tasks.some((t) => t.name === 'build' && t.task !== null))
|
|
407
|
+
.map((p) => p.name),
|
|
408
|
+
)
|
|
409
|
+
if (builds.size < 2) return
|
|
410
|
+
const graph = buildPackageGraph([...metas])
|
|
411
|
+
const holders = new Map<string, string[]>()
|
|
412
|
+
for (const name of builds) {
|
|
413
|
+
const found = new Set<string>()
|
|
414
|
+
const visited = new Set([name])
|
|
415
|
+
const frontier = [...graph.directDeps(name)]
|
|
416
|
+
while (frontier.length > 0) {
|
|
417
|
+
const at = frontier.pop()!
|
|
418
|
+
if (visited.has(at)) continue
|
|
419
|
+
visited.add(at)
|
|
420
|
+
if (builds.has(at)) found.add(at)
|
|
421
|
+
else frontier.push(...graph.directDeps(at))
|
|
422
|
+
}
|
|
423
|
+
holders.set(name, [...found].sort())
|
|
424
|
+
}
|
|
425
|
+
for (const cycle of cyclesOf(holders)) {
|
|
426
|
+
const members = new Set(cycle)
|
|
427
|
+
for (const p of projects) {
|
|
428
|
+
if (!members.has(p.name)) continue
|
|
429
|
+
const at = p.tasks.find(
|
|
430
|
+
(t) =>
|
|
431
|
+
Array.isArray(t.task?.['dependsOn']) &&
|
|
432
|
+
(t.task['dependsOn'] as string[]).includes('^build'),
|
|
433
|
+
)
|
|
434
|
+
if (at === undefined) continue
|
|
435
|
+
const outside = holders
|
|
436
|
+
.get(p.name)!
|
|
437
|
+
.filter((h) => !members.has(h))
|
|
438
|
+
.map((h) => `${h}#build`)
|
|
439
|
+
at.task!['dependsOn'] = (at.task!['dependsOn'] as string[]).flatMap((d) =>
|
|
440
|
+
d === '^build' ? outside : [d],
|
|
441
|
+
)
|
|
442
|
+
at.todos.push(
|
|
443
|
+
`its package is in a dependency cycle (${cycle.join(', ')}), where \`^build\` would refuse the run — it waits on the builds outside the cycle; order the ones inside it by hand`,
|
|
444
|
+
)
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** The strongly connected components of more than one node (Tarjan), each sorted. */
|
|
450
|
+
function cyclesOf(edges: ReadonlyMap<string, readonly string[]>): string[][] {
|
|
451
|
+
const index = new Map<string, number>()
|
|
452
|
+
const low = new Map<string, number>()
|
|
453
|
+
const stack: string[] = []
|
|
454
|
+
const onStack = new Set<string>()
|
|
455
|
+
const out: string[][] = []
|
|
456
|
+
const visit = (v: string): void => {
|
|
457
|
+
index.set(v, index.size)
|
|
458
|
+
low.set(v, index.get(v)!)
|
|
459
|
+
stack.push(v)
|
|
460
|
+
onStack.add(v)
|
|
461
|
+
for (const w of edges.get(v) ?? []) {
|
|
462
|
+
if (!index.has(w)) {
|
|
463
|
+
visit(w)
|
|
464
|
+
low.set(v, Math.min(low.get(v)!, low.get(w)!))
|
|
465
|
+
} else if (onStack.has(w)) {
|
|
466
|
+
low.set(v, Math.min(low.get(v)!, index.get(w)!))
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (low.get(v) === index.get(v)) {
|
|
470
|
+
const comp: string[] = []
|
|
471
|
+
let w: string
|
|
472
|
+
do {
|
|
473
|
+
w = stack.pop()!
|
|
474
|
+
onStack.delete(w)
|
|
475
|
+
comp.push(w)
|
|
476
|
+
} while (w !== v)
|
|
477
|
+
if (comp.length > 1) out.push(comp.sort())
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
for (const v of [...edges.keys()].sort()) if (!index.has(v)) visit(v)
|
|
481
|
+
return out
|
|
482
|
+
}
|
|
483
|
+
|
|
393
484
|
/** The member whose directory holds every other member's, if one does. */
|
|
394
485
|
function workspaceRootOf(metas: readonly ProjectMeta[]): ProjectMeta | undefined {
|
|
395
486
|
const outer = metas.reduce((a, b) => (b.dir.length < a.dir.length ? b : a))
|