@sightmap/react 0.7.1 → 0.8.0

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/index.js DELETED
@@ -1,1149 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/cli/index.ts
4
- import { Command } from "commander";
5
-
6
- // src/cli/commands/gen.ts
7
- import { relative as relative3 } from "path";
8
- import { createTwoFilesPatch } from "diff";
9
-
10
- // src/plugin/orchestrator.ts
11
- import { promises as fs5 } from "fs";
12
- import { dirname as dirname3, join as join4 } from "path";
13
-
14
- // src/plugin/adapters/index.ts
15
- import { promises as fs3 } from "fs";
16
- import { join as join3 } from "path";
17
-
18
- // src/plugin/adapters/rr7-declarative.ts
19
- import { promises as fs, existsSync } from "fs";
20
- import { dirname, join, relative, resolve } from "path";
21
- import { parse as parseAst } from "@swc/core";
22
-
23
- // src/plugin/router-adapter.ts
24
- function deriveFeatureName(pattern) {
25
- const segments = pattern.split("/").filter(Boolean);
26
- for (const seg of segments) {
27
- if (!seg.startsWith(":") && !seg.startsWith("*")) return sanitize(seg);
28
- }
29
- return "home";
30
- }
31
- function deriveViewName(pattern) {
32
- const segments = pattern.split("/").filter(Boolean);
33
- if (segments.length === 0) return "Home";
34
- const last = segments[segments.length - 1];
35
- const lastIsParam = last.startsWith(":");
36
- const lastIsWildcard = last.startsWith("*");
37
- if (lastIsParam || lastIsWildcard) {
38
- const suffix = lastIsWildcard ? "Catchall" : "Detail";
39
- for (let i = segments.length - 2; i >= 0; i--) {
40
- const seg = segments[i];
41
- if (!seg.startsWith(":") && !seg.startsWith("*")) {
42
- return capitalize(sanitize(seg)) + suffix;
43
- }
44
- }
45
- return suffix;
46
- }
47
- return capitalize(sanitize(last));
48
- }
49
- function sanitize(s) {
50
- return s.replace(/[^a-zA-Z0-9_-]/g, "-").toLowerCase();
51
- }
52
- function capitalize(s) {
53
- return s.charAt(0).toUpperCase() + s.slice(1);
54
- }
55
-
56
- // src/plugin/diagnostics.ts
57
- var REACT_DYNAMIC_DATA_SIGHTMAP = "react.dynamic-data-sightmap";
58
- var REACT_DYNAMIC_ROUTE_PATH = "react.dynamic-route-path";
59
- var REACT_NO_ROUTES_DISCOVERED = "react.no-routes-discovered";
60
- var REACT_UNKNOWN_ROUTER = "react.unknown-router";
61
- var REACT_ROUTE_NO_VIEW_IN_YAML = "react.route-no-view-in-yaml";
62
- var REACT_VIEW_ALREADY_DEFINED = "react.view-already-defined";
63
- var REACT_LIVE_ONLY_VIEW = "react.live-only-view";
64
-
65
- // src/plugin/adapters/rr7-declarative.ts
66
- var ENTRY_CANDIDATES = ["src/main.tsx", "src/main.ts", "src/main.jsx", "src/main.js"];
67
- function rr7DeclarativeAdapter() {
68
- return {
69
- name: "react-router-7-declarative",
70
- detect(_projectRoot) {
71
- return true;
72
- },
73
- async discoverRoutes(ctx) {
74
- const entry = await findEntry(ctx.projectRoot);
75
- if (!entry) return [];
76
- const visited = /* @__PURE__ */ new Set();
77
- const routePatterns = [];
78
- const sourceFilesPerRoute = /* @__PURE__ */ new Map();
79
- const pathsConfig = await loadPathsConfig(ctx.projectRoot);
80
- await walkModule(entry, ctx, pathsConfig, visited, routePatterns, sourceFilesPerRoute);
81
- return routePatterns.map((pattern) => ({
82
- pattern,
83
- featureName: deriveFeatureName(pattern),
84
- sourceFiles: Array.from(sourceFilesPerRoute.get(pattern) ?? /* @__PURE__ */ new Set())
85
- }));
86
- }
87
- };
88
- }
89
- async function findEntry(projectRoot) {
90
- for (const candidate of ENTRY_CANDIDATES) {
91
- const path = join(projectRoot, candidate);
92
- try {
93
- await fs.access(path);
94
- return path;
95
- } catch {
96
- }
97
- }
98
- return null;
99
- }
100
- async function walkModule(file, ctx, pathsConfig, visited, routePatterns, sourceFilesPerRoute) {
101
- if (visited.has(file)) return;
102
- visited.add(file);
103
- let source;
104
- try {
105
- source = await fs.readFile(file, "utf-8");
106
- } catch {
107
- return;
108
- }
109
- const ast = await parseAst(source, { syntax: "typescript", tsx: true, target: "es2022" });
110
- const localImports = /* @__PURE__ */ new Map();
111
- collectImports(ast, file, pathsConfig, localImports);
112
- const routesFound = [];
113
- collectRoutes(
114
- ast,
115
- (path, elementSpecs) => {
116
- routesFound.push({ path, elementSpecs });
117
- },
118
- ctx.diagnostics ? (diag) => ctx.diagnostics.push(diag) : void 0,
119
- file,
120
- ctx.projectRoot
121
- );
122
- for (const { path, elementSpecs } of routesFound) {
123
- routePatterns.push(path);
124
- if (!sourceFilesPerRoute.has(path)) sourceFilesPerRoute.set(path, /* @__PURE__ */ new Set());
125
- sourceFilesPerRoute.get(path).add(file);
126
- const visitedForRoute = /* @__PURE__ */ new Set();
127
- for (const spec of elementSpecs) {
128
- const resolved = localImports.get(spec) ?? null;
129
- if (resolved) {
130
- await walkComponentTree(resolved, pathsConfig, sourceFilesPerRoute.get(path), visitedForRoute);
131
- }
132
- }
133
- }
134
- for (const target of localImports.values()) {
135
- await walkModule(target, ctx, pathsConfig, visited, routePatterns, sourceFilesPerRoute);
136
- }
137
- }
138
- async function walkComponentTree(file, pathsConfig, sourceFiles, visited) {
139
- if (visited.has(file)) return;
140
- visited.add(file);
141
- sourceFiles.add(file);
142
- let source;
143
- try {
144
- source = await fs.readFile(file, "utf-8");
145
- } catch {
146
- return;
147
- }
148
- const ast = await parseAst(source, { syntax: "typescript", tsx: true, target: "es2022" });
149
- const localImports = /* @__PURE__ */ new Map();
150
- collectImports(ast, file, pathsConfig, localImports);
151
- for (const target of localImports.values()) {
152
- await walkComponentTree(target, pathsConfig, sourceFiles, visited);
153
- }
154
- }
155
- function collectImports(ast, fromFile, pathsConfig, into) {
156
- for (const stmt of ast.body ?? []) {
157
- if (stmt.type === "ImportDeclaration") {
158
- const sourcePath = stmt.source?.value;
159
- if (!sourcePath) continue;
160
- const resolved = resolveImport(fromFile, sourcePath, pathsConfig);
161
- if (!resolved) continue;
162
- for (const spec of stmt.specifiers ?? []) {
163
- const local = spec.local?.value;
164
- if (typeof local === "string") into.set(local, resolved);
165
- }
166
- continue;
167
- }
168
- if (stmt.type === "VariableDeclaration") {
169
- for (const decl of stmt.declarations ?? []) {
170
- const local = decl.id?.value;
171
- if (typeof local !== "string" || !decl.init) continue;
172
- const dynamicSpec = findFirstDynamicImportSpec(decl.init);
173
- if (!dynamicSpec) continue;
174
- const resolved = resolveImport(fromFile, dynamicSpec, pathsConfig);
175
- if (!resolved) continue;
176
- into.set(local, resolved);
177
- }
178
- }
179
- }
180
- }
181
- function resolveImport(fromFile, importSpec, pathsConfig) {
182
- if (importSpec.startsWith(".")) {
183
- return resolveLocalImport(fromFile, importSpec);
184
- }
185
- if (!pathsConfig) return null;
186
- const candidates = expandAlias(importSpec, pathsConfig);
187
- if (!candidates) return null;
188
- for (const candidate of candidates) {
189
- const stripped = candidate.replace(/\.(js|jsx|mjs)$/, "");
190
- for (const ext of [".tsx", ".ts", ".jsx", ".js"]) {
191
- const p = `${stripped}${ext}`;
192
- if (existsSync(p)) return p;
193
- }
194
- for (const indexBase of ["index.tsx", "index.ts", "index.jsx", "index.js"]) {
195
- const p = resolve(stripped, indexBase);
196
- if (existsSync(p)) return p;
197
- }
198
- if (stripped !== candidate && existsSync(candidate)) return candidate;
199
- }
200
- return null;
201
- }
202
- function findFirstDynamicImportSpec(node) {
203
- if (!node || typeof node !== "object") return null;
204
- if (node.type === "CallExpression" && node.callee?.type === "Import" && Array.isArray(node.arguments) && node.arguments[0]?.expression?.type === "StringLiteral") {
205
- return node.arguments[0].expression.value;
206
- }
207
- for (const key of Object.keys(node)) {
208
- const v = node[key];
209
- if (Array.isArray(v)) {
210
- for (const c of v) {
211
- const found = findFirstDynamicImportSpec(c);
212
- if (found) return found;
213
- }
214
- } else if (v && typeof v === "object") {
215
- const found = findFirstDynamicImportSpec(v);
216
- if (found) return found;
217
- }
218
- }
219
- return null;
220
- }
221
- function resolveLocalImport(fromFile, importSpec) {
222
- const dir = dirname(fromFile);
223
- const stripped = importSpec.replace(/\.(js|jsx|mjs)$/, "");
224
- for (const ext of [".tsx", ".ts", ".jsx", ".js"]) {
225
- const candidate = resolve(dir, `${stripped}${ext}`);
226
- if (existsSync(candidate)) return candidate;
227
- }
228
- for (const indexBase of ["index.tsx", "index.ts", "index.jsx", "index.js"]) {
229
- const candidate = resolve(dir, stripped, indexBase);
230
- if (existsSync(candidate)) return candidate;
231
- }
232
- if (stripped !== importSpec) {
233
- const original = resolve(dir, importSpec);
234
- if (existsSync(original)) return original;
235
- }
236
- return null;
237
- }
238
- function collectRoutes(ast, onRoute, onDiagnostic, fromFile, projectRoot) {
239
- function visit(node, parentPath) {
240
- if (!node || typeof node !== "object") return;
241
- let here = parentPath;
242
- if (node.type === "JSXElement" && node.opening?.name?.value === "Route") {
243
- let pathValue = null;
244
- let pathAttrIsNonLiteral = false;
245
- let outerElementName = null;
246
- const elementSpecs = [];
247
- for (const attr of node.opening.attributes ?? []) {
248
- if (attr.type !== "JSXAttribute") continue;
249
- if (attr.name?.value === "path") {
250
- if (attr.value?.type === "StringLiteral") {
251
- pathValue = attr.value.value;
252
- } else if (attr.value) {
253
- pathAttrIsNonLiteral = true;
254
- }
255
- }
256
- if (attr.name?.value === "element" && attr.value?.type === "JSXExpressionContainer") {
257
- const inner = attr.value.expression;
258
- if (inner?.type === "JSXElement") {
259
- collectJsxElementNames(inner, elementSpecs);
260
- outerElementName = inner.opening?.name?.value ?? null;
261
- }
262
- }
263
- if (attr.name?.value === "Component" && attr.value?.type === "JSXExpressionContainer") {
264
- const inner = attr.value.expression;
265
- if (inner?.type === "Identifier" && typeof inner.value === "string") {
266
- elementSpecs.push(inner.value);
267
- }
268
- }
269
- }
270
- if (pathValue) {
271
- const joined = pathValue.startsWith("/") ? pathValue : `${parentPath.replace(/\/$/, "")}/${pathValue}`;
272
- if (outerElementName !== "Navigate") {
273
- onRoute(joined, elementSpecs);
274
- }
275
- here = joined;
276
- } else if (pathAttrIsNonLiteral && onDiagnostic) {
277
- const where = fromFile && projectRoot ? relative(projectRoot, fromFile) : fromFile ?? "<unknown>";
278
- onDiagnostic({
279
- severity: "info",
280
- code: REACT_DYNAMIC_ROUTE_PATH,
281
- message: `Route in ${where} uses a non-literal \`path\` (template literal / identifier / expression). Static AST discovery cannot resolve dynamic paths; hand-author this view in a feature YAML or refactor to a literal string.`
282
- });
283
- }
284
- }
285
- for (const key of Object.keys(node)) {
286
- const v = node[key];
287
- if (Array.isArray(v)) v.forEach((c) => visit(c, here));
288
- else if (v && typeof v === "object") visit(v, here);
289
- }
290
- }
291
- visit(ast, "");
292
- }
293
- function collectJsxElementNames(node, into) {
294
- if (!node || typeof node !== "object") return;
295
- if (node.type === "JSXElement") {
296
- const name = node.opening?.name?.value;
297
- if (typeof name === "string") into.push(name);
298
- }
299
- for (const key of Object.keys(node)) {
300
- const v = node[key];
301
- if (Array.isArray(v)) v.forEach((c) => collectJsxElementNames(c, into));
302
- else if (v && typeof v === "object") collectJsxElementNames(v, into);
303
- }
304
- }
305
- async function loadPathsConfig(projectRoot) {
306
- const merged = {};
307
- let baseUrl = null;
308
- for (const name of ["tsconfig.json", "tsconfig.app.json"]) {
309
- const fp = join(projectRoot, name);
310
- let raw;
311
- try {
312
- raw = await fs.readFile(fp, "utf-8");
313
- } catch {
314
- continue;
315
- }
316
- const json = parseTsconfigJson(raw);
317
- const co = json?.compilerOptions;
318
- if (!co) continue;
319
- if (!baseUrl && typeof co.baseUrl === "string") {
320
- baseUrl = resolve(dirname(fp), co.baseUrl);
321
- }
322
- if (co.paths && typeof co.paths === "object") {
323
- for (const [k, v] of Object.entries(co.paths)) {
324
- if (Array.isArray(v)) merged[k] = v;
325
- }
326
- }
327
- }
328
- if (Object.keys(merged).length === 0) return null;
329
- return { baseUrl: baseUrl ?? projectRoot, paths: merged };
330
- }
331
- function parseTsconfigJson(raw) {
332
- const stripped = raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1").replace(/,(\s*[}\]])/g, "$1");
333
- try {
334
- return JSON.parse(stripped);
335
- } catch {
336
- return null;
337
- }
338
- }
339
- function expandAlias(importSpec, cfg) {
340
- for (const [alias, targets] of Object.entries(cfg.paths)) {
341
- if (alias.endsWith("/*")) {
342
- const prefix = alias.slice(0, -1);
343
- if (importSpec.startsWith(prefix)) {
344
- const rest = importSpec.slice(prefix.length);
345
- return targets.map((t) => {
346
- const tnorm = t.endsWith("/*") ? t.slice(0, -2) : t;
347
- return resolve(cfg.baseUrl, tnorm, rest);
348
- });
349
- }
350
- } else if (importSpec === alias) {
351
- return targets.map((t) => resolve(cfg.baseUrl, t));
352
- }
353
- }
354
- return null;
355
- }
356
-
357
- // src/plugin/adapters/rr7-framework.ts
358
- import { promises as fs2, existsSync as existsSync2 } from "fs";
359
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
360
- import { parse as parseAst2 } from "@swc/core";
361
- var ROUTES_CONFIG_CANDIDATES = [
362
- "app/routes.ts",
363
- "app/routes.tsx",
364
- "app/routes.js",
365
- "app/routes.jsx"
366
- ];
367
- var APP_DIR = "app";
368
- function rr7FrameworkAdapter() {
369
- return {
370
- name: "react-router-7-framework",
371
- detect(projectRoot) {
372
- return findRoutesConfigSync(projectRoot) !== null;
373
- },
374
- async discoverRoutes(ctx) {
375
- const routesFile = await findRoutesConfig(ctx.projectRoot);
376
- if (!routesFile) return [];
377
- const appDir = join2(ctx.projectRoot, APP_DIR);
378
- const source = await fs2.readFile(routesFile, "utf-8");
379
- const ast = await parseAst2(source, { syntax: "typescript", tsx: true, target: "es2022" });
380
- const defaultArray = findDefaultExportArray(ast);
381
- if (!defaultArray) return [];
382
- const collected = [];
383
- await walkRouteArray(defaultArray, { parentPattern: "", inheritedFiles: /* @__PURE__ */ new Set() }, appDir, collected);
384
- return collected.map(({ pattern, sourceFiles }) => ({
385
- pattern,
386
- featureName: deriveFeatureName(pattern),
387
- sourceFiles: Array.from(sourceFiles)
388
- }));
389
- }
390
- };
391
- }
392
- function findRoutesConfigSync(projectRoot) {
393
- for (const candidate of ROUTES_CONFIG_CANDIDATES) {
394
- const path = join2(projectRoot, candidate);
395
- if (existsSync2(path)) return path;
396
- }
397
- return null;
398
- }
399
- async function findRoutesConfig(projectRoot) {
400
- for (const candidate of ROUTES_CONFIG_CANDIDATES) {
401
- const path = join2(projectRoot, candidate);
402
- try {
403
- await fs2.access(path);
404
- return path;
405
- } catch {
406
- }
407
- }
408
- return null;
409
- }
410
- function findDefaultExportArray(ast) {
411
- for (const stmt of ast.body ?? []) {
412
- if (stmt.type === "ExportDefaultExpression") {
413
- return unwrapArray(stmt.expression);
414
- }
415
- if (stmt.type === "ExportDefaultDeclaration") {
416
- const decl = stmt.decl ?? stmt.declaration;
417
- if (decl) return unwrapArray(decl);
418
- }
419
- }
420
- return null;
421
- }
422
- function unwrapArray(node) {
423
- if (!node) return null;
424
- if (node.type === "ArrayExpression") return node;
425
- if (node.type === "TsSatisfiesExpression" || node.type === "TsAsExpression" || node.type === "TsTypeAssertion") {
426
- return unwrapArray(node.expression);
427
- }
428
- if (node.type === "ParenthesisExpression") return unwrapArray(node.expression);
429
- return null;
430
- }
431
- async function walkRouteArray(arrayNode, frame, appDir, out) {
432
- for (const elem of arrayNode.elements ?? []) {
433
- if (!elem) continue;
434
- const expr = elem.expression ?? elem;
435
- const isSpread = !!elem.spread;
436
- if (isSpread) {
437
- if (expr?.type === "CallExpression" && callName(expr) === "prefix") {
438
- const args = expr.arguments ?? [];
439
- const prefixArg = stringArg(args[0]);
440
- const childrenArr = arrayArg(args[1]);
441
- if (prefixArg !== null && childrenArr) {
442
- const childFrame = {
443
- parentPattern: joinPath(frame.parentPattern, prefixArg),
444
- inheritedFiles: frame.inheritedFiles
445
- };
446
- await walkRouteArray(childrenArr, childFrame, appDir, out);
447
- }
448
- }
449
- continue;
450
- }
451
- if (expr?.type !== "CallExpression") continue;
452
- await processRouteCall(expr, frame, appDir, out);
453
- }
454
- }
455
- async function processRouteCall(call, frame, appDir, out) {
456
- const name = callName(call);
457
- const args = call.arguments ?? [];
458
- if (name === "route") {
459
- const pathArg = stringArg(args[0]);
460
- const fileArg = stringArg(args[1]);
461
- if (pathArg === null || fileArg === null) return;
462
- const pattern = joinPath(frame.parentPattern, pathArg);
463
- const sourceFiles = new Set(frame.inheritedFiles);
464
- const resolvedFile = resolveAppFile(appDir, fileArg);
465
- if (resolvedFile) await walkComponentTree2(resolvedFile, sourceFiles, /* @__PURE__ */ new Set());
466
- out.push({ pattern, sourceFiles });
467
- const childrenArr = arrayArg(args[2]);
468
- if (childrenArr) {
469
- const childFrame = {
470
- parentPattern: pattern,
471
- inheritedFiles: union(frame.inheritedFiles, sourceFiles)
472
- };
473
- await walkRouteArray(childrenArr, childFrame, appDir, out);
474
- }
475
- return;
476
- }
477
- if (name === "index") {
478
- const fileArg = stringArg(args[0]);
479
- if (fileArg === null) return;
480
- const pattern = frame.parentPattern || "/";
481
- const sourceFiles = new Set(frame.inheritedFiles);
482
- const resolvedFile = resolveAppFile(appDir, fileArg);
483
- if (resolvedFile) await walkComponentTree2(resolvedFile, sourceFiles, /* @__PURE__ */ new Set());
484
- out.push({ pattern, sourceFiles });
485
- return;
486
- }
487
- if (name === "layout") {
488
- const fileArg = stringArg(args[0]);
489
- const childrenArr = arrayArg(args[1]);
490
- if (fileArg === null || !childrenArr) return;
491
- const layoutFiles = /* @__PURE__ */ new Set();
492
- const resolvedFile = resolveAppFile(appDir, fileArg);
493
- if (resolvedFile) await walkComponentTree2(resolvedFile, layoutFiles, /* @__PURE__ */ new Set());
494
- const childFrame = {
495
- parentPattern: frame.parentPattern,
496
- inheritedFiles: union(frame.inheritedFiles, layoutFiles)
497
- };
498
- await walkRouteArray(childrenArr, childFrame, appDir, out);
499
- return;
500
- }
501
- }
502
- function callName(call) {
503
- const callee = call?.callee;
504
- if (!callee) return null;
505
- if (callee.type === "Identifier") return callee.value ?? null;
506
- return null;
507
- }
508
- function stringArg(arg) {
509
- const expr = arg?.expression ?? arg;
510
- if (expr?.type === "StringLiteral") return expr.value ?? null;
511
- return null;
512
- }
513
- function arrayArg(arg) {
514
- const expr = arg?.expression ?? arg;
515
- if (expr?.type === "ArrayExpression") return expr;
516
- return null;
517
- }
518
- function joinPath(parent, child) {
519
- if (child.startsWith("/")) return child;
520
- if (!child) return parent || "/";
521
- const base = (parent || "").replace(/\/+$/, "");
522
- const tail = child.replace(/^\/+/, "");
523
- return base === "" ? `/${tail}` : `${base}/${tail}`;
524
- }
525
- function resolveAppFile(appDir, fileSpec) {
526
- const base = resolve2(appDir, fileSpec);
527
- if (existsSync2(base)) return base;
528
- const stripped = base.replace(/\.(js|jsx|mjs)$/, "");
529
- for (const ext of [".tsx", ".ts", ".jsx", ".js"]) {
530
- const candidate = `${stripped}${ext}`;
531
- if (existsSync2(candidate)) return candidate;
532
- }
533
- return null;
534
- }
535
- function union(a, b) {
536
- const out = new Set(a);
537
- for (const v of b) out.add(v);
538
- return out;
539
- }
540
- async function walkComponentTree2(file, sourceFiles, visited) {
541
- if (visited.has(file)) return;
542
- visited.add(file);
543
- sourceFiles.add(file);
544
- let source;
545
- try {
546
- source = await fs2.readFile(file, "utf-8");
547
- } catch {
548
- return;
549
- }
550
- const ast = await parseAst2(source, { syntax: "typescript", tsx: true, target: "es2022" });
551
- for (const stmt of ast.body ?? []) {
552
- if (stmt.type !== "ImportDeclaration") continue;
553
- const sourcePath = stmt.source?.value;
554
- if (!sourcePath || !sourcePath.startsWith(".")) continue;
555
- const resolved = resolveLocalImport2(file, sourcePath);
556
- if (!resolved) continue;
557
- await walkComponentTree2(resolved, sourceFiles, visited);
558
- }
559
- }
560
- function resolveLocalImport2(fromFile, importSpec) {
561
- const dir = dirname2(fromFile);
562
- const stripped = importSpec.replace(/\.(js|jsx|mjs)$/, "");
563
- for (const ext of [".tsx", ".ts", ".jsx", ".js"]) {
564
- const candidate = resolve2(dir, `${stripped}${ext}`);
565
- if (existsSync2(candidate)) return candidate;
566
- }
567
- if (stripped !== importSpec) {
568
- const original = resolve2(dir, importSpec);
569
- if (existsSync2(original)) return original;
570
- }
571
- return null;
572
- }
573
-
574
- // src/plugin/adapters/index.ts
575
- async function resolveAdapter(choice, projectRoot) {
576
- if (typeof choice === "string") {
577
- if (choice === "react-router-7") return rr7DeclarativeAdapter();
578
- if (choice === "react-router-7-framework") return rr7FrameworkAdapter();
579
- if (choice === "auto") {
580
- if (projectRoot) {
581
- const framework = rr7FrameworkAdapter();
582
- if (framework.detect(projectRoot)) return framework;
583
- }
584
- const pkg = await readPackageJson(projectRoot);
585
- if (pkg?.dependencies?.["react-router"] || pkg?.devDependencies?.["react-router"]) {
586
- return rr7DeclarativeAdapter();
587
- }
588
- return null;
589
- }
590
- }
591
- return choice;
592
- }
593
- async function readPackageJson(projectRoot) {
594
- if (!projectRoot) return null;
595
- try {
596
- const raw = await fs3.readFile(join3(projectRoot, "package.json"), "utf-8");
597
- return JSON.parse(raw);
598
- } catch {
599
- return null;
600
- }
601
- }
602
-
603
- // src/plugin/ast/scan.ts
604
- import { promises as fs4 } from "fs";
605
- import { parse } from "@swc/core";
606
- async function scanFile(file) {
607
- const source = await fs4.readFile(file, "utf-8");
608
- const ast = await parse(source, {
609
- syntax: "typescript",
610
- tsx: true,
611
- target: "es2022"
612
- });
613
- const findings = [];
614
- const diagnostics = [];
615
- walk(ast, (node) => {
616
- if (node?.type === "JSXOpeningElement" && Array.isArray(node.attributes)) {
617
- for (const attr of node.attributes) {
618
- if (attr?.type !== "JSXAttribute") continue;
619
- const nameNode = attr.name;
620
- if (!nameNode || nameNode.value !== "data-sightmap") continue;
621
- const valueNode = attr.value;
622
- if (valueNode?.type === "StringLiteral") {
623
- findings.push({
624
- name: valueNode.value,
625
- file,
626
- line: attr.span?.start ?? 0,
627
- // SWC byte offset — converted to line in caller if needed
628
- col: 0
629
- });
630
- } else if (valueNode?.type === "JSXExpressionContainer") {
631
- diagnostics.push({
632
- severity: "warning",
633
- code: REACT_DYNAMIC_DATA_SIGHTMAP,
634
- message: `data-sightmap value must be a static string. Skipping.
635
- Consider one of:
636
- - Split into separate components with static markers (e.g. LoginForm + SignupForm)
637
- - Use a static marker on the parent and target via descendant selector:
638
- [data-sightmap="LoginPage"] form
639
- - If you genuinely need dynamic markers, hand-author the entries in extras.yaml`,
640
- file
641
- });
642
- }
643
- }
644
- }
645
- });
646
- return { findings, diagnostics };
647
- }
648
- function walk(node, visit) {
649
- if (!node || typeof node !== "object") return;
650
- visit(node);
651
- for (const key of Object.keys(node)) {
652
- const v = node[key];
653
- if (Array.isArray(v)) v.forEach((c) => walk(c, visit));
654
- else if (v && typeof v === "object") walk(v, visit);
655
- }
656
- }
657
-
658
- // src/plugin/merge/smart-merge.ts
659
- import { Document, parseDocument, isMap, isSeq, YAMLMap, YAMLSeq, Scalar } from "yaml";
660
-
661
- // src/plugin/merge/todo-scaffolding.ts
662
- var SCAFFOLDING = `#
663
- # Agent-authored fields below \u2014 uncomment and fill in.
664
- #
665
- # TODO: capture observed runtime behavior the agent should know about
666
- # (one short sentence per entry; no marketing copy).
667
- # memory:
668
- # - "Form auto-submits on Enter; verify disabled-button logic."
669
- # - "Submit returns 401 on bad credentials."
670
- #
671
- # TODO: describe what this view is for in one sentence.
672
- # intent: "Authenticate the user before entering the dashboard."
673
- #
674
- # TODO: list HTTP requests this view fires (method + path).
675
- # requests:
676
- # - method: POST
677
- # path: /api/login
678
- #
679
- `;
680
- function renderTodoScaffolding() {
681
- return SCAFFOLDING;
682
- }
683
-
684
- // src/plugin/merge/smart-merge.ts
685
- var HEADER = `# This file is co-maintained by @sightmap/react and your agent.
686
- # Plugin updates: name, route, selector, children.
687
- # Agent curates: memory, intent, requests, and any unknown fields.
688
- # See https://sightmap.org/react for the full convention.
689
- `;
690
- function smartMerge(input) {
691
- const { existing, desired } = input;
692
- const isNew = existing == null;
693
- const doc = isNew ? new Document({ version: 1, views: [] }) : parseDocument(existing);
694
- ensureScalar(doc, "version", 1);
695
- const viewsSeq = ensureSeq(doc, "views");
696
- reconcileView(viewsSeq, desired);
697
- let yaml = doc.toString();
698
- if (isNew) {
699
- yaml = HEADER + yaml;
700
- if (!yaml.endsWith("\n")) yaml += "\n";
701
- yaml += "\n";
702
- yaml += renderTodoScaffolding();
703
- }
704
- const changed = isNew || normalize(yaml) !== normalize(existing);
705
- return { changed, yaml };
706
- }
707
- function ensureScalar(doc, key, value) {
708
- doc.set(key, value);
709
- }
710
- function ensureSeq(doc, key) {
711
- const node = doc.get(key, true);
712
- if (node && isSeq(node)) return node;
713
- const seq = new YAMLSeq();
714
- doc.set(key, seq);
715
- return seq;
716
- }
717
- function reconcileView(viewsSeq, desired) {
718
- const { view, components } = desired;
719
- let existingView = null;
720
- let matchedBy = null;
721
- for (const item of viewsSeq.items) {
722
- if (isMap(item) && getString(item, "name") === view.name) {
723
- existingView = item;
724
- matchedBy = "name";
725
- break;
726
- }
727
- }
728
- if (!existingView) {
729
- for (const item of viewsSeq.items) {
730
- if (isMap(item) && getString(item, "route") === view.route) {
731
- existingView = item;
732
- matchedBy = "route";
733
- break;
734
- }
735
- }
736
- }
737
- if (!existingView) {
738
- existingView = new YAMLMap();
739
- viewsSeq.add(existingView);
740
- }
741
- if (matchedBy !== "route") setMapEntry(existingView, "name", view.name);
742
- setMapEntry(existingView, "route", view.route);
743
- let componentsSeq = existingView.get("components", true);
744
- if (!componentsSeq || !isSeq(componentsSeq)) {
745
- componentsSeq = new YAMLSeq();
746
- existingView.set("components", componentsSeq);
747
- }
748
- reconcileComponents(componentsSeq, components);
749
- }
750
- function reconcileComponents(seq, desired) {
751
- for (const desiredComp of desired) {
752
- let existing = null;
753
- for (const item of seq.items) {
754
- if (isMap(item) && getString(item, "name") === desiredComp.name) {
755
- existing = item;
756
- break;
757
- }
758
- }
759
- if (!existing) {
760
- existing = new YAMLMap();
761
- seq.add(existing);
762
- }
763
- setMapEntry(existing, "name", desiredComp.name);
764
- setMapEntry(existing, "selector", desiredComp.selector);
765
- if (desiredComp.children && desiredComp.children.length > 0) {
766
- let childSeq = existing.get("children", true);
767
- if (!childSeq || !isSeq(childSeq)) {
768
- childSeq = new YAMLSeq();
769
- existing.set("children", childSeq);
770
- }
771
- reconcileComponents(childSeq, desiredComp.children);
772
- }
773
- }
774
- }
775
- function getString(map, key) {
776
- const v = map.get(key, true);
777
- if (v instanceof Scalar && typeof v.value === "string") return v.value;
778
- return null;
779
- }
780
- function setMapEntry(map, key, value) {
781
- map.set(key, value);
782
- }
783
- function normalize(s) {
784
- return s.replace(/\s+$/, "");
785
- }
786
-
787
- // src/plugin/orchestrator.ts
788
- import { loadDirectory, canonicalize } from "@sightmap/sightmap";
789
- async function runOnce(opts) {
790
- const adapter = await resolveAdapter(opts.router, opts.projectRoot);
791
- if (!adapter) {
792
- return {
793
- written: [],
794
- changes: [],
795
- diagnostics: [
796
- {
797
- severity: "warning",
798
- code: REACT_UNKNOWN_ROUTER,
799
- message: `No supported router detected at ${opts.projectRoot}. Skipping sightmap generation.`
800
- }
801
- ]
802
- };
803
- }
804
- const sightmapDir = join4(opts.projectRoot, opts.outputDir);
805
- if (!opts.dryRun) await fs5.mkdir(sightmapDir, { recursive: true });
806
- let existingViewNames = /* @__PURE__ */ new Set();
807
- let existingViewRoutes = /* @__PURE__ */ new Set();
808
- try {
809
- const existing = await loadDirectory(sightmapDir);
810
- for (const v of existing.views ?? []) {
811
- existingViewNames.add(v.name);
812
- existingViewRoutes.add(v.route);
813
- }
814
- } catch {
815
- }
816
- const written = [];
817
- const changes = [];
818
- const allDiagnostics = [];
819
- const routes = await adapter.discoverRoutes({
820
- projectRoot: opts.projectRoot,
821
- resolveSource: (id) => join4(opts.projectRoot, id),
822
- diagnostics: allDiagnostics
823
- });
824
- if (routes.length === 0) {
825
- allDiagnostics.push({
826
- severity: "warning",
827
- code: REACT_NO_ROUTES_DISCOVERED,
828
- message: `No routes discovered by the ${adapter.name} adapter. Common causes: the entry file isn't where the adapter looks (declarative: \`src/main.{tsx,ts,jsx,js}\`; framework: \`app/routes.ts\`); the project uses RR7 data-mode (\`createBrowserRouter\`), not yet supported; the framework project uses \`flatRoutes()\` filesystem routing, not yet supported; the entry imports the app via a path alias defined outside tsconfig (e.g. only in \`vite.config.ts\`) so the adapter can't resolve it; or no \`<Routes>\` JSX / programmatic \`route(...)\` config is reachable from the entry. If your routes are real, surface them via \`sightmap-react gen --live <url>\`.`
829
- });
830
- }
831
- const componentsByRoute = /* @__PURE__ */ new Map();
832
- const componentRouteCount = /* @__PURE__ */ new Map();
833
- for (const route of routes) {
834
- const findings = await collectFindings(route.sourceFiles, allDiagnostics);
835
- componentsByRoute.set(route.pattern, findings.map(toComponent));
836
- for (const f of findings) {
837
- if (!componentRouteCount.has(f.name)) componentRouteCount.set(f.name, /* @__PURE__ */ new Set());
838
- componentRouteCount.get(f.name).add(route.pattern);
839
- }
840
- }
841
- const sharedComponents = [];
842
- for (const [name, routesFor] of componentRouteCount) {
843
- if (routesFor.size > 1) {
844
- sharedComponents.push({
845
- name,
846
- selector: `[data-sightmap="${name}"]`
847
- });
848
- for (const r of routesFor) {
849
- const list = componentsByRoute.get(r) ?? [];
850
- const filtered = list.filter((c) => c.name !== name);
851
- componentsByRoute.set(r, filtered);
852
- }
853
- }
854
- }
855
- const byFeature = /* @__PURE__ */ new Map();
856
- for (const r of routes) {
857
- if (!byFeature.has(r.featureName)) byFeature.set(r.featureName, []);
858
- byFeature.get(r.featureName).push(r);
859
- }
860
- for (const [featureName, featureRoutes] of byFeature) {
861
- const filePath = join4(sightmapDir, `${featureName}.yaml`);
862
- const targetFileExists = await fs5.access(filePath).then(() => true).catch(() => false);
863
- const before = await readIfExists(filePath);
864
- let currentYaml = before;
865
- let anyChanged = false;
866
- let emittedAnyView = false;
867
- for (const route of featureRoutes) {
868
- const viewName = deriveViewName(route.pattern);
869
- const components = componentsByRoute.get(route.pattern) ?? [];
870
- const isAlreadyDefinedElsewhere = !targetFileExists && (existingViewNames.has(viewName) || existingViewRoutes.has(route.pattern));
871
- if (isAlreadyDefinedElsewhere) {
872
- allDiagnostics.push({
873
- severity: "info",
874
- code: REACT_VIEW_ALREADY_DEFINED,
875
- message: `Route ${route.pattern} (view "${viewName}") is already defined in an existing YAML file outside the per-feature convention. Skipping stub emission. To migrate to the per-feature layout, move the view definition to ${featureName}.yaml.`
876
- });
877
- continue;
878
- }
879
- const merged = smartMerge({
880
- existing: currentYaml,
881
- desired: {
882
- view: { name: viewName, route: route.pattern },
883
- components
884
- }
885
- });
886
- currentYaml = merged.yaml;
887
- if (merged.changed) anyChanged = true;
888
- emittedAnyView = true;
889
- }
890
- if (emittedAnyView && currentYaml !== null) {
891
- const finalYaml = toCanonical(currentYaml, filePath);
892
- const changed = anyChanged || finalYaml !== before;
893
- changes.push({ path: filePath, before, after: finalYaml, changed });
894
- if (changed && !opts.dryRun) {
895
- await atomicWrite(filePath, finalYaml);
896
- written.push(filePath);
897
- }
898
- }
899
- }
900
- if (sharedComponents.length > 0) {
901
- const filePath = join4(sightmapDir, opts.sharedFile);
902
- const existing = await readIfExists(filePath);
903
- const merged = smartMerge({
904
- existing,
905
- desired: {
906
- view: { name: "Shared", route: "/**" },
907
- // sentinel — `Shared` is global, route matches any
908
- components: sharedComponents
909
- }
910
- });
911
- const finalYaml = toCanonical(merged.yaml, filePath);
912
- const changed = merged.changed || finalYaml !== existing;
913
- changes.push({ path: filePath, before: existing, after: finalYaml, changed });
914
- if (changed && !opts.dryRun) {
915
- await atomicWrite(filePath, finalYaml);
916
- written.push(filePath);
917
- }
918
- }
919
- if (opts.liveSightmap?.views?.length) {
920
- const staticViewNames = /* @__PURE__ */ new Set();
921
- const staticViewRoutes = /* @__PURE__ */ new Set();
922
- for (const r of routes) staticViewNames.add(deriveViewName(r.pattern));
923
- for (const n of existingViewNames) staticViewNames.add(n);
924
- for (const rt of existingViewRoutes) staticViewRoutes.add(rt);
925
- for (const c of changes) {
926
- for (const m of c.after.matchAll(/^\s*-\s+name:\s*(\S+)/gm)) {
927
- staticViewNames.add(m[1]);
928
- }
929
- }
930
- for (const liveView of opts.liveSightmap.views) {
931
- if (!liveView || typeof liveView.name !== "string") continue;
932
- if (staticViewNames.has(liveView.name) || staticViewRoutes.has(liveView.route)) continue;
933
- const safeFile = liveView.name.toLowerCase().replace(/[^a-z0-9-_]/g, "-");
934
- allDiagnostics.push({
935
- severity: "info",
936
- code: REACT_LIVE_ONLY_VIEW,
937
- message: `View "${liveView.name}" (route ${liveView.route}) appeared at runtime but was not discovered by the static AST scan. Add a data-sightmap attribute or hand-author a .sightmap/${safeFile}.yaml file.`
938
- });
939
- }
940
- }
941
- return { written, changes, diagnostics: allDiagnostics };
942
- }
943
- async function collectFindings(files, diagnostics) {
944
- const out = [];
945
- for (const f of files) {
946
- const result = await scanFile(f);
947
- out.push(...result.findings);
948
- diagnostics.push(...result.diagnostics);
949
- }
950
- return out;
951
- }
952
- function toComponent(f) {
953
- return { name: f.name, selector: `[data-sightmap="${f.name}"]` };
954
- }
955
- async function readIfExists(path) {
956
- try {
957
- return await fs5.readFile(path, "utf-8");
958
- } catch (e) {
959
- if (e.code === "ENOENT") return null;
960
- throw e;
961
- }
962
- }
963
- function toCanonical(yaml, file) {
964
- const c = canonicalize(yaml, { file });
965
- return c.kind === "canonical" ? c.text : yaml;
966
- }
967
- async function atomicWrite(path, contents) {
968
- await fs5.mkdir(dirname3(path), { recursive: true });
969
- const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
970
- await fs5.writeFile(tmp, contents, "utf-8");
971
- await fs5.rename(tmp, path);
972
- }
973
-
974
- // src/cli/commands/gen-summary.ts
975
- import { relative as relative2 } from "path";
976
- import { parse as yamlParse } from "yaml";
977
- var AUDIT_HINT = "Run /sightmap:audit (Claude Code plugin) to surface missing memory and broken selectors.";
978
- function formatSummary(input) {
979
- const { cwd, changes, written } = input;
980
- const allChanged = changes.filter((c) => c.changed);
981
- const anyChanged = allChanged.length > 0;
982
- const everyChangeIsNew = changes.length > 0 && changes.every((c) => c.before === null);
983
- if (!anyChanged) {
984
- return { kind: "up-to-date", lines: ["sightmap: up to date"] };
985
- }
986
- if (everyChangeIsNew) {
987
- return formatFirstRun(input);
988
- }
989
- const lines = [];
990
- lines.push(`sightmap: wrote ${written.length} file(s):`);
991
- for (const c of allChanged) {
992
- lines.push(` - ${relative2(cwd, c.path)}`);
993
- }
994
- return { kind: "structural-update", lines };
995
- }
996
- function formatFirstRun(input) {
997
- const { cwd, changes, written } = input;
998
- const lines = [];
999
- let viewCount = 0;
1000
- let componentCount = 0;
1001
- const viewNames = [];
1002
- for (const c of changes) {
1003
- try {
1004
- const parsed = yamlParse(c.after);
1005
- const views = parsed?.views ?? [];
1006
- for (const v of views) {
1007
- viewCount++;
1008
- if (typeof v.name === "string") viewNames.push(v.name);
1009
- const comps = Array.isArray(v.components) ? v.components : [];
1010
- componentCount += comps.length;
1011
- }
1012
- } catch {
1013
- }
1014
- }
1015
- const sortedForEdit = [...changes].sort(
1016
- (a, b) => relative2(cwd, a.path).localeCompare(relative2(cwd, b.path))
1017
- );
1018
- lines.push(`sightmap: wrote ${written.length} file(s) (first run on empty .sightmap/)`);
1019
- lines.push("");
1020
- lines.push(`Views (${viewCount} total)`);
1021
- lines.push(` views: ${viewNames.join(", ")}`);
1022
- lines.push(`Components linked: ${componentCount}`);
1023
- lines.push("");
1024
- lines.push("Next steps \u2014 open these files and replace the TODO blocks with real values:");
1025
- const editTargets = sortedForEdit.filter((c) => !c.path.endsWith("shared.yaml"));
1026
- for (const c of editTargets) {
1027
- lines.push(` - ${relative2(cwd, c.path)}`);
1028
- }
1029
- lines.push("");
1030
- lines.push(AUDIT_HINT);
1031
- return { kind: "first-run", lines };
1032
- }
1033
-
1034
- // src/cli/commands/gen.ts
1035
- async function genCommand(opts) {
1036
- const dryRun = !!(opts.check || opts.diff);
1037
- let liveSightmap;
1038
- if (opts.live) {
1039
- try {
1040
- const res = await fetch(opts.live);
1041
- if (!res.ok) {
1042
- console.error(
1043
- `sightmap: --live fetch failed with HTTP ${res.status}; continuing without live snapshot`
1044
- );
1045
- } else {
1046
- liveSightmap = await res.json();
1047
- }
1048
- } catch (err) {
1049
- console.error(
1050
- `sightmap: --live fetch failed (${err.message}); continuing without live snapshot`
1051
- );
1052
- }
1053
- }
1054
- const result = await runOnce({
1055
- projectRoot: opts.cwd,
1056
- router: opts.router,
1057
- outputDir: opts.output,
1058
- sharedFile: "shared.yaml",
1059
- prune: "warn",
1060
- dryRun,
1061
- ...liveSightmap ? { liveSightmap } : {}
1062
- });
1063
- const wouldChange = result.changes.filter((c) => c.changed);
1064
- if (opts.diff) {
1065
- for (const c of wouldChange) {
1066
- const rel = relative3(opts.cwd, c.path);
1067
- const oldName = c.before == null ? "/dev/null" : `a/${rel}`;
1068
- const newName = `b/${rel}`;
1069
- const patch = createTwoFilesPatch(oldName, newName, c.before ?? "", c.after);
1070
- process.stdout.write(patch);
1071
- }
1072
- }
1073
- if (opts.check) {
1074
- if (wouldChange.length > 0) {
1075
- console.error(
1076
- `sightmap: ${wouldChange.length} file(s) would change. Run \`sightmap-react gen\` to update.`
1077
- );
1078
- for (const c of wouldChange) console.error(` - ${relative3(opts.cwd, c.path)}`);
1079
- reportDiagnostics(result.diagnostics);
1080
- process.exit(1);
1081
- }
1082
- console.log("sightmap: up to date");
1083
- } else if (opts.diff) {
1084
- if (wouldChange.length === 0) console.log("sightmap: no changes");
1085
- } else {
1086
- const summary = formatSummary({
1087
- cwd: opts.cwd,
1088
- changes: result.changes,
1089
- written: result.written
1090
- });
1091
- for (const line of summary.lines) console.log(line);
1092
- }
1093
- if (reportDiagnostics(result.diagnostics)) process.exit(1);
1094
- }
1095
- function reportDiagnostics(diagnostics) {
1096
- let hadError = false;
1097
- for (const d of diagnostics) {
1098
- const tag = d.severity.toUpperCase();
1099
- const loc = [d.file, d.path].filter(Boolean).join(" :: ");
1100
- console.error(`[${tag}] ${d.code}: ${d.message}${loc ? ` (${loc})` : ""}`);
1101
- if (d.severity === "error") hadError = true;
1102
- }
1103
- return hadError;
1104
- }
1105
-
1106
- // src/cli/commands/validate.ts
1107
- import { join as join5 } from "path";
1108
- import { loadDirectory as loadDirectory2 } from "@sightmap/sightmap";
1109
- async function validateCommand(opts) {
1110
- const dir = join5(opts.cwd, opts.output);
1111
- const sightmap = await loadDirectory2(dir);
1112
- let exitCode = 0;
1113
- for (const diag of sightmap.diagnostics) {
1114
- if (diag.severity === "error") {
1115
- console.error(`[ERROR] ${diag.code}: ${diag.message}${diag.path ? ` (${diag.path})` : ""}`);
1116
- exitCode = 1;
1117
- }
1118
- }
1119
- const adapter = await resolveAdapter(opts.router, opts.cwd);
1120
- if (!adapter) {
1121
- console.error(`[ERROR] react.unknown-router: No supported router detected at ${opts.cwd}. Cannot validate.`);
1122
- process.exit(1);
1123
- }
1124
- const routes = await adapter.discoverRoutes({
1125
- projectRoot: opts.cwd,
1126
- resolveSource: (id) => join5(opts.cwd, id)
1127
- });
1128
- const viewRoutes = /* @__PURE__ */ new Set();
1129
- for (const v of sightmap.views ?? []) viewRoutes.add(v.route);
1130
- for (const r of routes) {
1131
- if (!viewRoutes.has(r.pattern)) {
1132
- console.error(`[WARNING] ${REACT_ROUTE_NO_VIEW_IN_YAML}: route ${r.pattern} exists in router but no view in YAML`);
1133
- }
1134
- }
1135
- if (exitCode !== 0) process.exit(exitCode);
1136
- }
1137
-
1138
- // src/cli/index.ts
1139
- var program = new Command().name("sightmap-react").description("CLI for @sightmap/react: regenerate, validate, and inspect React sightmaps offline.").version("0.1.0");
1140
- program.command("gen").description("Run the build pipeline once and write any changed feature files.").option("--cwd <path>", "project root", process.cwd()).option("--router <name>", "router adapter", "react-router-7").option("--output <dir>", "output dir", ".sightmap").option("--check", "dry-run; exit 1 if any .sightmap file would change").option("--diff", "dry-run; print unified diff of would-be changes to stdout").option(
1141
- "--live <url>",
1142
- "ALSO fetch a runtime sightmap from a running dev server at <url> (e.g. http://localhost:5173/__sightmap__/sightmap.json) and surface live-only views as info diagnostics. Optional; when omitted, only static AST discovery runs."
1143
- ).action(genCommand);
1144
- program.command("validate").description("Validate the .sightmap/ directory; surface React-specific drift diagnostics.").option("--cwd <path>", "project root", process.cwd()).option("--output <dir>", "output dir", ".sightmap").option("--router <name>", "router adapter (used for drift checks)", "react-router-7").action(validateCommand);
1145
- program.parseAsync(process.argv).catch((err) => {
1146
- console.error(err);
1147
- process.exit(1);
1148
- });
1149
- //# sourceMappingURL=index.js.map