@nifrajs/cli 4.0.0 → 4.0.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.
- package/dist/cli.js +1 -1
- package/dist/command-catalog.d.ts +6 -1
- package/dist/command-catalog.d.ts.map +1 -1
- package/dist/command-catalog.js +5 -9
- package/dist/command-catalog.js.map +1 -1
- package/dist/fix-recipes.d.ts.map +1 -1
- package/dist/fix-recipes.js +7 -1
- package/dist/fix-recipes.js.map +1 -1
- package/dist/mcp-http.js +1 -1
- package/dist/migrate-layout.d.ts.map +1 -1
- package/dist/migrate-layout.js +185 -12
- package/dist/migrate-layout.js.map +1 -1
- package/dist/recipes/4.0.0.d.ts.map +1 -1
- package/dist/recipes/4.0.0.js +2 -0
- package/dist/recipes/4.0.0.js.map +1 -1
- package/dist/sync-manifest.d.ts +10 -2
- package/dist/sync-manifest.d.ts.map +1 -1
- package/dist/sync-manifest.js +42 -18
- package/dist/sync-manifest.js.map +1 -1
- package/docs/types.json +2 -2
- package/package.json +12 -12
- package/src/cli.ts +1 -1
- package/src/command-catalog.ts +15 -10
- package/src/fix-recipes.ts +9 -1
- package/src/mcp-http.ts +1 -1
- package/src/migrate-layout.ts +229 -12
- package/src/recipes/4.0.0.ts +2 -0
- package/src/sync-manifest.ts +48 -18
package/src/sync-manifest.ts
CHANGED
|
@@ -21,6 +21,14 @@ const GENERATED_MARKER = "GENERATED by @nifrajs/web generateServerManifest"
|
|
|
21
21
|
// routes dir relative to the manifest and strips imports to route-relative keys.
|
|
22
22
|
const ROUTES_PREFIX = /["'](\.{1,2}(?:\/[^"'/]+)*?\/routes\/)[^"']+["']/
|
|
23
23
|
const IGNORED = /(^|\/)(node_modules|dist(-[a-z0-9]+)?|build|\.nifra|\.git|\.wrangler|coverage)\//
|
|
24
|
+
// The fs codes that mean the routes dir is absent or unreadable. Every other throw is the route table
|
|
25
|
+
// refusing to build, which must fail the command rather than read as "no manifest found".
|
|
26
|
+
const ROUTES_DIR_UNAVAILABLE = new Set(["ENOENT", "ENOTDIR", "EACCES", "EPERM"])
|
|
27
|
+
|
|
28
|
+
function routesDirUnavailable(error: unknown): boolean {
|
|
29
|
+
if (typeof error !== "object" || error === null || !("code" in error)) return false
|
|
30
|
+
return typeof error.code === "string" && ROUTES_DIR_UNAVAILABLE.has(error.code)
|
|
31
|
+
}
|
|
24
32
|
|
|
25
33
|
export interface SyncManifestResult {
|
|
26
34
|
/** The committed server-manifest file (relative to cwd). */
|
|
@@ -34,12 +42,16 @@ export interface SyncManifestResult {
|
|
|
34
42
|
/** Set when the sync was REFUSED because discovery found zero routes for a manifest that had some
|
|
35
43
|
* (`routes/` likely emptied by mistake) - the manifest was left untouched rather than wiped. */
|
|
36
44
|
readonly refusedEmpty?: boolean
|
|
45
|
+
/** Set when the `routes/` tree could not be built into a route table (a duplicate route, two routes of
|
|
46
|
+
* one shape sharing a path, ...) - the manifest was left untouched and the sync failed. */
|
|
47
|
+
readonly error?: string
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
/**
|
|
40
51
|
* Discover every generated `server-manifest.ts` under `cwd` and re-sync its route table from the live
|
|
41
52
|
* `routes/` tree, preserving baked client assets. Returns one result per generated manifest. Never throws
|
|
42
|
-
* on a per-manifest
|
|
53
|
+
* on a per-manifest failure: an absent or unreadable routes dir is skipped, and a route table that fails
|
|
54
|
+
* to build is reported in that manifest's `error`.
|
|
43
55
|
*/
|
|
44
56
|
export async function syncServerManifests(cwd: string): Promise<SyncManifestResult[]> {
|
|
45
57
|
const { discoverRoutes } = await import("@nifrajs/web/fs")
|
|
@@ -60,8 +72,11 @@ export async function syncServerManifests(cwd: string): Promise<SyncManifestResu
|
|
|
60
72
|
let next: string
|
|
61
73
|
try {
|
|
62
74
|
next = resyncServerManifestSource(source, discoverRoutes(routesDir), routesPrefix)
|
|
63
|
-
} catch {
|
|
64
|
-
continue //
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (routesDirUnavailable(error)) continue // not a manifest we can sync
|
|
77
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
78
|
+
results.push({ file: rel, changed: false, added: [], removed: [], error: message })
|
|
79
|
+
continue
|
|
65
80
|
}
|
|
66
81
|
const before = parseManifestRouteFiles(source, routesPrefix)
|
|
67
82
|
const after = parseManifestRouteFiles(next, routesPrefix)
|
|
@@ -81,33 +96,48 @@ export async function syncServerManifests(cwd: string): Promise<SyncManifestResu
|
|
|
81
96
|
return results.sort((a, b) => codeUnitOrder(a.file, b.file))
|
|
82
97
|
}
|
|
83
98
|
|
|
84
|
-
/**
|
|
85
|
-
export
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
/** True when no manifest's route table failed to build. */
|
|
100
|
+
export function syncManifestsOk(results: readonly SyncManifestResult[]): boolean {
|
|
101
|
+
return results.every((r) => r.error === undefined)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Per-file summary lines + the client-rebuild caveat. */
|
|
105
|
+
export function renderSyncManifestResults(results: readonly SyncManifestResult[]): string[] {
|
|
106
|
+
if (results.length === 0)
|
|
107
|
+
return ["nifra sync-manifest: no generated server-manifest.ts found under this directory."]
|
|
108
|
+
const lines: string[] = []
|
|
91
109
|
let anyChanged = false
|
|
92
110
|
for (const r of results) {
|
|
93
|
-
if (r.
|
|
94
|
-
|
|
111
|
+
if (r.error !== undefined) {
|
|
112
|
+
lines.push(
|
|
113
|
+
`✖ ${r.file}: routes/ does not build a route table; left untouched.`,
|
|
114
|
+
` ${r.error}`,
|
|
115
|
+
)
|
|
116
|
+
} else if (r.refusedEmpty) {
|
|
117
|
+
lines.push(
|
|
95
118
|
`⚠ ${r.file}: refused - routes/ has zero routes but the manifest had some; left untouched (delete the manifest yourself if this is intentional).`,
|
|
96
119
|
)
|
|
97
120
|
} else if (r.changed) {
|
|
98
121
|
anyChanged = true
|
|
99
|
-
|
|
100
|
-
if (r.added.length > 0)
|
|
101
|
-
if (r.removed.length > 0)
|
|
122
|
+
lines.push(`✓ synced ${r.file}`)
|
|
123
|
+
if (r.added.length > 0) lines.push(` added: ${r.added.join(", ")}`)
|
|
124
|
+
if (r.removed.length > 0) lines.push(` removed: ${r.removed.join(", ")}`)
|
|
102
125
|
} else {
|
|
103
|
-
|
|
126
|
+
lines.push(`• ${r.file} already in sync`)
|
|
104
127
|
}
|
|
105
128
|
}
|
|
106
129
|
if (anyChanged) {
|
|
107
|
-
|
|
130
|
+
lines.push(
|
|
108
131
|
"\nRe-synced the server manifest's route table. If you added or changed a HYDRATING route\n" +
|
|
109
132
|
"component (not just a loader / SSR-only route), also run a full build so the client bundle matches.",
|
|
110
133
|
)
|
|
111
134
|
}
|
|
112
|
-
return
|
|
135
|
+
return lines
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** CLI entry: sync every generated manifest and print the summary. False when a route table failed to build. */
|
|
139
|
+
export async function runSyncManifest(cwd: string): Promise<boolean> {
|
|
140
|
+
const results = await syncServerManifests(cwd)
|
|
141
|
+
console.log(renderSyncManifestResults(results).join("\n"))
|
|
142
|
+
return syncManifestsOk(results)
|
|
113
143
|
}
|