@vzn/vx 0.0.574 → 0.0.575
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 +5 -5
- package/src/graph/task-graph.ts +52 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.575",
|
|
4
4
|
"description": "The fastest task runner and build cache for JavaScript monorepos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
23
23
|
"optionalDependencies": {
|
|
24
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
25
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
26
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
27
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
24
|
+
"@vzn/vx-linux-x64": "0.0.575",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.575",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.575",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.575"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/graph/task-graph.ts
CHANGED
|
@@ -684,6 +684,10 @@ export function excludeDependencies(
|
|
|
684
684
|
* 445). One rule, one place: the copy is gone.
|
|
685
685
|
*/
|
|
686
686
|
export function outputsOverlap(rawA: string, rawB: string): boolean {
|
|
687
|
+
// A built path proves overlap only between globs as written: a literal's
|
|
688
|
+
// `/**` twin reads it as a directory, and `dist/types.d.ts` met
|
|
689
|
+
// `dist/**/*.js` at `dist/types.d.ts/x.js`, a file no one can write.
|
|
690
|
+
const bothGlobs = !isLiteralPattern(rawA) && !isLiteralPattern(rawB)
|
|
687
691
|
for (const a of asTrees([rawA])) {
|
|
688
692
|
for (const b of asTrees([rawB])) {
|
|
689
693
|
if (isLiteralPattern(a) && isLiteralPattern(b)) {
|
|
@@ -692,7 +696,41 @@ export function outputsOverlap(rawA: string, rawB: string): boolean {
|
|
|
692
696
|
if (taskGlob(b).match(a)) return true
|
|
693
697
|
} else if (isLiteralPattern(b)) {
|
|
694
698
|
if (taskGlob(a).match(b)) return true
|
|
695
|
-
} else if (a === b || covers(a, b) || covers(b, a)
|
|
699
|
+
} else if (a === b || covers(a, b) || covers(b, a) || (bothGlobs && witnessed(a, b)))
|
|
700
|
+
return true
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return false
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/** `staticPrefix` names the project root `.`; a path built under it starts bare. */
|
|
707
|
+
const rootless = (prefix: string): string => (prefix === '.' ? '' : prefix)
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Do two globs both match a path built from them? Each candidate is a
|
|
711
|
+
* real path, so a hit proves the overlap and a miss decides nothing. The
|
|
712
|
+
* path sits under the deeper of the two literal prefixes and ends in
|
|
713
|
+
* either glob's last segment with its wildcards filled: a glob for every
|
|
714
|
+
* `.js` file under `dist` beside `dist/sth/**` meets at `dist/sth/x.js`,
|
|
715
|
+
* a pair `covers` cannot decide since neither is a subtree holding the
|
|
716
|
+
* other's prefix.
|
|
717
|
+
*/
|
|
718
|
+
function witnessed(a: string, b: string): boolean {
|
|
719
|
+
const pa = rootless(staticPrefix(a))
|
|
720
|
+
const pb = rootless(staticPrefix(b))
|
|
721
|
+
const nested = (outer: string, inner: string): boolean =>
|
|
722
|
+
outer === '' || inner === outer || inner.startsWith(`${outer}/`)
|
|
723
|
+
if (!nested(pa, pb) && !nested(pb, pa)) return false
|
|
724
|
+
const deeper = pa.length >= pb.length ? pa : pb
|
|
725
|
+
const ga = taskGlob(a)
|
|
726
|
+
const gb = taskGlob(b)
|
|
727
|
+
for (const glob of [a, b]) {
|
|
728
|
+
const last = glob.slice(glob.lastIndexOf('/') + 1)
|
|
729
|
+
if (/[[\]{}()!+@]/.test(last)) continue
|
|
730
|
+
const leaf = last.replace(/\*+|\?/g, 'x')
|
|
731
|
+
for (const mid of ['', 'x/']) {
|
|
732
|
+
const candidate = deeper === '' ? `${mid}${leaf}` : `${deeper}/${mid}${leaf}`
|
|
733
|
+
if (ga.match(candidate) && gb.match(candidate)) return true
|
|
696
734
|
}
|
|
697
735
|
}
|
|
698
736
|
return false
|
|
@@ -953,6 +991,19 @@ function overlapCandidates<T>(
|
|
|
953
991
|
for (let y = x + 1; y < list.length; y++) pair(list[x]!, list[y]!)
|
|
954
992
|
}
|
|
955
993
|
}
|
|
994
|
+
// `witnessed`: two globs meet only when one's literal prefix is the
|
|
995
|
+
// other's or under it, so each looks up the globs filed at its prefix
|
|
996
|
+
// and at each ancestor.
|
|
997
|
+
const byPrefix = new Map<string, number[]>()
|
|
998
|
+
for (const [i, prefix] of prefixes) file(byPrefix, rootless(prefix), i)
|
|
999
|
+
for (const [i, raw] of prefixes) {
|
|
1000
|
+
const prefix = rootless(raw)
|
|
1001
|
+
for (const j of byPrefix.get(prefix) ?? []) pair(i, j)
|
|
1002
|
+
if (prefix !== '') for (const j of byPrefix.get('') ?? []) pair(i, j)
|
|
1003
|
+
for (let sep = prefix.indexOf('/'); sep !== -1; sep = prefix.indexOf('/', sep + 1)) {
|
|
1004
|
+
for (const j of byPrefix.get(prefix.slice(0, sep)) ?? []) pair(i, j)
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
956
1007
|
for (const [i, prefix] of prefixes) {
|
|
957
1008
|
for (const j of subtrees.get(prefix) ?? []) pair(i, j)
|
|
958
1009
|
for (let sep = prefix.indexOf('/'); sep !== -1; sep = prefix.indexOf('/', sep + 1)) {
|