proteum 2.0.0 → 2.1.0-2
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 +488 -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 +28 -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
package/cli/compiler/index.ts
CHANGED
|
@@ -5,51 +5,26 @@
|
|
|
5
5
|
// Npm
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import fs from 'fs-extra';
|
|
8
|
-
import serialize from 'serialize-javascript';
|
|
9
8
|
import { rspack, type Compiler as RspackCompiler } from '@rspack/core';
|
|
10
|
-
import ts from 'typescript';
|
|
11
9
|
|
|
12
10
|
// Core
|
|
13
11
|
import app from '../app';
|
|
14
|
-
import cli from '..';
|
|
15
12
|
import createServerConfig from './server';
|
|
16
13
|
import createClientConfig from './client';
|
|
17
14
|
import { TCompileMode, TCompileOutputTarget } from './common';
|
|
18
|
-
import {
|
|
19
|
-
indexControllers,
|
|
20
|
-
generateControllerClientTree,
|
|
21
|
-
printControllerTree,
|
|
22
|
-
type TControllerServiceRoot,
|
|
23
|
-
} from './common/controllers';
|
|
24
15
|
import { writeClientManifest } from './common/clientManifest';
|
|
25
|
-
import {
|
|
26
|
-
import
|
|
16
|
+
import { logVerbose } from '../runtime/verbose';
|
|
17
|
+
import { createCompileReporter, type TCompileReporter } from '../presentation/compileReporter';
|
|
18
|
+
import { generateRoutingArtifacts } from './artifacts/routing';
|
|
19
|
+
import { generateControllerArtifacts } from './artifacts/controllers';
|
|
20
|
+
import { generateServiceArtifacts } from './artifacts/services';
|
|
21
|
+
import { writeCurrentProteumManifest } from './artifacts/manifest';
|
|
22
|
+
import { normalizePath } from './artifacts/shared';
|
|
27
23
|
|
|
28
24
|
type TCompilerCallback = (compiler: RspackCompiler) => void;
|
|
29
25
|
|
|
30
|
-
type TServiceMetas = {
|
|
31
|
-
id: string;
|
|
32
|
-
name: string;
|
|
33
|
-
parent: string;
|
|
34
|
-
dependences: string;
|
|
35
|
-
importationPath: string;
|
|
36
|
-
priority: number;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
type TRegisteredService = {
|
|
40
|
-
id?: string;
|
|
41
|
-
name: string;
|
|
42
|
-
className: string;
|
|
43
|
-
instanciation: (parentRef?: string, appRef?: string) => string;
|
|
44
|
-
priority: number;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
type TClientRouteLoader = { filepath: string; chunkId: string; preload: boolean };
|
|
48
|
-
|
|
49
26
|
type TRecentCompilationResult = { succeeded: boolean; hash?: string; modifiedFiles?: string[] };
|
|
50
27
|
|
|
51
|
-
const normalizePath = (value: string) => value.replace(/\\/g, '/');
|
|
52
|
-
|
|
53
28
|
/*----------------------------------
|
|
54
29
|
- FONCTION
|
|
55
30
|
----------------------------------*/
|
|
@@ -58,6 +33,7 @@ export default class Compiler {
|
|
|
58
33
|
private recentCompilationResults: { [compiler: string]: TRecentCompilationResult } = {};
|
|
59
34
|
private recentModifiedFiles: { [compiler: string]: string[] } = {};
|
|
60
35
|
private refreshingGeneratedArtifacts?: Promise<void>;
|
|
36
|
+
private compileReporter?: TCompileReporter;
|
|
61
37
|
|
|
62
38
|
public constructor(
|
|
63
39
|
private mode: TCompileMode,
|
|
@@ -86,9 +62,9 @@ export default class Compiler {
|
|
|
86
62
|
public fixNpmLinkIssues() {
|
|
87
63
|
const corePath = path.join(app.paths.root, '/node_modules/proteum');
|
|
88
64
|
if (!fs.lstatSync(corePath).isSymbolicLink())
|
|
89
|
-
return
|
|
65
|
+
return logVerbose("Not fixing npm issue because proteum wasn't installed with npm link.");
|
|
90
66
|
|
|
91
|
-
this.debug &&
|
|
67
|
+
this.debug && logVerbose(`Fix NPM link issues ...`);
|
|
92
68
|
const outputPath = app.outputPath(this.outputTarget);
|
|
93
69
|
|
|
94
70
|
const appModules = path.join(app.paths.root, 'node_modules');
|
|
@@ -155,858 +131,6 @@ export default class Compiler {
|
|
|
155
131
|
fs.symlinkSync(targetPath, linkPath);
|
|
156
132
|
}
|
|
157
133
|
|
|
158
|
-
private findServices(dir: string) {
|
|
159
|
-
const blacklist = ['node_modules', 'proteum'];
|
|
160
|
-
const files: string[] = [];
|
|
161
|
-
const dirents = fs.readdirSync(dir, { withFileTypes: true });
|
|
162
|
-
|
|
163
|
-
for (let dirent of dirents) {
|
|
164
|
-
let fileName = dirent.name;
|
|
165
|
-
let filePath = path.resolve(dir, fileName);
|
|
166
|
-
|
|
167
|
-
if (blacklist.includes(fileName)) continue;
|
|
168
|
-
|
|
169
|
-
// Define is we should recursively find service in the current item
|
|
170
|
-
let iterate: boolean = false;
|
|
171
|
-
if (dirent.isSymbolicLink()) {
|
|
172
|
-
const realPath = path.resolve(dir, fs.readlinkSync(filePath));
|
|
173
|
-
const destinationInfos = fs.lstatSync(realPath);
|
|
174
|
-
if (destinationInfos.isDirectory()) iterate = true;
|
|
175
|
-
} else if (dirent.isDirectory()) iterate = true;
|
|
176
|
-
|
|
177
|
-
// Update the list of found services
|
|
178
|
-
if (iterate) {
|
|
179
|
-
files.push(...this.findServices(filePath));
|
|
180
|
-
} else if (dirent.name === 'service.json') {
|
|
181
|
-
files.push(path.dirname(filePath));
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return files;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
private findClientRouteFiles(dir: string): string[] {
|
|
188
|
-
return this.findRegisteredRouteFiles(dir, { excludeLayoutDirectories: true });
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
private findServerRouteFiles(dir: string): string[] {
|
|
192
|
-
return this.findRegisteredRouteFiles(dir);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
private findRegisteredRouteFiles(dir: string, options: { excludeLayoutDirectories?: boolean } = {}): string[] {
|
|
196
|
-
if (!fs.existsSync(dir)) return [];
|
|
197
|
-
|
|
198
|
-
const files: string[] = [];
|
|
199
|
-
|
|
200
|
-
for (const dirent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
201
|
-
const filePath = path.join(dir, dirent.name);
|
|
202
|
-
|
|
203
|
-
if (dirent.isDirectory()) {
|
|
204
|
-
if (options.excludeLayoutDirectories && dirent.name === '_layout') continue;
|
|
205
|
-
|
|
206
|
-
files.push(...this.findRegisteredRouteFiles(filePath, options));
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (!dirent.isFile()) continue;
|
|
211
|
-
|
|
212
|
-
if (!/\.(ts|tsx)$/.test(dirent.name)) continue;
|
|
213
|
-
|
|
214
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
215
|
-
if (!this.hasRegisteredRouteDefinitions(filePath, content)) continue;
|
|
216
|
-
|
|
217
|
-
files.push(filePath);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return files;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
private hasRegisteredRouteDefinitions(filepath: string, content: string) {
|
|
224
|
-
const sourceFile = ts.createSourceFile(
|
|
225
|
-
filepath,
|
|
226
|
-
content,
|
|
227
|
-
ts.ScriptTarget.Latest,
|
|
228
|
-
true,
|
|
229
|
-
filepath.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
return sourceFile.statements.some((statement) => {
|
|
233
|
-
if (!ts.isExpressionStatement(statement)) return false;
|
|
234
|
-
if (!ts.isCallExpression(statement.expression)) return false;
|
|
235
|
-
if (!ts.isPropertyAccessExpression(statement.expression.expression)) return false;
|
|
236
|
-
|
|
237
|
-
const callee = statement.expression.expression;
|
|
238
|
-
|
|
239
|
-
return (
|
|
240
|
-
ts.isIdentifier(callee.expression) &&
|
|
241
|
-
callee.expression.text === 'Router' &&
|
|
242
|
-
['page', 'error', 'get', 'post', 'put', 'delete', 'patch'].includes(callee.name.text)
|
|
243
|
-
);
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
private findLayoutFiles(dir: string): string[] {
|
|
248
|
-
if (!fs.existsSync(dir)) return [];
|
|
249
|
-
|
|
250
|
-
const files: string[] = [];
|
|
251
|
-
|
|
252
|
-
for (const dirent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
253
|
-
const filePath = path.join(dir, dirent.name);
|
|
254
|
-
|
|
255
|
-
if (dirent.isDirectory()) {
|
|
256
|
-
files.push(...this.findLayoutFiles(filePath));
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (!dirent.isFile()) continue;
|
|
261
|
-
|
|
262
|
-
if (dirent.name !== 'index.tsx') continue;
|
|
263
|
-
|
|
264
|
-
if (!normalizePath(filePath).includes('/_layout/')) continue;
|
|
265
|
-
|
|
266
|
-
files.push(filePath);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return files;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
private getGeneratedImportPath(fromDir: string, targetFile: string) {
|
|
273
|
-
const relativeImportPath = path.relative(fromDir, targetFile).replace(/\\/g, '/');
|
|
274
|
-
const normalizedImportPath = relativeImportPath.startsWith('.') ? relativeImportPath : './' + relativeImportPath;
|
|
275
|
-
|
|
276
|
-
return normalizedImportPath.replace(/\.(ts|tsx|js|jsx)$/, '');
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
private cleanupObsoleteGeneratedArtifacts() {
|
|
280
|
-
fs.removeSync(path.join(app.paths.client.generated, 'index.ts'));
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private readPreloadedRouteChunks() {
|
|
284
|
-
const preloadPath = path.join(app.paths.pages, 'preload.json');
|
|
285
|
-
|
|
286
|
-
if (!fs.existsSync(preloadPath)) return new Set<string>();
|
|
287
|
-
|
|
288
|
-
const content = fs.readJsonSync(preloadPath);
|
|
289
|
-
|
|
290
|
-
if (!Array.isArray(content))
|
|
291
|
-
throw new Error(`Invalid client/pages/preload.json format: expected an array of chunk ids.`);
|
|
292
|
-
|
|
293
|
-
return new Set<string>(content.filter((value): value is string => typeof value === 'string'));
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
private getGeneratedClientRouteModuleFilepath(filepath: string) {
|
|
297
|
-
return getGeneratedRouteModuleFilepath(app.paths.client.generated, app.paths.pages, filepath);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
private getGeneratedServerRouteModuleFilepath(filepath: string) {
|
|
301
|
-
return getGeneratedRouteModuleFilepath(app.paths.server.generated, app.paths.root, filepath);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
private generateClientRouteWrapperModules() {
|
|
305
|
-
const clientRouteFiles = this.findClientRouteFiles(app.paths.pages).sort((a, b) => a.localeCompare(b));
|
|
306
|
-
const routeSourceFilepaths = new Set(clientRouteFiles.map((filepath) => normalizePath(path.resolve(filepath))));
|
|
307
|
-
|
|
308
|
-
for (const filepath of clientRouteFiles) {
|
|
309
|
-
const pageChunk = cli.paths.getPageChunk(app, filepath);
|
|
310
|
-
|
|
311
|
-
writeGeneratedRouteModule({
|
|
312
|
-
outputFilepath: this.getGeneratedClientRouteModuleFilepath(filepath),
|
|
313
|
-
runtime: 'client',
|
|
314
|
-
side: 'client',
|
|
315
|
-
sourceFilepath: filepath,
|
|
316
|
-
clientRoute: { chunkId: pageChunk.chunkId, filepath: pageChunk.filepath },
|
|
317
|
-
routeSourceFilepaths,
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
writeGeneratedRouteModule({
|
|
321
|
-
outputFilepath: this.getGeneratedServerRouteModuleFilepath(filepath),
|
|
322
|
-
runtime: 'server',
|
|
323
|
-
side: 'client',
|
|
324
|
-
sourceFilepath: filepath,
|
|
325
|
-
clientRoute: { chunkId: pageChunk.chunkId, filepath: pageChunk.filepath },
|
|
326
|
-
routeSourceFilepaths,
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
private generateServerRouteWrapperModules() {
|
|
332
|
-
const serverRouteFiles = this.findServerRouteFiles(path.join(app.paths.root, 'server', 'routes')).sort((a, b) =>
|
|
333
|
-
a.localeCompare(b),
|
|
334
|
-
);
|
|
335
|
-
const routeSourceFilepaths = new Set(serverRouteFiles.map((filepath) => normalizePath(path.resolve(filepath))));
|
|
336
|
-
|
|
337
|
-
for (const filepath of serverRouteFiles) {
|
|
338
|
-
writeGeneratedRouteModule({
|
|
339
|
-
outputFilepath: this.getGeneratedServerRouteModuleFilepath(filepath),
|
|
340
|
-
runtime: 'server',
|
|
341
|
-
side: 'server',
|
|
342
|
-
sourceFilepath: filepath,
|
|
343
|
-
routeSourceFilepaths,
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
private generateClientRoutesModule() {
|
|
349
|
-
const routeLoadersFile = path.join(app.paths.client.generated, 'routes.ts');
|
|
350
|
-
const preloadedChunks = this.readPreloadedRouteChunks();
|
|
351
|
-
|
|
352
|
-
const routes = this.findClientRouteFiles(app.paths.pages)
|
|
353
|
-
.sort((a, b) => a.localeCompare(b))
|
|
354
|
-
.map<TClientRouteLoader>((filepath) => {
|
|
355
|
-
const { chunkId } = cli.paths.getPageChunk(app, filepath);
|
|
356
|
-
|
|
357
|
-
return { filepath, chunkId, preload: preloadedChunks.has(chunkId) };
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
const imports: string[] = [];
|
|
361
|
-
const routeEntries: string[] = [];
|
|
362
|
-
|
|
363
|
-
routes.forEach((route, index) => {
|
|
364
|
-
const normalizedImportPath = this.getGeneratedImportPath(
|
|
365
|
-
app.paths.client.generated,
|
|
366
|
-
this.getGeneratedClientRouteModuleFilepath(route.filepath),
|
|
367
|
-
);
|
|
368
|
-
|
|
369
|
-
if (route.preload) {
|
|
370
|
-
const localIdentifier = `preloadedRoute${index}`;
|
|
371
|
-
imports.push(
|
|
372
|
-
`import { __register as ${localIdentifier} } from ${JSON.stringify(normalizedImportPath)};`,
|
|
373
|
-
);
|
|
374
|
-
routeEntries.push(
|
|
375
|
-
` ${JSON.stringify(route.chunkId)}: () => Promise.resolve({ __register: ${localIdentifier} }),`,
|
|
376
|
-
);
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
routeEntries.push(
|
|
381
|
-
` ${JSON.stringify(route.chunkId)}: () => import(/* webpackChunkName: ${JSON.stringify(route.chunkId)} */ ${JSON.stringify(normalizedImportPath)}),`,
|
|
382
|
-
);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
const content = `/*----------------------------------
|
|
386
|
-
- GENERATED FILE
|
|
387
|
-
----------------------------------*/
|
|
388
|
-
|
|
389
|
-
// This file is generated by Proteum to avoid rebuilding the page loader map in Babel.
|
|
390
|
-
// Do not edit it manually.
|
|
391
|
-
|
|
392
|
-
${imports.join('\n')}
|
|
393
|
-
${imports.length ? '\n' : ''}const routes = {
|
|
394
|
-
${routeEntries.join('\n')}
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
export default routes;
|
|
398
|
-
`;
|
|
399
|
-
|
|
400
|
-
writeIfChanged(routeLoadersFile, content);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
private generateClientLayoutsModule() {
|
|
404
|
-
const layoutsFile = path.join(app.paths.client.generated, 'layouts.ts');
|
|
405
|
-
|
|
406
|
-
const layouts = this.findLayoutFiles(app.paths.pages)
|
|
407
|
-
.map((filepath) => {
|
|
408
|
-
const { chunkId } = cli.paths.getLayoutChunk(app, filepath);
|
|
409
|
-
const importPath = this.getGeneratedImportPath(app.paths.client.generated, filepath);
|
|
410
|
-
const relativePath = normalizePath(path.relative(app.paths.root, filepath));
|
|
411
|
-
const depth = relativePath.split('/').filter(Boolean).length;
|
|
412
|
-
|
|
413
|
-
return { filepath: relativePath, chunkId, depth, importPath };
|
|
414
|
-
})
|
|
415
|
-
.sort((a, b) => {
|
|
416
|
-
if (b.depth !== a.depth) return b.depth - a.depth;
|
|
417
|
-
return a.filepath.localeCompare(b.filepath);
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
const imports = layouts
|
|
421
|
-
.map((layout, index) => `import * as layoutModule${index} from ${JSON.stringify(layout.importPath)};`)
|
|
422
|
-
.join('\n');
|
|
423
|
-
|
|
424
|
-
const layoutEntries = layouts
|
|
425
|
-
.map((layout, index) => ` ${JSON.stringify(layout.chunkId)}: layoutModule${index},`)
|
|
426
|
-
.join('\n');
|
|
427
|
-
|
|
428
|
-
const orderedLayoutIds = layouts.map((layout) => ` ${JSON.stringify(layout.chunkId)},`).join('\n');
|
|
429
|
-
|
|
430
|
-
const content = `/*----------------------------------
|
|
431
|
-
- GENERATED FILE
|
|
432
|
-
----------------------------------*/
|
|
433
|
-
|
|
434
|
-
// This file is generated by Proteum from app layout files.
|
|
435
|
-
// Do not edit it manually.
|
|
436
|
-
|
|
437
|
-
${imports}
|
|
438
|
-
${imports ? '\n' : ''}const layouts = {
|
|
439
|
-
${layoutEntries}
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
export const layoutOrder = [
|
|
443
|
-
${orderedLayoutIds}
|
|
444
|
-
];
|
|
445
|
-
|
|
446
|
-
export default layouts;
|
|
447
|
-
`;
|
|
448
|
-
|
|
449
|
-
writeIfChanged(layoutsFile, content);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
private generateServerRoutesModule() {
|
|
453
|
-
const routeModulesFile = path.join(app.paths.server.generated, 'routes.ts');
|
|
454
|
-
const serverRouteFiles = this.findServerRouteFiles(path.join(app.paths.root, 'server', 'routes'))
|
|
455
|
-
.sort((a, b) => a.localeCompare(b))
|
|
456
|
-
.map((filepath) => ({
|
|
457
|
-
filepath: normalizePath(path.relative(app.paths.root, filepath)),
|
|
458
|
-
importPath: this.getGeneratedImportPath(
|
|
459
|
-
app.paths.server.generated,
|
|
460
|
-
this.getGeneratedServerRouteModuleFilepath(filepath),
|
|
461
|
-
),
|
|
462
|
-
}));
|
|
463
|
-
|
|
464
|
-
const pageRouteFiles = this.findClientRouteFiles(app.paths.pages)
|
|
465
|
-
.sort((a, b) => a.localeCompare(b))
|
|
466
|
-
.map((filepath) => ({
|
|
467
|
-
filepath: normalizePath(path.relative(app.paths.root, filepath)),
|
|
468
|
-
importPath: this.getGeneratedImportPath(
|
|
469
|
-
app.paths.server.generated,
|
|
470
|
-
this.getGeneratedServerRouteModuleFilepath(filepath),
|
|
471
|
-
),
|
|
472
|
-
}));
|
|
473
|
-
|
|
474
|
-
const routeModules = [...serverRouteFiles, ...pageRouteFiles];
|
|
475
|
-
|
|
476
|
-
const imports = routeModules
|
|
477
|
-
.map(
|
|
478
|
-
(routeModule, index) =>
|
|
479
|
-
`const routeModule${index} = require(${JSON.stringify(routeModule.importPath)});`,
|
|
480
|
-
)
|
|
481
|
-
.join('\n');
|
|
482
|
-
|
|
483
|
-
const routeEntries = routeModules
|
|
484
|
-
.map(
|
|
485
|
-
(routeModule, index) => ` {
|
|
486
|
-
filepath: ${JSON.stringify(routeModule.filepath)},
|
|
487
|
-
register: routeModule${index}.__register,
|
|
488
|
-
},`,
|
|
489
|
-
)
|
|
490
|
-
.join('\n');
|
|
491
|
-
|
|
492
|
-
const content = `/*----------------------------------
|
|
493
|
-
- GENERATED FILE
|
|
494
|
-
----------------------------------*/
|
|
495
|
-
|
|
496
|
-
// This file is generated by Proteum from route registration files.
|
|
497
|
-
// Do not edit it manually.
|
|
498
|
-
|
|
499
|
-
import type { TRouteModule } from "@common/router";
|
|
500
|
-
${imports ? '\n' + imports : ''}
|
|
501
|
-
|
|
502
|
-
export type TGeneratedRouteModule = {
|
|
503
|
-
filepath: string,
|
|
504
|
-
register?: TRouteModule["__register"],
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
const routeModules: TGeneratedRouteModule[] = [
|
|
508
|
-
${routeEntries}
|
|
509
|
-
];
|
|
510
|
-
|
|
511
|
-
export default routeModules;
|
|
512
|
-
`;
|
|
513
|
-
|
|
514
|
-
writeIfChanged(routeModulesFile, content);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
private generateRoutingModules() {
|
|
518
|
-
this.cleanupObsoleteGeneratedArtifacts();
|
|
519
|
-
this.generateClientRouteWrapperModules();
|
|
520
|
-
this.generateServerRouteWrapperModules();
|
|
521
|
-
this.generateServerRoutesModule();
|
|
522
|
-
this.generateClientRoutesModule();
|
|
523
|
-
this.generateClientLayoutsModule();
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
private indexControllers() {
|
|
527
|
-
const registeredServiceNamesById = new Map<string, string>(
|
|
528
|
-
Object.values(app.registered).flatMap((service: { id?: string; name?: string }) =>
|
|
529
|
-
service.id && service.name ? [[service.id, service.name]] : [],
|
|
530
|
-
),
|
|
531
|
-
);
|
|
532
|
-
|
|
533
|
-
const appControllerServiceRoots = this.findServices(path.join(app.paths.root, 'server', 'services'))
|
|
534
|
-
.map<TControllerServiceRoot | null>((serviceDir) => {
|
|
535
|
-
const metasFile = path.join(serviceDir, 'service.json');
|
|
536
|
-
const serviceMetas = fs.readJsonSync(metasFile) as { id?: string };
|
|
537
|
-
const alias = serviceMetas.id ? registeredServiceNamesById.get(serviceMetas.id) : undefined;
|
|
538
|
-
|
|
539
|
-
if (!alias) return null;
|
|
540
|
-
|
|
541
|
-
return { alias, dir: serviceDir };
|
|
542
|
-
})
|
|
543
|
-
.filter((serviceRoot): serviceRoot is TControllerServiceRoot => !!serviceRoot)
|
|
544
|
-
.sort((a, b) => b.dir.length - a.dir.length);
|
|
545
|
-
|
|
546
|
-
return indexControllers([
|
|
547
|
-
{ importPrefix: '@server/services/', root: path.join(cli.paths.core.root, 'server', 'services') },
|
|
548
|
-
{
|
|
549
|
-
importPrefix: '@/server/services/',
|
|
550
|
-
root: path.join(app.paths.root, 'server', 'services'),
|
|
551
|
-
serviceRoots: appControllerServiceRoots,
|
|
552
|
-
},
|
|
553
|
-
]);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
private generateControllerModules() {
|
|
557
|
-
const controllers = this.indexControllers();
|
|
558
|
-
const clientTree = generateControllerClientTree(controllers);
|
|
559
|
-
|
|
560
|
-
const getControllerLeafMeta = (leaf: string) => {
|
|
561
|
-
const meta = JSON.parse(leaf) as {
|
|
562
|
-
routePath: string;
|
|
563
|
-
importPath: string;
|
|
564
|
-
className: string;
|
|
565
|
-
methodName: string;
|
|
566
|
-
hasInput: boolean;
|
|
567
|
-
};
|
|
568
|
-
const controllerIndex = controllers.findIndex((controller) => controller.importPath === meta.importPath);
|
|
569
|
-
|
|
570
|
-
if (controllerIndex === -1) {
|
|
571
|
-
throw new Error(`Unable to find controller import ${meta.importPath} while generating controller types.`);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
return { ...meta, controllerIndex };
|
|
575
|
-
};
|
|
576
|
-
|
|
577
|
-
const runtimeLeaf = (leaf: string) => {
|
|
578
|
-
const meta = getControllerLeafMeta(leaf);
|
|
579
|
-
const resultType = `TControllerResult<Controller${meta.controllerIndex}, ${JSON.stringify(meta.methodName)}>`;
|
|
580
|
-
|
|
581
|
-
return meta.hasInput
|
|
582
|
-
? `(data) => api.createFetcher<${resultType}>('POST', ${JSON.stringify(meta.routePath)}, data)`
|
|
583
|
-
: `() => api.createFetcher<${resultType}>('POST', ${JSON.stringify(meta.routePath)})`;
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
const typeImports = controllers
|
|
587
|
-
.map((controller, index) => `import type Controller${index} from ${JSON.stringify(controller.importPath)};`)
|
|
588
|
-
.join('\n');
|
|
589
|
-
|
|
590
|
-
const typeLeaf = (leaf: string) => {
|
|
591
|
-
const meta = getControllerLeafMeta(leaf);
|
|
592
|
-
const fetcherType = `TControllerFetcher<Controller${meta.controllerIndex}, ${JSON.stringify(meta.methodName)}>`;
|
|
593
|
-
|
|
594
|
-
return meta.hasInput ? `(data: any) => ${fetcherType}` : `() => ${fetcherType}`;
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
const createControllersContent = `/*----------------------------------
|
|
598
|
-
- GENERATED FILE
|
|
599
|
-
----------------------------------*/
|
|
600
|
-
|
|
601
|
-
// This file is generated by Proteum from server controller files.
|
|
602
|
-
// Do not edit it manually.
|
|
603
|
-
|
|
604
|
-
import type ApiClient from '@common/router/request/api';
|
|
605
|
-
import type { TFetcher } from '@common/router/request/api';
|
|
606
|
-
${typeImports ? '\n' + typeImports : ''}
|
|
607
|
-
|
|
608
|
-
type TControllerResult<TController, TMethod extends keyof TController> =
|
|
609
|
-
TController[TMethod] extends (...args: any[]) => infer TResult ? Awaited<TResult> : never;
|
|
610
|
-
|
|
611
|
-
type TControllerFetcher<TController, TMethod extends keyof TController> = TFetcher<TControllerResult<TController, TMethod>>;
|
|
612
|
-
|
|
613
|
-
export type TControllers = ${printControllerTree(clientTree, typeLeaf)};
|
|
614
|
-
|
|
615
|
-
export const createControllers = (
|
|
616
|
-
api: Pick<ApiClient, 'createFetcher'>
|
|
617
|
-
): TControllers => (
|
|
618
|
-
${printControllerTree(clientTree, runtimeLeaf)}
|
|
619
|
-
);
|
|
620
|
-
|
|
621
|
-
export default createControllers;
|
|
622
|
-
`;
|
|
623
|
-
|
|
624
|
-
writeIfChanged(path.join(app.paths.common.generated, 'controllers.ts'), createControllersContent);
|
|
625
|
-
|
|
626
|
-
writeIfChanged(
|
|
627
|
-
path.join(app.paths.client.generated, 'controllers.ts'),
|
|
628
|
-
`export { createControllers, default } from '@/common/.generated/controllers';
|
|
629
|
-
export type { TControllers } from '@/common/.generated/controllers';
|
|
630
|
-
`,
|
|
631
|
-
);
|
|
632
|
-
|
|
633
|
-
const controllerImports = controllers
|
|
634
|
-
.map((controller, index) => `import Controller${index} from ${JSON.stringify(controller.importPath)};`)
|
|
635
|
-
.join('\n');
|
|
636
|
-
|
|
637
|
-
const controllerEntries = controllers.flatMap((controller, controllerIndex) =>
|
|
638
|
-
controller.methods.map(
|
|
639
|
-
(method) => ` {
|
|
640
|
-
path: ${JSON.stringify('/api/' + method.routePath)},
|
|
641
|
-
Controller: Controller${controllerIndex},
|
|
642
|
-
method: ${JSON.stringify(method.name)},
|
|
643
|
-
},`,
|
|
644
|
-
),
|
|
645
|
-
);
|
|
646
|
-
|
|
647
|
-
writeIfChanged(
|
|
648
|
-
path.join(app.paths.server.generated, 'controllers.ts'),
|
|
649
|
-
`/*----------------------------------
|
|
650
|
-
- GENERATED FILE
|
|
651
|
-
----------------------------------*/
|
|
652
|
-
|
|
653
|
-
// This file is generated by Proteum from server controller files.
|
|
654
|
-
// Do not edit it manually.
|
|
655
|
-
|
|
656
|
-
import type Controller from '@server/app/controller';
|
|
657
|
-
${controllerImports ? '\n' + controllerImports : ''}
|
|
658
|
-
|
|
659
|
-
export type TGeneratedControllerDefinition = {
|
|
660
|
-
path: string,
|
|
661
|
-
Controller: new (request: any) => Controller,
|
|
662
|
-
method: string,
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
const controllers: TGeneratedControllerDefinition[] = [
|
|
666
|
-
${controllerEntries.join('\n')}
|
|
667
|
-
];
|
|
668
|
-
|
|
669
|
-
export default controllers;
|
|
670
|
-
`,
|
|
671
|
-
);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
private indexServices() {
|
|
675
|
-
// Index services
|
|
676
|
-
const searchDirs = [
|
|
677
|
-
// The less priority is the first
|
|
678
|
-
{ path: '@server/services/', priority: -1, root: path.join(cli.paths.core.root, 'server', 'services') },
|
|
679
|
-
{ path: '@/server/services/', priority: 0, root: path.join(app.paths.root, 'server', 'services') },
|
|
680
|
-
// Temp disabled because compile issue on vercel
|
|
681
|
-
//'': path.join(app.paths.root, 'node_modules'),
|
|
682
|
-
];
|
|
683
|
-
|
|
684
|
-
// Generate app class file
|
|
685
|
-
const servicesAvailable: { [id: string]: TServiceMetas } = {};
|
|
686
|
-
for (const searchDir of searchDirs) {
|
|
687
|
-
const services = this.findServices(searchDir.root);
|
|
688
|
-
|
|
689
|
-
for (const serviceDir of services) {
|
|
690
|
-
const metasFile = path.join(serviceDir, 'service.json');
|
|
691
|
-
|
|
692
|
-
// The +1 is to remove the slash
|
|
693
|
-
const importationPath = searchDir.path + serviceDir.substring(searchDir.root.length + 1);
|
|
694
|
-
|
|
695
|
-
const serviceMetas = require(metasFile);
|
|
696
|
-
|
|
697
|
-
servicesAvailable[serviceMetas.id] = { importationPath, priority: searchDir.priority, ...serviceMetas };
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// Read app services
|
|
702
|
-
const imported: string[] = [];
|
|
703
|
-
const referencedNames: { [serviceId: string]: string } = {}; // ID to Name
|
|
704
|
-
let serviceImportIndex = 0;
|
|
705
|
-
|
|
706
|
-
const refService = (serviceName: string, serviceConfig: any, level: number = 0): TRegisteredService => {
|
|
707
|
-
if (serviceConfig.refTo !== undefined) {
|
|
708
|
-
const refTo = serviceConfig.refTo;
|
|
709
|
-
return {
|
|
710
|
-
name: serviceName,
|
|
711
|
-
className: serviceName,
|
|
712
|
-
instanciation: (_parentRef, appRef = 'this') => `${appRef}.${refTo}`,
|
|
713
|
-
priority: 0,
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
const serviceMetas = servicesAvailable[serviceConfig.id];
|
|
718
|
-
if (serviceMetas === undefined)
|
|
719
|
-
throw new Error(
|
|
720
|
-
`Service ${serviceConfig.id} not found. Referenced services: ${Object.keys(servicesAvailable).join('\n')}`,
|
|
721
|
-
);
|
|
722
|
-
|
|
723
|
-
const referencedName = referencedNames[serviceConfig.id];
|
|
724
|
-
if (referencedName !== undefined)
|
|
725
|
-
throw new Error(`Service ${serviceConfig.id} is already setup as ${referencedName}`);
|
|
726
|
-
|
|
727
|
-
// Generate index & typings
|
|
728
|
-
const importIdentifier = `${serviceMetas.name}Class${serviceImportIndex++}`;
|
|
729
|
-
imported.push(`import ${importIdentifier} from "${serviceMetas.importationPath}";`);
|
|
730
|
-
|
|
731
|
-
if (serviceConfig.name !== undefined) referencedNames[serviceConfig.id] = serviceConfig.name;
|
|
732
|
-
|
|
733
|
-
const processConfig = (config: any, level: number = 0, appRef: string = 'this') => {
|
|
734
|
-
let propsStr = '';
|
|
735
|
-
for (const key in config) {
|
|
736
|
-
const value = config[key];
|
|
737
|
-
|
|
738
|
-
if (!value || typeof value !== 'object')
|
|
739
|
-
propsStr += `"${key}":${serialize(value, { space: 4 })},\n`;
|
|
740
|
-
// Reference to a service
|
|
741
|
-
else if (value.type === 'service.setup' || value.type === 'service.ref')
|
|
742
|
-
// TODO: more reliable way to detect a service reference
|
|
743
|
-
propsStr += `${key}:` + refService(key, value, level + 1).instanciation(undefined, appRef) + ',\n';
|
|
744
|
-
// Recursion
|
|
745
|
-
else if (level <= 4 && !Array.isArray(value))
|
|
746
|
-
propsStr += `"${key}":` + processConfig(value, level + 1, appRef) + ',\n';
|
|
747
|
-
else propsStr += `"${key}":${serialize(value, { space: 4 })},\n`;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
return `{ ${propsStr} }`;
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
// Generate the service instance
|
|
754
|
-
const instanciation = (parentRef?: string, appRef: string = 'this') => {
|
|
755
|
-
const config = processConfig(serviceConfig.config || {}, 0, appRef);
|
|
756
|
-
const typedRouterConfig =
|
|
757
|
-
serviceMetas.id === 'Core/Router' && parentRef
|
|
758
|
-
? `defineServiceConfig(${config} satisfies ConstructorParameters<typeof ${importIdentifier}>[1])`
|
|
759
|
-
: `defineServiceConfig(${config})`;
|
|
760
|
-
|
|
761
|
-
return `new ${importIdentifier}(
|
|
762
|
-
${parentRef ? `${parentRef},` : ''}
|
|
763
|
-
${typedRouterConfig},
|
|
764
|
-
${appRef}
|
|
765
|
-
)`;
|
|
766
|
-
};
|
|
767
|
-
|
|
768
|
-
return {
|
|
769
|
-
id: serviceConfig.id,
|
|
770
|
-
name: serviceName,
|
|
771
|
-
instanciation,
|
|
772
|
-
className: importIdentifier,
|
|
773
|
-
priority: serviceConfig.config?.priority || serviceMetas.priority || 0,
|
|
774
|
-
};
|
|
775
|
-
};
|
|
776
|
-
|
|
777
|
-
const servicesCode = Object.values(app.registered).map((s) => refService(s.name, s, 0));
|
|
778
|
-
const sortedServices = servicesCode.sort((a, b) => a.priority - b.priority);
|
|
779
|
-
|
|
780
|
-
// Define the app class identifier
|
|
781
|
-
const appClassIdentifier = app.identity.identifier;
|
|
782
|
-
const containerServices = app.containerServices.map((s) => "'" + s + "'").join('|');
|
|
783
|
-
const generatedFactories = sortedServices
|
|
784
|
-
.map((service) => {
|
|
785
|
-
const factoryIdentifier = `create${service.className}`;
|
|
786
|
-
const instanceIdentifier = `${service.className}Instance`;
|
|
787
|
-
|
|
788
|
-
return `const ${factoryIdentifier} = (app: ${appClassIdentifier}) => ${service.instanciation('app', 'app')};
|
|
789
|
-
|
|
790
|
-
type ${instanceIdentifier} = ReturnType<typeof ${factoryIdentifier}>;`;
|
|
791
|
-
})
|
|
792
|
-
.join('\n\n');
|
|
793
|
-
|
|
794
|
-
// @/client/.generated/services.d.ts
|
|
795
|
-
writeIfChanged(
|
|
796
|
-
path.join(app.paths.client.generated, 'services.d.ts'),
|
|
797
|
-
`declare type ${appClassIdentifier} = import("@/server/.generated/app").default;
|
|
798
|
-
|
|
799
|
-
declare module "@app" {
|
|
800
|
-
|
|
801
|
-
import { ${appClassIdentifier} as ${appClassIdentifier}Client } from "@/client";
|
|
802
|
-
import ${appClassIdentifier}Server from "@/server/.generated/app";
|
|
803
|
-
|
|
804
|
-
export const Router: ${appClassIdentifier}Client['Router'];
|
|
805
|
-
|
|
806
|
-
${sortedServices
|
|
807
|
-
.map((service) =>
|
|
808
|
-
service.name !== 'Router'
|
|
809
|
-
? `export const ${service.name}: ${appClassIdentifier}Server["${service.name}"];`
|
|
810
|
-
: '',
|
|
811
|
-
)
|
|
812
|
-
.join('\n')}
|
|
813
|
-
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
declare module '@models/types' {
|
|
817
|
-
export * from '@/var/prisma/index';
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
declare module '@common/errors' {
|
|
821
|
-
|
|
822
|
-
export * from '@common/errors/index';
|
|
823
|
-
export { default } from '@common/errors/index';
|
|
824
|
-
|
|
825
|
-
export const AuthRequired: typeof import('@common/errors/index').AuthRequired<FeatureKeys>;
|
|
826
|
-
export type AuthRequired = import('@common/errors/index').AuthRequired<FeatureKeys>;
|
|
827
|
-
|
|
828
|
-
export const UpgradeRequired: typeof import('@common/errors/index').UpgradeRequired<FeatureKeys>;
|
|
829
|
-
export type UpgradeRequired = import('@common/errors/index').UpgradeRequired<FeatureKeys>;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
declare namespace preact.JSX {
|
|
833
|
-
interface HTMLAttributes {
|
|
834
|
-
src?: string;
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
`,
|
|
838
|
-
);
|
|
839
|
-
|
|
840
|
-
// @/client/.generated/context.ts
|
|
841
|
-
writeIfChanged(
|
|
842
|
-
path.join(app.paths.client.generated, 'context.ts'),
|
|
843
|
-
`// TODO: move it into core (but how to make sure usecontext returns ${appClassIdentifier}'s context ?)
|
|
844
|
-
import React from 'react';
|
|
845
|
-
|
|
846
|
-
import type ${appClassIdentifier}Client from '@/client/index';
|
|
847
|
-
|
|
848
|
-
export type ClientContext = ${appClassIdentifier}Client["Router"]["context"];
|
|
849
|
-
|
|
850
|
-
export const ReactClientContext = React.createContext<ClientContext>({} as ClientContext);
|
|
851
|
-
export default (): ClientContext => React.useContext<ClientContext>(ReactClientContext);`,
|
|
852
|
-
);
|
|
853
|
-
|
|
854
|
-
// @/common/.generated/services.d.ts
|
|
855
|
-
writeIfChanged(
|
|
856
|
-
path.join(app.paths.common.generated, 'services.d.ts'),
|
|
857
|
-
`declare type ${appClassIdentifier} = import("@/server/.generated/app").default;
|
|
858
|
-
|
|
859
|
-
declare module '@models/types' {
|
|
860
|
-
export * from '@/var/prisma/index';
|
|
861
|
-
}`,
|
|
862
|
-
);
|
|
863
|
-
|
|
864
|
-
// @/common/generated.d.ts
|
|
865
|
-
writeIfChanged(
|
|
866
|
-
path.join(app.paths.root, 'common', 'generated.d.ts'),
|
|
867
|
-
`/// <reference path="./.generated/services.d.ts" />
|
|
868
|
-
`,
|
|
869
|
-
);
|
|
870
|
-
|
|
871
|
-
// @/server/.generated/app.ts
|
|
872
|
-
writeIfChanged(
|
|
873
|
-
path.join(app.paths.server.generated, 'app.ts'),
|
|
874
|
-
`
|
|
875
|
-
import { Application } from '@server/app/index';
|
|
876
|
-
import { ServicesContainer } from '@server/app/service/container';
|
|
877
|
-
|
|
878
|
-
${imported.join('\n')}
|
|
879
|
-
|
|
880
|
-
type TLooseServiceConfig<TConfig> =
|
|
881
|
-
TConfig extends (...args: any[]) => any ? TConfig
|
|
882
|
-
: TConfig extends Array<infer TItem> ? Array<TLooseServiceConfig<TItem>>
|
|
883
|
-
: TConfig extends object ? ({ [K in keyof TConfig]?: TLooseServiceConfig<TConfig[K]> } & Record<string, unknown>)
|
|
884
|
-
: TConfig;
|
|
885
|
-
|
|
886
|
-
const defineServiceConfig = <TConfig>(value: TConfig): TConfig => value;
|
|
887
|
-
|
|
888
|
-
${generatedFactories}
|
|
889
|
-
|
|
890
|
-
export default class ${appClassIdentifier} extends Application<ServicesContainer, CurrentUser> {
|
|
891
|
-
|
|
892
|
-
// Make sure the services typigs are reflecting the config and referring to the app
|
|
893
|
-
${sortedServices
|
|
894
|
-
.map(
|
|
895
|
-
(service) =>
|
|
896
|
-
`public ${service.name}!: ${service.className}Instance;`,
|
|
897
|
-
)
|
|
898
|
-
.join('\n')}
|
|
899
|
-
|
|
900
|
-
protected registered: Record<string, { name: string; priority: number; start: () => import('@server/app/service').AnyService }> = {
|
|
901
|
-
${sortedServices
|
|
902
|
-
.map(
|
|
903
|
-
(service) =>
|
|
904
|
-
`"${service.id}": {
|
|
905
|
-
name: "${service.name}",
|
|
906
|
-
priority: ${service.priority},
|
|
907
|
-
start: () => create${service.className}(this)
|
|
908
|
-
}`,
|
|
909
|
-
)
|
|
910
|
-
.join(',\n')}
|
|
911
|
-
};
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
`,
|
|
916
|
-
);
|
|
917
|
-
|
|
918
|
-
// @/server/.generated/services.d.ts
|
|
919
|
-
writeIfChanged(
|
|
920
|
-
path.join(app.paths.server.generated, 'services.d.ts'),
|
|
921
|
-
`type InstalledServices = Record<string, import('@server/app/service').AnyService>;
|
|
922
|
-
|
|
923
|
-
declare type ${appClassIdentifier} = import("@/server/.generated/app").default;
|
|
924
|
-
|
|
925
|
-
declare module '@cli/app' {
|
|
926
|
-
|
|
927
|
-
type TSetupConfig<TConfig> =
|
|
928
|
-
TConfig extends (...args: any[]) => any ? TConfig
|
|
929
|
-
: TConfig extends Array<infer TItem> ? Array<TSetupConfig<TItem>>
|
|
930
|
-
: TConfig extends object ? ({
|
|
931
|
-
[K in keyof TConfig]?: TSetupConfig<TConfig[K]> | TServiceSetup | TServiceRef
|
|
932
|
-
} & Record<string, unknown>)
|
|
933
|
-
: TConfig;
|
|
934
|
-
|
|
935
|
-
type App = {
|
|
936
|
-
|
|
937
|
-
env: TEnvConfig;
|
|
938
|
-
|
|
939
|
-
use: (referenceName: string) => TServiceRef;
|
|
940
|
-
|
|
941
|
-
setup: <TServiceName extends keyof ${appClassIdentifier}>(...args: [
|
|
942
|
-
// { user: app.setup('Core/User') }
|
|
943
|
-
servicePath: string,
|
|
944
|
-
serviceConfig?: {}
|
|
945
|
-
] | [
|
|
946
|
-
// app.setup('User', 'Core/User')
|
|
947
|
-
serviceName: TServiceName,
|
|
948
|
-
servicePath: string,
|
|
949
|
-
serviceConfig?: TSetupConfig<${appClassIdentifier}[TServiceName]["config"]>
|
|
950
|
-
]) => TServiceSetup;
|
|
951
|
-
}
|
|
952
|
-
const app: App;
|
|
953
|
-
export = app;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
declare module "@app" {
|
|
957
|
-
|
|
958
|
-
import { ApplicationContainer } from '@server/app/container';
|
|
959
|
-
|
|
960
|
-
const ServerServices: (
|
|
961
|
-
Pick<
|
|
962
|
-
ApplicationContainer<InstalledServices>,
|
|
963
|
-
${containerServices}
|
|
964
|
-
>
|
|
965
|
-
&
|
|
966
|
-
${appClassIdentifier}
|
|
967
|
-
)
|
|
968
|
-
|
|
969
|
-
export = ServerServices
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
declare module '@server/app' {
|
|
973
|
-
|
|
974
|
-
import { Application } from "@server/app";
|
|
975
|
-
import { Environment } from "@server/app";
|
|
976
|
-
import { ServicesContainer } from "@server/app/service/container";
|
|
977
|
-
|
|
978
|
-
abstract class ApplicationWithServices extends Application<
|
|
979
|
-
ServicesContainer<InstalledServices>
|
|
980
|
-
> {}
|
|
981
|
-
|
|
982
|
-
export interface Exported {
|
|
983
|
-
Application: typeof ApplicationWithServices,
|
|
984
|
-
Environment: Environment,
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
const foo: Exported;
|
|
988
|
-
|
|
989
|
-
export = foo;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
declare module '@common/errors' {
|
|
993
|
-
|
|
994
|
-
export * from '@common/errors/index';
|
|
995
|
-
export { default } from '@common/errors/index';
|
|
996
|
-
|
|
997
|
-
export const AuthRequired: typeof import('@common/errors/index').AuthRequired<FeatureKeys>;
|
|
998
|
-
export type AuthRequired = import('@common/errors/index').AuthRequired<FeatureKeys>;
|
|
999
|
-
|
|
1000
|
-
export const UpgradeRequired: typeof import('@common/errors/index').UpgradeRequired<FeatureKeys>;
|
|
1001
|
-
export type UpgradeRequired = import('@common/errors/index').UpgradeRequired<FeatureKeys>;
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
declare module '@models/types' {
|
|
1005
|
-
export * from '@/var/prisma/index';
|
|
1006
|
-
}`,
|
|
1007
|
-
);
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
134
|
private async warmupApp() {
|
|
1011
135
|
await app.warmup();
|
|
1012
136
|
}
|
|
@@ -1014,9 +138,16 @@ declare module '@models/types' {
|
|
|
1014
138
|
private async refreshGeneratedArtifacts() {
|
|
1015
139
|
if (!this.refreshingGeneratedArtifacts) {
|
|
1016
140
|
this.refreshingGeneratedArtifacts = (async () => {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
141
|
+
const services = generateServiceArtifacts();
|
|
142
|
+
const controllers = generateControllerArtifacts();
|
|
143
|
+
const { clientRoutes, serverRoutes, layouts } = generateRoutingArtifacts();
|
|
144
|
+
|
|
145
|
+
writeCurrentProteumManifest({
|
|
146
|
+
services,
|
|
147
|
+
controllers,
|
|
148
|
+
routes: { client: clientRoutes, server: serverRoutes },
|
|
149
|
+
layouts,
|
|
150
|
+
});
|
|
1020
151
|
})().finally(() => {
|
|
1021
152
|
this.refreshingGeneratedArtifacts = undefined;
|
|
1022
153
|
});
|
|
@@ -1030,6 +161,11 @@ declare module '@models/types' {
|
|
|
1030
161
|
await this.refreshGeneratedArtifacts();
|
|
1031
162
|
}
|
|
1032
163
|
|
|
164
|
+
public dispose() {
|
|
165
|
+
this.compileReporter?.stop();
|
|
166
|
+
this.compileReporter = undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
1033
169
|
public consumeRecentCompilationResults() {
|
|
1034
170
|
const recentCompilationResults = { ...this.recentCompilationResults };
|
|
1035
171
|
this.recentCompilationResults = {};
|
|
@@ -1049,6 +185,9 @@ declare module '@models/types' {
|
|
|
1049
185
|
createServerConfig(app, this.mode, this.outputTarget),
|
|
1050
186
|
createClientConfig(app, this.mode, this.outputTarget),
|
|
1051
187
|
]);
|
|
188
|
+
this.compileReporter = createCompileReporter({
|
|
189
|
+
enabled: this.mode === 'dev' && this.outputTarget === 'dev',
|
|
190
|
+
});
|
|
1052
191
|
|
|
1053
192
|
for (const compiler of multiCompiler.compilers) {
|
|
1054
193
|
const name = compiler.name;
|
|
@@ -1072,7 +211,8 @@ declare module '@models/types' {
|
|
|
1072
211
|
this.compiling[name] = new Promise((resolve) => (finished = resolve));
|
|
1073
212
|
|
|
1074
213
|
timeStart = new Date();
|
|
1075
|
-
|
|
214
|
+
this.compileReporter?.start(name, this.recentModifiedFiles[name] || []);
|
|
215
|
+
logVerbose(`[${name}] Compiling ...`);
|
|
1076
216
|
});
|
|
1077
217
|
|
|
1078
218
|
/* TODO: Ne pas résoudre la promise tant que la recompilation des données indexées (icones, identité, ...)
|
|
@@ -1088,20 +228,17 @@ declare module '@models/types' {
|
|
|
1088
228
|
// Shiow status
|
|
1089
229
|
const timeEnd = new Date();
|
|
1090
230
|
const time = timeEnd.getTime() - timeStart.getTime();
|
|
231
|
+
this.compileReporter?.finish(name, { succeeded: compilationSucceeded, durationMs: time });
|
|
1091
232
|
if (!compilationSucceeded) {
|
|
1092
233
|
console.info(stats.toString(compiler.options.stats));
|
|
1093
234
|
console.error(`[${name}] Failed to compile after ${time} ms`);
|
|
1094
|
-
|
|
1095
|
-
// Exit process with code 0, so the CI container can understand building failed
|
|
1096
|
-
// Only in prod, because in dev, we want the compiler watcher continue running
|
|
1097
|
-
if (this.mode === 'prod') process.exit(0);
|
|
1098
235
|
} else {
|
|
1099
236
|
if (name === 'client') {
|
|
1100
237
|
writeClientManifest(stats, app.outputPath(this.outputTarget));
|
|
1101
238
|
}
|
|
1102
239
|
|
|
1103
|
-
this.debug &&
|
|
1104
|
-
|
|
240
|
+
this.debug && logVerbose(stats.toString(compiler.options.stats));
|
|
241
|
+
logVerbose(`[${name}] Finished compilation after ${time} ms`);
|
|
1105
242
|
}
|
|
1106
243
|
|
|
1107
244
|
// Mark as finished
|