@vzn/vx-migrate 0.0.492 → 0.0.494
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 +5 -1
- package/package.json +2 -2
- package/src/adoption-plugin.ts +1 -0
- package/src/mapping-cache.ts +13 -3
- package/src/nx/nx-map.ts +12 -1
package/README.md
CHANGED
|
@@ -164,6 +164,10 @@ Two cached tasks on one workspace path cannot both keep their cache, and the fir
|
|
|
164
164
|
|
|
165
165
|
`maxCacheSize` (or `NX_MAX_CACHE_SIZE`, above it as in Nx) is the run's `cacheRetention.maxSize` when `vx.workspace.ts` sets no retention, in Nx's grammar (`10GB`, `1.5 GB`, bare bytes); `0` is no cap.
|
|
166
166
|
|
|
167
|
+
### Project tags
|
|
168
|
+
|
|
169
|
+
An Nx project's `tags` are its vx `tags`, so `vx run build --filter tag:scope:web` (or Nx's `--projects tag:scope:web`) selects what `nx run-many -t build -p tag:scope:web` does. A package whose `vx.config` declares `tags` keeps its own. A blank tag is dropped (vx refuses one).
|
|
170
|
+
|
|
167
171
|
### The graph snapshot
|
|
168
172
|
|
|
169
173
|
Once per run the plugin keys its snapshot (`<cache dir>/nx-project-graph.json`) on what Nx computes the graph from: `nx.json` and the files its `extends` chain names by content, and the worktree as git sees it — `HEAD`, `git status -uall` (core's own for the run, `DiscoverContext.worktreeChanges`: the tree is walked once, which took ~96 ms per run on refine; a `root` elsewhere asks git itself), and the content of every listed path under a project root (the last graph's too, so a project `discover` names counts before it is one), any `project.json`, or at the root (manifests, lockfiles, `tsconfig*.json`). A different key, or no snapshot, runs `node_modules/.bin/nx graph --file=<snapshot>` — the one time Nx itself runs, served from the daemon when one is up. So a source edit that adds a cross-package `import` (an edge Nx derives) or a config an Nx plugin infers targets from (`vite.config.ts`) re-exports before the run, tracked or untracked, and a touch without an edit, or a stray file a task writes at the root, does not. Outside a git worktree the plugin falls back to the manifests' mtimes. The snapshot and its key live in the cache dir, which ignores itself (a `*` `.gitignore` inside it), so writing them moves nothing. Nx's own caches (`.nx/cache/`, `.nx/workspace-data/`, which the export writes) never enter the key: under a root project (a standalone repo) that does not ignore them, each export re-exported on the next run. An export that fails with a snapshot in hand warns and runs on the previous graph. The snapshot is machine-local, like the cache: nothing about it enters a key. Measured at 1,000 projects: the export runs once (1.5 s with the daemon off after an edit, 0.9 s with it on; Nx's own computation), and a warm run with nothing changed pays the key, 43 ms at min where the manifest stats it replaced took 9 (item 1075), plus the mapping. Under `vx watch` the same rule runs per cycle: a `project.json` edit is the next cycle's tasks, the export included — 1.3 s from the edit to the new command's effect on that workspace. The mapping itself is kept beside it (`<cache dir>/vx-migrate-nx-mapping.json`), keyed on everything it reads — the graph, `nx.json` and its `extends` chain, every package manifest, each project dir's `.env` names, `NX_LOAD_DOT_ENV_FILES`, which bins are installed and the mapper's own code — so a warm run with nothing changed maps nothing.
|
|
@@ -222,7 +226,7 @@ Reads the root pipeline (`tasks` in Turbo 2, `pipeline` in Turbo 1), per-package
|
|
|
222
226
|
|
|
223
227
|
### Nx
|
|
224
228
|
|
|
225
|
-
Reads the **resolved** project graph only (`.nx/workspace-data/project-graph.json` when exported, else the one the workspace's own `nx graph` exports into a temp file, as `nx()` does), through the same mapper `nx()` runs live, executors aside (below). Targets Nx plugins infer at runtime are frozen as the snapshot saw them. `nx:run-commands` is the one shell line `nx()` runs (see [`nx:run-commands`](#nxrun-commands) above: where, parallel or in order, forwarded arguments, `env`, `readyWhen`) — storybook's `compile` is `cd ../../.. && node ./scripts/build/build-package.ts --cwd code/lib/cli`; a plain `command` is that shorthand; `nx:run-script` is the package's script body with its `pre<name>` / `post<name>` hooks folded in (or `yarn run <name>` when the body calls yarn's `run` builtin; an empty script is the placeholder with a todo), `nx:noop` is a group task; Nx 15–16's `@nrwl/workspace:run-commands` and `run-script` (and their `@nx/workspace:` names) are the `nx:` executors they re-exported; **an executor target is its `nx-exec` line**, as `nx()` runs it: the migrator translates no executor, so Nx and `@vzn/vx-migrate` stay installed until each such line is rewritten as the command it runs (a server executor Nx knows, `@nx/js:node` or a dev server, is still a persistent task). The report also says what `vx.workspace.ts` still holds, as the Turbo migration does: the `nx()` `vx init` declared, which keeps reading nx.json, and a lockfile with no `@vzn/vx-lockfile` plugin, where Nx keyed each project on the npm packages it depends on. A written command that still runs Nx itself (a run-commands `nx run b:build`, `npx nx test b`, a script's `nx exec -- tsc`) carries a TODO: it works only while Nx is installed. An Nx project no workspace glob lists (an integrated repo's `project.json` library) gets a `package.json` (`name`, `private`) where it has none, and a note names the directories to add to `workspaces` (or `pnpm-workspace.yaml`): core finds a project only through those globs, and the migrator never edits the root manifest. A target with `configurations` writes one task per configuration (`build`, `build:ci`). Named inputs expand from `nx.json` when readable. An output path is kept as written, a `!` one too (`{projectRoot}/dist` → `dist`, `{projectRoot}/bin/tool` → `bin/tool`; one naming an unset `{options.x}` is dropped, as Nx drops it; a dotted `{options.outputPath.base}` walks the options, as Nx does; an extglob is put in vx's grammar: Next's inferred `.next/!(cache)/**/*` is `.next/*/**/*` with `!.next/cache/**/*`, `@(js|map)` is `{js,map}`, and a form with no vx spelling is dropped with a TODO): vx reads a bare path as the file or the whole tree under it, so a directory and an extensionless binary both save and restore. nx.json's `parallel`, `defaultBase` and `maxCacheSize`, which `nx()` applies live, are each a note naming the `vx.workspace.ts` field to add (`concurrency`, `affectedBase`, `cacheRetention.maxSize`): the workspace file is written without them, and from nx.json alone, never the environment. vx derives package edges from `package.json`; an Nx graph edge with no manifest path (`implicitDependencies`, a tsconfig path) becomes, for each `^target` of the dependant, an explicit `pkg#target` edge to what Nx's own walk reaches — each dependency that has the target, and through one that lacks it, its dependencies — so the order and the key are Nx's. The reverse too: where a project's manifest names a workspace package its Nx graph does not reach (an `implicitDependencies: ["!a"]` that breaks a manifest cycle), its `^target` becomes the explicit edges Nx draws, since vx's `^` would follow the manifest and bring the cycle back.
|
|
229
|
+
Reads the **resolved** project graph only (`.nx/workspace-data/project-graph.json` when exported, else the one the workspace's own `nx graph` exports into a temp file, as `nx()` does), through the same mapper `nx()` runs live, executors aside (below). Targets Nx plugins infer at runtime are frozen as the snapshot saw them. `nx:run-commands` is the one shell line `nx()` runs (see [`nx:run-commands`](#nxrun-commands) above: where, parallel or in order, forwarded arguments, `env`, `readyWhen`) — storybook's `compile` is `cd ../../.. && node ./scripts/build/build-package.ts --cwd code/lib/cli`; a plain `command` is that shorthand; `nx:run-script` is the package's script body with its `pre<name>` / `post<name>` hooks folded in (or `yarn run <name>` when the body calls yarn's `run` builtin; an empty script is the placeholder with a todo), `nx:noop` is a group task; Nx 15–16's `@nrwl/workspace:run-commands` and `run-script` (and their `@nx/workspace:` names) are the `nx:` executors they re-exported; **an executor target is its `nx-exec` line**, as `nx()` runs it: the migrator translates no executor, so Nx and `@vzn/vx-migrate` stay installed until each such line is rewritten as the command it runs (a server executor Nx knows, `@nx/js:node` or a dev server, is still a persistent task). The report also says what `vx.workspace.ts` still holds, as the Turbo migration does: the `nx()` `vx init` declared, which keeps reading nx.json, and a lockfile with no `@vzn/vx-lockfile` plugin, where Nx keyed each project on the npm packages it depends on. A written command that still runs Nx itself (a run-commands `nx run b:build`, `npx nx test b`, a script's `nx exec -- tsc`) carries a TODO: it works only while Nx is installed. An Nx project no workspace glob lists (an integrated repo's `project.json` library) gets a `package.json` (`name`, `private`) where it has none, and a note names the directories to add to `workspaces` (or `pnpm-workspace.yaml`): core finds a project only through those globs, and the migrator never edits the root manifest. A target with `configurations` writes one task per configuration (`build`, `build:ci`). A project's Nx `tags` are written as its `tags`. Named inputs expand from `nx.json` when readable. An output path is kept as written, a `!` one too (`{projectRoot}/dist` → `dist`, `{projectRoot}/bin/tool` → `bin/tool`; one naming an unset `{options.x}` is dropped, as Nx drops it; a dotted `{options.outputPath.base}` walks the options, as Nx does; an extglob is put in vx's grammar: Next's inferred `.next/!(cache)/**/*` is `.next/*/**/*` with `!.next/cache/**/*`, `@(js|map)` is `{js,map}`, and a form with no vx spelling is dropped with a TODO): vx reads a bare path as the file or the whole tree under it, so a directory and an extensionless binary both save and restore. nx.json's `parallel`, `defaultBase` and `maxCacheSize`, which `nx()` applies live, are each a note naming the `vx.workspace.ts` field to add (`concurrency`, `affectedBase`, `cacheRetention.maxSize`): the workspace file is written without them, and from nx.json alone, never the environment. vx derives package edges from `package.json`; an Nx graph edge with no manifest path (`implicitDependencies`, a tsconfig path) becomes, for each `^target` of the dependant, an explicit `pkg#target` edge to what Nx's own walk reaches — each dependency that has the target, and through one that lacks it, its dependencies — so the order and the key are Nx's. The reverse too: where a project's manifest names a workspace package its Nx graph does not reach (an `implicitDependencies: ["!a"]` that breaks a manifest cycle), its `^target` becomes the explicit edges Nx draws, since vx's `^` would follow the manifest and bring the cycle back.
|
|
226
230
|
|
|
227
231
|
## `turboCache()` — a Turbo remote cache
|
|
228
232
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx-migrate",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.494",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Adopt @vzn/vx from Turborepo or Nx: run the repo unchanged, migrate it to vx.config.ts, and keep your Turbo or Nx remote cache",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@vzn/vx": "^0.0.
|
|
42
|
+
"@vzn/vx": "^0.0.494"
|
|
43
43
|
},
|
|
44
44
|
"repository": {
|
|
45
45
|
"type": "git",
|
package/src/adoption-plugin.ts
CHANGED
|
@@ -101,6 +101,7 @@ export function adoptionPlugin(
|
|
|
101
101
|
}
|
|
102
102
|
const project = mapped.byName.get(ctx.name)
|
|
103
103
|
if (project === undefined) return
|
|
104
|
+
if (project.tags !== undefined) config.tags ??= project.tags
|
|
104
105
|
config.tasks ??= {}
|
|
105
106
|
for (const t of project.tasks) {
|
|
106
107
|
if (t.task === null) continue
|
package/src/mapping-cache.ts
CHANGED
|
@@ -19,11 +19,12 @@ import {
|
|
|
19
19
|
} from '@vzn/vx'
|
|
20
20
|
import type { Gaps } from './plugin-gaps.js'
|
|
21
21
|
|
|
22
|
-
/** What the stage uses of a mapping: tasks per package name, and the gaps to report once. */
|
|
22
|
+
/** What the stage uses of a mapping: tags and tasks per package name, and the gaps to report once. */
|
|
23
23
|
export interface AdoptionMapping {
|
|
24
24
|
readonly byName: ReadonlyMap<
|
|
25
25
|
string,
|
|
26
26
|
{
|
|
27
|
+
readonly tags?: readonly string[]
|
|
27
28
|
readonly tasks: readonly {
|
|
28
29
|
readonly name: string
|
|
29
30
|
readonly task: Record<string, unknown> | null
|
|
@@ -57,7 +58,13 @@ function hex(s: string): string {
|
|
|
57
58
|
|
|
58
59
|
interface Held {
|
|
59
60
|
readonly key: string
|
|
60
|
-
readonly byName: [
|
|
61
|
+
readonly byName: [
|
|
62
|
+
string,
|
|
63
|
+
{
|
|
64
|
+
tags?: readonly string[]
|
|
65
|
+
tasks: { name: string; task: Record<string, unknown> | null }[]
|
|
66
|
+
},
|
|
67
|
+
][]
|
|
61
68
|
readonly notes: string[]
|
|
62
69
|
readonly todos: [string, string[]][]
|
|
63
70
|
}
|
|
@@ -85,7 +92,10 @@ export async function cachedMapping(
|
|
|
85
92
|
key,
|
|
86
93
|
byName: [...mapping.byName].map(([name, p]) => [
|
|
87
94
|
name,
|
|
88
|
-
{
|
|
95
|
+
{
|
|
96
|
+
...(p.tags !== undefined ? { tags: p.tags } : {}),
|
|
97
|
+
tasks: p.tasks.map((t) => ({ name: t.name, task: t.task })),
|
|
98
|
+
},
|
|
89
99
|
]),
|
|
90
100
|
notes: [...mapping.gaps.notes],
|
|
91
101
|
todos: [...mapping.gaps.todos].map(([todo, ids]) => [todo, [...ids]]),
|
package/src/nx/nx-map.ts
CHANGED
|
@@ -68,6 +68,7 @@ interface NxNode {
|
|
|
68
68
|
name?: string
|
|
69
69
|
data?: {
|
|
70
70
|
root?: string
|
|
71
|
+
tags?: unknown[]
|
|
71
72
|
namedInputs?: Record<string, unknown[]>
|
|
72
73
|
targets?: Record<string, NxTarget>
|
|
73
74
|
metadata?: { targetGroups?: unknown }
|
|
@@ -282,6 +283,14 @@ export interface NxMapping {
|
|
|
282
283
|
readonly notes: string[]
|
|
283
284
|
}
|
|
284
285
|
|
|
286
|
+
/** A node's tags as vx takes them: a blank one would be refused at load. */
|
|
287
|
+
function nxTags(node: NxNode | undefined): string[] {
|
|
288
|
+
const tags = node?.data?.tags
|
|
289
|
+
return Array.isArray(tags)
|
|
290
|
+
? tags.filter((t): t is string => typeof t === 'string' && t.trim() !== '')
|
|
291
|
+
: []
|
|
292
|
+
}
|
|
293
|
+
|
|
285
294
|
function normRel(p: string): string {
|
|
286
295
|
return p === '' || p === '.' ? '.' : p.replace(/\/$/, '')
|
|
287
296
|
}
|
|
@@ -541,10 +550,12 @@ export async function mapNxWorkspace(
|
|
|
541
550
|
}
|
|
542
551
|
dropUnheldDeps(t, emitted, emittedIds)
|
|
543
552
|
}
|
|
553
|
+
const tags = nxTags(nodeName === undefined ? undefined : nodeMap[nodeName])
|
|
544
554
|
projects.push({
|
|
545
555
|
name: meta.name,
|
|
546
556
|
dir: meta.dir,
|
|
547
557
|
importLines: [],
|
|
558
|
+
...(tags.length > 0 ? { tags } : {}),
|
|
548
559
|
tasks: resolveSharedOutputs(tasks),
|
|
549
560
|
})
|
|
550
561
|
}
|
|
@@ -820,7 +831,7 @@ function buildTask(
|
|
|
820
831
|
patterns,
|
|
821
832
|
Object.entries(nodeMap).map(([name, n]) => ({
|
|
822
833
|
name,
|
|
823
|
-
tags: (
|
|
834
|
+
tags: nxTags(n),
|
|
824
835
|
...(typeof n?.data?.root === 'string' ? { root: n.data.root } : {}),
|
|
825
836
|
})),
|
|
826
837
|
),
|