next-route-graph 1.0.2 → 1.2.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/CHANGELOG.md +95 -0
- package/CONTRIBUTING.md +74 -0
- package/README.md +169 -134
- package/SECURITY.md +19 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +126 -44
- package/dist/core/analyzer.d.ts +16 -0
- package/dist/core/analyzer.d.ts.map +1 -0
- package/dist/core/analyzer.js +113 -29
- package/dist/core/detector.d.ts +19 -0
- package/dist/core/detector.d.ts.map +1 -0
- package/dist/core/detector.js +31 -3
- package/dist/core/inspect.d.ts +6 -0
- package/dist/core/inspect.d.ts.map +1 -0
- package/dist/core/inspect.js +21 -0
- package/dist/core/linter.d.ts +11 -0
- package/dist/core/linter.d.ts.map +1 -0
- package/dist/core/linter.js +194 -0
- package/dist/core/parser.d.ts +95 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +242 -15
- package/dist/core/scanner.d.ts +12 -0
- package/dist/core/scanner.d.ts.map +1 -0
- package/dist/core/scanner.js +32 -6
- package/dist/errors/index.d.ts +35 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +65 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/preview.d.ts +4 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +25 -0
- package/dist/render/dot.d.ts +13 -0
- package/dist/render/dot.d.ts.map +1 -0
- package/dist/render/dot.js +66 -0
- package/dist/render/flat.d.ts +13 -0
- package/dist/render/flat.d.ts.map +1 -0
- package/dist/render/flat.js +14 -2
- package/dist/render/graph.d.ts +13 -0
- package/dist/render/graph.d.ts.map +1 -0
- package/dist/render/graph.js +53 -0
- package/dist/render/html.d.ts +8 -0
- package/dist/render/html.d.ts.map +1 -0
- package/dist/render/html.js +718 -0
- package/dist/render/index.d.ts +20 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +37 -0
- package/dist/render/json.d.ts +13 -0
- package/dist/render/json.d.ts.map +1 -0
- package/dist/render/json.js +12 -14
- package/dist/render/markdown.d.ts +13 -0
- package/dist/render/markdown.d.ts.map +1 -0
- package/dist/render/markdown.js +59 -0
- package/dist/render/mermaid.d.ts +13 -0
- package/dist/render/mermaid.d.ts.map +1 -0
- package/dist/render/mermaid.js +64 -0
- package/dist/render/renderer.interface.d.ts +8 -0
- package/dist/render/renderer.interface.d.ts.map +1 -0
- package/dist/render/renderer.interface.js +1 -0
- package/dist/render/svg.d.ts +8 -0
- package/dist/render/svg.d.ts.map +1 -0
- package/dist/render/svg.js +76 -0
- package/dist/render/tree.d.ts +13 -0
- package/dist/render/tree.d.ts.map +1 -0
- package/dist/render/tree.js +57 -7
- package/dist/types/index.d.ts +132 -0
- package/dist/types/index.d.ts.map +1 -0
- package/docs/ARCHITECTURE.md +71 -0
- package/docs/NEW_FEATURES.md +16 -0
- package/docs/adr/0001-offline-reports-and-shared-route-graph.md +29 -0
- package/docs/route-report.png +0 -0
- package/package.json +71 -52
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { OutputFormat } from "../types/index.js";
|
|
2
|
+
import { DotRenderer, renderDot } from "./dot.js";
|
|
3
|
+
import { FlatRenderer, renderFlat } from "./flat.js";
|
|
4
|
+
import { HtmlRenderer, renderHtml } from "./html.js";
|
|
5
|
+
import { JsonRenderer, renderJson } from "./json.js";
|
|
6
|
+
import { MarkdownRenderer, renderMarkdown } from "./markdown.js";
|
|
7
|
+
import { MermaidRenderer, renderMermaid } from "./mermaid.js";
|
|
8
|
+
import type { OutputRenderer } from "./renderer.interface.js";
|
|
9
|
+
import { renderTree, TreeRenderer } from "./tree.js";
|
|
10
|
+
import { renderSvg, SvgRenderer } from "./svg.js";
|
|
11
|
+
export { DotRenderer, FlatRenderer, HtmlRenderer, JsonRenderer, MarkdownRenderer, MermaidRenderer, renderDot, renderFlat, renderHtml, renderJson, renderMarkdown, renderMermaid, renderTree, TreeRenderer, renderSvg, SvgRenderer, };
|
|
12
|
+
export type { OutputRenderer };
|
|
13
|
+
/**
|
|
14
|
+
* Factory function to retrieve an OutputRenderer instance by format name (Strategy Pattern).
|
|
15
|
+
*
|
|
16
|
+
* @param format - Output format name (tree, flat, json, mermaid, dot, markdown, html, svg)
|
|
17
|
+
* @returns Configured OutputRenderer implementation
|
|
18
|
+
*/
|
|
19
|
+
export declare function getRenderer(format: OutputFormat): OutputRenderer;
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAElD,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,WAAW,GACZ,CAAC;AAEF,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,CAqBhE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { DotRenderer, renderDot } from "./dot.js";
|
|
2
|
+
import { FlatRenderer, renderFlat } from "./flat.js";
|
|
3
|
+
import { HtmlRenderer, renderHtml } from "./html.js";
|
|
4
|
+
import { JsonRenderer, renderJson } from "./json.js";
|
|
5
|
+
import { MarkdownRenderer, renderMarkdown } from "./markdown.js";
|
|
6
|
+
import { MermaidRenderer, renderMermaid } from "./mermaid.js";
|
|
7
|
+
import { renderTree, TreeRenderer } from "./tree.js";
|
|
8
|
+
import { renderSvg, SvgRenderer } from "./svg.js";
|
|
9
|
+
export { DotRenderer, FlatRenderer, HtmlRenderer, JsonRenderer, MarkdownRenderer, MermaidRenderer, renderDot, renderFlat, renderHtml, renderJson, renderMarkdown, renderMermaid, renderTree, TreeRenderer, renderSvg, SvgRenderer, };
|
|
10
|
+
/**
|
|
11
|
+
* Factory function to retrieve an OutputRenderer instance by format name (Strategy Pattern).
|
|
12
|
+
*
|
|
13
|
+
* @param format - Output format name (tree, flat, json, mermaid, dot, markdown, html, svg)
|
|
14
|
+
* @returns Configured OutputRenderer implementation
|
|
15
|
+
*/
|
|
16
|
+
export function getRenderer(format) {
|
|
17
|
+
switch (format) {
|
|
18
|
+
case "flat":
|
|
19
|
+
return new FlatRenderer();
|
|
20
|
+
case "json":
|
|
21
|
+
return new JsonRenderer();
|
|
22
|
+
case "mermaid":
|
|
23
|
+
return new MermaidRenderer();
|
|
24
|
+
case "dot":
|
|
25
|
+
return new DotRenderer();
|
|
26
|
+
case "markdown":
|
|
27
|
+
return new MarkdownRenderer();
|
|
28
|
+
case "html":
|
|
29
|
+
return new HtmlRenderer();
|
|
30
|
+
case "svg":
|
|
31
|
+
return new SvgRenderer();
|
|
32
|
+
case "tree":
|
|
33
|
+
return new TreeRenderer();
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(`Unsupported output format: ${String(format)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AnalysisResult } from "../types/index.js";
|
|
2
|
+
import type { OutputRenderer } from "./renderer.interface.js";
|
|
3
|
+
/**
|
|
4
|
+
* Formats analysis result as structured, indented JSON.
|
|
5
|
+
*
|
|
6
|
+
* @param result - AnalysisResult object
|
|
7
|
+
* @returns Serialized JSON string
|
|
8
|
+
*/
|
|
9
|
+
export declare function renderJson(result: AnalysisResult): string;
|
|
10
|
+
export declare class JsonRenderer implements OutputRenderer {
|
|
11
|
+
render(result: AnalysisResult): string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/render/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEzD;AAED,qBAAa,YAAa,YAAW,cAAc;IACjD,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM;CAGvC"}
|
package/dist/render/json.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats analysis result as structured, indented JSON.
|
|
3
|
+
*
|
|
4
|
+
* @param result - AnalysisResult object
|
|
5
|
+
* @returns Serialized JSON string
|
|
6
|
+
*/
|
|
1
7
|
export function renderJson(result) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
type: r.type,
|
|
9
|
-
file: r.file,
|
|
10
|
-
segmentType: r.segmentType,
|
|
11
|
-
rendering: r.rendering,
|
|
12
|
-
})),
|
|
13
|
-
summary: result.summary,
|
|
14
|
-
};
|
|
15
|
-
return JSON.stringify(output, null, 2);
|
|
8
|
+
return JSON.stringify(result, null, 2);
|
|
9
|
+
}
|
|
10
|
+
export class JsonRenderer {
|
|
11
|
+
render(result) {
|
|
12
|
+
return renderJson(result);
|
|
13
|
+
}
|
|
16
14
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AnalysisResult } from "../types/index.js";
|
|
2
|
+
import type { OutputRenderer } from "./renderer.interface.js";
|
|
3
|
+
/**
|
|
4
|
+
* Formats the route analysis result as a GitHub-flavored Markdown document with a table.
|
|
5
|
+
*
|
|
6
|
+
* @param result - AnalysisResult
|
|
7
|
+
* @returns Formatted Markdown string
|
|
8
|
+
*/
|
|
9
|
+
export declare function renderMarkdown(result: AnalysisResult): string;
|
|
10
|
+
export declare class MarkdownRenderer implements OutputRenderer {
|
|
11
|
+
render(result: AnalysisResult): string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/render/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAS9D;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAyD7D;AAED,qBAAa,gBAAiB,YAAW,cAAc;IACrD,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM;CAGvC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escapes pipe characters so Markdown table cells do not break table layout.
|
|
3
|
+
*/
|
|
4
|
+
function escapeCell(val) {
|
|
5
|
+
return val.replace(/\|/g, "\\|");
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Formats the route analysis result as a GitHub-flavored Markdown document with a table.
|
|
9
|
+
*
|
|
10
|
+
* @param result - AnalysisResult
|
|
11
|
+
* @returns Formatted Markdown string
|
|
12
|
+
*/
|
|
13
|
+
export function renderMarkdown(result) {
|
|
14
|
+
const lines = [];
|
|
15
|
+
lines.push(`### Next.js Route Graph: \`${result.project}\``);
|
|
16
|
+
lines.push("");
|
|
17
|
+
lines.push(`- **Router:** ${result.router}`);
|
|
18
|
+
lines.push(`- **Root:** \`${result.root}\``);
|
|
19
|
+
lines.push(`- **Total Routes:** ${result.summary.routes}`);
|
|
20
|
+
lines.push(`- **Breakdown:** ${result.summary.server} Server, ${result.summary.client} Client, ${result.summary.dynamic} Dynamic`);
|
|
21
|
+
lines.push("");
|
|
22
|
+
lines.push("| Route Path | Type | Segment | Rendering | Methods / Details | File |");
|
|
23
|
+
lines.push("| :--- | :--- | :--- | :--- | :--- | :--- |");
|
|
24
|
+
for (const route of result.routes) {
|
|
25
|
+
const methods = route.methods && route.methods.length > 0
|
|
26
|
+
? `\`${escapeCell(route.methods.join(", "))}\``
|
|
27
|
+
: "-";
|
|
28
|
+
const segment = `\`${escapeCell(route.segmentType)}\``;
|
|
29
|
+
const rendering = route.rendering === "client" ? "**Client**" : "Server";
|
|
30
|
+
const file = `\`${escapeCell(route.file)}\``;
|
|
31
|
+
lines.push(`| \`${escapeCell(route.path)}\` | ${route.type} | ${segment} | ${rendering} | ${methods} | ${file} |`);
|
|
32
|
+
}
|
|
33
|
+
if (result.routes.length === 0) {
|
|
34
|
+
lines.push("| *(No routes discovered)* | - | - | - | - | - |");
|
|
35
|
+
}
|
|
36
|
+
if (result.lint && result.lint.issues.length > 0) {
|
|
37
|
+
lines.push("");
|
|
38
|
+
lines.push("#### Route Health Warnings & Issues");
|
|
39
|
+
lines.push("");
|
|
40
|
+
lines.push("| Severity | Rule | Message | File / Location |");
|
|
41
|
+
lines.push("| :--- | :--- | :--- | :--- |");
|
|
42
|
+
for (const issue of result.lint.issues) {
|
|
43
|
+
const badge = issue.severity === "error"
|
|
44
|
+
? "🛑 Error"
|
|
45
|
+
: issue.severity === "warning"
|
|
46
|
+
? "⚠️ Warning"
|
|
47
|
+
: "ℹ️ Info";
|
|
48
|
+
const loc = issue.filePath ? `\`${escapeCell(issue.filePath)}\`` : "-";
|
|
49
|
+
lines.push(`| ${badge} | \`${escapeCell(issue.rule)}\` | ${escapeCell(issue.message.replace(/\n/g, " "))} | ${loc} |`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
lines.push("");
|
|
53
|
+
return lines.join("\n");
|
|
54
|
+
}
|
|
55
|
+
export class MarkdownRenderer {
|
|
56
|
+
render(result) {
|
|
57
|
+
return renderMarkdown(result);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AnalysisResult } from "../types/index.js";
|
|
2
|
+
import type { OutputRenderer } from "./renderer.interface.js";
|
|
3
|
+
/**
|
|
4
|
+
* Generates a Mermaid flowchart diagram representing the App Router routes.
|
|
5
|
+
*
|
|
6
|
+
* @param result - AnalysisResult
|
|
7
|
+
* @returns Mermaid flowchart markdown block
|
|
8
|
+
*/
|
|
9
|
+
export declare function renderMermaid(result: AnalysisResult): string;
|
|
10
|
+
export declare class MermaidRenderer implements OutputRenderer {
|
|
11
|
+
render(result: AnalysisResult): string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=mermaid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../../src/render/mermaid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAc,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAgC9D;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA+B5D;AAED,qBAAa,eAAgB,YAAW,cAAc;IACpD,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM;CAGvC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { buildRouteGraph } from "./graph.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a clean Mermaid node label for a route.
|
|
4
|
+
* Replaces square brackets with HTML character entities to avoid breaking Mermaid node shape syntax.
|
|
5
|
+
*/
|
|
6
|
+
function createNodeLabel(route) {
|
|
7
|
+
const parts = [route.path];
|
|
8
|
+
if (route.methods && route.methods.length > 0) {
|
|
9
|
+
parts.push(`(${route.methods.join("/")})`);
|
|
10
|
+
}
|
|
11
|
+
else if (route.rendering === "client") {
|
|
12
|
+
parts.push("(client)");
|
|
13
|
+
}
|
|
14
|
+
else if (route.segmentType !== "static") {
|
|
15
|
+
parts.push(`(${route.segmentType})`);
|
|
16
|
+
}
|
|
17
|
+
// Escape square brackets and quotation marks for safe Mermaid rendering
|
|
18
|
+
return parts
|
|
19
|
+
.join(" ")
|
|
20
|
+
.replace(/&/g, "#38;")
|
|
21
|
+
.replace(/</g, "#60;")
|
|
22
|
+
.replace(/>/g, "#62;")
|
|
23
|
+
.replace(/\r?\n/g, " ")
|
|
24
|
+
.replace(/\[/g, "#91;")
|
|
25
|
+
.replace(/\]/g, "#93;")
|
|
26
|
+
.replace(/"/g, "#quot;");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Generates a Mermaid flowchart diagram representing the App Router routes.
|
|
30
|
+
*
|
|
31
|
+
* @param result - AnalysisResult
|
|
32
|
+
* @returns Mermaid flowchart markdown block
|
|
33
|
+
*/
|
|
34
|
+
export function renderMermaid(result) {
|
|
35
|
+
const lines = [];
|
|
36
|
+
lines.push("```mermaid");
|
|
37
|
+
lines.push("flowchart TD");
|
|
38
|
+
lines.push(` %% Next.js Route Graph for ${result.project.replace(/[\r\n]/g, " ")}`);
|
|
39
|
+
const routes = result.routes;
|
|
40
|
+
if (routes.length === 0) {
|
|
41
|
+
lines.push(' empty["(No routes discovered)"]');
|
|
42
|
+
lines.push("```");
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
}
|
|
45
|
+
for (const node of buildRouteGraph(routes)) {
|
|
46
|
+
const label = node.routes.length
|
|
47
|
+
? node.routes.map(createNodeLabel).join(" | ")
|
|
48
|
+
: createNodeLabel({
|
|
49
|
+
path: node.path,
|
|
50
|
+
rendering: "server",
|
|
51
|
+
segmentType: "static",
|
|
52
|
+
});
|
|
53
|
+
lines.push(` ${node.id}["${label}"]`);
|
|
54
|
+
if (node.parentId)
|
|
55
|
+
lines.push(` ${node.parentId} --> ${node.id}`);
|
|
56
|
+
}
|
|
57
|
+
lines.push("```");
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
60
|
+
export class MermaidRenderer {
|
|
61
|
+
render(result) {
|
|
62
|
+
return renderMermaid(result);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.interface.d.ts","sourceRoot":"","sources":["../../src/render/renderer.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC;CACxC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AnalysisResult } from "../types/index.js";
|
|
2
|
+
import type { OutputRenderer } from "./renderer.interface.js";
|
|
3
|
+
/** A real, offline vector diagram suitable for reports, READMEs, and design tools. */
|
|
4
|
+
export declare function renderSvg(result: AnalysisResult): string;
|
|
5
|
+
export declare class SvgRenderer implements OutputRenderer {
|
|
6
|
+
render(result: AnalysisResult): string;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=svg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svg.d.ts","sourceRoot":"","sources":["../../src/render/svg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAgFxD;AAED,qBAAa,WAAY,YAAW,cAAc;IAChD,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM;CAGvC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { buildRouteGraph, escapeMarkup } from "./graph.js";
|
|
2
|
+
/** A real, offline vector diagram suitable for reports, READMEs, and design tools. */
|
|
3
|
+
export function renderSvg(result) {
|
|
4
|
+
const nodes = buildRouteGraph(result.routes);
|
|
5
|
+
const rowHeight = 70;
|
|
6
|
+
const width = Math.max(760, ...nodes.map((node) => 340 + node.depth * 28 + Array.from(node.path).length * 9));
|
|
7
|
+
const height = Math.max(180, 110 + nodes.length * rowHeight);
|
|
8
|
+
const rows = new Map(nodes.map((node, index) => [node.id, index]));
|
|
9
|
+
const edges = nodes
|
|
10
|
+
.filter((node) => node.parentId)
|
|
11
|
+
.map((node) => {
|
|
12
|
+
const parentRow = rows.get(node.parentId);
|
|
13
|
+
const row = rows.get(node.id);
|
|
14
|
+
const x = 30 + (node.depth - 1) * 28;
|
|
15
|
+
return `<path d="M${x},${100 + parentRow * rowHeight} V${100 + row * rowHeight} H${x + 28}" fill="none" stroke="#475569"/>`;
|
|
16
|
+
})
|
|
17
|
+
.join("\n");
|
|
18
|
+
const cards = nodes
|
|
19
|
+
.map((node, index) => {
|
|
20
|
+
const x = 18 + node.depth * 28;
|
|
21
|
+
const y = 76 + index * rowHeight;
|
|
22
|
+
const route = node.routes[0];
|
|
23
|
+
const color = !route
|
|
24
|
+
? "#64748b"
|
|
25
|
+
: route.type === "route"
|
|
26
|
+
? "#d8b4fe"
|
|
27
|
+
: route.rendering === "client"
|
|
28
|
+
? "#67e8f9"
|
|
29
|
+
: "#a5b4fc";
|
|
30
|
+
const description = node.routes.length === 0
|
|
31
|
+
? "segment · no endpoint"
|
|
32
|
+
: node.routes
|
|
33
|
+
.map((route) => [
|
|
34
|
+
route.type === "route"
|
|
35
|
+
? `API ${(route.methods ?? []).join(" / ")}`
|
|
36
|
+
: `${route.rendering} page`,
|
|
37
|
+
route.segmentType !== "static" ? route.segmentType : "",
|
|
38
|
+
...(route.parallelSlots ?? []),
|
|
39
|
+
route.intercepting ? "intercepted" : "",
|
|
40
|
+
]
|
|
41
|
+
.filter(Boolean)
|
|
42
|
+
.join(" · "))
|
|
43
|
+
.join(" | ");
|
|
44
|
+
const title = [
|
|
45
|
+
node.path,
|
|
46
|
+
...node.routes.map((route) => `${route.file}${route.layoutChain?.length ? ` — layouts: ${route.layoutChain.join(" → ")}` : ""}`),
|
|
47
|
+
].join("\n");
|
|
48
|
+
const maxDescription = Math.floor((width - x - 50) / 7);
|
|
49
|
+
const shortDescription = description.length > maxDescription
|
|
50
|
+
? description.slice(0, maxDescription - 1) + "…"
|
|
51
|
+
: description;
|
|
52
|
+
return `<g data-node-index="${index}" tabindex="0" role="button" aria-label="${escapeMarkup(title)}">
|
|
53
|
+
<title>${escapeMarkup(title)}</title>
|
|
54
|
+
<rect x="${x}" y="${y}" width="${width - x - 18}" height="54" rx="8" fill="#172033" stroke="#334155"/>
|
|
55
|
+
<rect x="${x}" y="${y + 8}" width="3" height="38" rx="1" fill="${color}"/>
|
|
56
|
+
<text x="${x + 14}" y="${y + 22}" fill="${color}" font-size="14">${escapeMarkup(node.path)}</text>
|
|
57
|
+
<text x="${x + 14}" y="${y + 41}" fill="#cbd5e1" font-size="11">${escapeMarkup(shortDescription)}</text>
|
|
58
|
+
</g>`;
|
|
59
|
+
})
|
|
60
|
+
.join("\n");
|
|
61
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}" role="img" aria-labelledby="graph-title graph-description">
|
|
62
|
+
<title id="graph-title">${escapeMarkup(result.project)} — route map</title>
|
|
63
|
+
<desc id="graph-description">URL segment hierarchy. Source files, slots, and intercepted variants are preserved. This shows route structure, not imports or navigation.</desc>
|
|
64
|
+
<style>text{font-family:ui-monospace,Consolas,monospace}g:focus rect:first-of-type,g:hover rect:first-of-type{stroke:#67e8f9;stroke-width:2}g{cursor:pointer;outline:none}</style>
|
|
65
|
+
<rect width="100%" height="100%" rx="12" fill="#0b1020"/>
|
|
66
|
+
<text x="22" y="32" fill="#f1f5f9" font-size="17">${escapeMarkup(result.project)} / route map</text>
|
|
67
|
+
<text x="22" y="54" fill="#94a3b8" font-size="12">${result.routes.length} endpoints · ${escapeMarkup(result.root)} · URL hierarchy</text>
|
|
68
|
+
${edges}
|
|
69
|
+
${cards || '<text x="22" y="106" fill="#94a3b8" font-size="14">No routes discovered</text>'}
|
|
70
|
+
</svg>`;
|
|
71
|
+
}
|
|
72
|
+
export class SvgRenderer {
|
|
73
|
+
render(result) {
|
|
74
|
+
return renderSvg(result);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AnalysisResult } from "../types/index.js";
|
|
2
|
+
import type { OutputRenderer } from "./renderer.interface.js";
|
|
3
|
+
/**
|
|
4
|
+
* Formats the route graph and project summary as a styled terminal tree.
|
|
5
|
+
*
|
|
6
|
+
* @param result - Analysis result data
|
|
7
|
+
* @returns Styled terminal string representation
|
|
8
|
+
*/
|
|
9
|
+
export declare function renderTree(result: AnalysisResult): string;
|
|
10
|
+
export declare class TreeRenderer implements OutputRenderer {
|
|
11
|
+
render(result: AnalysisResult): string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/render/tree.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA2GzD;AAED,qBAAa,YAAa,YAAW,cAAc;IACjD,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM;CAGvC"}
|
package/dist/render/tree.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import pc from "picocolors";
|
|
2
|
+
/**
|
|
3
|
+
* Formats the route graph and project summary as a styled terminal tree.
|
|
4
|
+
*
|
|
5
|
+
* @param result - Analysis result data
|
|
6
|
+
* @returns Styled terminal string representation
|
|
7
|
+
*/
|
|
2
8
|
export function renderTree(result) {
|
|
3
9
|
const lines = [];
|
|
4
10
|
lines.push("");
|
|
@@ -11,21 +17,34 @@ export function renderTree(result) {
|
|
|
11
17
|
lines.push(pc.bold("Routes"));
|
|
12
18
|
lines.push(pc.dim("────────────────────────────────────────"));
|
|
13
19
|
const routes = result.routes;
|
|
20
|
+
const maxPathLen = Math.max(28, ...routes.map((r) => r.path.length));
|
|
14
21
|
routes.forEach((route, index) => {
|
|
15
22
|
const isLast = index === routes.length - 1;
|
|
16
23
|
const prefix = isLast ? "└── " : "├── ";
|
|
17
|
-
const pathStr = route.path.padEnd(
|
|
24
|
+
const pathStr = route.path.padEnd(maxPathLen, " ");
|
|
18
25
|
const fileStr = pc.dim(route.file.split("/").pop() || "");
|
|
19
|
-
|
|
26
|
+
const badges = [];
|
|
27
|
+
// HTTP Methods for API handlers
|
|
28
|
+
if (route.methods && route.methods.length > 0) {
|
|
29
|
+
badges.push(pc.magenta(`[${route.methods.join(", ")}]`));
|
|
30
|
+
}
|
|
31
|
+
// Rendering or Segment Type
|
|
20
32
|
if (route.rendering === "client") {
|
|
21
|
-
|
|
22
|
-
badge = ` ${clientBadge}`;
|
|
33
|
+
badges.push(pc.cyan("[client]"));
|
|
23
34
|
}
|
|
24
35
|
else if (route.segmentType !== "static") {
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
badges.push(pc.yellow(`[${route.segmentType}]`));
|
|
37
|
+
}
|
|
38
|
+
// Parallel route slots
|
|
39
|
+
if (route.parallelSlots && route.parallelSlots.length > 0) {
|
|
40
|
+
badges.push(pc.blue(`[${route.parallelSlots.join(", ")}]`));
|
|
41
|
+
}
|
|
42
|
+
// Intercepting route indicator
|
|
43
|
+
if (route.intercepting) {
|
|
44
|
+
badges.push(pc.magenta(`[${route.intercepting}]`));
|
|
27
45
|
}
|
|
28
|
-
const
|
|
46
|
+
const badgeStr = badges.length > 0 ? ` ${badges.join(" ")}` : "";
|
|
47
|
+
const routeLine = `${pc.dim(prefix)}${pc.bold(pathStr)} ${fileStr}${badgeStr}`;
|
|
29
48
|
lines.push(routeLine);
|
|
30
49
|
});
|
|
31
50
|
if (routes.length === 0) {
|
|
@@ -41,8 +60,39 @@ export function renderTree(result) {
|
|
|
41
60
|
lines.push(`${pc.dim("Layouts ")}${result.summary.layouts}`);
|
|
42
61
|
lines.push(`${pc.dim("Loading ")}${result.summary.loading}`);
|
|
43
62
|
lines.push(`${pc.dim("Errors ")}${result.summary.errors}`);
|
|
63
|
+
if (result.summary.apiRoutes && result.summary.apiRoutes > 0) {
|
|
64
|
+
lines.push(`${pc.dim("API Routes ")}${result.summary.apiRoutes}`);
|
|
65
|
+
}
|
|
66
|
+
if (result.summary.parallelRoutes && result.summary.parallelRoutes > 0) {
|
|
67
|
+
lines.push(`${pc.dim("Parallel ")}${result.summary.parallelRoutes}`);
|
|
68
|
+
}
|
|
69
|
+
if (result.summary.interceptedRoutes && result.summary.interceptedRoutes > 0) {
|
|
70
|
+
lines.push(`${pc.dim("Intercepted ")}${result.summary.interceptedRoutes}`);
|
|
71
|
+
}
|
|
72
|
+
// Display lint issues if present
|
|
73
|
+
if (result.lint && result.lint.issues.length > 0) {
|
|
74
|
+
lines.push("");
|
|
75
|
+
lines.push(pc.bold("Route Health"));
|
|
76
|
+
lines.push(pc.dim("────────────────────────────────────────"));
|
|
77
|
+
for (const issue of result.lint.issues) {
|
|
78
|
+
const tag = issue.severity === "error"
|
|
79
|
+
? pc.red("✖ [error]")
|
|
80
|
+
: issue.severity === "warning"
|
|
81
|
+
? pc.yellow("▲ [warning]")
|
|
82
|
+
: pc.blue("ℹ [info]");
|
|
83
|
+
lines.push(`${tag} ${pc.bold(issue.rule)}: ${issue.message}`);
|
|
84
|
+
if (issue.hint) {
|
|
85
|
+
lines.push(` ${pc.dim("hint: " + issue.hint)}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
44
89
|
lines.push("");
|
|
45
90
|
lines.push(pc.green(`✓ Analysis completed in ${result.executionTimeMs}ms`));
|
|
46
91
|
lines.push("");
|
|
47
92
|
return lines.join("\n");
|
|
48
93
|
}
|
|
94
|
+
export class TreeRenderer {
|
|
95
|
+
render(result) {
|
|
96
|
+
return renderTree(result);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported Next.js segment dynamics
|
|
3
|
+
*/
|
|
4
|
+
export type SegmentType = "static" | "dynamic" | "catch-all" | "optional-catch-all";
|
|
5
|
+
/**
|
|
6
|
+
* Next.js App Router special convention files
|
|
7
|
+
*/
|
|
8
|
+
export type SpecialFileType = "page" | "layout" | "loading" | "error" | "global-error" | "not-found" | "template" | "default" | "route";
|
|
9
|
+
/**
|
|
10
|
+
* React rendering environment
|
|
11
|
+
*/
|
|
12
|
+
export type RenderingType = "client" | "server";
|
|
13
|
+
/**
|
|
14
|
+
* Standard HTTP methods supported in route handlers
|
|
15
|
+
*/
|
|
16
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
17
|
+
/**
|
|
18
|
+
* Discovered file in the App Router tree
|
|
19
|
+
*/
|
|
20
|
+
export interface DiscoveredFile {
|
|
21
|
+
relativePath: string;
|
|
22
|
+
fullPath: string;
|
|
23
|
+
fileName: string;
|
|
24
|
+
extension: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* App Router convention file entry
|
|
28
|
+
*/
|
|
29
|
+
export interface RouteFile {
|
|
30
|
+
type: SpecialFileType;
|
|
31
|
+
fileName: string;
|
|
32
|
+
relativePath: string;
|
|
33
|
+
rendering: RenderingType;
|
|
34
|
+
methods?: HttpMethod[];
|
|
35
|
+
hasServerActions?: boolean;
|
|
36
|
+
runtime?: "nodejs" | "edge" | string;
|
|
37
|
+
dynamicConfig?: "auto" | "force-dynamic" | "error" | "force-static" | string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Represents a resolved Next.js route endpoint (page or route handler)
|
|
41
|
+
*/
|
|
42
|
+
export interface RouteEntry {
|
|
43
|
+
path: string;
|
|
44
|
+
type: "page" | "route";
|
|
45
|
+
file: string;
|
|
46
|
+
segmentType: SegmentType;
|
|
47
|
+
rendering: RenderingType;
|
|
48
|
+
methods?: HttpMethod[];
|
|
49
|
+
hasServerActions?: boolean;
|
|
50
|
+
runtime?: "nodejs" | "edge" | string;
|
|
51
|
+
dynamicConfig?: "auto" | "force-dynamic" | "error" | "force-static" | string;
|
|
52
|
+
files: RouteFile[];
|
|
53
|
+
parallelSlots?: string[];
|
|
54
|
+
intercepting?: string;
|
|
55
|
+
layoutChain?: string[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Lint severity level
|
|
59
|
+
*/
|
|
60
|
+
export type LintSeverity = "error" | "warning" | "info";
|
|
61
|
+
/**
|
|
62
|
+
* Lint issue discovered during route inspection
|
|
63
|
+
*/
|
|
64
|
+
export interface LintIssue {
|
|
65
|
+
rule: string;
|
|
66
|
+
severity: LintSeverity;
|
|
67
|
+
message: string;
|
|
68
|
+
routePath?: string;
|
|
69
|
+
filePath?: string;
|
|
70
|
+
hint?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Lint summary report
|
|
74
|
+
*/
|
|
75
|
+
export interface LintResult {
|
|
76
|
+
issues: LintIssue[];
|
|
77
|
+
hasErrors: boolean;
|
|
78
|
+
warningCount: number;
|
|
79
|
+
errorCount: number;
|
|
80
|
+
infoCount?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Summary statistics for the scanned project
|
|
84
|
+
*/
|
|
85
|
+
export interface SummaryStats {
|
|
86
|
+
routes: number;
|
|
87
|
+
dynamic: number;
|
|
88
|
+
client: number;
|
|
89
|
+
server: number;
|
|
90
|
+
layouts: number;
|
|
91
|
+
loading: number;
|
|
92
|
+
errors: number;
|
|
93
|
+
apiRoutes?: number;
|
|
94
|
+
parallelRoutes?: number;
|
|
95
|
+
interceptedRoutes?: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Overall analysis output
|
|
99
|
+
*/
|
|
100
|
+
export interface AnalysisResult {
|
|
101
|
+
project: string;
|
|
102
|
+
router: "App Router";
|
|
103
|
+
root: string;
|
|
104
|
+
routes: RouteEntry[];
|
|
105
|
+
summary: SummaryStats;
|
|
106
|
+
executionTimeMs: number;
|
|
107
|
+
lint?: LintResult;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Output format types
|
|
111
|
+
*/
|
|
112
|
+
export type OutputFormat = "tree" | "flat" | "json" | "mermaid" | "dot" | "markdown" | "html" | "svg";
|
|
113
|
+
/**
|
|
114
|
+
* CLI command line options
|
|
115
|
+
*/
|
|
116
|
+
export interface CLIOptions {
|
|
117
|
+
flat?: boolean;
|
|
118
|
+
json?: boolean;
|
|
119
|
+
mermaid?: boolean;
|
|
120
|
+
dot?: boolean;
|
|
121
|
+
markdown?: boolean;
|
|
122
|
+
svg?: boolean;
|
|
123
|
+
output?: string;
|
|
124
|
+
html?: boolean | string;
|
|
125
|
+
serve?: boolean | number | string;
|
|
126
|
+
format?: OutputFormat;
|
|
127
|
+
search?: string;
|
|
128
|
+
check?: boolean;
|
|
129
|
+
strict?: boolean;
|
|
130
|
+
methods?: boolean;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|