@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.
@@ -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 miss (an unreadable/absent routes dir is skipped).
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 // routes dir gone/unreadable - not a manifest we can sync
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
- /** CLI entry: sync every generated manifest, print a per-file summary + the client-rebuild caveat. */
85
- export async function runSyncManifest(cwd: string): Promise<boolean> {
86
- const results = await syncServerManifests(cwd)
87
- if (results.length === 0) {
88
- console.log("nifra sync-manifest: no generated server-manifest.ts found under this directory.")
89
- return true
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.refusedEmpty) {
94
- console.log(
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
- console.log(`✓ synced ${r.file}`)
100
- if (r.added.length > 0) console.log(` added: ${r.added.join(", ")}`)
101
- if (r.removed.length > 0) console.log(` removed: ${r.removed.join(", ")}`)
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
- console.log(`• ${r.file} already in sync`)
126
+ lines.push(`• ${r.file} already in sync`)
104
127
  }
105
128
  }
106
129
  if (anyChanged) {
107
- console.log(
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 true
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
  }