@vzn/vx 0.0.113 → 0.0.115
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/README.md +16 -15
- package/package.json +5 -5
- package/src/cli/completions.ts +2 -1
- package/src/workspace/migrate-scripts.ts +42 -2
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ burns 35 s of CPU in vx, 73 s in Turborepo and 114 minutes in Nx; a
|
|
|
38
38
|
fully cached run replays the graph in 510 ms.
|
|
39
39
|
<!-- bench:end -->
|
|
40
40
|
|
|
41
|
-
Measured on
|
|
41
|
+
Measured on twelve real Turborepo, Nx and moon repos too: [tried on real repos](#tried-on-real-repos).
|
|
42
42
|
|
|
43
43
|
## Install
|
|
44
44
|
|
|
@@ -145,24 +145,25 @@ Turbo and Nx feature is in vx, pinned by a test:
|
|
|
145
145
|
|
|
146
146
|
## Tried on real repos
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
Twelve public monorepos, each run with its own `turbo.json`, Nx graph or moon config
|
|
149
149
|
and nothing rewritten, against the tool it ships with. Both tools on the same machine and
|
|
150
150
|
tasks per row, medians; vx first, theirs second. Where the other tool
|
|
151
151
|
wins, the row says so.
|
|
152
152
|
|
|
153
|
-
| Repo
|
|
154
|
-
|
|
|
155
|
-
| [solidjs/solid](https://github.com/solidjs/solid)
|
|
156
|
-
| [withastro/astro](https://github.com/withastro/astro)
|
|
157
|
-
| [payloadcms/payload](https://github.com/payloadcms/payload)
|
|
158
|
-
| [medusajs/medusa](https://github.com/medusajs/medusa)
|
|
159
|
-
| [n8n-io/n8n](https://github.com/n8n-io/n8n)
|
|
160
|
-
| [calcom/cal.com](https://github.com/calcom/cal.com)
|
|
161
|
-
| [TanStack/query](https://github.com/TanStack/query)
|
|
162
|
-
| [strapi/strapi](https://github.com/strapi/strapi)
|
|
163
|
-
| [novuhq/novu](https://github.com/novuhq/novu)
|
|
164
|
-
| [TanStack/router](https://github.com/TanStack/router)
|
|
165
|
-
| [refinedev/refine](https://github.com/refinedev/refine)
|
|
153
|
+
| Repo | Against | Cold build | Restore outputs | Nothing to do |
|
|
154
|
+
| --------------------------------------------------------------------- | ------- | --------------------- | --------------- | ------------------- |
|
|
155
|
+
| [solidjs/solid](https://github.com/solidjs/solid) | Turbo | 40.6 s / 45.5 s | 66 ms / 127 ms | 51 ms / 95 ms |
|
|
156
|
+
| [withastro/astro](https://github.com/withastro/astro) | Turbo | 48.0 s / 57.9 s | 478 ms / 1.43 s | 249 ms / 1.38 s |
|
|
157
|
+
| [payloadcms/payload](https://github.com/payloadcms/payload) | Turbo | 126.9 s / 127.5 s | 3.44 s / 3.46 s | 256 ms / **237 ms** |
|
|
158
|
+
| [medusajs/medusa](https://github.com/medusajs/medusa) | Turbo | 308 s / 315 s | 3.86 s / 7.16 s | 947 ms / 3.29 s |
|
|
159
|
+
| [n8n-io/n8n](https://github.com/n8n-io/n8n) | Turbo | 137.2 s / **133.7 s** | 8.27 s / 12.5 s | 842 ms / 1.36 s |
|
|
160
|
+
| [calcom/cal.com](https://github.com/calcom/cal.com) | Turbo | 245.7 s / 250.7 s | 17.4 s / 19.9 s | 14.7 s / 18.5 s |
|
|
161
|
+
| [TanStack/query](https://github.com/TanStack/query) | Nx | 47.4 s / 55.1 s | 656 ms / 1.98 s | 174 ms / 1.88 s |
|
|
162
|
+
| [strapi/strapi](https://github.com/strapi/strapi) | Nx | 238.2 s / 245.7 s | 1.61 s / 3.94 s | 341 ms / 4.02 s |
|
|
163
|
+
| [novuhq/novu](https://github.com/novuhq/novu) | Nx | 291.2 s / 299.4 s | 3.10 s / 9.05 s | 655 ms / 8.55 s |
|
|
164
|
+
| [TanStack/router](https://github.com/TanStack/router) | Nx | 199.9 s / 226.7 s | 1.00 s / 3.33 s | 505 ms / 3.21 s |
|
|
165
|
+
| [refinedev/refine](https://github.com/refinedev/refine) | Nx | 96.2 s / 105.9 s | 1.06 s / 1.19 s | 303 ms / 1.16 s |
|
|
166
|
+
| [kindspells/astro-shield](https://github.com/kindspells/astro-shield) | moon | 2.64 s / 3.78 s | 125 ms / 2.46 s | 111 ms / 2.37 s |
|
|
166
167
|
|
|
167
168
|
**Bold** marks the other tool winning. Versions, revisions, scopes and
|
|
168
169
|
harness per repo: [benchmarks](packages/vx/docs/benchmarks.md) (astro
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.115",
|
|
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.115",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.115",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.115",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.115"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/completions.ts
CHANGED
|
@@ -101,7 +101,8 @@ export async function completionsCmd(
|
|
|
101
101
|
return 1
|
|
102
102
|
}
|
|
103
103
|
const verbs = [...CORE_VERBS.filter((v) => v !== 'help' && v !== 'version'), 'help', 'version']
|
|
104
|
-
|
|
104
|
+
// No overlap to skip: the workspace load refuses a plugin verb named like a core one.
|
|
105
|
+
verbs.push(...pluginVerbs)
|
|
105
106
|
process.stdout.write(completionScript(shell as CompletionShell, verbs))
|
|
106
107
|
return 0
|
|
107
108
|
}
|
|
@@ -23,7 +23,11 @@
|
|
|
23
23
|
// hook is usually `rimraf dist`); and a script that is nothing but
|
|
24
24
|
// `<pm> run <other>` becomes a GROUP over `<other>`, so the graph sees the
|
|
25
25
|
// dependency instead of a package-manager subprocess it cannot cache.
|
|
26
|
+
// Yarn 2+ runs no such hooks (probed: `yarn run build` printed BUILD alone),
|
|
27
|
+
// so under it `prebuild` is a script like any other (D-31).
|
|
26
28
|
|
|
29
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
30
|
+
import path from 'node:path'
|
|
27
31
|
import { taskNameProblem } from './config-schema.js'
|
|
28
32
|
import type { ProjectMeta } from './workspace.js'
|
|
29
33
|
import {
|
|
@@ -82,6 +86,40 @@ const OWN_COMMANDS: Readonly<Record<string, ReadonlySet<string>>> = {
|
|
|
82
86
|
),
|
|
83
87
|
}
|
|
84
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Whether the package manager that owns `dir` is Yarn 2+: the nearest
|
|
91
|
+
* `packageManager: yarn@<2+>`, or the nearest lockfile being a Berry
|
|
92
|
+
* `yarn.lock` (it opens with `__metadata:`). Another lockfile, or none,
|
|
93
|
+
* is a manager that runs `pre` / `post` hooks.
|
|
94
|
+
*/
|
|
95
|
+
function isYarnBerry(dir: string, memo: Map<string, boolean>): boolean {
|
|
96
|
+
const known = memo.get(dir)
|
|
97
|
+
if (known !== undefined) return known
|
|
98
|
+
let answer: boolean | undefined
|
|
99
|
+
const manifest = path.join(dir, 'package.json')
|
|
100
|
+
try {
|
|
101
|
+
const pm = (JSON.parse(readFileSync(manifest, 'utf8')) as { packageManager?: unknown })
|
|
102
|
+
.packageManager
|
|
103
|
+
if (typeof pm === 'string') answer = /^yarn@([2-9]|\d{2,})/.test(pm)
|
|
104
|
+
} catch {}
|
|
105
|
+
if (answer === undefined) {
|
|
106
|
+
const lock = path.join(dir, 'yarn.lock')
|
|
107
|
+
if (existsSync(lock)) answer = readFileSync(lock, 'utf8').includes('\n__metadata:')
|
|
108
|
+
else if (
|
|
109
|
+
['package-lock.json', 'pnpm-lock.yaml', 'bun.lock', 'bun.lockb'].some((f) =>
|
|
110
|
+
existsSync(path.join(dir, f)),
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
answer = false
|
|
114
|
+
}
|
|
115
|
+
if (answer === undefined) {
|
|
116
|
+
const up = path.dirname(dir)
|
|
117
|
+
answer = up === dir ? false : isYarnBerry(up, memo)
|
|
118
|
+
}
|
|
119
|
+
memo.set(dir, answer)
|
|
120
|
+
return answer
|
|
121
|
+
}
|
|
122
|
+
|
|
85
123
|
function scriptsOf(meta: ProjectMeta): Record<string, unknown> {
|
|
86
124
|
// package.json is a boundary: `scripts` is whatever the file holds. A
|
|
87
125
|
// string or an array would enumerate its indices as script names.
|
|
@@ -132,8 +170,10 @@ function upstreamBuildOnWorker(tasks: GeneratedTask[]): void {
|
|
|
132
170
|
|
|
133
171
|
export function migrateScripts(metas: readonly ProjectMeta[]): MigrationPlan {
|
|
134
172
|
const projects: GeneratedProject[] = []
|
|
173
|
+
const berry = new Map<string, boolean>()
|
|
135
174
|
for (const meta of metas) {
|
|
136
175
|
const scripts = scriptsOf(meta)
|
|
176
|
+
const runsHooks = !isYarnBerry(meta.dir, berry)
|
|
137
177
|
const runnable = Object.keys(scripts).filter(
|
|
138
178
|
(n) => typeof scripts[n] === 'string' && scripts[n] !== '',
|
|
139
179
|
)
|
|
@@ -153,7 +193,7 @@ export function migrateScripts(metas: readonly ProjectMeta[]): MigrationPlan {
|
|
|
153
193
|
// of a script that exists rides inside that script's command.
|
|
154
194
|
const isTask = (n: string): boolean => {
|
|
155
195
|
if (!has(n) || LIFECYCLE.test(n)) return false
|
|
156
|
-
const hookOf = /^(pre|post)(.+)$/.exec(n)
|
|
196
|
+
const hookOf = runsHooks ? /^(pre|post)(.+)$/.exec(n) : null
|
|
157
197
|
return hookOf === null || !has(hookOf[2]!) || LIFECYCLE.test(hookOf[2]!)
|
|
158
198
|
}
|
|
159
199
|
const tasks: GeneratedTask[] = []
|
|
@@ -165,7 +205,7 @@ export function migrateScripts(metas: readonly ProjectMeta[]): MigrationPlan {
|
|
|
165
205
|
const delegate = delegatedScript(own)
|
|
166
206
|
// npm lifecycle hooks (`prepack`, `prepublishOnly`, …) belong to the
|
|
167
207
|
// package manager and never ride inside a task — `pack` stays alone.
|
|
168
|
-
const hook = (h: string): boolean => has(h) && !LIFECYCLE.test(h)
|
|
208
|
+
const hook = (h: string): boolean => runsHooks && has(h) && !LIFECYCLE.test(h)
|
|
169
209
|
const hooks = [`pre${name}`, `post${name}`].filter(hook)
|
|
170
210
|
// A group over a script that is no task (`setup: npm run prepare`)
|
|
171
211
|
// named a task nothing defines, and the run `vx init` suggested
|