@sightmap/react 0.7.1 → 0.9.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/index.d.ts +13 -2
- package/dist/index.js +349 -5
- package/dist/index.js.map +1 -1
- package/dist/runtime-types-Cvx7dsB4.d.ts +91 -0
- package/dist/runtime-types.d.ts +2 -51
- package/dist/vite.d.ts +21 -8
- package/dist/vite.js +54 -29
- package/dist/vite.js.map +1 -1
- package/package.json +5 -26
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +0 -1149
- package/dist/cli/index.js.map +0 -1
- package/dist/plugin/adapters/rr7-declarative.d.ts +0 -6
- package/dist/plugin/adapters/rr7-declarative.js +0 -315
- package/dist/plugin/adapters/rr7-declarative.js.map +0 -1
- package/dist/plugin/adapters/rr7-framework.d.ts +0 -7
- package/dist/plugin/adapters/rr7-framework.js +0 -235
- package/dist/plugin/adapters/rr7-framework.js.map +0 -1
- package/dist/router-adapter-CsmLmHVQ.d.ts +0 -29
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/plugin/adapters/rr7-declarative.ts","../../../src/plugin/router-adapter.ts","../../../src/plugin/diagnostics.ts"],"sourcesContent":["// src/plugin/adapters/rr7-declarative.ts\n//\n// React Router 7 \"declarative\" adapter — discovers routes from <Routes> / <Route> JSX.\n//\n// v0.1 limitations (intentional — deferred to v0.2):\n// - Only resolves explicit string `path` props. Template-expression paths are not supported.\n// - Only follows local (relative) imports. Bare-specifier or aliased imports are skipped.\n// - Does not discover routes added via `useRoutes(routesArray)` — that is the data-mode\n// adapter, planned for v0.2.\n//\n// Note: `require(\"fs\").accessSync` was avoided here because this package is ESM-only\n// (\"type\": \"module\"). `require` is not defined in ESM. `existsSync` from \"node:fs\" is\n// used instead — it is synchronous, does not throw on missing files, and returns a boolean.\n\nimport { promises as fs, existsSync } from \"node:fs\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { parse as parseAst } from \"@swc/core\";\nimport type { Diagnostic } from \"@sightmap/sightmap\";\nimport type { RouterAdapter, AdapterContext, RouteDescriptor } from \"../router-adapter.js\";\nimport { deriveFeatureName } from \"../router-adapter.js\";\nimport { REACT_DYNAMIC_ROUTE_PATH } from \"../diagnostics.js\";\n\nconst ENTRY_CANDIDATES = [\"src/main.tsx\", \"src/main.ts\", \"src/main.jsx\", \"src/main.js\"];\n\nexport function rr7DeclarativeAdapter(): RouterAdapter {\n return {\n name: \"react-router-7-declarative\",\n detect(_projectRoot: string): boolean {\n // Best-effort sniff: does the project have react-router as a dep AND a main.* with <Routes>?\n // Defer detection logic to v0.2; in v0.1 this adapter is selected explicitly.\n return true;\n },\n async discoverRoutes(ctx: AdapterContext): Promise<RouteDescriptor[]> {\n const entry = await findEntry(ctx.projectRoot);\n if (!entry) return [];\n const visited = new Set<string>();\n const routePatterns: string[] = [];\n const sourceFilesPerRoute = new Map<string, Set<string>>();\n const pathsConfig = await loadPathsConfig(ctx.projectRoot);\n\n await walkModule(entry, ctx, pathsConfig, visited, routePatterns, sourceFilesPerRoute);\n\n return routePatterns.map((pattern) => ({\n pattern,\n featureName: deriveFeatureName(pattern),\n sourceFiles: Array.from(sourceFilesPerRoute.get(pattern) ?? new Set()),\n }));\n },\n };\n}\n\nasync function findEntry(projectRoot: string): Promise<string | null> {\n for (const candidate of ENTRY_CANDIDATES) {\n const path = join(projectRoot, candidate);\n try {\n await fs.access(path);\n return path;\n } catch {\n /* not present */\n }\n }\n return null;\n}\n\nasync function walkModule(\n file: string,\n ctx: AdapterContext,\n pathsConfig: PathsConfig | null,\n visited: Set<string>,\n routePatterns: string[],\n sourceFilesPerRoute: Map<string, Set<string>>,\n): Promise<void> {\n if (visited.has(file)) return;\n visited.add(file);\n let source: string;\n try {\n source = await fs.readFile(file, \"utf-8\");\n } catch {\n return;\n }\n\n const ast = await parseAst(source, { syntax: \"typescript\", tsx: true, target: \"es2022\" });\n\n // Pass 1: collect local import bindings (local name → resolved absolute path).\n const localImports = new Map<string, string>();\n collectImports(ast, file, pathsConfig, localImports);\n\n // Pass 2: find <Route path=\"...\"> elements and record source files.\n // collectRoutes is synchronous; we capture results and process async below\n // so we can walk each element's component subtree.\n const routesFound: Array<{ path: string; elementSpecs: string[] }> = [];\n collectRoutes(\n ast,\n (path, elementSpecs) => {\n routesFound.push({ path, elementSpecs });\n },\n ctx.diagnostics ? (diag) => ctx.diagnostics!.push(diag) : undefined,\n file,\n ctx.projectRoot,\n );\n\n for (const { path, elementSpecs } of routesFound) {\n routePatterns.push(path);\n if (!sourceFilesPerRoute.has(path)) sourceFilesPerRoute.set(path, new Set());\n sourceFilesPerRoute.get(path)!.add(file);\n\n // Walk every locally-bound component referenced by the route's element\n // subtree. The visited set is shared across specs for one route so two\n // siblings sharing a sub-component don't re-walk it; it's per-route so\n // the same file can still appear under multiple routes (the orchestrator\n // promotes multi-route components to shared.yaml).\n const visitedForRoute = new Set<string>();\n for (const spec of elementSpecs) {\n const resolved = localImports.get(spec) ?? null;\n if (resolved) {\n await walkComponentTree(resolved, pathsConfig, sourceFilesPerRoute.get(path)!, visitedForRoute);\n }\n }\n }\n\n // Pass 3: recurse into other imports so nested route trees are discovered.\n for (const target of localImports.values()) {\n await walkModule(target, ctx, pathsConfig, visited, routePatterns, sourceFilesPerRoute);\n }\n}\n\n// Walks a component's import tree, adding every reachable file to `sourceFiles`.\n// Cycle-protected via `visited`. The visited set is per-call (per-route) so\n// the same component can appear in multiple routes' sourceFiles — the\n// orchestrator promotes multi-route components to shared.yaml.\nasync function walkComponentTree(\n file: string,\n pathsConfig: PathsConfig | null,\n sourceFiles: Set<string>,\n visited: Set<string>,\n): Promise<void> {\n if (visited.has(file)) return;\n visited.add(file);\n sourceFiles.add(file);\n\n let source: string;\n try {\n source = await fs.readFile(file, \"utf-8\");\n } catch {\n return;\n }\n\n const ast = await parseAst(source, { syntax: \"typescript\", tsx: true, target: \"es2022\" });\n const localImports = new Map<string, string>();\n collectImports(ast, file, pathsConfig, localImports);\n\n for (const target of localImports.values()) {\n await walkComponentTree(target, pathsConfig, sourceFiles, visited);\n }\n}\n\nfunction collectImports(\n ast: any,\n fromFile: string,\n pathsConfig: PathsConfig | null,\n into: Map<string, string>,\n): void {\n for (const stmt of ast.body ?? []) {\n if (stmt.type === \"ImportDeclaration\") {\n const sourcePath = stmt.source?.value as string | undefined;\n if (!sourcePath) continue;\n const resolved = resolveImport(fromFile, sourcePath, pathsConfig);\n if (!resolved) continue;\n for (const spec of stmt.specifiers ?? []) {\n const local = spec.local?.value;\n if (typeof local === \"string\") into.set(local, resolved);\n }\n continue;\n }\n // Recognize `const Foo = lazy(() => import(\"./Foo\"))` (and variants like\n // `lazy(() => import(\"./Foo\").then(m => m.Foo))`, `lazy(async () => ...)`).\n // Treats the variable's binding name as the local import name and the dynamic\n // `import(...)` argument as the source spec — so the same downstream walker\n // that handles static imports also walks lazy-loaded route components.\n if (stmt.type === \"VariableDeclaration\") {\n for (const decl of stmt.declarations ?? []) {\n const local = decl.id?.value;\n if (typeof local !== \"string\" || !decl.init) continue;\n const dynamicSpec = findFirstDynamicImportSpec(decl.init);\n if (!dynamicSpec) continue;\n const resolved = resolveImport(fromFile, dynamicSpec, pathsConfig);\n if (!resolved) continue;\n into.set(local, resolved);\n }\n }\n }\n}\n\n// Resolves an import specifier to an absolute file path, handling both\n// relative imports and tsconfig path-aliases. Returns null for bare-package\n// imports (`react`, `@sightmap/sightmap`, etc.) — those are out of scope for\n// route discovery and silently skipped, same as before.\nfunction resolveImport(\n fromFile: string,\n importSpec: string,\n pathsConfig: PathsConfig | null,\n): string | null {\n if (importSpec.startsWith(\".\")) {\n return resolveLocalImport(fromFile, importSpec);\n }\n if (!pathsConfig) return null;\n const candidates = expandAlias(importSpec, pathsConfig);\n if (!candidates) return null;\n // Each candidate is an absolute path stem (with or without extension); try\n // file-form, directory-form, and the .js-extension TS quirk in the same\n // order as `resolveLocalImport`.\n for (const candidate of candidates) {\n const stripped = candidate.replace(/\\.(js|jsx|mjs)$/, \"\");\n for (const ext of [\".tsx\", \".ts\", \".jsx\", \".js\"]) {\n const p = `${stripped}${ext}`;\n if (existsSync(p)) return p;\n }\n for (const indexBase of [\"index.tsx\", \"index.ts\", \"index.jsx\", \"index.js\"]) {\n const p = resolve(stripped, indexBase);\n if (existsSync(p)) return p;\n }\n if (stripped !== candidate && existsSync(candidate)) return candidate;\n }\n return null;\n}\n\n// Subtree search for the first `import(\"...\")` call inside a node, returning\n// the static-string argument or null. Handles the common shapes — `lazy(() =>\n// import(\"./X\"))`, `lazy(() => import(\"./X\").then(...))`, `lazy(async () =>\n// import(\"./X\"))` — without trying to model arbitrary control flow.\nfunction findFirstDynamicImportSpec(node: any): string | null {\n if (!node || typeof node !== \"object\") return null;\n if (\n node.type === \"CallExpression\" &&\n node.callee?.type === \"Import\" &&\n Array.isArray(node.arguments) &&\n node.arguments[0]?.expression?.type === \"StringLiteral\"\n ) {\n return node.arguments[0].expression.value as string;\n }\n for (const key of Object.keys(node)) {\n const v = node[key];\n if (Array.isArray(v)) {\n for (const c of v) {\n const found = findFirstDynamicImportSpec(c);\n if (found) return found;\n }\n } else if (v && typeof v === \"object\") {\n const found = findFirstDynamicImportSpec(v);\n if (found) return found;\n }\n }\n return null;\n}\n\n// Uses existsSync (not require(\"fs\").accessSync) because this package is ESM-only.\n// require is not available in ESM modules and would throw ReferenceError at runtime.\n//\n// Handles three import styles:\n// 1. Extensionless (\"./Login\") → tries ./Login.tsx, .ts, .jsx, .js\n// 2. TS verbatimModuleSyntax (\".js\" on\n// a .ts/.tsx source: \"./Login.js\") → strips the trailing .js/.jsx/.mjs\n// and tries ./Login.tsx, .ts, .jsx, .js\n// 3. Directory-style (\"./pages/login\") → tries ./pages/login/index.{tsx,ts,jsx,js}\n// after the file-form lookups fail. Order\n// matches Node/bundler precedence: a\n// sibling `./pages/login.tsx` wins over a\n// `./pages/login/index.tsx`.\nfunction resolveLocalImport(fromFile: string, importSpec: string): string | null {\n const dir = dirname(fromFile);\n const stripped = importSpec.replace(/\\.(js|jsx|mjs)$/, \"\");\n for (const ext of [\".tsx\", \".ts\", \".jsx\", \".js\"]) {\n const candidate = resolve(dir, `${stripped}${ext}`);\n if (existsSync(candidate)) return candidate;\n }\n for (const indexBase of [\"index.tsx\", \"index.ts\", \"index.jsx\", \"index.js\"]) {\n const candidate = resolve(dir, stripped, indexBase);\n if (existsSync(candidate)) return candidate;\n }\n // Fall back to the original spec in case it's a real .js/.mjs file\n // (the strip-and-retry above won't hit it because we already tried `${stripped}.js`).\n if (stripped !== importSpec) {\n const original = resolve(dir, importSpec);\n if (existsSync(original)) return original;\n }\n return null;\n}\n\nfunction collectRoutes(\n ast: any,\n onRoute: (path: string, elementSpecs: string[]) => void,\n onDiagnostic?: (d: Diagnostic) => void,\n fromFile?: string,\n projectRoot?: string,\n): void {\n function visit(node: any, parentPath: string): void {\n if (!node || typeof node !== \"object\") return;\n let here = parentPath;\n if (node.type === \"JSXElement\" && node.opening?.name?.value === \"Route\") {\n let pathValue: string | null = null;\n let pathAttrIsNonLiteral = false;\n let outerElementName: string | null = null;\n const elementSpecs: string[] = [];\n for (const attr of node.opening.attributes ?? []) {\n if (attr.type !== \"JSXAttribute\") continue;\n if (attr.name?.value === \"path\") {\n if (attr.value?.type === \"StringLiteral\") {\n pathValue = attr.value.value;\n } else if (attr.value) {\n // Any other shape (template literal, identifier, expression, member\n // access) — emit a diagnostic so the curator knows we saw the route\n // but couldn't resolve its path.\n pathAttrIsNonLiteral = true;\n }\n }\n if (attr.name?.value === \"element\" && attr.value?.type === \"JSXExpressionContainer\") {\n const inner = attr.value.expression;\n if (inner?.type === \"JSXElement\") {\n // The outermost element name is often a cross-cutting wrapper\n // (`Suspense`, `ErrorBoundary`, `RequireAuth`, …) that lives in a\n // bare-package import, not a local binding. Collect every\n // JSXElement name in the subtree so the caller can resolve all\n // of them against `localImports` and walk the matches. The\n // canonical RR7 lazy-route pattern is\n // `element={<Suspense><Foo /></Suspense>}`, where `Foo` is the\n // local binding we actually care about.\n collectJsxElementNames(inner, elementSpecs);\n outerElementName = inner.opening?.name?.value ?? null;\n }\n }\n // RR7's `<Route Component={X} />` prop is functionally equivalent to\n // `element={<X />}` but takes the component reference directly. Real-world\n // apps mix the two freely (often using `Component` with `React.lazy(...)`).\n if (attr.name?.value === \"Component\" && attr.value?.type === \"JSXExpressionContainer\") {\n const inner = attr.value.expression;\n if (inner?.type === \"Identifier\" && typeof inner.value === \"string\") {\n elementSpecs.push(inner.value);\n }\n }\n }\n if (pathValue) {\n const joined = pathValue.startsWith(\"/\") ? pathValue : `${parentPath.replace(/\\/$/, \"\")}/${pathValue}`;\n // Skip pure-redirect routes — `<Route element={<Navigate ... />} />` doesn't\n // render a user-facing surface and only adds noise as an empty-components\n // view in the YAML. Match only when `Navigate` is the *outermost* element\n // (the redirect form); a `Navigate` rendered inside a real wrapper is\n // unusual but still a real route.\n if (outerElementName !== \"Navigate\") {\n onRoute(joined, elementSpecs);\n }\n here = joined;\n } else if (pathAttrIsNonLiteral && onDiagnostic) {\n const where = fromFile && projectRoot ? relative(projectRoot, fromFile) : fromFile ?? \"<unknown>\";\n onDiagnostic({\n severity: \"info\",\n code: REACT_DYNAMIC_ROUTE_PATH,\n 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.`,\n });\n }\n }\n for (const key of Object.keys(node)) {\n const v = (node as any)[key];\n if (Array.isArray(v)) v.forEach((c) => visit(c, here));\n else if (v && typeof v === \"object\") visit(v, here);\n }\n }\n visit(ast, \"\");\n}\n\n// Recursively walks a JSX expression subtree, pushing every JSXElement\n// opening name we encounter into `into`. Caller filters by `localImports`,\n// so package-level wrappers (`Suspense`, `ErrorBoundary`, …) drop out\n// naturally without needing a name-based denylist.\nfunction collectJsxElementNames(node: any, into: string[]): void {\n if (!node || typeof node !== \"object\") return;\n if (node.type === \"JSXElement\") {\n const name = node.opening?.name?.value;\n if (typeof name === \"string\") into.push(name);\n }\n for (const key of Object.keys(node)) {\n const v = (node as any)[key];\n if (Array.isArray(v)) v.forEach((c) => collectJsxElementNames(c, into));\n else if (v && typeof v === \"object\") collectJsxElementNames(v, into);\n }\n}\n\n/**\n * Resolved alias-resolution context derived from the project's tsconfig(s).\n *\n * `baseUrl` is absolute. `paths` keys are kept verbatim from the tsconfig\n * (e.g. `\"@/*\"`); each value is the array of substitution targets from the\n * tsconfig (e.g. `[\"./src/*\"]`), themselves relative to `baseUrl`.\n */\ntype PathsConfig = {\n baseUrl: string;\n paths: Record<string, string[]>;\n};\n\n// Loads tsconfig path-alias config from the project. Reads tsconfig.json AND\n// tsconfig.app.json (the typical Vite layout where the root config is just a\n// `references` shell and the alias-bearing config lives in tsconfig.app.json).\n// Returns null if neither defines `paths`. Tolerates JSON-with-comments —\n// tsconfig is JSON-with-comments by convention and TypeScript's own parser\n// is more lenient than `JSON.parse`. We don't try to model `extends`; the\n// flat case covers the dominant idiom.\nasync function loadPathsConfig(projectRoot: string): Promise<PathsConfig | null> {\n const merged: Record<string, string[]> = {};\n let baseUrl: string | null = null;\n for (const name of [\"tsconfig.json\", \"tsconfig.app.json\"]) {\n const fp = join(projectRoot, name);\n let raw: string;\n try {\n raw = await fs.readFile(fp, \"utf-8\");\n } catch {\n continue;\n }\n const json = parseTsconfigJson(raw);\n const co = json?.compilerOptions;\n if (!co) continue;\n if (!baseUrl && typeof co.baseUrl === \"string\") {\n baseUrl = resolve(dirname(fp), co.baseUrl);\n }\n if (co.paths && typeof co.paths === \"object\") {\n for (const [k, v] of Object.entries(co.paths)) {\n if (Array.isArray(v)) merged[k] = v as string[];\n }\n }\n }\n if (Object.keys(merged).length === 0) return null;\n return { baseUrl: baseUrl ?? projectRoot, paths: merged };\n}\n\n// Strip the two tsconfig-style \"JSON with comments\" extensions: `// line`\n// comments and `/* block */` comments. Trailing commas before `}` / `]` are\n// also tolerated. Conservative — it doesn't try to be a full JSON5 parser,\n// just enough to handle the conventions tsconfig templates use.\nfunction parseTsconfigJson(raw: string): any | null {\n const stripped = raw\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/(^|[^:])\\/\\/.*$/gm, \"$1\")\n .replace(/,(\\s*[}\\]])/g, \"$1\");\n try {\n return JSON.parse(stripped);\n } catch {\n return null;\n }\n}\n\n// Expand a non-relative import spec against the configured aliases. Returns\n// the array of absolute candidate paths to try (one per alias target), or\n// null if no alias matched. Bare-package imports return null and stay\n// silently skipped — same as before path-alias support landed.\nfunction expandAlias(importSpec: string, cfg: PathsConfig): string[] | null {\n for (const [alias, targets] of Object.entries(cfg.paths)) {\n if (alias.endsWith(\"/*\")) {\n const prefix = alias.slice(0, -1); // includes trailing slash, e.g. \"@/\"\n if (importSpec.startsWith(prefix)) {\n const rest = importSpec.slice(prefix.length);\n return targets.map((t) => {\n const tnorm = t.endsWith(\"/*\") ? t.slice(0, -2) : t;\n return resolve(cfg.baseUrl, tnorm, rest);\n });\n }\n } else if (importSpec === alias) {\n return targets.map((t) => resolve(cfg.baseUrl, t));\n }\n }\n return null;\n}\n","// src/plugin/router-adapter.ts\nimport type { Diagnostic } from \"@sightmap/sightmap\";\n\nexport type RouteDescriptor = {\n pattern: string; // normalized to spec route grammar\n featureName: string; // chunking key — derives the YAML filename\n sourceFiles: string[]; // absolute paths reachable from this route's tree\n meta?: Record<string, unknown>; // raw router metadata; informational only in v1\n};\n\nexport type AdapterContext = {\n projectRoot: string;\n resolveSource: (id: string) => string | null;\n /**\n * Optional sink the adapter can push diagnostics into during discovery — e.g.\n * a `<Route>` it found but couldn't fully process. Adapters MUST tolerate\n * this being absent (callers from tests may not pass one). The orchestrator\n * always provides it and forwards the contents into its own diagnostic\n * stream so they reach the CLI summary.\n */\n diagnostics?: Diagnostic[];\n};\n\nexport interface RouterAdapter {\n name: string;\n detect(projectRoot: string): boolean;\n discoverRoutes(ctx: AdapterContext): Promise<RouteDescriptor[]>;\n /** Optional client-side helper. Falls back to `location.pathname` if absent. */\n currentRoute?(): string;\n}\n\n/** Derive the feature name from a route pattern. First non-parameter segment, or \"home\" for \"/\". */\nexport function deriveFeatureName(pattern: string): string {\n const segments = pattern.split(\"/\").filter(Boolean);\n for (const seg of segments) {\n if (!seg.startsWith(\":\") && !seg.startsWith(\"*\")) return sanitize(seg);\n }\n return \"home\";\n}\n\n/**\n * Derive a view name from a route pattern.\n *\n * Picks the LAST non-parameter segment, capitalized — so `/app/dashboard` →\n * `Dashboard`, `/settings/billing` → `Billing`, `/login` → `Login`.\n *\n * Routes ending in a parameter (`/users/:id`) get the preceding segment with\n * `Detail` appended (`UsersDetail`) so they don't collide with the list view.\n *\n * Routes ending in a wildcard (`/files/*`, `/*`) get `Catchall` appended —\n * a different concept from `:param` (catch-all matches multiple segments;\n * named params match one).\n *\n * Falls back to `Home` for `/`.\n */\nexport function deriveViewName(pattern: string): string {\n const segments = pattern.split(\"/\").filter(Boolean);\n if (segments.length === 0) return \"Home\";\n\n const last = segments[segments.length - 1]!;\n const lastIsParam = last.startsWith(\":\");\n const lastIsWildcard = last.startsWith(\"*\");\n\n if (lastIsParam || lastIsWildcard) {\n const suffix = lastIsWildcard ? \"Catchall\" : \"Detail\";\n // Walk back to the last static segment and append the suffix.\n for (let i = segments.length - 2; i >= 0; i--) {\n const seg = segments[i]!;\n if (!seg.startsWith(\":\") && !seg.startsWith(\"*\")) {\n return capitalize(sanitize(seg)) + suffix;\n }\n }\n return suffix;\n }\n return capitalize(sanitize(last));\n}\n\nfunction sanitize(s: string): string {\n return s.replace(/[^a-zA-Z0-9_-]/g, \"-\").toLowerCase();\n}\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n","// React-specific diagnostic codes. Stable, kebab-case. Renames require a minor bump and a deprecation note.\n\nexport const REACT_DYNAMIC_DATA_SIGHTMAP = \"react.dynamic-data-sightmap\";\nexport const REACT_DYNAMIC_ROUTE_PATH = \"react.dynamic-route-path\";\nexport const REACT_NO_ROUTES_DISCOVERED = \"react.no-routes-discovered\";\nexport const REACT_REMOVED_COMPONENT_STILL_IN_YAML = \"react.removed-component-still-in-yaml\";\nexport const REACT_UNKNOWN_ROUTER = \"react.unknown-router\";\nexport const REACT_ROUTE_NO_VIEW_IN_YAML = \"react.route-no-view-in-yaml\";\nexport const REACT_VIEW_ALREADY_DEFINED = \"react.view-already-defined\";\nexport const REACT_LIVE_ONLY_VIEW = \"react.live-only-view\";\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { R as RouterAdapter } from '../../router-adapter-CsmLmHVQ.js';
|
|
2
|
-
import '@sightmap/sightmap';
|
|
3
|
-
|
|
4
|
-
declare function rr7FrameworkAdapter(): RouterAdapter;
|
|
5
|
-
declare function joinPath(parent: string, child: string): string;
|
|
6
|
-
|
|
7
|
-
export { joinPath, rr7FrameworkAdapter };
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
// src/plugin/adapters/rr7-framework.ts
|
|
2
|
-
import { promises as fs, existsSync } from "fs";
|
|
3
|
-
import { dirname, join, resolve } from "path";
|
|
4
|
-
import { parse as parseAst } from "@swc/core";
|
|
5
|
-
|
|
6
|
-
// src/plugin/router-adapter.ts
|
|
7
|
-
function deriveFeatureName(pattern) {
|
|
8
|
-
const segments = pattern.split("/").filter(Boolean);
|
|
9
|
-
for (const seg of segments) {
|
|
10
|
-
if (!seg.startsWith(":") && !seg.startsWith("*")) return sanitize(seg);
|
|
11
|
-
}
|
|
12
|
-
return "home";
|
|
13
|
-
}
|
|
14
|
-
function sanitize(s) {
|
|
15
|
-
return s.replace(/[^a-zA-Z0-9_-]/g, "-").toLowerCase();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// src/plugin/adapters/rr7-framework.ts
|
|
19
|
-
var ROUTES_CONFIG_CANDIDATES = [
|
|
20
|
-
"app/routes.ts",
|
|
21
|
-
"app/routes.tsx",
|
|
22
|
-
"app/routes.js",
|
|
23
|
-
"app/routes.jsx"
|
|
24
|
-
];
|
|
25
|
-
var APP_DIR = "app";
|
|
26
|
-
function rr7FrameworkAdapter() {
|
|
27
|
-
return {
|
|
28
|
-
name: "react-router-7-framework",
|
|
29
|
-
detect(projectRoot) {
|
|
30
|
-
return findRoutesConfigSync(projectRoot) !== null;
|
|
31
|
-
},
|
|
32
|
-
async discoverRoutes(ctx) {
|
|
33
|
-
const routesFile = await findRoutesConfig(ctx.projectRoot);
|
|
34
|
-
if (!routesFile) return [];
|
|
35
|
-
const appDir = join(ctx.projectRoot, APP_DIR);
|
|
36
|
-
const source = await fs.readFile(routesFile, "utf-8");
|
|
37
|
-
const ast = await parseAst(source, { syntax: "typescript", tsx: true, target: "es2022" });
|
|
38
|
-
const defaultArray = findDefaultExportArray(ast);
|
|
39
|
-
if (!defaultArray) return [];
|
|
40
|
-
const collected = [];
|
|
41
|
-
await walkRouteArray(defaultArray, { parentPattern: "", inheritedFiles: /* @__PURE__ */ new Set() }, appDir, collected);
|
|
42
|
-
return collected.map(({ pattern, sourceFiles }) => ({
|
|
43
|
-
pattern,
|
|
44
|
-
featureName: deriveFeatureName(pattern),
|
|
45
|
-
sourceFiles: Array.from(sourceFiles)
|
|
46
|
-
}));
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
function findRoutesConfigSync(projectRoot) {
|
|
51
|
-
for (const candidate of ROUTES_CONFIG_CANDIDATES) {
|
|
52
|
-
const path = join(projectRoot, candidate);
|
|
53
|
-
if (existsSync(path)) return path;
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
async function findRoutesConfig(projectRoot) {
|
|
58
|
-
for (const candidate of ROUTES_CONFIG_CANDIDATES) {
|
|
59
|
-
const path = join(projectRoot, candidate);
|
|
60
|
-
try {
|
|
61
|
-
await fs.access(path);
|
|
62
|
-
return path;
|
|
63
|
-
} catch {
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
function findDefaultExportArray(ast) {
|
|
69
|
-
for (const stmt of ast.body ?? []) {
|
|
70
|
-
if (stmt.type === "ExportDefaultExpression") {
|
|
71
|
-
return unwrapArray(stmt.expression);
|
|
72
|
-
}
|
|
73
|
-
if (stmt.type === "ExportDefaultDeclaration") {
|
|
74
|
-
const decl = stmt.decl ?? stmt.declaration;
|
|
75
|
-
if (decl) return unwrapArray(decl);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
function unwrapArray(node) {
|
|
81
|
-
if (!node) return null;
|
|
82
|
-
if (node.type === "ArrayExpression") return node;
|
|
83
|
-
if (node.type === "TsSatisfiesExpression" || node.type === "TsAsExpression" || node.type === "TsTypeAssertion") {
|
|
84
|
-
return unwrapArray(node.expression);
|
|
85
|
-
}
|
|
86
|
-
if (node.type === "ParenthesisExpression") return unwrapArray(node.expression);
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
async function walkRouteArray(arrayNode, frame, appDir, out) {
|
|
90
|
-
for (const elem of arrayNode.elements ?? []) {
|
|
91
|
-
if (!elem) continue;
|
|
92
|
-
const expr = elem.expression ?? elem;
|
|
93
|
-
const isSpread = !!elem.spread;
|
|
94
|
-
if (isSpread) {
|
|
95
|
-
if (expr?.type === "CallExpression" && callName(expr) === "prefix") {
|
|
96
|
-
const args = expr.arguments ?? [];
|
|
97
|
-
const prefixArg = stringArg(args[0]);
|
|
98
|
-
const childrenArr = arrayArg(args[1]);
|
|
99
|
-
if (prefixArg !== null && childrenArr) {
|
|
100
|
-
const childFrame = {
|
|
101
|
-
parentPattern: joinPath(frame.parentPattern, prefixArg),
|
|
102
|
-
inheritedFiles: frame.inheritedFiles
|
|
103
|
-
};
|
|
104
|
-
await walkRouteArray(childrenArr, childFrame, appDir, out);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (expr?.type !== "CallExpression") continue;
|
|
110
|
-
await processRouteCall(expr, frame, appDir, out);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
async function processRouteCall(call, frame, appDir, out) {
|
|
114
|
-
const name = callName(call);
|
|
115
|
-
const args = call.arguments ?? [];
|
|
116
|
-
if (name === "route") {
|
|
117
|
-
const pathArg = stringArg(args[0]);
|
|
118
|
-
const fileArg = stringArg(args[1]);
|
|
119
|
-
if (pathArg === null || fileArg === null) return;
|
|
120
|
-
const pattern = joinPath(frame.parentPattern, pathArg);
|
|
121
|
-
const sourceFiles = new Set(frame.inheritedFiles);
|
|
122
|
-
const resolvedFile = resolveAppFile(appDir, fileArg);
|
|
123
|
-
if (resolvedFile) await walkComponentTree(resolvedFile, sourceFiles, /* @__PURE__ */ new Set());
|
|
124
|
-
out.push({ pattern, sourceFiles });
|
|
125
|
-
const childrenArr = arrayArg(args[2]);
|
|
126
|
-
if (childrenArr) {
|
|
127
|
-
const childFrame = {
|
|
128
|
-
parentPattern: pattern,
|
|
129
|
-
inheritedFiles: union(frame.inheritedFiles, sourceFiles)
|
|
130
|
-
};
|
|
131
|
-
await walkRouteArray(childrenArr, childFrame, appDir, out);
|
|
132
|
-
}
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
if (name === "index") {
|
|
136
|
-
const fileArg = stringArg(args[0]);
|
|
137
|
-
if (fileArg === null) return;
|
|
138
|
-
const pattern = frame.parentPattern || "/";
|
|
139
|
-
const sourceFiles = new Set(frame.inheritedFiles);
|
|
140
|
-
const resolvedFile = resolveAppFile(appDir, fileArg);
|
|
141
|
-
if (resolvedFile) await walkComponentTree(resolvedFile, sourceFiles, /* @__PURE__ */ new Set());
|
|
142
|
-
out.push({ pattern, sourceFiles });
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
if (name === "layout") {
|
|
146
|
-
const fileArg = stringArg(args[0]);
|
|
147
|
-
const childrenArr = arrayArg(args[1]);
|
|
148
|
-
if (fileArg === null || !childrenArr) return;
|
|
149
|
-
const layoutFiles = /* @__PURE__ */ new Set();
|
|
150
|
-
const resolvedFile = resolveAppFile(appDir, fileArg);
|
|
151
|
-
if (resolvedFile) await walkComponentTree(resolvedFile, layoutFiles, /* @__PURE__ */ new Set());
|
|
152
|
-
const childFrame = {
|
|
153
|
-
parentPattern: frame.parentPattern,
|
|
154
|
-
inheritedFiles: union(frame.inheritedFiles, layoutFiles)
|
|
155
|
-
};
|
|
156
|
-
await walkRouteArray(childrenArr, childFrame, appDir, out);
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
function callName(call) {
|
|
161
|
-
const callee = call?.callee;
|
|
162
|
-
if (!callee) return null;
|
|
163
|
-
if (callee.type === "Identifier") return callee.value ?? null;
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
function stringArg(arg) {
|
|
167
|
-
const expr = arg?.expression ?? arg;
|
|
168
|
-
if (expr?.type === "StringLiteral") return expr.value ?? null;
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
function arrayArg(arg) {
|
|
172
|
-
const expr = arg?.expression ?? arg;
|
|
173
|
-
if (expr?.type === "ArrayExpression") return expr;
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
function joinPath(parent, child) {
|
|
177
|
-
if (child.startsWith("/")) return child;
|
|
178
|
-
if (!child) return parent || "/";
|
|
179
|
-
const base = (parent || "").replace(/\/+$/, "");
|
|
180
|
-
const tail = child.replace(/^\/+/, "");
|
|
181
|
-
return base === "" ? `/${tail}` : `${base}/${tail}`;
|
|
182
|
-
}
|
|
183
|
-
function resolveAppFile(appDir, fileSpec) {
|
|
184
|
-
const base = resolve(appDir, fileSpec);
|
|
185
|
-
if (existsSync(base)) return base;
|
|
186
|
-
const stripped = base.replace(/\.(js|jsx|mjs)$/, "");
|
|
187
|
-
for (const ext of [".tsx", ".ts", ".jsx", ".js"]) {
|
|
188
|
-
const candidate = `${stripped}${ext}`;
|
|
189
|
-
if (existsSync(candidate)) return candidate;
|
|
190
|
-
}
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
function union(a, b) {
|
|
194
|
-
const out = new Set(a);
|
|
195
|
-
for (const v of b) out.add(v);
|
|
196
|
-
return out;
|
|
197
|
-
}
|
|
198
|
-
async function walkComponentTree(file, sourceFiles, visited) {
|
|
199
|
-
if (visited.has(file)) return;
|
|
200
|
-
visited.add(file);
|
|
201
|
-
sourceFiles.add(file);
|
|
202
|
-
let source;
|
|
203
|
-
try {
|
|
204
|
-
source = await fs.readFile(file, "utf-8");
|
|
205
|
-
} catch {
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const ast = await parseAst(source, { syntax: "typescript", tsx: true, target: "es2022" });
|
|
209
|
-
for (const stmt of ast.body ?? []) {
|
|
210
|
-
if (stmt.type !== "ImportDeclaration") continue;
|
|
211
|
-
const sourcePath = stmt.source?.value;
|
|
212
|
-
if (!sourcePath || !sourcePath.startsWith(".")) continue;
|
|
213
|
-
const resolved = resolveLocalImport(file, sourcePath);
|
|
214
|
-
if (!resolved) continue;
|
|
215
|
-
await walkComponentTree(resolved, sourceFiles, visited);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
function resolveLocalImport(fromFile, importSpec) {
|
|
219
|
-
const dir = dirname(fromFile);
|
|
220
|
-
const stripped = importSpec.replace(/\.(js|jsx|mjs)$/, "");
|
|
221
|
-
for (const ext of [".tsx", ".ts", ".jsx", ".js"]) {
|
|
222
|
-
const candidate = resolve(dir, `${stripped}${ext}`);
|
|
223
|
-
if (existsSync(candidate)) return candidate;
|
|
224
|
-
}
|
|
225
|
-
if (stripped !== importSpec) {
|
|
226
|
-
const original = resolve(dir, importSpec);
|
|
227
|
-
if (existsSync(original)) return original;
|
|
228
|
-
}
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
export {
|
|
232
|
-
joinPath,
|
|
233
|
-
rr7FrameworkAdapter
|
|
234
|
-
};
|
|
235
|
-
//# sourceMappingURL=rr7-framework.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/plugin/adapters/rr7-framework.ts","../../../src/plugin/router-adapter.ts"],"sourcesContent":["// src/plugin/adapters/rr7-framework.ts\n//\n// React Router 7 \"framework mode\" adapter — discovers routes from the\n// programmatic config in `app/routes.ts` (the API exported by\n// `@react-router/dev/routes`: `route`, `index`, `layout`, `prefix`).\n//\n// v0.1 scope:\n// - Reads `app/routes.ts` (or `.tsx`/`.js`/`.jsx`); the `app/` directory is\n// hardcoded. Custom `appDirectory` from `react-router.config.ts` is not yet\n// honored — projects using a different app-dir name fall through to \"no\n// supported router\".\n// - Recognizes top-level helpers by direct identifier name: `route`,\n// `index`, `layout`, `prefix`. Namespaced (`routes.route(...)`) or\n// re-exported / aliased helpers are not parsed.\n// - String-literal arguments only. Template-expression paths or computed\n// file specs are ignored.\n// - `flatRoutes()` (filesystem-based routing) is not supported in v0.1 —\n// projects relying on it will return zero routes.\n//\n// Layout semantics: `layout(file, [children])` adds the layout's component\n// tree to every descendant route's `sourceFiles`, so markers inside a layout\n// surface on every child route. The orchestrator's multi-route promotion then\n// hoists those markers into `shared.yaml`, matching the runtime where the\n// layout wraps each child via `<Outlet />`.\n\nimport { promises as fs, existsSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { parse as parseAst } from \"@swc/core\";\nimport type { RouterAdapter, AdapterContext, RouteDescriptor } from \"../router-adapter.js\";\nimport { deriveFeatureName } from \"../router-adapter.js\";\n\nconst ROUTES_CONFIG_CANDIDATES = [\n \"app/routes.ts\",\n \"app/routes.tsx\",\n \"app/routes.js\",\n \"app/routes.jsx\",\n];\n\nconst APP_DIR = \"app\";\n\nexport function rr7FrameworkAdapter(): RouterAdapter {\n return {\n name: \"react-router-7-framework\",\n detect(projectRoot: string): boolean {\n return findRoutesConfigSync(projectRoot) !== null;\n },\n async discoverRoutes(ctx: AdapterContext): Promise<RouteDescriptor[]> {\n const routesFile = await findRoutesConfig(ctx.projectRoot);\n if (!routesFile) return [];\n const appDir = join(ctx.projectRoot, APP_DIR);\n\n const source = await fs.readFile(routesFile, \"utf-8\");\n const ast = await parseAst(source, { syntax: \"typescript\", tsx: true, target: \"es2022\" });\n\n const defaultArray = findDefaultExportArray(ast);\n if (!defaultArray) return [];\n\n const collected: Array<{ pattern: string; sourceFiles: Set<string> }> = [];\n await walkRouteArray(defaultArray, { parentPattern: \"\", inheritedFiles: new Set() }, appDir, collected);\n\n return collected.map(({ pattern, sourceFiles }) => ({\n pattern,\n featureName: deriveFeatureName(pattern),\n sourceFiles: Array.from(sourceFiles),\n }));\n },\n };\n}\n\nfunction findRoutesConfigSync(projectRoot: string): string | null {\n for (const candidate of ROUTES_CONFIG_CANDIDATES) {\n const path = join(projectRoot, candidate);\n if (existsSync(path)) return path;\n }\n return null;\n}\n\nasync function findRoutesConfig(projectRoot: string): Promise<string | null> {\n for (const candidate of ROUTES_CONFIG_CANDIDATES) {\n const path = join(projectRoot, candidate);\n try {\n await fs.access(path);\n return path;\n } catch {\n /* not present */\n }\n }\n return null;\n}\n\n// Locate `export default [ ... ]` and return the inner ArrayExpression node.\n// Also handles `export default [...] satisfies RouteConfig` (TS satisfies wraps\n// the array in a TsSatisfiesExpression / TsAsExpression in swc) and the\n// `export default <expr>` -> ArrayExpression direct case.\nfunction findDefaultExportArray(ast: any): any | null {\n for (const stmt of ast.body ?? []) {\n if (stmt.type === \"ExportDefaultExpression\") {\n return unwrapArray(stmt.expression);\n }\n if (stmt.type === \"ExportDefaultDeclaration\") {\n const decl = stmt.decl ?? stmt.declaration;\n if (decl) return unwrapArray(decl);\n }\n }\n return null;\n}\n\nfunction unwrapArray(node: any): any | null {\n if (!node) return null;\n if (node.type === \"ArrayExpression\") return node;\n // Unwrap `satisfies` / `as` casts and parens.\n if (node.type === \"TsSatisfiesExpression\" || node.type === \"TsAsExpression\" || node.type === \"TsTypeAssertion\") {\n return unwrapArray(node.expression);\n }\n if (node.type === \"ParenthesisExpression\") return unwrapArray(node.expression);\n return null;\n}\n\ntype Frame = {\n parentPattern: string;\n inheritedFiles: Set<string>;\n};\n\nasync function walkRouteArray(\n arrayNode: any,\n frame: Frame,\n appDir: string,\n out: Array<{ pattern: string; sourceFiles: Set<string> }>,\n): Promise<void> {\n for (const elem of arrayNode.elements ?? []) {\n if (!elem) continue;\n // ArrayExpression elements in swc are wrapped: `{ spread: null, expression: <expr> }`\n // when in array literal context. Spread elements have `spread` set to a span.\n const expr = elem.expression ?? elem;\n const isSpread = !!elem.spread;\n if (isSpread) {\n // Only spread of a `prefix(...)` call is meaningful in v0.1.\n if (expr?.type === \"CallExpression\" && callName(expr) === \"prefix\") {\n const args = expr.arguments ?? [];\n const prefixArg = stringArg(args[0]);\n const childrenArr = arrayArg(args[1]);\n if (prefixArg !== null && childrenArr) {\n const childFrame: Frame = {\n parentPattern: joinPath(frame.parentPattern, prefixArg),\n inheritedFiles: frame.inheritedFiles,\n };\n await walkRouteArray(childrenArr, childFrame, appDir, out);\n }\n }\n continue;\n }\n if (expr?.type !== \"CallExpression\") continue;\n await processRouteCall(expr, frame, appDir, out);\n }\n}\n\nasync function processRouteCall(\n call: any,\n frame: Frame,\n appDir: string,\n out: Array<{ pattern: string; sourceFiles: Set<string> }>,\n): Promise<void> {\n const name = callName(call);\n const args = call.arguments ?? [];\n\n if (name === \"route\") {\n const pathArg = stringArg(args[0]);\n const fileArg = stringArg(args[1]);\n if (pathArg === null || fileArg === null) return;\n const pattern = joinPath(frame.parentPattern, pathArg);\n const sourceFiles = new Set<string>(frame.inheritedFiles);\n const resolvedFile = resolveAppFile(appDir, fileArg);\n if (resolvedFile) await walkComponentTree(resolvedFile, sourceFiles, new Set());\n out.push({ pattern, sourceFiles });\n\n // Children inherit the parent route's component tree. At runtime, child\n // routes render inside the parent's `<Outlet />`, so markers in the\n // parent's component tree appear on every descendant view.\n const childrenArr = arrayArg(args[2]);\n if (childrenArr) {\n const childFrame: Frame = {\n parentPattern: pattern,\n inheritedFiles: union(frame.inheritedFiles, sourceFiles),\n };\n await walkRouteArray(childrenArr, childFrame, appDir, out);\n }\n return;\n }\n\n if (name === \"index\") {\n const fileArg = stringArg(args[0]);\n if (fileArg === null) return;\n // Index routes match the parent pattern exactly. At the top level that's \"/\".\n const pattern = frame.parentPattern || \"/\";\n const sourceFiles = new Set<string>(frame.inheritedFiles);\n const resolvedFile = resolveAppFile(appDir, fileArg);\n if (resolvedFile) await walkComponentTree(resolvedFile, sourceFiles, new Set());\n out.push({ pattern, sourceFiles });\n return;\n }\n\n if (name === \"layout\") {\n const fileArg = stringArg(args[0]);\n const childrenArr = arrayArg(args[1]);\n if (fileArg === null || !childrenArr) return;\n const layoutFiles = new Set<string>();\n const resolvedFile = resolveAppFile(appDir, fileArg);\n if (resolvedFile) await walkComponentTree(resolvedFile, layoutFiles, new Set());\n const childFrame: Frame = {\n parentPattern: frame.parentPattern,\n inheritedFiles: union(frame.inheritedFiles, layoutFiles),\n };\n await walkRouteArray(childrenArr, childFrame, appDir, out);\n return;\n }\n\n // Unknown helper — silently skip. v0.2 may add `flatRoutes()` etc.\n}\n\nfunction callName(call: any): string | null {\n const callee = call?.callee;\n if (!callee) return null;\n if (callee.type === \"Identifier\") return callee.value ?? null;\n return null;\n}\n\nfunction stringArg(arg: any): string | null {\n // ArrayExpression-style argument wrapper: `{ spread: null, expression: <expr> }`.\n const expr = arg?.expression ?? arg;\n if (expr?.type === \"StringLiteral\") return expr.value ?? null;\n return null;\n}\n\nfunction arrayArg(arg: any): any | null {\n const expr = arg?.expression ?? arg;\n if (expr?.type === \"ArrayExpression\") return expr;\n return null;\n}\n\n// Join a child path onto a parent pattern using RR7 semantics:\n// - child starting with \"/\" is absolute and replaces the parent\n// - empty child returns the parent (used for pathless `route(\"\", ...)`)\n// - otherwise the child is appended with a single \"/\" separator\nexport function joinPath(parent: string, child: string): string {\n if (child.startsWith(\"/\")) return child;\n if (!child) return parent || \"/\";\n const base = (parent || \"\").replace(/\\/+$/, \"\");\n const tail = child.replace(/^\\/+/, \"\");\n return base === \"\" ? `/${tail}` : `${base}/${tail}`;\n}\n\nfunction resolveAppFile(appDir: string, fileSpec: string): string | null {\n // RR7 file specs are relative to the app directory and may omit the extension.\n const base = resolve(appDir, fileSpec);\n if (existsSync(base)) return base;\n const stripped = base.replace(/\\.(js|jsx|mjs)$/, \"\");\n for (const ext of [\".tsx\", \".ts\", \".jsx\", \".js\"]) {\n const candidate = `${stripped}${ext}`;\n if (existsSync(candidate)) return candidate;\n }\n return null;\n}\n\nfunction union<T>(a: Set<T>, b: Set<T>): Set<T> {\n const out = new Set<T>(a);\n for (const v of b) out.add(v);\n return out;\n}\n\n// Walks a route file's local-import tree, adding every reachable file to\n// `sourceFiles`. Mirrors the declarative adapter's walker so marker scanning\n// in the orchestrator finds `data-sightmap` attributes inside imported\n// components, not just the route file itself.\nasync function walkComponentTree(\n file: string,\n sourceFiles: Set<string>,\n visited: Set<string>,\n): Promise<void> {\n if (visited.has(file)) return;\n visited.add(file);\n sourceFiles.add(file);\n\n let source: string;\n try {\n source = await fs.readFile(file, \"utf-8\");\n } catch {\n return;\n }\n\n const ast = await parseAst(source, { syntax: \"typescript\", tsx: true, target: \"es2022\" });\n for (const stmt of ast.body ?? []) {\n if (stmt.type !== \"ImportDeclaration\") continue;\n const sourcePath = stmt.source?.value as string | undefined;\n if (!sourcePath || !sourcePath.startsWith(\".\")) continue;\n const resolved = resolveLocalImport(file, sourcePath);\n if (!resolved) continue;\n await walkComponentTree(resolved, sourceFiles, visited);\n }\n}\n\nfunction resolveLocalImport(fromFile: string, importSpec: string): string | null {\n const dir = dirname(fromFile);\n const stripped = importSpec.replace(/\\.(js|jsx|mjs)$/, \"\");\n for (const ext of [\".tsx\", \".ts\", \".jsx\", \".js\"]) {\n const candidate = resolve(dir, `${stripped}${ext}`);\n if (existsSync(candidate)) return candidate;\n }\n if (stripped !== importSpec) {\n const original = resolve(dir, importSpec);\n if (existsSync(original)) return original;\n }\n return null;\n}\n","// src/plugin/router-adapter.ts\nimport type { Diagnostic } from \"@sightmap/sightmap\";\n\nexport type RouteDescriptor = {\n pattern: string; // normalized to spec route grammar\n featureName: string; // chunking key — derives the YAML filename\n sourceFiles: string[]; // absolute paths reachable from this route's tree\n meta?: Record<string, unknown>; // raw router metadata; informational only in v1\n};\n\nexport type AdapterContext = {\n projectRoot: string;\n resolveSource: (id: string) => string | null;\n /**\n * Optional sink the adapter can push diagnostics into during discovery — e.g.\n * a `<Route>` it found but couldn't fully process. Adapters MUST tolerate\n * this being absent (callers from tests may not pass one). The orchestrator\n * always provides it and forwards the contents into its own diagnostic\n * stream so they reach the CLI summary.\n */\n diagnostics?: Diagnostic[];\n};\n\nexport interface RouterAdapter {\n name: string;\n detect(projectRoot: string): boolean;\n discoverRoutes(ctx: AdapterContext): Promise<RouteDescriptor[]>;\n /** Optional client-side helper. Falls back to `location.pathname` if absent. */\n currentRoute?(): string;\n}\n\n/** Derive the feature name from a route pattern. First non-parameter segment, or \"home\" for \"/\". */\nexport function deriveFeatureName(pattern: string): string {\n const segments = pattern.split(\"/\").filter(Boolean);\n for (const seg of segments) {\n if (!seg.startsWith(\":\") && !seg.startsWith(\"*\")) return sanitize(seg);\n }\n return \"home\";\n}\n\n/**\n * Derive a view name from a route pattern.\n *\n * Picks the LAST non-parameter segment, capitalized — so `/app/dashboard` →\n * `Dashboard`, `/settings/billing` → `Billing`, `/login` → `Login`.\n *\n * Routes ending in a parameter (`/users/:id`) get the preceding segment with\n * `Detail` appended (`UsersDetail`) so they don't collide with the list view.\n *\n * Routes ending in a wildcard (`/files/*`, `/*`) get `Catchall` appended —\n * a different concept from `:param` (catch-all matches multiple segments;\n * named params match one).\n *\n * Falls back to `Home` for `/`.\n */\nexport function deriveViewName(pattern: string): string {\n const segments = pattern.split(\"/\").filter(Boolean);\n if (segments.length === 0) return \"Home\";\n\n const last = segments[segments.length - 1]!;\n const lastIsParam = last.startsWith(\":\");\n const lastIsWildcard = last.startsWith(\"*\");\n\n if (lastIsParam || lastIsWildcard) {\n const suffix = lastIsWildcard ? \"Catchall\" : \"Detail\";\n // Walk back to the last static segment and append the suffix.\n for (let i = segments.length - 2; i >= 0; i--) {\n const seg = segments[i]!;\n if (!seg.startsWith(\":\") && !seg.startsWith(\"*\")) {\n return capitalize(sanitize(seg)) + suffix;\n }\n }\n return suffix;\n }\n return capitalize(sanitize(last));\n}\n\nfunction sanitize(s: string): string {\n return s.replace(/[^a-zA-Z0-9_-]/g, \"-\").toLowerCase();\n}\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Diagnostic } from '@sightmap/sightmap';
|
|
2
|
-
|
|
3
|
-
type RouteDescriptor = {
|
|
4
|
-
pattern: string;
|
|
5
|
-
featureName: string;
|
|
6
|
-
sourceFiles: string[];
|
|
7
|
-
meta?: Record<string, unknown>;
|
|
8
|
-
};
|
|
9
|
-
type AdapterContext = {
|
|
10
|
-
projectRoot: string;
|
|
11
|
-
resolveSource: (id: string) => string | null;
|
|
12
|
-
/**
|
|
13
|
-
* Optional sink the adapter can push diagnostics into during discovery — e.g.
|
|
14
|
-
* a `<Route>` it found but couldn't fully process. Adapters MUST tolerate
|
|
15
|
-
* this being absent (callers from tests may not pass one). The orchestrator
|
|
16
|
-
* always provides it and forwards the contents into its own diagnostic
|
|
17
|
-
* stream so they reach the CLI summary.
|
|
18
|
-
*/
|
|
19
|
-
diagnostics?: Diagnostic[];
|
|
20
|
-
};
|
|
21
|
-
interface RouterAdapter {
|
|
22
|
-
name: string;
|
|
23
|
-
detect(projectRoot: string): boolean;
|
|
24
|
-
discoverRoutes(ctx: AdapterContext): Promise<RouteDescriptor[]>;
|
|
25
|
-
/** Optional client-side helper. Falls back to `location.pathname` if absent. */
|
|
26
|
-
currentRoute?(): string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export type { RouterAdapter as R };
|