proteum 2.0.0 → 2.1.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/AGENTS.md +13 -1
- package/README.md +375 -0
- package/agents/framework/AGENTS.md +917 -0
- package/agents/project/AGENTS.md +138 -0
- package/agents/{codex → project}/CODING_STYLE.md +3 -2
- package/agents/project/client/AGENTS.md +108 -0
- package/agents/{codex → project}/client/pages/AGENTS.md +8 -8
- package/agents/{codex → project}/server/routes/AGENTS.md +2 -1
- package/agents/project/server/services/AGENTS.md +170 -0
- package/agents/{codex → project}/tests/AGENTS.md +1 -0
- package/cli/app/config.ts +3 -2
- package/cli/app/index.ts +6 -66
- package/cli/bin.js +7 -2
- package/cli/commands/build.ts +94 -27
- package/cli/commands/check.ts +15 -1
- package/cli/commands/dev.ts +288 -132
- package/cli/commands/doctor.ts +108 -0
- package/cli/commands/explain.ts +226 -0
- package/cli/commands/init.ts +76 -70
- package/cli/commands/lint.ts +18 -1
- package/cli/commands/refresh.ts +16 -6
- package/cli/commands/typecheck.ts +14 -1
- package/cli/compiler/artifacts/controllers.ts +150 -0
- package/cli/compiler/artifacts/discovery.ts +132 -0
- package/cli/compiler/artifacts/manifest.ts +267 -0
- package/cli/compiler/artifacts/routing.ts +315 -0
- package/cli/compiler/artifacts/services.ts +480 -0
- package/cli/compiler/artifacts/shared.ts +12 -0
- package/cli/compiler/client/identite.ts +2 -1
- package/cli/compiler/client/index.ts +13 -3
- package/cli/compiler/common/controllers.ts +23 -28
- package/cli/compiler/common/files/style.ts +3 -4
- package/cli/compiler/common/generatedRouteModules.ts +333 -19
- package/cli/compiler/common/proteumManifest.ts +133 -0
- package/cli/compiler/index.ts +33 -896
- package/cli/compiler/server/index.ts +21 -4
- package/cli/context.ts +71 -0
- package/cli/index.ts +39 -181
- package/cli/presentation/commands.ts +208 -0
- package/cli/presentation/compileReporter.ts +65 -0
- package/cli/presentation/devSession.ts +70 -0
- package/cli/presentation/help.ts +193 -0
- package/cli/presentation/ink.ts +69 -0
- package/cli/presentation/layout.ts +83 -0
- package/cli/runtime/argv.ts +49 -0
- package/cli/runtime/command.ts +25 -0
- package/cli/runtime/commands.ts +221 -0
- package/cli/runtime/importEsm.ts +7 -0
- package/cli/runtime/verbose.ts +15 -0
- package/cli/utils/agents.ts +5 -4
- package/cli/utils/keyboard.ts +12 -6
- package/client/app/index.ts +0 -6
- package/client/services/router/index.tsx +1 -1
- package/client/services/router/response/index.tsx +2 -2
- package/common/dev/serverHotReload.ts +12 -0
- package/common/router/index.ts +3 -2
- package/common/router/layouts.ts +1 -1
- package/common/router/pageSetup.ts +1 -0
- package/package.json +10 -8
- package/prettier/router-registration-plugin.cjs +52 -0
- package/prettier.config.cjs +1 -0
- package/scripts/cleanup-generated-controllers.ts +2 -2
- package/scripts/fix-reference-app-typing.ts +2 -2
- package/scripts/format-router-registrations.ts +119 -0
- package/scripts/migrate-explicit-controllers-and-request.ts +423 -0
- package/scripts/refactor-server-controllers.ts +19 -18
- package/scripts/refactor-server-runtime-aliases.ts +1 -1
- package/server/app/commands.ts +309 -25
- package/server/app/container/config.ts +1 -1
- package/server/app/container/index.ts +2 -2
- package/server/app/controller/index.ts +13 -4
- package/server/app/index.ts +53 -37
- package/server/app/service/container.ts +26 -28
- package/server/app/service/index.ts +10 -20
- package/server/app.tsconfig.json +9 -2
- package/server/index.ts +32 -1
- package/server/services/auth/index.ts +234 -15
- package/server/services/auth/router/index.ts +39 -7
- package/server/services/auth/router/request.ts +40 -8
- package/server/services/disks/index.ts +1 -1
- package/server/services/prisma/Facet.ts +2 -2
- package/server/services/prisma/index.ts +22 -5
- package/server/services/prisma/mariadb.ts +47 -0
- package/server/services/router/http/index.ts +9 -1
- package/server/services/router/index.ts +10 -4
- package/server/services/router/response/index.ts +26 -6
- package/types/auth-check-rules.test.ts +51 -0
- package/types/controller-request-context.test.ts +55 -0
- package/types/service-config.test.ts +39 -0
- package/agents/codex/AGENTS.md +0 -95
- package/agents/codex/client/AGENTS.md +0 -102
- package/agents/codex/server/services/AGENTS.md +0 -137
- package/server/services/models.7z +0 -0
- /package/agents/{codex → project}/agents.md.zip +0 -0
|
@@ -2,14 +2,39 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import ts from 'typescript';
|
|
4
4
|
|
|
5
|
+
import { getRouteSetupOptionKey } from '../../../common/router/pageSetup';
|
|
5
6
|
import writeIfChanged from '../writeIfChanged';
|
|
6
7
|
|
|
7
8
|
type TRouteSide = 'client' | 'server';
|
|
8
9
|
type TRouteRuntime = 'client' | 'server';
|
|
10
|
+
export type TIndexedSourceLocation = { line: number; column: number };
|
|
11
|
+
export type TIndexedRouteTargetResolution = 'literal' | 'static-expression' | 'dynamic-expression';
|
|
9
12
|
|
|
10
13
|
type TImportedService = { importedName: string; localName: string };
|
|
11
14
|
|
|
12
|
-
type TRouteDefinition = {
|
|
15
|
+
type TRouteDefinition = {
|
|
16
|
+
args: ts.NodeArray<ts.Expression>;
|
|
17
|
+
methodName: string;
|
|
18
|
+
serviceLocalName: string;
|
|
19
|
+
callExpression: ts.CallExpression;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type TIndexedRouteDefinition = {
|
|
23
|
+
methodName: string;
|
|
24
|
+
serviceLocalName: string;
|
|
25
|
+
sourceLocation: TIndexedSourceLocation;
|
|
26
|
+
targetResolution: TIndexedRouteTargetResolution;
|
|
27
|
+
path?: string;
|
|
28
|
+
pathRaw?: string;
|
|
29
|
+
code?: number;
|
|
30
|
+
codeRaw?: string;
|
|
31
|
+
optionKeys: string[];
|
|
32
|
+
normalizedOptionKeys: string[];
|
|
33
|
+
invalidOptionKeys: string[];
|
|
34
|
+
reservedOptionKeys: string[];
|
|
35
|
+
optionsRaw?: string;
|
|
36
|
+
hasSetup: boolean;
|
|
37
|
+
};
|
|
13
38
|
|
|
14
39
|
type TGeneratedClientRouteModuleOptions = { chunkId: string; filepath: string };
|
|
15
40
|
|
|
@@ -39,6 +64,164 @@ const normalizeFilepath = (value: string) => path.resolve(value).replace(/\\/g,
|
|
|
39
64
|
const getNodeText = (sourceFile: ts.SourceFile, node: ts.Node) =>
|
|
40
65
|
sourceFile.text.slice(node.getStart(sourceFile), node.getEnd());
|
|
41
66
|
|
|
67
|
+
const getNodeLocation = (sourceFile: ts.SourceFile, node: ts.Node): TIndexedSourceLocation => {
|
|
68
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
69
|
+
|
|
70
|
+
return { line: line + 1, column: character + 1 };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const getLiteralStringValue = (node: ts.Expression) => {
|
|
74
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
|
|
75
|
+
return undefined;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const getLiteralNumberValue = (node: ts.Expression) => {
|
|
79
|
+
if (!ts.isNumericLiteral(node)) return undefined;
|
|
80
|
+
|
|
81
|
+
const value = Number(node.text);
|
|
82
|
+
|
|
83
|
+
return Number.isFinite(value) ? value : undefined;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const getObjectLiteralPropertyKey = (name: ts.PropertyName) => {
|
|
87
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text;
|
|
88
|
+
if (ts.isComputedPropertyName(name)) return undefined;
|
|
89
|
+
return undefined;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getObjectLiteralPropertyKeys = (node: ts.ObjectLiteralExpression) =>
|
|
93
|
+
node.properties.flatMap((property) => {
|
|
94
|
+
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property)) {
|
|
95
|
+
const key = getObjectLiteralPropertyKey(property.name);
|
|
96
|
+
return key ? [key] : [];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
|
|
100
|
+
const key = getObjectLiteralPropertyKey(property.name);
|
|
101
|
+
return key ? [key] : [];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return [];
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const tryEvaluateStaticExpression = (
|
|
108
|
+
node: ts.Expression,
|
|
109
|
+
bindingInitializers: Map<string, ts.Expression>,
|
|
110
|
+
resolvedBindings: Map<string, string | number | undefined>,
|
|
111
|
+
activeBindings = new Set<string>(),
|
|
112
|
+
): string | number | undefined => {
|
|
113
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
|
|
114
|
+
|
|
115
|
+
if (ts.isNumericLiteral(node)) {
|
|
116
|
+
const value = Number(node.text);
|
|
117
|
+
return Number.isFinite(value) ? value : undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (ts.isParenthesizedExpression(node)) {
|
|
121
|
+
return tryEvaluateStaticExpression(node.expression, bindingInitializers, resolvedBindings, activeBindings);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (ts.isIdentifier(node)) {
|
|
125
|
+
if (resolvedBindings.has(node.text)) return resolvedBindings.get(node.text);
|
|
126
|
+
|
|
127
|
+
const initializer = bindingInitializers.get(node.text);
|
|
128
|
+
if (!initializer || activeBindings.has(node.text)) return undefined;
|
|
129
|
+
|
|
130
|
+
activeBindings.add(node.text);
|
|
131
|
+
const value = tryEvaluateStaticExpression(initializer, bindingInitializers, resolvedBindings, activeBindings);
|
|
132
|
+
activeBindings.delete(node.text);
|
|
133
|
+
resolvedBindings.set(node.text, value);
|
|
134
|
+
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken) {
|
|
139
|
+
const operand = tryEvaluateStaticExpression(node.operand, bindingInitializers, resolvedBindings, activeBindings);
|
|
140
|
+
return typeof operand === 'number' ? -operand : undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
144
|
+
const left = tryEvaluateStaticExpression(node.left, bindingInitializers, resolvedBindings, activeBindings);
|
|
145
|
+
const right = tryEvaluateStaticExpression(node.right, bindingInitializers, resolvedBindings, activeBindings);
|
|
146
|
+
|
|
147
|
+
if (left === undefined || right === undefined) return undefined;
|
|
148
|
+
|
|
149
|
+
if (typeof left === 'string' || typeof right === 'string') return String(left) + String(right);
|
|
150
|
+
if (typeof left === 'number' && typeof right === 'number') return left + right;
|
|
151
|
+
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (ts.isTemplateExpression(node)) {
|
|
156
|
+
let output = node.head.text;
|
|
157
|
+
|
|
158
|
+
for (const span of node.templateSpans) {
|
|
159
|
+
const value = tryEvaluateStaticExpression(span.expression, bindingInitializers, resolvedBindings, activeBindings);
|
|
160
|
+
if (value === undefined) return undefined;
|
|
161
|
+
|
|
162
|
+
output += String(value) + span.literal.text;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return output;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return undefined;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const collectStaticBindings = (sourceFile: ts.SourceFile) => {
|
|
172
|
+
const bindingInitializers = new Map<string, ts.Expression>();
|
|
173
|
+
const resolvedBindings = new Map<string, string | number | undefined>();
|
|
174
|
+
|
|
175
|
+
for (const statement of sourceFile.statements) {
|
|
176
|
+
if (!ts.isVariableStatement(statement)) continue;
|
|
177
|
+
if (!(statement.declarationList.flags & ts.NodeFlags.Const)) continue;
|
|
178
|
+
|
|
179
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
180
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
181
|
+
|
|
182
|
+
bindingInitializers.set(declaration.name.text, declaration.initializer);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for (const bindingName of bindingInitializers.keys()) {
|
|
187
|
+
if (resolvedBindings.has(bindingName)) continue;
|
|
188
|
+
|
|
189
|
+
const initializer = bindingInitializers.get(bindingName);
|
|
190
|
+
if (!initializer) continue;
|
|
191
|
+
|
|
192
|
+
resolvedBindings.set(
|
|
193
|
+
bindingName,
|
|
194
|
+
tryEvaluateStaticExpression(initializer, bindingInitializers, resolvedBindings, new Set([bindingName])),
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return resolvedBindings;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const getRouteOptionMetadata = (node: ts.ObjectLiteralExpression | undefined) => {
|
|
202
|
+
const optionKeys = node ? getObjectLiteralPropertyKeys(node) : [];
|
|
203
|
+
const normalizedOptionKeys: string[] = [];
|
|
204
|
+
const invalidOptionKeys: string[] = [];
|
|
205
|
+
const reservedOptionKeys: string[] = [];
|
|
206
|
+
|
|
207
|
+
for (const optionKey of optionKeys) {
|
|
208
|
+
try {
|
|
209
|
+
const normalizedOptionKey = getRouteSetupOptionKey(optionKey);
|
|
210
|
+
|
|
211
|
+
if (normalizedOptionKey) {
|
|
212
|
+
normalizedOptionKeys.push(normalizedOptionKey);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
invalidOptionKeys.push(optionKey);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
reservedOptionKeys.push(optionKey);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { optionKeys, normalizedOptionKeys, invalidOptionKeys, reservedOptionKeys };
|
|
223
|
+
};
|
|
224
|
+
|
|
42
225
|
const routeModuleExtensions = ['.ts', '.tsx', '.js', '.jsx'];
|
|
43
226
|
|
|
44
227
|
const resolveRouteImport = (sourceFilepath: string, moduleSpecifier: string, routeSourceFilepaths?: Set<string>) => {
|
|
@@ -151,6 +334,7 @@ const collectRouteDefinitions = (
|
|
|
151
334
|
args: statement.expression.arguments,
|
|
152
335
|
methodName: callee.name.text,
|
|
153
336
|
serviceLocalName: callee.expression.text,
|
|
337
|
+
callExpression: statement.expression,
|
|
154
338
|
});
|
|
155
339
|
|
|
156
340
|
stripRanges.push({ start: statement.getStart(sourceFile), end: statement.getEnd() });
|
|
@@ -248,35 +432,28 @@ const buildDestructuring = (importedServices: TImportedService[]) => {
|
|
|
248
432
|
return `const { ${parts.join(', ')} } = app;`;
|
|
249
433
|
};
|
|
250
434
|
|
|
251
|
-
const
|
|
252
|
-
sourceFile: ts.SourceFile,
|
|
253
|
-
definition: TRouteDefinition,
|
|
254
|
-
clientRoute: TGeneratedClientRouteModuleOptions,
|
|
255
|
-
) => {
|
|
435
|
+
const getClientRouteSignature = (sourceFile: ts.SourceFile, definition: TRouteDefinition) => {
|
|
256
436
|
const [, ...routeArgs] = [...definition.args];
|
|
257
|
-
const injectedOptions = `{ id: ${JSON.stringify(clientRoute.chunkId)}, filepath: ${JSON.stringify(clientRoute.filepath)} }`;
|
|
258
437
|
|
|
259
438
|
if (routeArgs.length === 1) {
|
|
260
|
-
return
|
|
439
|
+
return { hasSetup: false, renderArg: routeArgs[0] };
|
|
261
440
|
}
|
|
262
441
|
|
|
263
442
|
if (routeArgs.length === 2) {
|
|
264
443
|
if (ts.isObjectLiteralExpression(routeArgs[0])) {
|
|
265
|
-
return [
|
|
266
|
-
`{ ...(${getNodeText(sourceFile, routeArgs[0])}), id: ${JSON.stringify(clientRoute.chunkId)}, filepath: ${JSON.stringify(clientRoute.filepath)} }`,
|
|
267
|
-
getNodeText(sourceFile, routeArgs[1]),
|
|
268
|
-
];
|
|
444
|
+
return { hasSetup: false, optionsArg: routeArgs[0], renderArg: routeArgs[1] };
|
|
269
445
|
}
|
|
270
446
|
|
|
271
|
-
return
|
|
447
|
+
return { hasSetup: true, setupArg: routeArgs[0], renderArg: routeArgs[1] };
|
|
272
448
|
}
|
|
273
449
|
|
|
274
450
|
if (routeArgs.length === 3 && ts.isObjectLiteralExpression(routeArgs[0])) {
|
|
275
|
-
return
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
451
|
+
return {
|
|
452
|
+
hasSetup: true,
|
|
453
|
+
optionsArg: routeArgs[0],
|
|
454
|
+
setupArg: routeArgs[1],
|
|
455
|
+
renderArg: routeArgs[2],
|
|
456
|
+
};
|
|
280
457
|
}
|
|
281
458
|
|
|
282
459
|
throw new Error(
|
|
@@ -284,6 +461,36 @@ const buildClientRegisterArgs = (
|
|
|
284
461
|
);
|
|
285
462
|
};
|
|
286
463
|
|
|
464
|
+
const buildClientRegisterArgs = (
|
|
465
|
+
sourceFile: ts.SourceFile,
|
|
466
|
+
definition: TRouteDefinition,
|
|
467
|
+
clientRoute: TGeneratedClientRouteModuleOptions,
|
|
468
|
+
) => {
|
|
469
|
+
const { optionsArg, setupArg, renderArg } = getClientRouteSignature(sourceFile, definition);
|
|
470
|
+
const injectedOptions = `{ id: ${JSON.stringify(clientRoute.chunkId)}, filepath: ${JSON.stringify(clientRoute.filepath)} }`;
|
|
471
|
+
|
|
472
|
+
if (!optionsArg && !setupArg) {
|
|
473
|
+
return [injectedOptions, getNodeText(sourceFile, renderArg)];
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (optionsArg && !setupArg) {
|
|
477
|
+
return [
|
|
478
|
+
`{ ...(${getNodeText(sourceFile, optionsArg)}), id: ${JSON.stringify(clientRoute.chunkId)}, filepath: ${JSON.stringify(clientRoute.filepath)} }`,
|
|
479
|
+
getNodeText(sourceFile, renderArg),
|
|
480
|
+
];
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (!optionsArg && setupArg) {
|
|
484
|
+
return [injectedOptions, getNodeText(sourceFile, setupArg), getNodeText(sourceFile, renderArg)];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return [
|
|
488
|
+
`{ ...(${getNodeText(sourceFile, optionsArg!)}), id: ${JSON.stringify(clientRoute.chunkId)}, filepath: ${JSON.stringify(clientRoute.filepath)} }`,
|
|
489
|
+
getNodeText(sourceFile, setupArg!),
|
|
490
|
+
getNodeText(sourceFile, renderArg),
|
|
491
|
+
];
|
|
492
|
+
};
|
|
493
|
+
|
|
287
494
|
const buildRegisterStatements = (
|
|
288
495
|
sourceFile: ts.SourceFile,
|
|
289
496
|
side: TRouteSide,
|
|
@@ -321,6 +528,113 @@ const buildRegisterStatements = (
|
|
|
321
528
|
export const getGeneratedRouteModuleFilepath = (generatedRoot: string, sourceRoot: string, sourceFilepath: string) =>
|
|
322
529
|
path.join(generatedRoot, 'route-modules', path.relative(sourceRoot, sourceFilepath));
|
|
323
530
|
|
|
531
|
+
export const indexRouteDefinitions = ({ side, sourceFilepath }: { side: TRouteSide; sourceFilepath: string }) => {
|
|
532
|
+
const code = fs.readFileSync(sourceFilepath, 'utf8');
|
|
533
|
+
const sourceFile = parseSourceFile(sourceFilepath, code);
|
|
534
|
+
const stripRanges: Array<{ start: number; end: number }> = [];
|
|
535
|
+
const importedServices = collectImportedServices(sourceFile, side, stripRanges);
|
|
536
|
+
const definitions = collectRouteDefinitions(sourceFile, importedServices, stripRanges);
|
|
537
|
+
const staticBindings = collectStaticBindings(sourceFile);
|
|
538
|
+
|
|
539
|
+
if (definitions.length === 0) {
|
|
540
|
+
throw new Error(`No route definitions were found in ${sourceFilepath}.`);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (side === 'client' && definitions.length !== 1) {
|
|
544
|
+
throw new Error(
|
|
545
|
+
`Frontend route definition files can contain only one route definition. ${definitions.length} were found in ${sourceFilepath}.`,
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return definitions.map<TIndexedRouteDefinition>((definition) => {
|
|
550
|
+
const sourceLocation = getNodeLocation(sourceFile, definition.callExpression);
|
|
551
|
+
const resolveStaticValue = (node: ts.Expression) => tryEvaluateStaticExpression(node, new Map(), staticBindings);
|
|
552
|
+
|
|
553
|
+
if (side === 'client') {
|
|
554
|
+
const targetArg = definition.args[0];
|
|
555
|
+
const clientSignature = getClientRouteSignature(sourceFile, definition);
|
|
556
|
+
const optionMetadata = getRouteOptionMetadata(clientSignature.optionsArg);
|
|
557
|
+
const resolvedStaticValue = resolveStaticValue(targetArg);
|
|
558
|
+
|
|
559
|
+
return definition.methodName === 'error'
|
|
560
|
+
? {
|
|
561
|
+
methodName: definition.methodName,
|
|
562
|
+
serviceLocalName: definition.serviceLocalName,
|
|
563
|
+
sourceLocation,
|
|
564
|
+
targetResolution:
|
|
565
|
+
getLiteralNumberValue(targetArg) !== undefined
|
|
566
|
+
? 'literal'
|
|
567
|
+
: typeof resolvedStaticValue === 'number'
|
|
568
|
+
? 'static-expression'
|
|
569
|
+
: 'dynamic-expression',
|
|
570
|
+
code:
|
|
571
|
+
getLiteralNumberValue(targetArg) ??
|
|
572
|
+
(typeof resolvedStaticValue === 'number' ? resolvedStaticValue : undefined),
|
|
573
|
+
codeRaw: getNodeText(sourceFile, targetArg),
|
|
574
|
+
optionKeys: optionMetadata.optionKeys,
|
|
575
|
+
normalizedOptionKeys: optionMetadata.normalizedOptionKeys,
|
|
576
|
+
invalidOptionKeys: optionMetadata.invalidOptionKeys,
|
|
577
|
+
reservedOptionKeys: optionMetadata.reservedOptionKeys,
|
|
578
|
+
optionsRaw: clientSignature.optionsArg
|
|
579
|
+
? getNodeText(sourceFile, clientSignature.optionsArg)
|
|
580
|
+
: undefined,
|
|
581
|
+
hasSetup: clientSignature.hasSetup,
|
|
582
|
+
}
|
|
583
|
+
: {
|
|
584
|
+
methodName: definition.methodName,
|
|
585
|
+
serviceLocalName: definition.serviceLocalName,
|
|
586
|
+
sourceLocation,
|
|
587
|
+
targetResolution:
|
|
588
|
+
getLiteralStringValue(targetArg) !== undefined
|
|
589
|
+
? 'literal'
|
|
590
|
+
: typeof resolvedStaticValue === 'string'
|
|
591
|
+
? 'static-expression'
|
|
592
|
+
: 'dynamic-expression',
|
|
593
|
+
path:
|
|
594
|
+
getLiteralStringValue(targetArg) ??
|
|
595
|
+
(typeof resolvedStaticValue === 'string' ? resolvedStaticValue : undefined),
|
|
596
|
+
pathRaw: getNodeText(sourceFile, targetArg),
|
|
597
|
+
optionKeys: optionMetadata.optionKeys,
|
|
598
|
+
normalizedOptionKeys: optionMetadata.normalizedOptionKeys,
|
|
599
|
+
invalidOptionKeys: optionMetadata.invalidOptionKeys,
|
|
600
|
+
reservedOptionKeys: optionMetadata.reservedOptionKeys,
|
|
601
|
+
optionsRaw: clientSignature.optionsArg
|
|
602
|
+
? getNodeText(sourceFile, clientSignature.optionsArg)
|
|
603
|
+
: undefined,
|
|
604
|
+
hasSetup: clientSignature.hasSetup,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const targetArg = definition.args[0];
|
|
609
|
+
const optionsArg =
|
|
610
|
+
definition.args.length >= 3 && ts.isObjectLiteralExpression(definition.args[1])
|
|
611
|
+
? definition.args[1]
|
|
612
|
+
: undefined;
|
|
613
|
+
const optionMetadata = getRouteOptionMetadata(optionsArg);
|
|
614
|
+
const resolvedPath = getLiteralStringValue(targetArg) ?? resolveStaticValue(targetArg);
|
|
615
|
+
|
|
616
|
+
return {
|
|
617
|
+
methodName: definition.methodName,
|
|
618
|
+
serviceLocalName: definition.serviceLocalName,
|
|
619
|
+
sourceLocation,
|
|
620
|
+
targetResolution:
|
|
621
|
+
getLiteralStringValue(targetArg) !== undefined
|
|
622
|
+
? 'literal'
|
|
623
|
+
: typeof resolvedPath === 'string'
|
|
624
|
+
? 'static-expression'
|
|
625
|
+
: 'dynamic-expression',
|
|
626
|
+
path: typeof resolvedPath === 'string' ? resolvedPath : undefined,
|
|
627
|
+
pathRaw: getNodeText(sourceFile, targetArg),
|
|
628
|
+
optionKeys: optionMetadata.optionKeys,
|
|
629
|
+
normalizedOptionKeys: optionMetadata.normalizedOptionKeys,
|
|
630
|
+
invalidOptionKeys: optionMetadata.invalidOptionKeys,
|
|
631
|
+
reservedOptionKeys: optionMetadata.reservedOptionKeys,
|
|
632
|
+
optionsRaw: optionsArg ? getNodeText(sourceFile, optionsArg) : undefined,
|
|
633
|
+
hasSetup: false,
|
|
634
|
+
};
|
|
635
|
+
});
|
|
636
|
+
};
|
|
637
|
+
|
|
324
638
|
export const writeGeneratedRouteModule = ({
|
|
325
639
|
outputFilepath,
|
|
326
640
|
runtime,
|
|
@@ -346,7 +660,7 @@ export const writeGeneratedRouteModule = ({
|
|
|
346
660
|
sourceFilepath,
|
|
347
661
|
);
|
|
348
662
|
const registerStatements = buildRegisterStatements(sourceFile, side, definitions, clientRoute);
|
|
349
|
-
const runtimeAppImportPath = runtime === 'client' ? '@/client/index' : '@/server
|
|
663
|
+
const runtimeAppImportPath = runtime === 'client' ? '@/client/index' : '@/server/index';
|
|
350
664
|
|
|
351
665
|
const content = `/*----------------------------------
|
|
352
666
|
- GENERATED FILE
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
|
|
4
|
+
import writeIfChanged from '../writeIfChanged';
|
|
5
|
+
|
|
6
|
+
export type TProteumManifestScope = 'app' | 'framework';
|
|
7
|
+
export type TProteumManifestSourceLocation = { line: number; column: number };
|
|
8
|
+
export type TProteumManifestRouteTargetResolution = 'literal' | 'static-expression' | 'dynamic-expression';
|
|
9
|
+
export type TProteumManifestDiagnosticLevel = 'warning' | 'error';
|
|
10
|
+
|
|
11
|
+
export type TProteumManifestDiagnostic = {
|
|
12
|
+
level: TProteumManifestDiagnosticLevel;
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
filepath: string;
|
|
16
|
+
sourceLocation?: TProteumManifestSourceLocation;
|
|
17
|
+
relatedFilepaths?: string[];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type TProteumManifestService = {
|
|
21
|
+
kind: 'service' | 'ref';
|
|
22
|
+
id?: string;
|
|
23
|
+
registeredName: string;
|
|
24
|
+
metaName?: string;
|
|
25
|
+
parent: string;
|
|
26
|
+
priority: number;
|
|
27
|
+
importPath?: string;
|
|
28
|
+
sourceDir?: string;
|
|
29
|
+
metasFilepath?: string;
|
|
30
|
+
refTo?: string;
|
|
31
|
+
scope: TProteumManifestScope;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type TProteumManifestController = {
|
|
35
|
+
className: string;
|
|
36
|
+
importPath: string;
|
|
37
|
+
filepath: string;
|
|
38
|
+
sourceLocation: TProteumManifestSourceLocation;
|
|
39
|
+
routeBasePath: string;
|
|
40
|
+
methodName: string;
|
|
41
|
+
inputCallsCount: number;
|
|
42
|
+
hasInput: boolean;
|
|
43
|
+
routePath: string;
|
|
44
|
+
httpPath: string;
|
|
45
|
+
clientAccessor: string;
|
|
46
|
+
scope: TProteumManifestScope;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type TProteumManifestRoute = {
|
|
50
|
+
kind: 'client-page' | 'client-error' | 'server-route';
|
|
51
|
+
methodName: string;
|
|
52
|
+
serviceLocalName: string;
|
|
53
|
+
filepath: string;
|
|
54
|
+
sourceLocation: TProteumManifestSourceLocation;
|
|
55
|
+
targetResolution: TProteumManifestRouteTargetResolution;
|
|
56
|
+
path?: string;
|
|
57
|
+
pathRaw?: string;
|
|
58
|
+
code?: number;
|
|
59
|
+
codeRaw?: string;
|
|
60
|
+
optionKeys: string[];
|
|
61
|
+
normalizedOptionKeys: string[];
|
|
62
|
+
invalidOptionKeys: string[];
|
|
63
|
+
reservedOptionKeys: string[];
|
|
64
|
+
optionsRaw?: string;
|
|
65
|
+
hasSetup: boolean;
|
|
66
|
+
chunkId?: string;
|
|
67
|
+
chunkFilepath?: string;
|
|
68
|
+
scope: TProteumManifestScope;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type TProteumManifestLayout = {
|
|
72
|
+
chunkId: string;
|
|
73
|
+
filepath: string;
|
|
74
|
+
importPath: string;
|
|
75
|
+
depth: number;
|
|
76
|
+
scope: TProteumManifestScope;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type TProteumManifest = {
|
|
80
|
+
version: 1;
|
|
81
|
+
app: {
|
|
82
|
+
root: string;
|
|
83
|
+
coreRoot: string;
|
|
84
|
+
identityFilepath: string;
|
|
85
|
+
identity: {
|
|
86
|
+
name: string;
|
|
87
|
+
identifier: string;
|
|
88
|
+
description: string;
|
|
89
|
+
language?: string;
|
|
90
|
+
locale?: string;
|
|
91
|
+
title?: string;
|
|
92
|
+
titleSuffix?: string;
|
|
93
|
+
fullTitle?: string;
|
|
94
|
+
webDescription?: string;
|
|
95
|
+
version?: string;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
conventions: {
|
|
99
|
+
routeSetupOptionKeys: string[];
|
|
100
|
+
reservedRouteSetupKeys: string[];
|
|
101
|
+
};
|
|
102
|
+
env: {
|
|
103
|
+
sourceFilepath: string;
|
|
104
|
+
loadedTopLevelKeys: string[];
|
|
105
|
+
requiredTopLevelKeys: string[];
|
|
106
|
+
};
|
|
107
|
+
services: {
|
|
108
|
+
app: TProteumManifestService[];
|
|
109
|
+
routerPlugins: TProteumManifestService[];
|
|
110
|
+
};
|
|
111
|
+
controllers: TProteumManifestController[];
|
|
112
|
+
routes: {
|
|
113
|
+
client: TProteumManifestRoute[];
|
|
114
|
+
server: TProteumManifestRoute[];
|
|
115
|
+
};
|
|
116
|
+
layouts: TProteumManifestLayout[];
|
|
117
|
+
diagnostics: TProteumManifestDiagnostic[];
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const getProteumManifestPath = (appRoot: string) => path.join(appRoot, '.proteum', 'manifest.json');
|
|
121
|
+
|
|
122
|
+
export const writeProteumManifest = (appRoot: string, manifest: TProteumManifest) =>
|
|
123
|
+
writeIfChanged(getProteumManifestPath(appRoot), JSON.stringify(manifest, null, 2) + '\n');
|
|
124
|
+
|
|
125
|
+
export const readProteumManifest = (appRoot: string) => {
|
|
126
|
+
const filepath = getProteumManifestPath(appRoot);
|
|
127
|
+
|
|
128
|
+
if (!fs.existsSync(filepath)) {
|
|
129
|
+
throw new Error(`Proteum manifest not found at ${filepath}. Run a Proteum command that refreshes generated artifacts first.`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return fs.readJsonSync(filepath) as TProteumManifest;
|
|
133
|
+
};
|