@flaid/devmap 0.6.0 → 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/README.md +2 -2
- package/dist/analyzers/detectors/adonisRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/adonisRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/adonisRouteDetector.js +242 -0
- package/dist/analyzers/detectors/adonisRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/angularRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/angularRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/angularRouteDetector.js +186 -0
- package/dist/analyzers/detectors/angularRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/astUtils.d.ts +31 -0
- package/dist/analyzers/detectors/astUtils.d.ts.map +1 -0
- package/dist/analyzers/detectors/astUtils.js +85 -0
- package/dist/analyzers/detectors/astUtils.js.map +1 -0
- package/dist/analyzers/detectors/clientRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/clientRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/clientRouteDetector.js +417 -0
- package/dist/analyzers/detectors/clientRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/elysiaRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/elysiaRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/elysiaRouteDetector.js +186 -0
- package/dist/analyzers/detectors/elysiaRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/expoRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/expoRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/expoRouteDetector.js +119 -0
- package/dist/analyzers/detectors/expoRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/frameworkDetector.d.ts +2 -2
- package/dist/analyzers/detectors/frameworkDetector.d.ts.map +1 -1
- package/dist/analyzers/detectors/frameworkDetector.js +86 -3
- package/dist/analyzers/detectors/frameworkDetector.js.map +1 -1
- package/dist/analyzers/detectors/honoRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/honoRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/honoRouteDetector.js +485 -0
- package/dist/analyzers/detectors/honoRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/koaRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/koaRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/koaRouteDetector.js +216 -0
- package/dist/analyzers/detectors/koaRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/moduleBindings.d.ts +52 -0
- package/dist/analyzers/detectors/moduleBindings.d.ts.map +1 -0
- package/dist/analyzers/detectors/moduleBindings.js +295 -0
- package/dist/analyzers/detectors/moduleBindings.js.map +1 -0
- package/dist/analyzers/detectors/qwikRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/qwikRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/qwikRouteDetector.js +53 -0
- package/dist/analyzers/detectors/qwikRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/reactRouterRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/reactRouterRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/reactRouterRouteDetector.js +142 -0
- package/dist/analyzers/detectors/reactRouterRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/remixRouteDetector.d.ts +42 -0
- package/dist/analyzers/detectors/remixRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/remixRouteDetector.js +123 -0
- package/dist/analyzers/detectors/remixRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/routeDetector.d.ts.map +1 -1
- package/dist/analyzers/detectors/routeDetector.js +61 -66
- package/dist/analyzers/detectors/routeDetector.js.map +1 -1
- package/dist/analyzers/detectors/routeUtils.d.ts +43 -0
- package/dist/analyzers/detectors/routeUtils.d.ts.map +1 -0
- package/dist/analyzers/detectors/routeUtils.js +162 -0
- package/dist/analyzers/detectors/routeUtils.js.map +1 -0
- package/dist/analyzers/detectors/solidStartRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/solidStartRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/solidStartRouteDetector.js +59 -0
- package/dist/analyzers/detectors/solidStartRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/tanstackRouteDetector.d.ts +4 -0
- package/dist/analyzers/detectors/tanstackRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/tanstackRouteDetector.js +100 -0
- package/dist/analyzers/detectors/tanstackRouteDetector.js.map +1 -0
- package/dist/analyzers/graph/aliasResolver.d.ts.map +1 -1
- package/dist/analyzers/graph/aliasResolver.js +3 -0
- package/dist/analyzers/graph/aliasResolver.js.map +1 -1
- package/dist/analyzers/graph/bundlerAliases.d.ts +12 -0
- package/dist/analyzers/graph/bundlerAliases.d.ts.map +1 -0
- package/dist/analyzers/graph/bundlerAliases.js +328 -0
- package/dist/analyzers/graph/bundlerAliases.js.map +1 -0
- package/dist/analyzers/graph/entryPoints.d.ts.map +1 -1
- package/dist/analyzers/graph/entryPoints.js +34 -3
- package/dist/analyzers/graph/entryPoints.js.map +1 -1
- package/dist/analyzers/pipeline/projectMap.d.ts.map +1 -1
- package/dist/analyzers/pipeline/projectMap.js +17 -6
- package/dist/analyzers/pipeline/projectMap.js.map +1 -1
- package/dist/analyzers/pipeline/projectMetadata.js +11 -0
- package/dist/analyzers/pipeline/projectMetadata.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { isArchitectureSource } from "../graph/index.js";
|
|
2
|
+
import { findProjectDirs, joinRoutePaths, sortRoutes } from "./routeUtils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Qwik City routes are folders under `src/routes/`, and a folder is a URL only
|
|
5
|
+
* when it holds an `index` file:
|
|
6
|
+
*
|
|
7
|
+
* src/routes/index.tsx → / (page)
|
|
8
|
+
* src/routes/blog/[slug]/index.tsx → /blog/[slug] (`[...rest]` and `[[optional]]` are kept as written)
|
|
9
|
+
* src/routes/(marketing)/about/index.mdx → /about (`(group)` folders add no URL)
|
|
10
|
+
* src/routes/api/ping/index.ts → /api/ping (endpoint: `onGet`, `onPost`, `onRequest`, ...)
|
|
11
|
+
* index@admin.tsx → same URL, rendered in the `admin` layout
|
|
12
|
+
*
|
|
13
|
+
* `layout.tsx`, `plugin@*.ts` and everything else in the folder are not routes.
|
|
14
|
+
*/
|
|
15
|
+
const QWIK = ["@builder.io/qwik-city", "@qwik.dev/router"];
|
|
16
|
+
const ROUTE_FILE = /^index(?:@[\w-]+)?\.(tsx|jsx|ts|js|mdx|md)$/;
|
|
17
|
+
const HANDLERS = {
|
|
18
|
+
onGet: "GET", onPost: "POST", onPut: "PUT", onPatch: "PATCH", onDelete: "DELETE",
|
|
19
|
+
onHead: "HEAD", onOptions: "OPTIONS", onRequest: undefined
|
|
20
|
+
};
|
|
21
|
+
export function detectQwikRoutes(files) {
|
|
22
|
+
const routes = new Map();
|
|
23
|
+
for (const projectDir of findProjectDirs(files, QWIK)) {
|
|
24
|
+
const root = `${projectDir}src/routes/`;
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
if (!file.path.startsWith(root) || !isArchitectureSource(file.path))
|
|
27
|
+
continue;
|
|
28
|
+
const segments = file.path.slice(root.length).split("/");
|
|
29
|
+
const name = segments.pop() ?? "";
|
|
30
|
+
const match = ROUTE_FILE.exec(name);
|
|
31
|
+
if (!match)
|
|
32
|
+
continue;
|
|
33
|
+
const path = joinRoutePaths(...segments.filter((segment) => !/^\(.*\)$/.test(segment)));
|
|
34
|
+
const extension = match[1];
|
|
35
|
+
if (extension === "tsx" || extension === "jsx" || extension === "md" || extension === "mdx") {
|
|
36
|
+
routes.set(`page ${path}`, { path, file: file.path, kind: "page" });
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const exported = Object.keys(HANDLERS).filter((handler) => new RegExp(`export\\s+(?:const|async\\s+function|function)\\s+${handler}\\b`).test(file.content));
|
|
40
|
+
if (exported.length === 0)
|
|
41
|
+
continue;
|
|
42
|
+
const methods = exported.map((handler) => HANDLERS[handler]).filter((method) => Boolean(method)).sort();
|
|
43
|
+
routes.set(`api ${path}`, {
|
|
44
|
+
path,
|
|
45
|
+
file: file.path,
|
|
46
|
+
kind: "api",
|
|
47
|
+
...(methods.length > 0 && !exported.includes("onRequest") ? { methods } : {})
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return sortRoutes([...routes.values()]);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=qwikRouteDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qwikRouteDetector.js","sourceRoot":"","sources":["../../../src/analyzers/detectors/qwikRouteDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactRouterRouteDetector.d.ts","sourceRoot":"","sources":["../../../src/analyzers/detectors/reactRouterRouteDetector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAqCpD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,SAAS,EAAE,CAyCzE"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
import { ts } from "ts-morph";
|
|
3
|
+
import { parseSource, stringValue, unwrapExpression } from "./astUtils.js";
|
|
4
|
+
import { detectFlatFileRoutes, readRouteModule } from "./remixRouteDetector.js";
|
|
5
|
+
import { dropShadowedLayouts, joinRoutePaths, sortRoutes } from "./routeUtils.js";
|
|
6
|
+
/**
|
|
7
|
+
* React Router v7 framework mode declares its routes in `app/routes.ts`:
|
|
8
|
+
*
|
|
9
|
+
* export default [
|
|
10
|
+
* index("routes/home.tsx"),
|
|
11
|
+
* route("teams/:teamId", "routes/team.tsx", [index("routes/team-home.tsx")]),
|
|
12
|
+
* layout("layouts/auth.tsx", [route("login", "routes/login.tsx")]),
|
|
13
|
+
* ...prefix("api", [route("users", "routes/api/users.ts")]),
|
|
14
|
+
* ...(await flatRoutes()) // file-name convention, see remixRouteDetector
|
|
15
|
+
* ] satisfies RouteConfig;
|
|
16
|
+
*
|
|
17
|
+
* The array is a plain value, so it is read from the AST rather than matched
|
|
18
|
+
* with regexes: nesting, `prefix()` and identifiers pointing at other arrays
|
|
19
|
+
* all compose the way React Router composes them.
|
|
20
|
+
*/
|
|
21
|
+
const ROUTES_MODULE = "@react-router/dev/routes";
|
|
22
|
+
export function detectReactRouterRoutes(files) {
|
|
23
|
+
const configs = files.filter((file) => /(^|\/)routes\.[cm]?[jt]s$/.test(file.path) && file.content.includes(ROUTES_MODULE));
|
|
24
|
+
const byPath = new Map(files.map((file) => [file.path, file]));
|
|
25
|
+
const routes = [];
|
|
26
|
+
for (const config of configs) {
|
|
27
|
+
const appDir = posix.dirname(config.path) === "." ? "" : posix.dirname(config.path);
|
|
28
|
+
const parsed = parseRouteConfig(config);
|
|
29
|
+
const entries = [];
|
|
30
|
+
collect(parsed.nodes, "/", (node, path) => {
|
|
31
|
+
if (!node.file || node.layout || node.group)
|
|
32
|
+
return;
|
|
33
|
+
const file = byPath.get(posix.normalize(posix.join(appDir, node.file)));
|
|
34
|
+
const module = file ? readRouteModule(file) : undefined;
|
|
35
|
+
if (!file)
|
|
36
|
+
return;
|
|
37
|
+
entries.push({
|
|
38
|
+
path,
|
|
39
|
+
file: file.path,
|
|
40
|
+
kind: module?.kind ?? "page",
|
|
41
|
+
...(module?.methods ? { methods: module.methods } : {}),
|
|
42
|
+
isIndex: Boolean(node.index),
|
|
43
|
+
isLayout: node.children.length > 0
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
routes.push(...dropShadowedLayouts(entries).map((entry) => ({
|
|
47
|
+
path: entry.path,
|
|
48
|
+
file: entry.file,
|
|
49
|
+
kind: entry.kind,
|
|
50
|
+
...(entry.methods ? { methods: entry.methods } : {})
|
|
51
|
+
})));
|
|
52
|
+
if (parsed.usesFlatRoutes) {
|
|
53
|
+
routes.push(...detectFlatFileRoutes(files, appDir === "" ? "routes/" : `${appDir}/routes/`));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return sortRoutes(routes);
|
|
57
|
+
}
|
|
58
|
+
function collect(nodes, base, visit) {
|
|
59
|
+
for (const node of nodes) {
|
|
60
|
+
const path = node.layout ? base : joinRoutePaths(base, node.path);
|
|
61
|
+
visit(node, path);
|
|
62
|
+
collect(node.children, path, visit);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function parseRouteConfig(file) {
|
|
66
|
+
const source = parseSource(file.path, file.content);
|
|
67
|
+
const helpers = new Map();
|
|
68
|
+
const variables = new Map();
|
|
69
|
+
let defaultExport;
|
|
70
|
+
let usesFlatRoutes = false;
|
|
71
|
+
for (const statement of source.statements) {
|
|
72
|
+
if (ts.isImportDeclaration(statement)) {
|
|
73
|
+
const module = stringValue(statement.moduleSpecifier);
|
|
74
|
+
const bindings = statement.importClause?.namedBindings;
|
|
75
|
+
if (bindings && ts.isNamedImports(bindings)) {
|
|
76
|
+
for (const element of bindings.elements) {
|
|
77
|
+
const imported = (element.propertyName ?? element.name).text;
|
|
78
|
+
if (module === ROUTES_MODULE)
|
|
79
|
+
helpers.set(element.name.text, imported);
|
|
80
|
+
if (imported === "flatRoutes")
|
|
81
|
+
helpers.set(element.name.text, "flatRoutes");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if (ts.isVariableStatement(statement)) {
|
|
86
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
87
|
+
if (ts.isIdentifier(declaration.name) && declaration.initializer) {
|
|
88
|
+
variables.set(declaration.name.text, declaration.initializer);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (ts.isExportAssignment(statement) && !statement.isExportEquals) {
|
|
93
|
+
defaultExport = statement.expression;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const seen = new Set();
|
|
97
|
+
const evaluate = (expression) => {
|
|
98
|
+
const node = unwrapExpression(expression);
|
|
99
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
100
|
+
return node.elements.flatMap((element) => evaluate(ts.isSpreadElement(element) ? element.expression : element));
|
|
101
|
+
}
|
|
102
|
+
if (ts.isIdentifier(node)) {
|
|
103
|
+
const initializer = variables.get(node.text);
|
|
104
|
+
if (!initializer || seen.has(node.text))
|
|
105
|
+
return [];
|
|
106
|
+
seen.add(node.text);
|
|
107
|
+
const result = evaluate(initializer);
|
|
108
|
+
seen.delete(node.text);
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
112
|
+
return evaluateHelper(helpers.get(node.expression.text), node.arguments);
|
|
113
|
+
}
|
|
114
|
+
return [];
|
|
115
|
+
};
|
|
116
|
+
const lastChildren = (args, from) => {
|
|
117
|
+
const candidate = args.slice(from).reverse().find((arg) => {
|
|
118
|
+
const unwrapped = unwrapExpression(arg);
|
|
119
|
+
return !ts.isObjectLiteralExpression(unwrapped) && !stringValue(unwrapped);
|
|
120
|
+
});
|
|
121
|
+
return candidate ? evaluate(candidate) : [];
|
|
122
|
+
};
|
|
123
|
+
const evaluateHelper = (helper, args) => {
|
|
124
|
+
switch (helper) {
|
|
125
|
+
case "index":
|
|
126
|
+
return [{ path: "", index: true, file: stringValue(args[0]), children: [] }];
|
|
127
|
+
case "route":
|
|
128
|
+
return [{ path: stringValue(args[0]) ?? "", file: stringValue(args[1]), children: lastChildren(args, 2) }];
|
|
129
|
+
case "layout":
|
|
130
|
+
return [{ path: "", file: stringValue(args[0]), layout: true, children: lastChildren(args, 1) }];
|
|
131
|
+
case "prefix":
|
|
132
|
+
return [{ path: stringValue(args[0]) ?? "", group: true, children: args[1] ? evaluate(args[1]) : [] }];
|
|
133
|
+
case "flatRoutes":
|
|
134
|
+
usesFlatRoutes = true;
|
|
135
|
+
return [];
|
|
136
|
+
default:
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
return { nodes: defaultExport ? evaluate(defaultExport) : [], usesFlatRoutes };
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=reactRouterRouteDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactRouterRouteDetector.js","sourceRoot":"","sources":["../../../src/analyzers/detectors/reactRouterRouteDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ScannedFile } from "../analysis/index.js";
|
|
2
|
+
import type { RouteInfo } from "./routeDetector.js";
|
|
3
|
+
/**
|
|
4
|
+
* Remix (v2 flat routes, v1 folders, remix-flat-routes `+` folders) and React
|
|
5
|
+
* Router v7's `flatRoutes()` share one file-name convention:
|
|
6
|
+
*
|
|
7
|
+
* notes.$noteId.tsx → /notes/:noteId
|
|
8
|
+
* notes._index.tsx → /notes (index route)
|
|
9
|
+
* _auth.login.tsx → /login (`_auth` is a pathless layout)
|
|
10
|
+
* posts_.$id.tsx → /posts/:id (trailing `_` escapes the nesting)
|
|
11
|
+
* ($lang).about.tsx → /:lang?/about (optional segment)
|
|
12
|
+
* files.$.tsx → /files/* (splat)
|
|
13
|
+
* sitemap[.]xml.tsx → /sitemap.xml (`[ ]` escapes)
|
|
14
|
+
*
|
|
15
|
+
* A route module without a default export is a resource route (`api`): it only
|
|
16
|
+
* answers `loader` (GET) and `action` (POST and friends).
|
|
17
|
+
*/
|
|
18
|
+
export type RouteModule = {
|
|
19
|
+
kind: RouteInfo["kind"];
|
|
20
|
+
methods?: string[];
|
|
21
|
+
rendersOutlet: boolean;
|
|
22
|
+
};
|
|
23
|
+
/** `undefined` when the file exports nothing a router would load. */
|
|
24
|
+
export declare function readRouteModule(file: ScannedFile): RouteModule | undefined;
|
|
25
|
+
export type FlatRoutePath = {
|
|
26
|
+
path: string;
|
|
27
|
+
isIndex: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* URL for a route file, given its path inside the routes folder. `undefined`
|
|
31
|
+
* for a pathless layout, which has no URL of its own.
|
|
32
|
+
*/
|
|
33
|
+
export declare function flatRoutePath(relative: string): FlatRoutePath | undefined;
|
|
34
|
+
/** Routes for every module under `routesDir` (a path prefix ending in `/`). */
|
|
35
|
+
export declare function detectFlatFileRoutes(files: ScannedFile[], routesDir: string): RouteInfo[];
|
|
36
|
+
/**
|
|
37
|
+
* `confirmed` means the project's framework is known to be Remix. Without it
|
|
38
|
+
* (a monorepo whose other apps won the framework slot) a `routes/` folder only
|
|
39
|
+
* counts when a package.json above it depends on Remix.
|
|
40
|
+
*/
|
|
41
|
+
export declare function detectRemixRoutes(files: ScannedFile[], confirmed?: boolean): RouteInfo[];
|
|
42
|
+
//# sourceMappingURL=remixRouteDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remixRouteDetector.d.ts","sourceRoot":"","sources":["../../../src/analyzers/detectors/remixRouteDetector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAQpD;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAWF,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS,CAgB1E;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CA8BzE;AAeD,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CA2BzF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,SAAS,UAAO,GAAG,SAAS,EAAE,CAYrF"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { isArchitectureSource } from "../graph/index.js";
|
|
2
|
+
import { dropShadowedLayouts, sortRoutes, splitFlatRouteName, stripRouteModuleExtension } from "./routeUtils.js";
|
|
3
|
+
const ROUTE_EXPORTS = "loader|action|clientLoader|clientAction|meta|links|handle|headers|shouldRevalidate|ErrorBoundary|HydrateFallback|Layout";
|
|
4
|
+
const DEFAULT_EXPORT = /export\s+default\b|export\s*\{[^}]*\bas\s+default\b/;
|
|
5
|
+
const NAMED_ROUTE_EXPORT = new RegExp(`export\\s+(?:async\\s+)?(?:function|const|let|var)\\s+(?:${ROUTE_EXPORTS})\\b`
|
|
6
|
+
+ `|export\\s*\\{[^}]*\\b(?:${ROUTE_EXPORTS})\\b`);
|
|
7
|
+
/** `undefined` when the file exports nothing a router would load. */
|
|
8
|
+
export function readRouteModule(file) {
|
|
9
|
+
const isMarkdown = /\.mdx?$/.test(file.path);
|
|
10
|
+
const hasDefault = isMarkdown || DEFAULT_EXPORT.test(file.content);
|
|
11
|
+
if (!hasDefault && !NAMED_ROUTE_EXPORT.test(file.content))
|
|
12
|
+
return undefined;
|
|
13
|
+
const rendersOutlet = /\bOutlet\b/.test(file.content);
|
|
14
|
+
if (hasDefault)
|
|
15
|
+
return { kind: "page", rendersOutlet };
|
|
16
|
+
const methods = new Set();
|
|
17
|
+
if (/export\s+(?:async\s+)?(?:function|const|let|var)\s+(?:loader|clientLoader)\b|export\s*\{[^}]*\b(?:loader|clientLoader)\b/.test(file.content)) {
|
|
18
|
+
methods.add("GET");
|
|
19
|
+
}
|
|
20
|
+
if (/export\s+(?:async\s+)?(?:function|const|let|var)\s+(?:action|clientAction)\b|export\s*\{[^}]*\b(?:action|clientAction)\b/.test(file.content)) {
|
|
21
|
+
methods.add("POST");
|
|
22
|
+
}
|
|
23
|
+
return { kind: "api", ...(methods.size > 0 ? { methods: [...methods].sort() } : {}), rendersOutlet };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* URL for a route file, given its path inside the routes folder. `undefined`
|
|
27
|
+
* for a pathless layout, which has no URL of its own.
|
|
28
|
+
*/
|
|
29
|
+
export function flatRoutePath(relative) {
|
|
30
|
+
const parts = stripRouteModuleExtension(relative).split("/");
|
|
31
|
+
// notes.$id/route.tsx, notes.$id/index.tsx and notes/index.tsx: the folder is the route.
|
|
32
|
+
const last = parts[parts.length - 1];
|
|
33
|
+
if (parts.length > 1 && (last === "route" || last === "index"))
|
|
34
|
+
parts.pop();
|
|
35
|
+
// remix-flat-routes: `users+/` folders only group their files.
|
|
36
|
+
const names = parts.map((part) => part.replace(/\+$/, ""));
|
|
37
|
+
const segments = splitFlatRouteName(names.join("/"));
|
|
38
|
+
const url = [];
|
|
39
|
+
let isIndex = false;
|
|
40
|
+
segments.forEach((segment, position) => {
|
|
41
|
+
const isLast = position === segments.length - 1;
|
|
42
|
+
if (isLast && (segment === "_index" || segment === "index")) {
|
|
43
|
+
isIndex = true;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Pathless layout: `_auth`, `__auth`.
|
|
47
|
+
if (segment.startsWith("_"))
|
|
48
|
+
return;
|
|
49
|
+
const name = segment.length > 1 && segment.endsWith("_") ? segment.slice(0, -1) : segment;
|
|
50
|
+
url.push(toUrlSegment(name));
|
|
51
|
+
});
|
|
52
|
+
if (url.length === 0 && !isIndex)
|
|
53
|
+
return undefined;
|
|
54
|
+
return { path: `/${url.join("/")}`, isIndex };
|
|
55
|
+
}
|
|
56
|
+
function toUrlSegment(segment) {
|
|
57
|
+
const optional = /^\((.+)\)$/.exec(segment);
|
|
58
|
+
if (optional) {
|
|
59
|
+
const inner = optional[1];
|
|
60
|
+
return inner.startsWith("$") ? `:${inner.slice(1)}?` : `${inner}?`;
|
|
61
|
+
}
|
|
62
|
+
if (segment === "$")
|
|
63
|
+
return "*";
|
|
64
|
+
if (segment.startsWith("$"))
|
|
65
|
+
return `:${segment.slice(1)}`;
|
|
66
|
+
return segment;
|
|
67
|
+
}
|
|
68
|
+
/** Routes for every module under `routesDir` (a path prefix ending in `/`). */
|
|
69
|
+
export function detectFlatFileRoutes(files, routesDir) {
|
|
70
|
+
const entries = [];
|
|
71
|
+
for (const file of files) {
|
|
72
|
+
if (!file.path.startsWith(routesDir) || !isArchitectureSource(file.path))
|
|
73
|
+
continue;
|
|
74
|
+
if (!/\.(?:[cm]?[jt]sx?|mdx?)$/.test(file.path) || file.path.endsWith(".d.ts"))
|
|
75
|
+
continue;
|
|
76
|
+
const route = flatRoutePath(file.path.slice(routesDir.length));
|
|
77
|
+
const module = route ? readRouteModule(file) : undefined;
|
|
78
|
+
if (!route || !module)
|
|
79
|
+
continue;
|
|
80
|
+
entries.push({
|
|
81
|
+
path: route.path,
|
|
82
|
+
file: file.path,
|
|
83
|
+
kind: module.kind,
|
|
84
|
+
...(module.methods ? { methods: module.methods } : {}),
|
|
85
|
+
isIndex: route.isIndex,
|
|
86
|
+
isLayout: module.rendersOutlet
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return dropShadowedLayouts(entries).map((entry) => ({
|
|
90
|
+
path: entry.path,
|
|
91
|
+
file: entry.file,
|
|
92
|
+
kind: entry.kind,
|
|
93
|
+
...(entry.methods ? { methods: entry.methods } : {})
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* `confirmed` means the project's framework is known to be Remix. Without it
|
|
98
|
+
* (a monorepo whose other apps won the framework slot) a `routes/` folder only
|
|
99
|
+
* counts when a package.json above it depends on Remix.
|
|
100
|
+
*/
|
|
101
|
+
export function detectRemixRoutes(files, confirmed = true) {
|
|
102
|
+
const routesDirs = new Set();
|
|
103
|
+
const remixRoots = confirmed ? undefined : remixPackageDirs(files);
|
|
104
|
+
for (const file of files) {
|
|
105
|
+
const match = /^(.*?(?:^|\/)app\/routes\/)/.exec(file.path);
|
|
106
|
+
if (!match)
|
|
107
|
+
continue;
|
|
108
|
+
if (remixRoots && !isBelowAny(match[1], remixRoots))
|
|
109
|
+
continue;
|
|
110
|
+
routesDirs.add(match[1]);
|
|
111
|
+
}
|
|
112
|
+
return sortRoutes([...routesDirs].flatMap((dir) => detectFlatFileRoutes(files, dir)));
|
|
113
|
+
}
|
|
114
|
+
/** Folders (`""` for the root) whose package.json depends on `@remix-run/*`, except its router. */
|
|
115
|
+
function remixPackageDirs(files) {
|
|
116
|
+
return files
|
|
117
|
+
.filter((file) => /(^|\/)package\.json$/.test(file.path) && /"@remix-run\/(?!router")/.test(file.content))
|
|
118
|
+
.map((file) => (file.path.includes("/") ? file.path.slice(0, file.path.lastIndexOf("/") + 1) : ""));
|
|
119
|
+
}
|
|
120
|
+
function isBelowAny(path, directories) {
|
|
121
|
+
return directories.some((directory) => path.startsWith(directory));
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=remixRouteDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remixRouteDetector.js","sourceRoot":"","sources":["../../../src/analyzers/detectors/remixRouteDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routeDetector.d.ts","sourceRoot":"","sources":["../../../src/analyzers/detectors/routeDetector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"routeDetector.d.ts","sourceRoot":"","sources":["../../../src/analyzers/detectors/routeDetector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAwBhE,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,WAAW,EAAE,EACpB,UAAU,EAAE,iBAAiB,EAAE,EAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAC/B,SAAS,EAAE,CAsDb"}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { isArchitectureSource } from "../graph/index.js";
|
|
2
|
+
import { detectAdonisRoutes } from "./adonisRouteDetector.js";
|
|
3
|
+
import { detectAngularRoutes } from "./angularRouteDetector.js";
|
|
4
|
+
import { detectClientRoutes } from "./clientRouteDetector.js";
|
|
5
|
+
import { detectElysiaRoutes } from "./elysiaRouteDetector.js";
|
|
6
|
+
import { detectExpoRoutes } from "./expoRouteDetector.js";
|
|
7
|
+
import { detectHonoRoutes } from "./honoRouteDetector.js";
|
|
8
|
+
import { detectKoaRoutes } from "./koaRouteDetector.js";
|
|
2
9
|
import { detectNestRoutes } from "./nestRouteDetector.js";
|
|
10
|
+
import { detectQwikRoutes } from "./qwikRouteDetector.js";
|
|
11
|
+
import { detectReactRouterRoutes } from "./reactRouterRouteDetector.js";
|
|
12
|
+
import { detectRemixRoutes } from "./remixRouteDetector.js";
|
|
13
|
+
import { detectSolidStartRoutes } from "./solidStartRouteDetector.js";
|
|
14
|
+
import { detectTanStackRoutes } from "./tanstackRouteDetector.js";
|
|
15
|
+
import { composeMountPath, findHttpMethods, resolveRouterTarget, sortRoutes } from "./routeUtils.js";
|
|
3
16
|
import { usesTrpc, detectTrpcRoutes } from "./trpcRouteDetector.js";
|
|
4
17
|
/**
|
|
5
18
|
* detectRoutes — run every detector whose framework was detected, then merge
|
|
@@ -30,6 +43,17 @@ export function detectRoutes(files, frameworks, graph) {
|
|
|
30
43
|
if (frameworks.includes("sveltekit")) {
|
|
31
44
|
routes.push(...detectSvelteKitRoutes(files));
|
|
32
45
|
}
|
|
46
|
+
// The detectors below find their own projects (a `routes.ts` that imports
|
|
47
|
+
// `@react-router/dev/routes`, a package that depends on `expo-router`, a
|
|
48
|
+
// `createFileRoute()` call), so they also work for the second frontend of a
|
|
49
|
+
// monorepo, where `frameworks` only names the winner of each category.
|
|
50
|
+
routes.push(...detectRemixRoutes(files, frameworks.includes("remix")));
|
|
51
|
+
routes.push(...detectReactRouterRoutes(files));
|
|
52
|
+
routes.push(...detectTanStackRoutes(files));
|
|
53
|
+
routes.push(...detectExpoRoutes(files));
|
|
54
|
+
routes.push(...detectSolidStartRoutes(files));
|
|
55
|
+
routes.push(...detectQwikRoutes(files));
|
|
56
|
+
routes.push(...detectAngularRoutes(files, graph));
|
|
33
57
|
if (frameworks.includes("nestjs")) {
|
|
34
58
|
routes.push(...detectNestRoutes(files));
|
|
35
59
|
}
|
|
@@ -39,25 +63,53 @@ export function detectRoutes(files, frameworks, graph) {
|
|
|
39
63
|
if (frameworks.includes("fastify")) {
|
|
40
64
|
routes.push(...detectFastifyRoutes(files, graph));
|
|
41
65
|
}
|
|
66
|
+
routes.push(...detectHonoRoutes(files, graph));
|
|
67
|
+
routes.push(...detectElysiaRoutes(files, graph));
|
|
68
|
+
routes.push(...detectKoaRoutes(files, graph));
|
|
69
|
+
routes.push(...detectAdonisRoutes(files, graph));
|
|
70
|
+
routes.push(...detectClientRoutes(files, graph));
|
|
42
71
|
return sortRoutes(routes);
|
|
43
72
|
}
|
|
73
|
+
// Files that Next serves at a fixed URL.
|
|
74
|
+
const METADATA_ROUTES = {
|
|
75
|
+
sitemap: "sitemap.xml",
|
|
76
|
+
robots: "robots.txt",
|
|
77
|
+
manifest: "manifest.webmanifest"
|
|
78
|
+
};
|
|
44
79
|
function detectNextRoutes(files) {
|
|
45
80
|
const routes = [];
|
|
81
|
+
const slotFiles = new Set();
|
|
46
82
|
for (const file of files.filter((item) => isArchitectureSource(item.path))) {
|
|
47
83
|
// App Router — match anywhere in path, not just root.
|
|
48
84
|
// Supports: src/app/, app/, apps/web/src/app/, packages/web/src/app/, dll.
|
|
49
85
|
// Sebelumnya pakai ^(?:src\/)? yang miss monorepo prefix seperti apps/web/.
|
|
50
|
-
const appMatch = file.path.match(/(?:^|\/)(?:src\/)?app\/(.+\/)?(page|route)\.[jt]sx?$/);
|
|
86
|
+
const appMatch = file.path.match(/(?:^|\/)(?:src\/)?app\/(.+\/)?(page|route|sitemap|robots|manifest)\.[jt]sx?$/);
|
|
51
87
|
if (appMatch) {
|
|
52
88
|
const segments = (appMatch[1] ?? "").split("/").filter(Boolean);
|
|
53
|
-
|
|
54
|
-
|
|
89
|
+
// Folders that are not URLs of their own: `_private`, and intercepting
|
|
90
|
+
// routes (`(.)photo`, `(..)feed`, `(...)shop`), which re-render a URL that
|
|
91
|
+
// already has its own page.
|
|
92
|
+
if (segments.some((segment) => segment.startsWith("_") || /^\(\.{1,3}\)/.test(segment)))
|
|
93
|
+
continue;
|
|
94
|
+
const type = appMatch[2];
|
|
95
|
+
const metadataFile = METADATA_ROUTES[type];
|
|
96
|
+
// robots and manifest only count at the root of the app folder (a `(group)` is not a folder).
|
|
97
|
+
if ((type === "robots" || type === "manifest") && segments.some((segment) => !/^\(.*\)$/.test(segment)))
|
|
98
|
+
continue;
|
|
99
|
+
const isSlot = segments.some((segment) => segment.startsWith("@"));
|
|
100
|
+
const routePath = metadataFile
|
|
101
|
+
? toRoutePath([...segments, metadataFile])
|
|
102
|
+
: toRoutePath(segments);
|
|
103
|
+
const kind = type === "page" ? "page" : "api";
|
|
55
104
|
routes.push({
|
|
56
105
|
path: routePath,
|
|
57
106
|
file: file.path,
|
|
58
107
|
kind,
|
|
59
|
-
...(
|
|
108
|
+
...(type === "route" ? { methods: findHttpMethods(file.content) } : {}),
|
|
109
|
+
...(metadataFile ? { methods: ["GET"] } : {})
|
|
60
110
|
});
|
|
111
|
+
if (isSlot)
|
|
112
|
+
slotFiles.add(file.path);
|
|
61
113
|
continue;
|
|
62
114
|
}
|
|
63
115
|
// Pages Router — sama, ganti ^ ke (?:^|\/) buat monorepo support.
|
|
@@ -74,7 +126,11 @@ function detectNextRoutes(files) {
|
|
|
74
126
|
kind: isApi ? "api" : "page"
|
|
75
127
|
});
|
|
76
128
|
}
|
|
77
|
-
|
|
129
|
+
// `app/dashboard/@team/page.tsx` fills a slot of `/dashboard`: when the URL has a
|
|
130
|
+
// page of its own the slot is part of it, not another route.
|
|
131
|
+
const ownPaths = new Set(routes.filter((route) => !slotFiles.has(route.file)).map((route) => `${route.kind}:${route.path}`));
|
|
132
|
+
const visible = routes.filter((route) => !slotFiles.has(route.file) || !ownPaths.has(`${route.kind}:${route.path}`));
|
|
133
|
+
return sortRoutes(visible);
|
|
78
134
|
}
|
|
79
135
|
/**
|
|
80
136
|
* detectAstroRoutes — pages live under src/pages/ (Astro always uses src/ as
|
|
@@ -375,69 +431,8 @@ function collectRouteMethods(content, pattern = ROUTE_METHOD_PATTERN) {
|
|
|
375
431
|
.map(([path, methods]) => [path, [...methods].sort()])
|
|
376
432
|
.sort(([left], [right]) => left.localeCompare(right));
|
|
377
433
|
}
|
|
378
|
-
function resolveRouterTarget(identifier, mountFile, eligibleFiles, byPath, graph, hasRouteMethods) {
|
|
379
|
-
if (!graph) {
|
|
380
|
-
return undefined;
|
|
381
|
-
}
|
|
382
|
-
const candidates = (graph[mountFile] ?? [])
|
|
383
|
-
.map((path) => byPath.get(path))
|
|
384
|
-
.filter((file) => Boolean(file))
|
|
385
|
-
.filter((file) => /\.[cm]?[jt]s$/.test(file.path));
|
|
386
|
-
if (candidates.length === 0) {
|
|
387
|
-
return undefined;
|
|
388
|
-
}
|
|
389
|
-
const identifierPattern = new RegExp(`\\b${escapeRegExp(identifier)}\\b`);
|
|
390
|
-
const identifierMatches = candidates.filter((file) => identifierPattern.test(file.content));
|
|
391
|
-
const withRouteMethods = hasRouteMethods
|
|
392
|
-
? candidates.filter(hasRouteMethods)
|
|
393
|
-
: [];
|
|
394
|
-
// Identifier match wins; fall back to "the only imported file that defines
|
|
395
|
-
// route methods" — covers the common one-router-per-import case without
|
|
396
|
-
// precise import parsing.
|
|
397
|
-
const pool = identifierMatches.length > 0 ? identifierMatches : withRouteMethods;
|
|
398
|
-
return pool.length === 1 ? pool[0] : undefined;
|
|
399
|
-
}
|
|
400
|
-
function composeMountPath(prefix, subPath) {
|
|
401
|
-
if (subPath === "/" || subPath === "") {
|
|
402
|
-
return prefix;
|
|
403
|
-
}
|
|
404
|
-
return `${prefix.replace(/\/$/, "")}/${subPath.replace(/^\//, "")}`;
|
|
405
|
-
}
|
|
406
|
-
function escapeRegExp(value) {
|
|
407
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
408
|
-
}
|
|
409
434
|
function toRoutePath(segments) {
|
|
410
435
|
const visible = segments.filter((segment) => !segment.startsWith("(") && !segment.startsWith("@"));
|
|
411
436
|
return `/${visible.join("/")}`.replace(/\/+/g, "/");
|
|
412
437
|
}
|
|
413
|
-
const HTTP_METHOD_NAMES = new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]);
|
|
414
|
-
function findHttpMethods(content) {
|
|
415
|
-
const methods = new Set();
|
|
416
|
-
const add = (name) => {
|
|
417
|
-
if (name && HTTP_METHOD_NAMES.has(name))
|
|
418
|
-
methods.add(name);
|
|
419
|
-
};
|
|
420
|
-
// export async function GET() {} / export const POST = ...
|
|
421
|
-
for (const match of content.matchAll(/export\s+(?:async\s+)?(?:function|const)\s+(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\b/g)) {
|
|
422
|
-
add(match[1]);
|
|
423
|
-
}
|
|
424
|
-
// export { handler as GET, handler as POST }, export { GET } from "./handlers"
|
|
425
|
-
for (const match of content.matchAll(/export\s*(?:type\s*)?\{([^}]*)\}/g)) {
|
|
426
|
-
for (const specifier of match[1].split(",")) {
|
|
427
|
-
const exported = specifier.trim().split(/\s+as\s+/).pop()?.trim();
|
|
428
|
-
add(exported);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
// export const { GET, POST } = handlers; (Auth.js and similar)
|
|
432
|
-
for (const match of content.matchAll(/export\s+const\s*\{([^}]*)\}\s*=/g)) {
|
|
433
|
-
for (const element of match[1].split(",")) {
|
|
434
|
-
const bound = element.trim().split(":").pop()?.trim().split("=")[0].trim();
|
|
435
|
-
add(bound);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
return [...methods].sort();
|
|
439
|
-
}
|
|
440
|
-
function sortRoutes(routes) {
|
|
441
|
-
return routes.sort((left, right) => left.path.localeCompare(right.path) || left.file.localeCompare(right.file));
|
|
442
|
-
}
|
|
443
438
|
//# sourceMappingURL=routeDetector.js.map
|