@lark-apaas/devtool-kits 1.2.17-alpha.4 → 1.2.17-alpha.41
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/bin/generate-api-routes.cjs +189 -0
- package/dist/bin/generate-api-routes.cjs.map +1 -0
- package/dist/bin/generate-api-routes.d.cts +1 -0
- package/dist/bin/generate-api-routes.d.ts +1 -0
- package/dist/bin/generate-api-routes.js +43 -0
- package/dist/bin/generate-api-routes.js.map +1 -0
- package/dist/bin/generate-page-routes.cjs +191 -0
- package/dist/bin/generate-page-routes.cjs.map +1 -0
- package/dist/bin/generate-page-routes.d.cts +1 -0
- package/dist/bin/generate-page-routes.d.ts +1 -0
- package/dist/bin/generate-page-routes.js +170 -0
- package/dist/bin/generate-page-routes.js.map +1 -0
- package/dist/chunk-7QVYU63E.js +7 -0
- package/dist/chunk-7QVYU63E.js.map +1 -0
- package/dist/chunk-LSHFHCDF.js +350 -0
- package/dist/chunk-LSHFHCDF.js.map +1 -0
- package/dist/index.cjs +241 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +170 -270
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
+
mod
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
// src/bin/generate-api-routes.ts
|
|
28
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
29
|
+
var import_path = __toESM(require("path"), 1);
|
|
30
|
+
|
|
31
|
+
// src/middlewares/openapi/services.ts
|
|
32
|
+
var import_node_fs2 = require("fs");
|
|
33
|
+
var fsSync = __toESM(require("fs"), 1);
|
|
34
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
35
|
+
var import_typescript = __toESM(require("typescript"), 1);
|
|
36
|
+
|
|
37
|
+
// src/middlewares/openapi/utils.ts
|
|
38
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
39
|
+
var import_node_fs = require("fs");
|
|
40
|
+
|
|
41
|
+
// src/middlewares/openapi/services.ts
|
|
42
|
+
var HTTP_DECORATOR_NAMES = [
|
|
43
|
+
"Get",
|
|
44
|
+
"Post",
|
|
45
|
+
"Put",
|
|
46
|
+
"Delete",
|
|
47
|
+
"Patch",
|
|
48
|
+
"Options",
|
|
49
|
+
"Head",
|
|
50
|
+
"All"
|
|
51
|
+
];
|
|
52
|
+
function findControllerFilesSync(dir) {
|
|
53
|
+
const files = [];
|
|
54
|
+
function scan(currentDir) {
|
|
55
|
+
let entries;
|
|
56
|
+
try {
|
|
57
|
+
entries = fsSync.readdirSync(currentDir, {
|
|
58
|
+
withFileTypes: true
|
|
59
|
+
});
|
|
60
|
+
} catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
const fullPath = import_node_path2.default.join(currentDir, entry.name);
|
|
65
|
+
if (entry.isDirectory()) {
|
|
66
|
+
scan(fullPath);
|
|
67
|
+
} else if (entry.isFile() && entry.name.endsWith(".controller.ts")) {
|
|
68
|
+
files.push(fullPath);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
__name(scan, "scan");
|
|
73
|
+
scan(dir);
|
|
74
|
+
return files;
|
|
75
|
+
}
|
|
76
|
+
__name(findControllerFilesSync, "findControllerFilesSync");
|
|
77
|
+
function normalizeApiPath(controllerPath, routePath) {
|
|
78
|
+
const combined = routePath ? `${controllerPath}/${routePath}` : controllerPath;
|
|
79
|
+
let normalized = combined.replace(/\/+/g, "/");
|
|
80
|
+
if (!normalized.startsWith("/")) normalized = `/${normalized}`;
|
|
81
|
+
if (normalized.length > 1 && normalized.endsWith("/")) normalized = normalized.slice(0, -1);
|
|
82
|
+
return normalized;
|
|
83
|
+
}
|
|
84
|
+
__name(normalizeApiPath, "normalizeApiPath");
|
|
85
|
+
function parseControllerRoutes(filePath) {
|
|
86
|
+
let content;
|
|
87
|
+
try {
|
|
88
|
+
content = fsSync.readFileSync(filePath, "utf-8");
|
|
89
|
+
} catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
const sourceFile = import_typescript.default.createSourceFile(filePath, content, import_typescript.default.ScriptTarget.Latest, true);
|
|
93
|
+
const routes = [];
|
|
94
|
+
function getDecoratorsCompat(node) {
|
|
95
|
+
if ("modifiers" in node && Array.isArray(node.modifiers)) {
|
|
96
|
+
return node.modifiers.filter((mod) => mod.kind === import_typescript.default.SyntaxKind.Decorator);
|
|
97
|
+
}
|
|
98
|
+
if ("decorators" in node && Array.isArray(node.decorators)) {
|
|
99
|
+
return node.decorators;
|
|
100
|
+
}
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
__name(getDecoratorsCompat, "getDecoratorsCompat");
|
|
104
|
+
function getStringArg(expr) {
|
|
105
|
+
if (expr.arguments.length > 0 && import_typescript.default.isStringLiteral(expr.arguments[0])) {
|
|
106
|
+
return expr.arguments[0].text;
|
|
107
|
+
}
|
|
108
|
+
return "";
|
|
109
|
+
}
|
|
110
|
+
__name(getStringArg, "getStringArg");
|
|
111
|
+
function visit(node) {
|
|
112
|
+
if (import_typescript.default.isClassDeclaration(node)) {
|
|
113
|
+
let controllerPath = "";
|
|
114
|
+
for (const dec of getDecoratorsCompat(node)) {
|
|
115
|
+
if (import_typescript.default.isCallExpression(dec.expression)) {
|
|
116
|
+
const name = dec.expression.expression.getText(sourceFile);
|
|
117
|
+
if (name === "Controller") {
|
|
118
|
+
controllerPath = getStringArg(dec.expression);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const member of node.members) {
|
|
124
|
+
if (!import_typescript.default.isMethodDeclaration(member)) continue;
|
|
125
|
+
for (const dec of getDecoratorsCompat(member)) {
|
|
126
|
+
if (!import_typescript.default.isCallExpression(dec.expression)) continue;
|
|
127
|
+
const decoratorName = dec.expression.expression.getText(sourceFile);
|
|
128
|
+
if (!HTTP_DECORATOR_NAMES.includes(decoratorName)) continue;
|
|
129
|
+
const routePath = getStringArg(dec.expression);
|
|
130
|
+
const fullPath = normalizeApiPath(controllerPath, routePath);
|
|
131
|
+
const method = decoratorName === "All" ? "*" : decoratorName.toUpperCase();
|
|
132
|
+
routes.push({
|
|
133
|
+
method,
|
|
134
|
+
path: fullPath
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
import_typescript.default.forEachChild(node, visit);
|
|
140
|
+
}
|
|
141
|
+
__name(visit, "visit");
|
|
142
|
+
visit(sourceFile);
|
|
143
|
+
return routes;
|
|
144
|
+
}
|
|
145
|
+
__name(parseControllerRoutes, "parseControllerRoutes");
|
|
146
|
+
function parseApiRoutes(serverDir2) {
|
|
147
|
+
const resolvedDir = import_node_path2.default.resolve(serverDir2);
|
|
148
|
+
const controllerFiles = findControllerFilesSync(resolvedDir);
|
|
149
|
+
const routes = [];
|
|
150
|
+
for (const filePath of controllerFiles) {
|
|
151
|
+
routes.push(...parseControllerRoutes(filePath));
|
|
152
|
+
}
|
|
153
|
+
return routes;
|
|
154
|
+
}
|
|
155
|
+
__name(parseApiRoutes, "parseApiRoutes");
|
|
156
|
+
|
|
157
|
+
// src/bin/generate-api-routes.ts
|
|
158
|
+
var args = process.argv.slice(2);
|
|
159
|
+
function getArg(name, defaultValue) {
|
|
160
|
+
const index = args.indexOf(name);
|
|
161
|
+
if (index !== -1 && args[index + 1]) {
|
|
162
|
+
return args[index + 1];
|
|
163
|
+
}
|
|
164
|
+
return defaultValue;
|
|
165
|
+
}
|
|
166
|
+
__name(getArg, "getArg");
|
|
167
|
+
var serverDir = import_path.default.resolve(process.cwd(), getArg("--server-dir", "./server"));
|
|
168
|
+
var outDir = import_path.default.resolve(process.cwd(), getArg("--out-dir", "./dist"));
|
|
169
|
+
var filename = getArg("--filename", "api-routes.json");
|
|
170
|
+
try {
|
|
171
|
+
const routes = parseApiRoutes(serverDir);
|
|
172
|
+
if (!import_fs.default.existsSync(outDir)) {
|
|
173
|
+
import_fs.default.mkdirSync(outDir, {
|
|
174
|
+
recursive: true
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const outPath = import_path.default.resolve(outDir, filename);
|
|
178
|
+
import_fs.default.writeFileSync(outPath, JSON.stringify(routes, null, 2));
|
|
179
|
+
console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.warn("[api-routes] Failed to generate api-routes.json, writing empty fallback:", error);
|
|
182
|
+
if (!import_fs.default.existsSync(outDir)) {
|
|
183
|
+
import_fs.default.mkdirSync(outDir, {
|
|
184
|
+
recursive: true
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
import_fs.default.writeFileSync(import_path.default.resolve(outDir, filename), JSON.stringify([], null, 2));
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=generate-api-routes.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bin/generate-api-routes.ts","../../src/middlewares/openapi/services.ts","../../src/middlewares/openapi/utils.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parseApiRoutes } from '../middlewares/openapi/services';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst serverDir = path.resolve(process.cwd(), getArg('--server-dir', './server'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'api-routes.json');\n\ntry {\n const routes = parseApiRoutes(serverDir);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.warn('[api-routes] Failed to generate api-routes.json, writing empty fallback:', error);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));\n}\n","import { promises as fs } from 'node:fs';\nimport * as fsSync from 'node:fs';\nimport path from 'node:path';\nimport ts from 'typescript';\nimport type { SourceInfo, EnhanceOptions, EnhanceResult } from './types';\nimport { findControllerFiles, buildSourceMap, enhanceOpenApiPaths } from './utils';\n\n/**\n * Enhances OpenAPI JSON with source file location metadata\n * Can be called programmatically or run as a script\n */\nexport async function enhanceOpenApiWithSourceInfo(options: EnhanceOptions = {}): Promise<EnhanceResult> {\n const startTime = Date.now();\n\n const openapiPath = options.openapiPath || path.resolve(__dirname, '../client/src/api/gen/openapi.json');\n const serverDir = options.serverDir || path.resolve(__dirname, '../server');\n const writeFile = options.writeFile !== false;\n\n let openapi: any;\n if (options.openapiData) {\n // Use provided data (for in-memory enhancement)\n openapi = JSON.parse(JSON.stringify(options.openapiData)); // Deep clone\n } else {\n // Read from file\n const openapiContent = await fs.readFile(openapiPath, 'utf-8');\n openapi = JSON.parse(openapiContent);\n }\n\n const controllerFiles = await findControllerFiles(serverDir);\n const sourceMap = await buildSourceMap(controllerFiles, processControllerFile);\n const enhanced = enhanceOpenApiPaths(openapi, sourceMap);\n\n if (writeFile) {\n await fs.writeFile(openapiPath, JSON.stringify(openapi, null, 2) + '\\n', 'utf-8');\n }\n\n const duration = Date.now() - startTime;\n\n return {\n openapi,\n stats: {\n duration,\n controllersFound: controllerFiles.length,\n endpointsExtracted: sourceMap.size,\n endpointsEnhanced: enhanced,\n },\n };\n}\n\n/**\n * Process a single controller file\n */\nasync function processControllerFile(filePath: string): Promise<Map<string, SourceInfo>> {\n const relativePath = path.relative(process.cwd(), filePath);\n\n // Parse file\n const content = await fs.readFile(filePath, 'utf-8');\n const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n\n return extractControllerMetadata(sourceFile, relativePath);\n}\n\n/**\n * Extract controller metadata from TypeScript source file\n */\nfunction extractControllerMetadata(sourceFile: ts.SourceFile, filePath: string): Map<string, SourceInfo> {\n const metadata = new Map<string, SourceInfo>();\n let controllerPath = '';\n let className = '';\n\n // Helper function to get decorators from both old and new TypeScript APIs\n function getDecorators(node: ts.Node): readonly ts.Decorator[] {\n // TypeScript 5.x: decorators are in modifiers array\n if ('modifiers' in node && Array.isArray(node.modifiers)) {\n return (node.modifiers as ts.ModifierLike[]).filter(\n (mod): mod is ts.Decorator => mod.kind === ts.SyntaxKind.Decorator,\n );\n }\n // TypeScript 4.x: decorators are in decorators array\n if ('decorators' in node && Array.isArray(node.decorators)) {\n return node.decorators as readonly ts.Decorator[];\n }\n return [];\n }\n\n function visit(node: ts.Node): void {\n // Extract @Controller decorator and its path\n if (ts.isClassDeclaration(node)) {\n const decorators = getDecorators(node);\n\n // Extract class name\n if (node.name) {\n className = node.name.getText(sourceFile);\n }\n\n for (const decorator of decorators) {\n if (ts.isCallExpression(decorator.expression)) {\n const expression = decorator.expression;\n const decoratorName = expression.expression.getText(sourceFile);\n\n if (decoratorName === 'Controller') {\n if (expression.arguments.length > 0) {\n const arg = expression.arguments[0];\n if (ts.isStringLiteral(arg)) {\n controllerPath = arg.text;\n }\n }\n }\n }\n }\n }\n\n // Extract methods with HTTP decorators\n if (ts.isMethodDeclaration(node) && node.name) {\n const methodName = node.name.getText(sourceFile);\n let httpMethod = '';\n let routePath = '';\n const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));\n\n const decorators = getDecorators(node);\n\n for (const decorator of decorators) {\n if (ts.isCallExpression(decorator.expression)) {\n const decoratorName = decorator.expression.expression.getText(sourceFile);\n if (['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head', 'All'].includes(decoratorName)) {\n httpMethod = decoratorName.toLowerCase();\n if (decorator.expression.arguments.length > 0) {\n const arg = decorator.expression.arguments[0];\n if (ts.isStringLiteral(arg)) {\n routePath = arg.text;\n }\n }\n }\n }\n }\n\n if (httpMethod && methodName && className) {\n const operationId = `${className}_${methodName}`;\n metadata.set(operationId, {\n file: filePath,\n line: line + 1,\n method: httpMethod,\n controllerPath,\n routePath,\n });\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return metadata;\n}\n\n// --- API Route Parsing (synchronous, for build-time injection) ---\n\nconst HTTP_DECORATOR_NAMES = ['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head', 'All'];\n\nfunction findControllerFilesSync(dir: string): string[] {\n const files: string[] = [];\n function scan(currentDir: string): void {\n let entries: fsSync.Dirent[];\n try {\n entries = fsSync.readdirSync(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n scan(fullPath);\n } else if (entry.isFile() && entry.name.endsWith('.controller.ts')) {\n files.push(fullPath);\n }\n }\n }\n scan(dir);\n return files;\n}\n\nfunction normalizeApiPath(controllerPath: string, routePath: string): string {\n const combined = routePath ? `${controllerPath}/${routePath}` : controllerPath;\n let normalized = combined.replace(/\\/+/g, '/');\n if (!normalized.startsWith('/')) normalized = `/${normalized}`;\n if (normalized.length > 1 && normalized.endsWith('/')) normalized = normalized.slice(0, -1);\n return normalized;\n}\n\nfunction parseControllerRoutes(filePath: string): Array<{ method: string; path: string }> {\n let content: string;\n try {\n content = fsSync.readFileSync(filePath, 'utf-8');\n } catch {\n return [];\n }\n\n const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n const routes: Array<{ method: string; path: string }> = [];\n\n function getDecoratorsCompat(node: ts.Node): readonly ts.Decorator[] {\n if ('modifiers' in node && Array.isArray(node.modifiers)) {\n return (node.modifiers as ts.ModifierLike[]).filter(\n (mod): mod is ts.Decorator => mod.kind === ts.SyntaxKind.Decorator,\n );\n }\n if ('decorators' in node && Array.isArray(node.decorators)) {\n return node.decorators as readonly ts.Decorator[];\n }\n return [];\n }\n\n function getStringArg(expr: ts.CallExpression): string {\n if (expr.arguments.length > 0 && ts.isStringLiteral(expr.arguments[0])) {\n return expr.arguments[0].text;\n }\n return '';\n }\n\n function visit(node: ts.Node): void {\n if (ts.isClassDeclaration(node)) {\n let controllerPath = '';\n for (const dec of getDecoratorsCompat(node)) {\n if (ts.isCallExpression(dec.expression)) {\n const name = dec.expression.expression.getText(sourceFile);\n if (name === 'Controller') {\n controllerPath = getStringArg(dec.expression);\n break;\n }\n }\n }\n\n for (const member of node.members) {\n if (!ts.isMethodDeclaration(member)) continue;\n for (const dec of getDecoratorsCompat(member)) {\n if (!ts.isCallExpression(dec.expression)) continue;\n const decoratorName = dec.expression.expression.getText(sourceFile);\n if (!HTTP_DECORATOR_NAMES.includes(decoratorName)) continue;\n\n const routePath = getStringArg(dec.expression);\n const fullPath = normalizeApiPath(controllerPath, routePath);\n const method = decoratorName === 'All' ? '*' : decoratorName.toUpperCase();\n routes.push({ method, path: fullPath });\n }\n }\n }\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return routes;\n}\n\n/**\n * Scan serverDir for NestJS controller files and extract all parameterized API routes.\n * Synchronous — safe to call at preset config time (before bundling).\n */\nexport function parseApiRoutes(serverDir: string): Array<{ method: string; path: string }> {\n const resolvedDir = path.resolve(serverDir);\n const controllerFiles = findControllerFilesSync(resolvedDir);\n const routes: Array<{ method: string; path: string }> = [];\n for (const filePath of controllerFiles) {\n routes.push(...parseControllerRoutes(filePath));\n }\n return routes;\n}\n","import path from 'node:path';\nimport { promises as fs } from 'node:fs';\nimport type { SourceInfo } from './types';\n\n/**\n * Find all controller files in a directory\n */\nexport async function findControllerFiles(dir: string): Promise<string[]> {\n const files: string[] = [];\n\n async function scan(currentDir: string): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n\n if (entry.isDirectory()) {\n await scan(fullPath);\n } else if (entry.isFile() && entry.name.endsWith('.controller.ts')) {\n files.push(fullPath);\n }\n }\n }\n\n await scan(dir);\n return files;\n}\n\n/**\n * Build source map from controller files\n */\nexport async function buildSourceMap(\n controllerFiles: string[],\n processFile: (filePath: string) => Promise<Map<string, SourceInfo>>,\n): Promise<Map<string, SourceInfo>> {\n const sourceMap = new Map<string, SourceInfo>();\n\n // Process files in parallel with a concurrency limit\n const concurrency = 10;\n const results: Map<string, SourceInfo>[] = [];\n\n for (let i = 0; i < controllerFiles.length; i += concurrency) {\n const batch = controllerFiles.slice(i, i + concurrency);\n const batchResults = await Promise.all(batch.map((filePath) => processFile(filePath)));\n results.push(...batchResults);\n }\n\n // Merge results\n for (const metadata of results) {\n for (const [operationId, info] of metadata.entries()) {\n sourceMap.set(operationId, info);\n }\n }\n\n return sourceMap;\n}\n\n/**\n * Try to match operationId with different formats\n * Supports:\n * - Direct match: ClassName_methodName\n * - Camel case: classNameMethodName\n * - Method only: methodName\n */\nfunction findSourceInfo(operationId: string, sourceMap: Map<string, SourceInfo>): SourceInfo | undefined {\n // Try direct match first\n const directMatch = sourceMap.get(operationId);\n if (directMatch) {\n return directMatch;\n }\n\n // Try matching with different formats\n for (const [key, value] of sourceMap.entries()) {\n // key format: ClassName_methodName\n const [className, methodName] = key.split('_');\n if (!className || !methodName) continue;\n\n // Try camelCase format: classNameMethodName\n const camelCaseId = className.charAt(0).toLowerCase() + className.slice(1) + methodName.charAt(0).toUpperCase() + methodName.slice(1);\n if (operationId === camelCaseId) {\n return value;\n }\n\n // Try method name only\n if (operationId === methodName) {\n return value;\n }\n }\n\n return undefined;\n}\n\n/**\n * Enhance OpenAPI paths with source information\n */\nexport function enhanceOpenApiPaths(openapi: any, sourceMap: Map<string, SourceInfo>): number {\n let enhancedCount = 0;\n\n if (!openapi.paths) {\n return enhancedCount;\n }\n\n for (const pathItem of Object.values(openapi.paths)) {\n if (!pathItem || typeof pathItem !== 'object') continue;\n\n for (const operation of Object.values(pathItem)) {\n if (operation && typeof operation === 'object' && 'operationId' in operation) {\n const sourceInfo = findSourceInfo(operation.operationId as string, sourceMap);\n if (sourceInfo) {\n operation['x-source'] = {\n file: sourceInfo.file,\n line: sourceInfo.line,\n };\n enhancedCount++;\n }\n }\n }\n }\n\n return enhancedCount;\n}\n\n/**\n * Transform OpenAPI paths by removing basePath prefix\n */\nexport function transformOpenapiPaths(openapi: any, basePath: string): any {\n if (basePath === '/' || !openapi.paths) {\n return openapi;\n }\n\n const newPaths: any = {};\n Object.keys(openapi.paths).forEach((key) => {\n const staticApiKey = key.startsWith(basePath) ? key.slice(basePath.length) : key;\n newPaths[staticApiKey] = openapi.paths[key];\n });\n\n return {\n ...openapi,\n paths: newPaths,\n basePath,\n };\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","HTTP_DECORATOR_NAMES","findControllerFilesSync","dir","files","scan","currentDir","entries","readdirSync","withFileTypes","entry","fullPath","path","join","name","isDirectory","isFile","endsWith","push","normalizeApiPath","controllerPath","routePath","combined","normalized","replace","startsWith","length","slice","parseControllerRoutes","filePath","content","readFileSync","sourceFile","ts","createSourceFile","ScriptTarget","Latest","routes","getDecoratorsCompat","node","Array","isArray","modifiers","filter","mod","kind","SyntaxKind","Decorator","decorators","getStringArg","expr","arguments","isStringLiteral","text","visit","isClassDeclaration","dec","isCallExpression","expression","getText","member","members","isMethodDeclaration","decoratorName","includes","method","toUpperCase","forEachChild","parseApiRoutes","serverDir","resolvedDir","resolve","controllerFiles","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","serverDir","path","resolve","cwd","outDir","filename","routes","parseApiRoutes","fs","existsSync","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","length","error","warn"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
parseApiRoutes
|
|
4
|
+
} from "../chunk-LSHFHCDF.js";
|
|
5
|
+
import {
|
|
6
|
+
__name
|
|
7
|
+
} from "../chunk-7QVYU63E.js";
|
|
8
|
+
|
|
9
|
+
// src/bin/generate-api-routes.ts
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import path from "path";
|
|
12
|
+
var args = process.argv.slice(2);
|
|
13
|
+
function getArg(name, defaultValue) {
|
|
14
|
+
const index = args.indexOf(name);
|
|
15
|
+
if (index !== -1 && args[index + 1]) {
|
|
16
|
+
return args[index + 1];
|
|
17
|
+
}
|
|
18
|
+
return defaultValue;
|
|
19
|
+
}
|
|
20
|
+
__name(getArg, "getArg");
|
|
21
|
+
var serverDir = path.resolve(process.cwd(), getArg("--server-dir", "./server"));
|
|
22
|
+
var outDir = path.resolve(process.cwd(), getArg("--out-dir", "./dist"));
|
|
23
|
+
var filename = getArg("--filename", "api-routes.json");
|
|
24
|
+
try {
|
|
25
|
+
const routes = parseApiRoutes(serverDir);
|
|
26
|
+
if (!fs.existsSync(outDir)) {
|
|
27
|
+
fs.mkdirSync(outDir, {
|
|
28
|
+
recursive: true
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const outPath = path.resolve(outDir, filename);
|
|
32
|
+
fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));
|
|
33
|
+
console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.warn("[api-routes] Failed to generate api-routes.json, writing empty fallback:", error);
|
|
36
|
+
if (!fs.existsSync(outDir)) {
|
|
37
|
+
fs.mkdirSync(outDir, {
|
|
38
|
+
recursive: true
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=generate-api-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bin/generate-api-routes.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parseApiRoutes } from '../middlewares/openapi/services';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst serverDir = path.resolve(process.cwd(), getArg('--server-dir', './server'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'api-routes.json');\n\ntry {\n const routes = parseApiRoutes(serverDir);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.warn('[api-routes] Failed to generate api-routes.json, writing empty fallback:', error);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));\n}\n"],"mappings":";;;;;;;;;AACA,OAAOA,QAAQ;AACf,OAAOC,UAAU;AAGjB,IAAMC,OAAOC,QAAQC,KAAKC,MAAM,CAAA;AAEhC,SAASC,OAAOC,MAAcC,cAAoB;AAChD,QAAMC,QAAQP,KAAKQ,QAAQH,IAAAA;AAC3B,MAAIE,UAAU,MAAMP,KAAKO,QAAQ,CAAA,GAAI;AACnC,WAAOP,KAAKO,QAAQ,CAAA;EACtB;AACA,SAAOD;AACT;AANSF;AAQT,IAAMK,YAAYC,KAAKC,QAAQV,QAAQW,IAAG,GAAIR,OAAO,gBAAgB,UAAA,CAAA;AACrE,IAAMS,SAASH,KAAKC,QAAQV,QAAQW,IAAG,GAAIR,OAAO,aAAa,QAAA,CAAA;AAC/D,IAAMU,WAAWV,OAAO,cAAc,iBAAA;AAEtC,IAAI;AACF,QAAMW,SAASC,eAAeP,SAAAA;AAC9B,MAAI,CAACQ,GAAGC,WAAWL,MAAAA,GAAS;AAC1BI,OAAGE,UAAUN,QAAQ;MAAEO,WAAW;IAAK,CAAA;EACzC;AACA,QAAMC,UAAUX,KAAKC,QAAQE,QAAQC,QAAAA;AACrCG,KAAGK,cAAcD,SAASE,KAAKC,UAAUT,QAAQ,MAAM,CAAA,CAAA;AACvDU,UAAQC,IAAI,0BAA0BL,OAAAA,KAAYN,OAAOY,MAAM,UAAU;AAC3E,SAASC,OAAO;AACdH,UAAQI,KAAK,4EAA4ED,KAAAA;AACzF,MAAI,CAACX,GAAGC,WAAWL,MAAAA,GAAS;AAC1BI,OAAGE,UAAUN,QAAQ;MAAEO,WAAW;IAAK,CAAA;EACzC;AACAH,KAAGK,cAAcZ,KAAKC,QAAQE,QAAQC,QAAAA,GAAWS,KAAKC,UAAU,CAAA,GAAI,MAAM,CAAA,CAAA;AAC5E;","names":["fs","path","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","serverDir","path","resolve","cwd","outDir","filename","routes","parseApiRoutes","fs","existsSync","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","length","error","warn"]}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
+
mod
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
// src/bin/generate-page-routes.ts
|
|
28
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
29
|
+
var import_path = __toESM(require("path"), 1);
|
|
30
|
+
var import_parser = require("@babel/parser");
|
|
31
|
+
var import_traverse = __toESM(require("@babel/traverse"), 1);
|
|
32
|
+
var t = __toESM(require("@babel/types"), 1);
|
|
33
|
+
var traverse = typeof import_traverse.default === "function" ? import_traverse.default : import_traverse.default.default;
|
|
34
|
+
var args = process.argv.slice(2);
|
|
35
|
+
function getArg(name, defaultValue) {
|
|
36
|
+
const index = args.indexOf(name);
|
|
37
|
+
if (index !== -1 && args[index + 1]) {
|
|
38
|
+
return args[index + 1];
|
|
39
|
+
}
|
|
40
|
+
return defaultValue;
|
|
41
|
+
}
|
|
42
|
+
__name(getArg, "getArg");
|
|
43
|
+
var appPath = import_path.default.resolve(process.cwd(), getArg("--app-path", "./client/src/app.tsx"));
|
|
44
|
+
var outDir = import_path.default.resolve(process.cwd(), getArg("--out-dir", "./dist"));
|
|
45
|
+
var filename = getArg("--filename", "page-routes.json");
|
|
46
|
+
function isRouteComponent(openingElement) {
|
|
47
|
+
return t.isJSXIdentifier(openingElement.name) && openingElement.name.name === "Route";
|
|
48
|
+
}
|
|
49
|
+
__name(isRouteComponent, "isRouteComponent");
|
|
50
|
+
function evaluateTemplateLiteral(templateLiteral) {
|
|
51
|
+
const quasis = templateLiteral.quasis;
|
|
52
|
+
const expressions = templateLiteral.expressions;
|
|
53
|
+
if (quasis.length === 1 && expressions.length === 0) {
|
|
54
|
+
return quasis[0].value.raw;
|
|
55
|
+
}
|
|
56
|
+
return quasis.map((q) => q.value.raw).join("");
|
|
57
|
+
}
|
|
58
|
+
__name(evaluateTemplateLiteral, "evaluateTemplateLiteral");
|
|
59
|
+
function extractRouteInfo(openingElement) {
|
|
60
|
+
const routeInfo = {};
|
|
61
|
+
openingElement.attributes.forEach((attr) => {
|
|
62
|
+
if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
|
|
63
|
+
const name = attr.name.name;
|
|
64
|
+
let value;
|
|
65
|
+
if (attr.value) {
|
|
66
|
+
if (t.isStringLiteral(attr.value)) {
|
|
67
|
+
value = attr.value.value;
|
|
68
|
+
} else if (t.isJSXExpressionContainer(attr.value)) {
|
|
69
|
+
const expression = attr.value.expression;
|
|
70
|
+
if (t.isStringLiteral(expression)) {
|
|
71
|
+
value = expression.value;
|
|
72
|
+
} else if (t.isTemplateLiteral(expression)) {
|
|
73
|
+
value = evaluateTemplateLiteral(expression);
|
|
74
|
+
} else {
|
|
75
|
+
value = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
value = true;
|
|
80
|
+
}
|
|
81
|
+
routeInfo[name] = value;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return routeInfo;
|
|
85
|
+
}
|
|
86
|
+
__name(extractRouteInfo, "extractRouteInfo");
|
|
87
|
+
function buildFullPath(routeStack, currentRoute) {
|
|
88
|
+
let fullPath = "";
|
|
89
|
+
for (let i = 0; i < routeStack.length; i++) {
|
|
90
|
+
if (routeStack[i].path) {
|
|
91
|
+
let parentPath = routeStack[i].path;
|
|
92
|
+
if (!parentPath.startsWith("/")) parentPath = `/${parentPath}`;
|
|
93
|
+
if (parentPath.endsWith("/") && parentPath !== "/") {
|
|
94
|
+
parentPath = parentPath.slice(0, -1);
|
|
95
|
+
}
|
|
96
|
+
fullPath += parentPath === "/" ? "" : parentPath;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (currentRoute.index) {
|
|
100
|
+
return fullPath || "/";
|
|
101
|
+
} else if (currentRoute.path) {
|
|
102
|
+
const routePath = currentRoute.path;
|
|
103
|
+
if (routePath === "*") {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if (!routePath.startsWith("/")) {
|
|
107
|
+
fullPath = `${fullPath}/${routePath}`;
|
|
108
|
+
} else {
|
|
109
|
+
fullPath = routePath;
|
|
110
|
+
}
|
|
111
|
+
if (fullPath === "") fullPath = "/";
|
|
112
|
+
if (!fullPath.startsWith("/")) fullPath = `/${fullPath}`;
|
|
113
|
+
return fullPath;
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
__name(buildFullPath, "buildFullPath");
|
|
118
|
+
function parsePageRoutes(appFilePath) {
|
|
119
|
+
if (!import_fs.default.existsSync(appFilePath)) {
|
|
120
|
+
throw new Error(`App file does not exist: ${appFilePath}`);
|
|
121
|
+
}
|
|
122
|
+
const sourceCode = import_fs.default.readFileSync(appFilePath, "utf-8");
|
|
123
|
+
const ast = (0, import_parser.parse)(sourceCode, {
|
|
124
|
+
sourceType: "module",
|
|
125
|
+
plugins: [
|
|
126
|
+
"jsx",
|
|
127
|
+
"typescript",
|
|
128
|
+
"decorators-legacy",
|
|
129
|
+
"classProperties",
|
|
130
|
+
"objectRestSpread",
|
|
131
|
+
"functionBind",
|
|
132
|
+
"exportDefaultFrom",
|
|
133
|
+
"exportNamespaceFrom",
|
|
134
|
+
"dynamicImport",
|
|
135
|
+
"nullishCoalescingOperator",
|
|
136
|
+
"optionalChaining"
|
|
137
|
+
]
|
|
138
|
+
});
|
|
139
|
+
const routeSet = /* @__PURE__ */ new Set();
|
|
140
|
+
const routeStack = [];
|
|
141
|
+
traverse(ast, {
|
|
142
|
+
JSXElement: {
|
|
143
|
+
enter(nodePath) {
|
|
144
|
+
const { openingElement } = nodePath.node;
|
|
145
|
+
if (isRouteComponent(openingElement)) {
|
|
146
|
+
routeStack.push(extractRouteInfo(openingElement));
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
exit(nodePath) {
|
|
150
|
+
const { openingElement } = nodePath.node;
|
|
151
|
+
if (isRouteComponent(openingElement)) {
|
|
152
|
+
const currentRoute = routeStack.pop();
|
|
153
|
+
if (currentRoute && currentRoute.path === "*") return;
|
|
154
|
+
if (currentRoute && (currentRoute.path || currentRoute.index)) {
|
|
155
|
+
const fullPath = buildFullPath(routeStack, currentRoute);
|
|
156
|
+
if (fullPath) routeSet.add(fullPath);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
const routes = Array.from(routeSet).map((routePath) => ({
|
|
163
|
+
path: routePath
|
|
164
|
+
}));
|
|
165
|
+
return routes.length > 0 ? routes : [
|
|
166
|
+
{
|
|
167
|
+
path: "/"
|
|
168
|
+
}
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
__name(parsePageRoutes, "parsePageRoutes");
|
|
172
|
+
try {
|
|
173
|
+
const routes = parsePageRoutes(appPath);
|
|
174
|
+
if (!import_fs.default.existsSync(outDir)) {
|
|
175
|
+
import_fs.default.mkdirSync(outDir, {
|
|
176
|
+
recursive: true
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
const outPath = import_path.default.resolve(outDir, filename);
|
|
180
|
+
import_fs.default.writeFileSync(outPath, JSON.stringify(routes, null, 2));
|
|
181
|
+
console.log(`[page-routes] Generated ${outPath} (${routes.length} routes)`);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.warn("[page-routes] Failed to generate page-routes.json, writing empty fallback:", error);
|
|
184
|
+
if (!import_fs.default.existsSync(outDir)) {
|
|
185
|
+
import_fs.default.mkdirSync(outDir, {
|
|
186
|
+
recursive: true
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
import_fs.default.writeFileSync(import_path.default.resolve(outDir, filename), JSON.stringify([], null, 2));
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=generate-page-routes.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bin/generate-page-routes.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parse } from '@babel/parser';\nimport _traverse from '@babel/traverse';\n// Handle CJS default export interop in ESM context\nconst traverse = typeof _traverse === 'function' ? _traverse : (_traverse as any).default;\nimport * as t from '@babel/types';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst appPath = path.resolve(process.cwd(), getArg('--app-path', './client/src/app.tsx'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'page-routes.json');\n\ninterface RouteInfo {\n path?: string;\n index?: boolean;\n [key: string]: unknown;\n}\n\nfunction isRouteComponent(openingElement: t.JSXOpeningElement): boolean {\n return (\n t.isJSXIdentifier(openingElement.name) &&\n openingElement.name.name === 'Route'\n );\n}\n\nfunction evaluateTemplateLiteral(templateLiteral: t.TemplateLiteral): string {\n const quasis = templateLiteral.quasis;\n const expressions = templateLiteral.expressions;\n\n if (quasis.length === 1 && expressions.length === 0) {\n return quasis[0].value.raw;\n }\n\n return quasis.map((q) => q.value.raw).join('');\n}\n\nfunction extractRouteInfo(openingElement: t.JSXOpeningElement): RouteInfo {\n const routeInfo: RouteInfo = {};\n\n openingElement.attributes.forEach((attr) => {\n if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {\n const name = attr.name.name;\n let value: unknown;\n\n if (attr.value) {\n if (t.isStringLiteral(attr.value)) {\n value = attr.value.value;\n } else if (t.isJSXExpressionContainer(attr.value)) {\n const expression = attr.value.expression;\n if (t.isStringLiteral(expression)) {\n value = expression.value;\n } else if (t.isTemplateLiteral(expression)) {\n value = evaluateTemplateLiteral(expression);\n } else {\n value = true;\n }\n }\n } else {\n value = true;\n }\n\n routeInfo[name] = value;\n }\n });\n\n return routeInfo;\n}\n\nfunction buildFullPath(routeStack: RouteInfo[], currentRoute: RouteInfo): string | null {\n let fullPath = '';\n\n for (let i = 0; i < routeStack.length; i++) {\n if (routeStack[i].path) {\n let parentPath = routeStack[i].path!;\n if (!parentPath.startsWith('/')) parentPath = `/${parentPath}`;\n if (parentPath.endsWith('/') && parentPath !== '/') {\n parentPath = parentPath.slice(0, -1);\n }\n\n fullPath += parentPath === '/' ? '' : parentPath;\n }\n }\n\n if (currentRoute.index) {\n return fullPath || '/';\n } else if (currentRoute.path) {\n const routePath = currentRoute.path;\n\n if (routePath === '*') {\n return null;\n }\n\n if (!routePath.startsWith('/')) {\n fullPath = `${fullPath}/${routePath}`;\n } else {\n fullPath = routePath;\n }\n\n if (fullPath === '') fullPath = '/';\n if (!fullPath.startsWith('/')) fullPath = `/${fullPath}`;\n\n return fullPath;\n }\n\n return null;\n}\n\nfunction parsePageRoutes(appFilePath: string): Array<{ path: string }> {\n if (!fs.existsSync(appFilePath)) {\n throw new Error(`App file does not exist: ${appFilePath}`);\n }\n\n const sourceCode = fs.readFileSync(appFilePath, 'utf-8');\n\n const ast = parse(sourceCode, {\n sourceType: 'module',\n plugins: [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'objectRestSpread',\n 'functionBind',\n 'exportDefaultFrom',\n 'exportNamespaceFrom',\n 'dynamicImport',\n 'nullishCoalescingOperator',\n 'optionalChaining',\n ],\n });\n\n const routeSet = new Set<string>();\n const routeStack: RouteInfo[] = [];\n\n traverse(ast, {\n JSXElement: {\n enter(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n routeStack.push(extractRouteInfo(openingElement));\n }\n },\n exit(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n const currentRoute = routeStack.pop();\n if (currentRoute && currentRoute.path === '*') return;\n if (currentRoute && (currentRoute.path || currentRoute.index)) {\n const fullPath = buildFullPath(routeStack, currentRoute);\n if (fullPath) routeSet.add(fullPath);\n }\n }\n },\n },\n });\n\n // page-routes 不带 basePath 前缀,供日志服务消费\n const routes = Array.from(routeSet).map((routePath) => ({ path: routePath }));\n return routes.length > 0 ? routes : [{ path: '/' }];\n}\n\ntry {\n const routes = parsePageRoutes(appPath);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[page-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.warn('[page-routes] Failed to generate page-routes.json, writing empty fallback:', error);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));\n}\n"],"mappings":"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","names":["traverse","_traverse","default","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","appPath","path","resolve","cwd","outDir","filename","isRouteComponent","openingElement","isJSXIdentifier","evaluateTemplateLiteral","templateLiteral","quasis","expressions","length","value","raw","map","q","join","extractRouteInfo","routeInfo","attributes","forEach","attr","isJSXAttribute","isStringLiteral","isJSXExpressionContainer","expression","isTemplateLiteral","buildFullPath","routeStack","currentRoute","fullPath","i","parentPath","startsWith","endsWith","routePath","parsePageRoutes","appFilePath","fs","existsSync","Error","sourceCode","readFileSync","ast","parse","sourceType","plugins","routeSet","Set","JSXElement","enter","nodePath","node","push","exit","pop","add","routes","Array","from","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","error","warn"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|