@agentuity/cli 1.0.48 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cmd/build/app-router-detector.d.ts +2 -5
- package/dist/cmd/build/app-router-detector.d.ts.map +1 -1
- package/dist/cmd/build/app-router-detector.js +130 -154
- package/dist/cmd/build/app-router-detector.js.map +1 -1
- package/dist/cmd/build/ids.d.ts +11 -0
- package/dist/cmd/build/ids.d.ts.map +1 -0
- package/dist/cmd/build/ids.js +18 -0
- package/dist/cmd/build/ids.js.map +1 -0
- package/dist/cmd/build/vite/agent-discovery.d.ts +8 -4
- package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.js +166 -487
- package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.d.ts +10 -16
- package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.js +67 -134
- package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
- package/dist/cmd/build/vite/docs-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/docs-generator.js +0 -2
- package/dist/cmd/build/vite/docs-generator.js.map +1 -1
- package/dist/cmd/build/vite/index.d.ts.map +1 -1
- package/dist/cmd/build/vite/index.js +0 -36
- package/dist/cmd/build/vite/index.js.map +1 -1
- package/dist/cmd/build/vite/lifecycle-generator.d.ts +10 -2
- package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/lifecycle-generator.js +302 -23
- package/dist/cmd/build/vite/lifecycle-generator.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts +11 -38
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js +97 -177
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/build/vite/server-bundler.js +1 -1
- package/dist/cmd/build/vite/server-bundler.js.map +1 -1
- package/dist/cmd/build/vite/static-renderer.d.ts.map +1 -1
- package/dist/cmd/build/vite/static-renderer.js +1 -9
- package/dist/cmd/build/vite/static-renderer.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts +6 -3
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js +171 -21
- package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.d.ts +8 -3
- package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.js +14 -13
- package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +6 -36
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/vite/ws-proxy.d.ts +53 -0
- package/dist/cmd/build/vite/ws-proxy.d.ts.map +1 -0
- package/dist/cmd/build/vite/ws-proxy.js +95 -0
- package/dist/cmd/build/vite/ws-proxy.js.map +1 -0
- package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
- package/dist/cmd/build/vite-bundler.js +0 -3
- package/dist/cmd/build/vite-bundler.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +0 -1
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
- package/dist/cmd/dev/file-watcher.js +2 -8
- package/dist/cmd/dev/file-watcher.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +369 -720
- package/dist/cmd/dev/index.js.map +1 -1
- package/package.json +6 -8
- package/src/cmd/ai/prompt/agent.md +0 -1
- package/src/cmd/ai/prompt/api.md +0 -7
- package/src/cmd/ai/prompt/web.md +51 -213
- package/src/cmd/build/app-router-detector.ts +152 -182
- package/src/cmd/build/ids.ts +19 -0
- package/src/cmd/build/vite/agent-discovery.ts +208 -679
- package/src/cmd/build/vite/bun-dev-server.ts +78 -154
- package/src/cmd/build/vite/docs-generator.ts +0 -2
- package/src/cmd/build/vite/index.ts +1 -42
- package/src/cmd/build/vite/lifecycle-generator.ts +345 -21
- package/src/cmd/build/vite/route-discovery.ts +116 -274
- package/src/cmd/build/vite/server-bundler.ts +1 -1
- package/src/cmd/build/vite/static-renderer.ts +1 -11
- package/src/cmd/build/vite/vite-asset-server-config.ts +196 -23
- package/src/cmd/build/vite/vite-asset-server.ts +25 -15
- package/src/cmd/build/vite/vite-builder.ts +6 -53
- package/src/cmd/build/vite/ws-proxy.ts +126 -0
- package/src/cmd/build/vite-bundler.ts +0 -4
- package/src/cmd/cloud/deploy.ts +0 -1
- package/src/cmd/dev/file-watcher.ts +2 -9
- package/src/cmd/dev/index.ts +409 -832
- package/dist/cmd/build/ast.d.ts +0 -78
- package/dist/cmd/build/ast.d.ts.map +0 -1
- package/dist/cmd/build/ast.js +0 -2703
- package/dist/cmd/build/ast.js.map +0 -1
- package/dist/cmd/build/entry-generator.d.ts +0 -25
- package/dist/cmd/build/entry-generator.d.ts.map +0 -1
- package/dist/cmd/build/entry-generator.js +0 -695
- package/dist/cmd/build/entry-generator.js.map +0 -1
- package/dist/cmd/build/vite/api-mount-path.d.ts +0 -61
- package/dist/cmd/build/vite/api-mount-path.d.ts.map +0 -1
- package/dist/cmd/build/vite/api-mount-path.js +0 -83
- package/dist/cmd/build/vite/api-mount-path.js.map +0 -1
- package/dist/cmd/build/vite/registry-generator.d.ts +0 -19
- package/dist/cmd/build/vite/registry-generator.d.ts.map +0 -1
- package/dist/cmd/build/vite/registry-generator.js +0 -1108
- package/dist/cmd/build/vite/registry-generator.js.map +0 -1
- package/dist/cmd/build/vite/tailwind-source-plugin.d.ts +0 -13
- package/dist/cmd/build/vite/tailwind-source-plugin.d.ts.map +0 -1
- package/dist/cmd/build/vite/tailwind-source-plugin.js +0 -44
- package/dist/cmd/build/vite/tailwind-source-plugin.js.map +0 -1
- package/dist/cmd/build/webanalytics-generator.d.ts +0 -16
- package/dist/cmd/build/webanalytics-generator.d.ts.map +0 -1
- package/dist/cmd/build/webanalytics-generator.js +0 -178
- package/dist/cmd/build/webanalytics-generator.js.map +0 -1
- package/dist/cmd/build/workbench.d.ts +0 -7
- package/dist/cmd/build/workbench.d.ts.map +0 -1
- package/dist/cmd/build/workbench.js +0 -55
- package/dist/cmd/build/workbench.js.map +0 -1
- package/dist/utils/route-migration.d.ts +0 -62
- package/dist/utils/route-migration.d.ts.map +0 -1
- package/dist/utils/route-migration.js +0 -630
- package/dist/utils/route-migration.js.map +0 -1
- package/src/cmd/build/ast.ts +0 -3529
- package/src/cmd/build/entry-generator.ts +0 -760
- package/src/cmd/build/vite/api-mount-path.ts +0 -87
- package/src/cmd/build/vite/registry-generator.ts +0 -1267
- package/src/cmd/build/vite/tailwind-source-plugin.ts +0 -54
- package/src/cmd/build/webanalytics-generator.ts +0 -197
- package/src/cmd/build/workbench.ts +0 -58
- package/src/utils/route-migration.ts +0 -757
|
@@ -5,23 +5,15 @@
|
|
|
5
5
|
* to `createApp()`. If detected, resolves the router variable(s) to their import
|
|
6
6
|
* sources and mount paths.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Uses TypeScript's compiler API to reliably detect the pattern, consistent with
|
|
9
|
+
* the lifecycle generator approach.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import
|
|
12
|
+
import ts from 'typescript';
|
|
13
13
|
import { join, dirname, resolve } from 'node:path';
|
|
14
|
-
import {
|
|
14
|
+
import { statSync } from 'node:fs';
|
|
15
15
|
import type { Logger } from '../../types';
|
|
16
16
|
|
|
17
|
-
interface ASTNode {
|
|
18
|
-
type: string;
|
|
19
|
-
start?: number;
|
|
20
|
-
end?: number;
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
-
[key: string]: any;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
17
|
/**
|
|
26
18
|
* A resolved mount point from `createApp({ router })`.
|
|
27
19
|
*/
|
|
@@ -46,7 +38,7 @@ export interface AppRouterDetection {
|
|
|
46
38
|
* Resolve an import path to an actual file on disk.
|
|
47
39
|
* Tries the path as-is, then with common extensions.
|
|
48
40
|
*/
|
|
49
|
-
function resolveImportFile(fromDir: string, importPath: string): string | null {
|
|
41
|
+
async function resolveImportFile(fromDir: string, importPath: string): Promise<string | null> {
|
|
50
42
|
if (!importPath.startsWith('.') && !importPath.startsWith('/')) {
|
|
51
43
|
return null; // Package import — can't resolve
|
|
52
44
|
}
|
|
@@ -54,7 +46,8 @@ function resolveImportFile(fromDir: string, importPath: string): string | null {
|
|
|
54
46
|
const basePath = resolve(fromDir, importPath);
|
|
55
47
|
const extensions = ['.ts', '.tsx', '/index.ts', '/index.tsx'];
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+
const baseFile = Bun.file(basePath);
|
|
50
|
+
if (await baseFile.exists()) {
|
|
58
51
|
try {
|
|
59
52
|
if (statSync(basePath).isFile()) return basePath;
|
|
60
53
|
} catch {
|
|
@@ -64,7 +57,7 @@ function resolveImportFile(fromDir: string, importPath: string): string | null {
|
|
|
64
57
|
|
|
65
58
|
for (const ext of extensions) {
|
|
66
59
|
const candidate = basePath + ext;
|
|
67
|
-
if (
|
|
60
|
+
if (await Bun.file(candidate).exists()) {
|
|
68
61
|
return candidate;
|
|
69
62
|
}
|
|
70
63
|
}
|
|
@@ -73,91 +66,158 @@ function resolveImportFile(fromDir: string, importPath: string): string | null {
|
|
|
73
66
|
}
|
|
74
67
|
|
|
75
68
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* Handles three forms:
|
|
79
|
-
* - `createApp({ router: myVar })` → plain Hono, default /api mount
|
|
80
|
-
* - `createApp({ router: { path: '/v1', router: myVar } })` → single RouteMount
|
|
81
|
-
* - `createApp({ router: [{ path: '/v1', router: v1 }, ...] })` → array of RouteMounts
|
|
69
|
+
* A router mount extracted from the AST before file resolution.
|
|
82
70
|
*/
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (!callNode.arguments || callNode.arguments.length === 0) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
71
|
+
interface RawMount {
|
|
72
|
+
path: string;
|
|
73
|
+
varName: string;
|
|
74
|
+
}
|
|
90
75
|
|
|
91
|
-
|
|
92
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Extract router mounts from a createApp() call using TypeScript's AST.
|
|
78
|
+
* Returns null if no router property found.
|
|
79
|
+
*/
|
|
80
|
+
function extractRouterMounts(sourceFile: ts.SourceFile): RawMount[] | null {
|
|
81
|
+
let result: RawMount[] | null = null;
|
|
82
|
+
|
|
83
|
+
function getStringLiteral(node: ts.Expression): string | null {
|
|
84
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
85
|
+
return node.text;
|
|
86
|
+
}
|
|
93
87
|
return null;
|
|
94
88
|
}
|
|
95
89
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
p.type === 'Property' && p.key?.type === 'Identifier' && p.key?.name === 'router'
|
|
100
|
-
);
|
|
90
|
+
function extractMountFromObject(obj: ts.ObjectLiteralExpression): RawMount | null {
|
|
91
|
+
let path: string | undefined;
|
|
92
|
+
let varName: string | undefined;
|
|
101
93
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
94
|
+
for (const prop of obj.properties) {
|
|
95
|
+
if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue;
|
|
96
|
+
|
|
97
|
+
if (prop.name.text === 'path') {
|
|
98
|
+
path = getStringLiteral(prop.initializer) ?? undefined;
|
|
99
|
+
}
|
|
100
|
+
if (prop.name.text === 'router') {
|
|
101
|
+
if (ts.isIdentifier(prop.initializer)) {
|
|
102
|
+
varName = prop.initializer.text;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
// Also handle shorthand: { path: '/v1', router } where router is shorthand
|
|
108
|
+
for (const prop of obj.properties) {
|
|
109
|
+
if (ts.isShorthandPropertyAssignment(prop) && prop.name.text === 'router') {
|
|
110
|
+
varName = prop.name.text;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
107
113
|
|
|
108
|
-
|
|
109
|
-
if (routerValue.type === 'Identifier') {
|
|
110
|
-
return [{ path: '/api', varName: routerValue.name }];
|
|
114
|
+
return path && varName ? { path, varName } : null;
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
function processRouterValue(value: ts.Expression): RawMount[] | null {
|
|
118
|
+
// Form 1: Identifier → createApp({ router: myRouter })
|
|
119
|
+
if (ts.isIdentifier(value)) {
|
|
120
|
+
return [{ path: '/api', varName: value.text }];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Form 2: Object → createApp({ router: { path: '/v1', router: myRouter } })
|
|
124
|
+
if (ts.isObjectLiteralExpression(value)) {
|
|
125
|
+
const mount = extractMountFromObject(value);
|
|
126
|
+
return mount ? [mount] : null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Form 3: Array → createApp({ router: [...] })
|
|
130
|
+
if (ts.isArrayLiteralExpression(value)) {
|
|
131
|
+
const mounts: RawMount[] = [];
|
|
132
|
+
for (const element of value.elements) {
|
|
133
|
+
if (ts.isObjectLiteralExpression(element)) {
|
|
134
|
+
const mount = extractMountFromObject(element);
|
|
135
|
+
if (mount) mounts.push(mount);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return mounts.length > 0 ? mounts : null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return null;
|
|
117
142
|
}
|
|
118
143
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
144
|
+
function visit(node: ts.Node): void {
|
|
145
|
+
if (result) return;
|
|
146
|
+
|
|
147
|
+
// Find createApp(...) — with or without await
|
|
148
|
+
let callExpr: ts.CallExpression | undefined;
|
|
149
|
+
|
|
150
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
151
|
+
if (node.expression.text === 'createApp') callExpr = node;
|
|
152
|
+
} else if (ts.isAwaitExpression(node) && ts.isCallExpression(node.expression)) {
|
|
153
|
+
const call = node.expression;
|
|
154
|
+
if (ts.isIdentifier(call.expression) && call.expression.text === 'createApp') {
|
|
155
|
+
callExpr = call;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (callExpr && callExpr.arguments.length > 0) {
|
|
160
|
+
const configArg = callExpr.arguments[0];
|
|
161
|
+
if (configArg && ts.isObjectLiteralExpression(configArg)) {
|
|
162
|
+
for (const prop of configArg.properties) {
|
|
163
|
+
// Handle: router: value
|
|
164
|
+
if (
|
|
165
|
+
ts.isPropertyAssignment(prop) &&
|
|
166
|
+
ts.isIdentifier(prop.name) &&
|
|
167
|
+
prop.name.text === 'router'
|
|
168
|
+
) {
|
|
169
|
+
result = processRouterValue(prop.initializer);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Handle shorthand: createApp({ router })
|
|
174
|
+
if (ts.isShorthandPropertyAssignment(prop) && prop.name.text === 'router') {
|
|
175
|
+
result = [{ path: '/api', varName: 'router' }];
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
126
179
|
}
|
|
127
180
|
}
|
|
128
|
-
|
|
181
|
+
|
|
182
|
+
ts.forEachChild(node, visit);
|
|
129
183
|
}
|
|
130
184
|
|
|
131
|
-
|
|
185
|
+
visit(sourceFile);
|
|
186
|
+
return result;
|
|
132
187
|
}
|
|
133
188
|
|
|
134
189
|
/**
|
|
135
|
-
*
|
|
190
|
+
* Build import map from the source file: variable name → import path
|
|
136
191
|
*/
|
|
137
|
-
function
|
|
138
|
-
|
|
139
|
-
let varName: string | undefined;
|
|
192
|
+
function buildImportMap(sourceFile: ts.SourceFile): Map<string, string> {
|
|
193
|
+
const importMap = new Map<string, string>();
|
|
140
194
|
|
|
141
|
-
for (const
|
|
142
|
-
if (
|
|
195
|
+
for (const stmt of sourceFile.statements) {
|
|
196
|
+
if (!ts.isImportDeclaration(stmt) || !ts.isStringLiteral(stmt.moduleSpecifier)) continue;
|
|
143
197
|
|
|
144
|
-
|
|
145
|
-
|
|
198
|
+
const importPath = stmt.moduleSpecifier.text;
|
|
199
|
+
const clause = stmt.importClause;
|
|
200
|
+
if (!clause) continue;
|
|
201
|
+
|
|
202
|
+
// Default import: import router from './api'
|
|
203
|
+
if (clause.name) {
|
|
204
|
+
importMap.set(clause.name.text, importPath);
|
|
146
205
|
}
|
|
147
|
-
|
|
148
|
-
|
|
206
|
+
|
|
207
|
+
// Named imports: import { v1, v2 } from './routers'
|
|
208
|
+
if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
209
|
+
for (const spec of clause.namedBindings.elements) {
|
|
210
|
+
importMap.set(spec.name.text, importPath);
|
|
211
|
+
}
|
|
149
212
|
}
|
|
150
213
|
}
|
|
151
214
|
|
|
152
|
-
return
|
|
215
|
+
return importMap;
|
|
153
216
|
}
|
|
154
217
|
|
|
155
218
|
/**
|
|
156
219
|
* Detect whether `src/app.ts` uses `createApp({ router })`.
|
|
157
220
|
*
|
|
158
|
-
* Parses the file with acorn-loose, finds `createApp()` calls,
|
|
159
|
-
* and resolves router variables to their import source files.
|
|
160
|
-
*
|
|
161
221
|
* Returns `{ detected: false, mounts: [] }` when:
|
|
162
222
|
* - `src/app.ts` doesn't exist
|
|
163
223
|
* - `createApp()` is called without a `router` property
|
|
@@ -169,76 +229,57 @@ export async function detectExplicitRouter(
|
|
|
169
229
|
): Promise<AppRouterDetection> {
|
|
170
230
|
const noDetection: AppRouterDetection = { detected: false, mounts: [] };
|
|
171
231
|
|
|
172
|
-
// Look for app.ts in src/ (standard location)
|
|
173
|
-
|
|
174
|
-
if (!
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (!existsSync(rootAppFile)) {
|
|
232
|
+
// Look for app.ts in src/ (standard location), then root
|
|
233
|
+
let appFile = join(rootDir, 'src', 'app.ts');
|
|
234
|
+
if (!(await Bun.file(appFile).exists())) {
|
|
235
|
+
appFile = join(rootDir, 'app.ts');
|
|
236
|
+
if (!(await Bun.file(appFile).exists())) {
|
|
178
237
|
logger.trace('[router-detect] No app.ts found');
|
|
179
238
|
return noDetection;
|
|
180
239
|
}
|
|
181
|
-
return detectInFile(rootAppFile, logger);
|
|
182
240
|
}
|
|
183
241
|
|
|
184
|
-
return detectInFile(appFile, logger);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async function detectInFile(appFile: string, logger: Logger): Promise<AppRouterDetection> {
|
|
188
|
-
const noDetection: AppRouterDetection = { detected: false, mounts: [] };
|
|
189
|
-
const appDir = dirname(appFile);
|
|
190
|
-
|
|
191
242
|
try {
|
|
192
243
|
const source = await Bun.file(appFile).text();
|
|
193
|
-
const
|
|
194
|
-
const contents = transpiler.transformSync(source);
|
|
244
|
+
const appDir = dirname(appFile);
|
|
195
245
|
|
|
196
|
-
// Quick
|
|
197
|
-
if (!
|
|
246
|
+
// Quick bail-out before parsing
|
|
247
|
+
if (!source.includes('createApp') || !source.includes('router')) {
|
|
198
248
|
logger.trace('[router-detect] No createApp + router pattern found in %s', appFile);
|
|
199
249
|
return noDetection;
|
|
200
250
|
}
|
|
201
251
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
for (const node of ast.body || []) {
|
|
211
|
-
if (node.type === 'ImportDeclaration' && node.source?.value) {
|
|
212
|
-
for (const spec of node.specifiers || []) {
|
|
213
|
-
if (spec.local?.name) {
|
|
214
|
-
importMap.set(spec.local.name, String(node.source.value));
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Walk all statements looking for createApp() calls
|
|
221
|
-
const routerMounts = findCreateAppRouterCalls(ast, importMap);
|
|
252
|
+
// Parse with TypeScript
|
|
253
|
+
const sourceFile = ts.createSourceFile(
|
|
254
|
+
appFile,
|
|
255
|
+
source,
|
|
256
|
+
ts.ScriptTarget.Latest,
|
|
257
|
+
true,
|
|
258
|
+
ts.ScriptKind.TS
|
|
259
|
+
);
|
|
222
260
|
|
|
223
|
-
|
|
261
|
+
const rawMounts = extractRouterMounts(sourceFile);
|
|
262
|
+
if (!rawMounts || rawMounts.length === 0) {
|
|
224
263
|
logger.trace('[router-detect] createApp() found but no router property');
|
|
225
264
|
return noDetection;
|
|
226
265
|
}
|
|
227
266
|
|
|
267
|
+
// Build import map to resolve variable names to file paths
|
|
268
|
+
const importMap = buildImportMap(sourceFile);
|
|
269
|
+
|
|
228
270
|
// Resolve each router variable to its file
|
|
229
271
|
const mounts: DetectedRouteMount[] = [];
|
|
230
|
-
for (const { path, varName } of
|
|
272
|
+
for (const { path, varName } of rawMounts) {
|
|
231
273
|
const importPath = importMap.get(varName);
|
|
232
274
|
if (!importPath) {
|
|
233
275
|
logger.debug(
|
|
234
276
|
'[router-detect] Router variable %s is not imported — may be defined locally',
|
|
235
277
|
varName
|
|
236
278
|
);
|
|
237
|
-
// Could be defined in the same file — skip for now
|
|
238
279
|
continue;
|
|
239
280
|
}
|
|
240
281
|
|
|
241
|
-
const resolvedFile = resolveImportFile(appDir, importPath);
|
|
282
|
+
const resolvedFile = await resolveImportFile(appDir, importPath);
|
|
242
283
|
if (!resolvedFile) {
|
|
243
284
|
logger.warn(
|
|
244
285
|
'[router-detect] Could not resolve import %s for router variable %s',
|
|
@@ -277,74 +318,3 @@ async function detectInFile(appFile: string, logger: Logger): Promise<AppRouterD
|
|
|
277
318
|
return noDetection;
|
|
278
319
|
}
|
|
279
320
|
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Walk the AST looking for `createApp({ router: ... })` calls.
|
|
283
|
-
* Handles:
|
|
284
|
-
* - `createApp({ router })` (top-level expression)
|
|
285
|
-
* - `const app = await createApp({ router })` (variable declaration)
|
|
286
|
-
* - `export const app = await createApp({ router })` (exported)
|
|
287
|
-
*/
|
|
288
|
-
function findCreateAppRouterCalls(
|
|
289
|
-
ast: ASTNode,
|
|
290
|
-
importMap: Map<string, string>
|
|
291
|
-
): Array<{ path: string; varName: string }> | null {
|
|
292
|
-
for (const node of ast.body || []) {
|
|
293
|
-
// Check expression statements: createApp({ router })
|
|
294
|
-
if (node.type === 'ExpressionStatement') {
|
|
295
|
-
const result = checkForCreateAppCall(node.expression, importMap);
|
|
296
|
-
if (result) return result;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Check variable declarations: const app = await createApp({ router })
|
|
300
|
-
if (node.type === 'VariableDeclaration') {
|
|
301
|
-
for (const decl of node.declarations || []) {
|
|
302
|
-
if (decl.init) {
|
|
303
|
-
const result = checkForCreateAppCall(decl.init, importMap);
|
|
304
|
-
if (result) return result;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Check exports: export const app = await createApp({ router })
|
|
310
|
-
if (node.type === 'ExportNamedDeclaration' && node.declaration) {
|
|
311
|
-
if (node.declaration.type === 'VariableDeclaration') {
|
|
312
|
-
for (const decl of node.declaration.declarations || []) {
|
|
313
|
-
if (decl.init) {
|
|
314
|
-
const result = checkForCreateAppCall(decl.init, importMap);
|
|
315
|
-
if (result) return result;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
return null;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Check if an expression node is a `createApp({ router })` call.
|
|
327
|
-
* Unwraps `await` expressions.
|
|
328
|
-
*/
|
|
329
|
-
function checkForCreateAppCall(
|
|
330
|
-
expr: ASTNode,
|
|
331
|
-
importMap: Map<string, string>
|
|
332
|
-
): Array<{ path: string; varName: string }> | null {
|
|
333
|
-
if (!expr) return null;
|
|
334
|
-
|
|
335
|
-
// Unwrap AwaitExpression: await createApp(...)
|
|
336
|
-
if (expr.type === 'AwaitExpression' && expr.argument) {
|
|
337
|
-
return checkForCreateAppCall(expr.argument, importMap);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Check for createApp({ router })
|
|
341
|
-
if (
|
|
342
|
-
expr.type === 'CallExpression' &&
|
|
343
|
-
expr.callee?.type === 'Identifier' &&
|
|
344
|
-
expr.callee?.name === 'createApp'
|
|
345
|
-
) {
|
|
346
|
-
return extractRouterFromCreateApp(expr);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return null;
|
|
350
|
-
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time ID generation utilities.
|
|
3
|
+
*
|
|
4
|
+
* Pure hash functions for generating deterministic IDs.
|
|
5
|
+
* No AST parsing — just SHA1 hashing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function hashSHA1(...val: string[]): string {
|
|
9
|
+
const hasher = new Bun.CryptoHasher('sha1');
|
|
10
|
+
val.map((val) => hasher.update(val));
|
|
11
|
+
return hasher.digest().toHex();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate a deterministic deployment ID for devmode.
|
|
16
|
+
*/
|
|
17
|
+
export function getDevmodeDeploymentId(projectId: string, endpointId: string): string {
|
|
18
|
+
return `devmode_${hashSHA1(projectId, endpointId)}`;
|
|
19
|
+
}
|