proteum 1.0.3 → 2.0.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 +92 -0
- package/agents/codex/AGENTS.md +95 -0
- package/agents/codex/CODING_STYLE.md +71 -0
- package/agents/codex/agents.md.zip +0 -0
- package/agents/codex/client/AGENTS.md +102 -0
- package/agents/codex/client/pages/AGENTS.md +35 -0
- package/agents/codex/server/routes/AGENTS.md +12 -0
- package/agents/codex/server/services/AGENTS.md +137 -0
- package/agents/codex/tests/AGENTS.md +8 -0
- package/cli/app/config.ts +12 -17
- package/cli/app/index.ts +59 -99
- package/cli/bin.js +1 -1
- package/cli/commands/build.ts +23 -12
- package/cli/commands/check.ts +19 -0
- package/cli/commands/deploy/app.ts +4 -8
- package/cli/commands/deploy/web.ts +16 -20
- package/cli/commands/dev.ts +185 -75
- package/cli/commands/devEvents.ts +106 -0
- package/cli/commands/init.ts +63 -57
- package/cli/commands/lint.ts +21 -0
- package/cli/commands/refresh.ts +6 -6
- package/cli/commands/typecheck.ts +18 -0
- package/cli/compiler/client/identite.ts +79 -49
- package/cli/compiler/client/index.ts +132 -214
- package/cli/compiler/common/bundleAnalysis.ts +94 -0
- package/cli/compiler/common/clientManifest.ts +67 -0
- package/cli/compiler/common/controllers.ts +288 -0
- package/cli/compiler/common/files/autres.ts +7 -18
- package/cli/compiler/common/files/images.ts +40 -37
- package/cli/compiler/common/files/style.ts +12 -25
- package/cli/compiler/common/generatedRouteModules.ts +368 -0
- package/cli/compiler/common/index.ts +29 -68
- package/cli/compiler/common/loaders/forbid-ssr-import.js +13 -0
- package/cli/compiler/common/rspackAliases.ts +13 -0
- package/cli/compiler/common/scripts.ts +37 -0
- package/cli/compiler/index.ts +764 -234
- package/cli/compiler/server/index.ts +52 -77
- package/cli/compiler/writeIfChanged.ts +21 -0
- package/cli/index.ts +65 -90
- package/cli/paths.ts +51 -57
- package/cli/print.ts +17 -11
- package/cli/tsconfig.json +5 -4
- package/cli/utils/agents.ts +100 -0
- package/cli/utils/check.ts +71 -0
- package/cli/utils/index.ts +1 -3
- package/cli/utils/keyboard.ts +8 -25
- package/cli/utils/runProcess.ts +30 -0
- package/client/app/component.tsx +29 -29
- package/client/app/index.ts +36 -57
- package/client/app/service.ts +7 -12
- package/client/app.tsconfig.json +2 -2
- package/client/components/Dialog/Manager.ssr.tsx +40 -0
- package/client/components/Dialog/Manager.tsx +119 -150
- package/client/components/Dialog/status.tsx +3 -3
- package/client/components/index.ts +1 -1
- package/client/components/types.d.ts +1 -3
- package/client/dev/hmr.ts +65 -0
- package/client/global.d.ts +2 -2
- package/client/hooks.ts +6 -9
- package/client/index.ts +2 -1
- package/client/islands/index.ts +7 -0
- package/client/islands/useDeferredModule.ts +199 -0
- package/client/pages/_layout/index.tsx +4 -12
- package/client/pages/useHeader.tsx +14 -21
- package/client/router.ts +27 -0
- package/client/services/router/components/Link.tsx +34 -27
- package/client/services/router/components/Page.tsx +6 -14
- package/client/services/router/components/router.ssr.tsx +36 -0
- package/client/services/router/components/router.tsx +63 -83
- package/client/services/router/index.tsx +185 -220
- package/client/services/router/request/api.ts +97 -119
- package/client/services/router/request/history.ts +2 -2
- package/client/services/router/request/index.ts +13 -12
- package/client/services/router/request/multipart.ts +72 -62
- package/client/services/router/response/index.tsx +68 -61
- package/client/services/router/response/page.ts +28 -32
- package/client/utils/dom.ts +17 -33
- package/common/app/index.ts +3 -3
- package/common/data/chaines/index.ts +22 -23
- package/common/data/dates.ts +35 -70
- package/common/data/markdown.ts +42 -39
- package/common/dev/serverHotReload.ts +26 -0
- package/common/errors/index.tsx +110 -142
- package/common/router/contracts.ts +29 -0
- package/common/router/index.ts +89 -108
- package/common/router/layouts.ts +34 -47
- package/common/router/pageSetup.ts +50 -0
- package/common/router/register.ts +53 -24
- package/common/router/request/api.ts +30 -36
- package/common/router/request/index.ts +2 -8
- package/common/router/response/index.ts +8 -15
- package/common/router/response/page.ts +70 -58
- package/common/utils.ts +1 -1
- package/eslint.js +62 -0
- package/package.json +12 -45
- package/prettier.config.cjs +9 -0
- package/scripts/cleanup-generated-controllers.ts +62 -0
- package/scripts/fix-reference-app-typing.ts +490 -0
- package/scripts/refactor-client-app-imports.ts +244 -0
- package/scripts/refactor-client-pages.ts +587 -0
- package/scripts/refactor-server-controllers.ts +470 -0
- package/scripts/refactor-server-runtime-aliases.ts +360 -0
- package/scripts/restore-client-app-import-files.ts +41 -0
- package/scripts/restore-files-from-git-head.ts +20 -0
- package/scripts/update-codex-agents.ts +35 -0
- package/server/app/commands.ts +35 -64
- package/server/app/container/config.ts +39 -69
- package/server/app/container/console/index.ts +202 -248
- package/server/app/container/index.ts +33 -71
- package/server/app/controller/index.ts +61 -0
- package/server/app/index.ts +39 -105
- package/server/app/service/container.ts +41 -42
- package/server/app/service/index.ts +120 -147
- package/server/context.ts +1 -1
- package/server/index.ts +25 -1
- package/server/services/auth/index.ts +75 -115
- package/server/services/auth/router/index.ts +31 -32
- package/server/services/auth/router/request.ts +14 -16
- package/server/services/cron/CronTask.ts +13 -26
- package/server/services/cron/index.ts +14 -36
- package/server/services/disks/driver.ts +40 -58
- package/server/services/disks/drivers/local/index.ts +79 -90
- package/server/services/disks/drivers/s3/index.ts +116 -163
- package/server/services/disks/index.ts +23 -38
- package/server/services/email/index.ts +45 -104
- package/server/services/email/utils.ts +14 -27
- package/server/services/fetch/index.ts +53 -85
- package/server/services/prisma/Facet.ts +39 -91
- package/server/services/prisma/index.ts +74 -110
- package/server/services/router/generatedRuntime.ts +29 -0
- package/server/services/router/http/index.ts +77 -72
- package/server/services/router/http/multipart.ts +19 -42
- package/server/services/router/index.ts +378 -365
- package/server/services/router/request/api.ts +26 -25
- package/server/services/router/request/index.ts +44 -51
- package/server/services/router/request/service.ts +7 -11
- package/server/services/router/request/validation/zod.ts +111 -148
- package/server/services/router/response/index.ts +110 -125
- package/server/services/router/response/mask/Filter.ts +31 -72
- package/server/services/router/response/mask/index.ts +8 -15
- package/server/services/router/response/mask/selecteurs.ts +11 -25
- package/server/services/router/response/page/clientManifest.ts +25 -0
- package/server/services/router/response/page/document.tsx +199 -127
- package/server/services/router/response/page/index.tsx +89 -94
- package/server/services/router/service.ts +13 -15
- package/server/services/schema/index.ts +17 -26
- package/server/services/schema/request.ts +19 -33
- package/server/services/schema/router/index.ts +8 -11
- package/server/services/security/encrypt/aes/index.ts +15 -35
- package/server/utils/slug.ts +29 -32
- package/skills/clean-project-code/SKILL.md +63 -0
- package/skills/clean-project-code/agents/openai.yaml +4 -0
- package/tsconfig.common.json +4 -3
- package/tsconfig.json +4 -1
- package/types/aliases.d.ts +17 -21
- package/types/controller-input.test.ts +48 -0
- package/types/express-extra.d.ts +6 -0
- package/types/global/constants.d.ts +1 -0
- package/types/global/express-extra.d.ts +6 -0
- package/types/global/modules.d.ts +13 -16
- package/types/global/utils.d.ts +17 -49
- package/types/global/vendors.d.ts +62 -0
- package/types/icons.d.ts +65 -1
- package/types/uuid.d.ts +3 -0
- package/types/vendors.d.ts +62 -0
- package/cli/compiler/common/babel/index.ts +0 -173
- package/cli/compiler/common/babel/plugins/index.ts +0 -0
- package/cli/compiler/common/babel/plugins/services.ts +0 -586
- package/cli/compiler/common/babel/routes/imports.ts +0 -127
- package/cli/compiler/common/babel/routes/routes.ts +0 -1170
- package/client/services/captcha/index.ts +0 -67
- package/client/services/socket/index.ts +0 -147
- package/common/data/rte/nodes.ts +0 -83
- package/common/data/stats.ts +0 -90
- package/common/utils/rte.ts +0 -183
- package/server/services/auth/old.ts +0 -277
- package/server/services/cache/commands.ts +0 -41
- package/server/services/cache/index.ts +0 -297
- package/server/services/cache/service.json +0 -6
- package/server/services/socket/index.ts +0 -162
- package/server/services/socket/scope.ts +0 -226
- package/server/services/socket/service.json +0 -6
- package/server/services_old/SocketClient.ts +0 -92
- package/server/services_old/Token.old.ts +0 -97
package/cli/compiler/index.ts
CHANGED
|
@@ -4,12 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
// Npm
|
|
6
6
|
import path from 'path';
|
|
7
|
-
import webpack from 'webpack';
|
|
8
7
|
import fs from 'fs-extra';
|
|
9
8
|
import serialize from 'serialize-javascript';
|
|
10
|
-
|
|
11
|
-
import
|
|
12
|
-
const smp = new SpeedMeasurePlugin({ disable: true });
|
|
9
|
+
import { rspack, type Compiler as RspackCompiler } from '@rspack/core';
|
|
10
|
+
import ts from 'typescript';
|
|
13
11
|
|
|
14
12
|
// Core
|
|
15
13
|
import app from '../app';
|
|
@@ -17,69 +15,67 @@ import cli from '..';
|
|
|
17
15
|
import createServerConfig from './server';
|
|
18
16
|
import createClientConfig from './client';
|
|
19
17
|
import { TCompileMode, TCompileOutputTarget } from './common';
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
import {
|
|
19
|
+
indexControllers,
|
|
20
|
+
generateControllerClientTree,
|
|
21
|
+
printControllerTree,
|
|
22
|
+
type TControllerServiceRoot,
|
|
23
|
+
} from './common/controllers';
|
|
24
|
+
import { writeClientManifest } from './common/clientManifest';
|
|
25
|
+
import { getGeneratedRouteModuleFilepath, writeGeneratedRouteModule } from './common/generatedRouteModules';
|
|
26
|
+
import writeIfChanged from './writeIfChanged';
|
|
27
|
+
|
|
28
|
+
type TCompilerCallback = (compiler: RspackCompiler) => void;
|
|
22
29
|
|
|
23
30
|
type TServiceMetas = {
|
|
24
|
-
id: string
|
|
25
|
-
name: string
|
|
26
|
-
parent: string
|
|
27
|
-
dependences: string
|
|
28
|
-
importationPath: string
|
|
29
|
-
priority: number
|
|
30
|
-
}
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
parent: string;
|
|
34
|
+
dependences: string;
|
|
35
|
+
importationPath: string;
|
|
36
|
+
priority: number;
|
|
37
|
+
};
|
|
31
38
|
|
|
32
39
|
type TRegisteredService = {
|
|
33
|
-
id?: string
|
|
34
|
-
name: string
|
|
35
|
-
className: string
|
|
36
|
-
instanciation: (parentRef?: string) => string
|
|
37
|
-
priority: number
|
|
38
|
-
}
|
|
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
|
+
type TRecentCompilationResult = { succeeded: boolean; hash?: string; modifiedFiles?: string[] };
|
|
50
|
+
|
|
51
|
+
const normalizePath = (value: string) => value.replace(/\\/g, '/');
|
|
39
52
|
|
|
40
53
|
/*----------------------------------
|
|
41
54
|
- FONCTION
|
|
42
55
|
----------------------------------*/
|
|
43
56
|
export default class Compiler {
|
|
44
|
-
|
|
45
|
-
|
|
57
|
+
public compiling: { [compiler: string]: Promise<void> } = {};
|
|
58
|
+
private recentCompilationResults: { [compiler: string]: TRecentCompilationResult } = {};
|
|
59
|
+
private recentModifiedFiles: { [compiler: string]: string[] } = {};
|
|
60
|
+
private refreshingGeneratedArtifacts?: Promise<void>;
|
|
46
61
|
|
|
47
62
|
public constructor(
|
|
48
63
|
private mode: TCompileMode,
|
|
49
|
-
private callbacks: {
|
|
50
|
-
before?: TCompilerCallback,
|
|
51
|
-
after?: TCompilerCallback,
|
|
52
|
-
} = {},
|
|
64
|
+
private callbacks: { before?: TCompilerCallback; after?: TCompilerCallback } = {},
|
|
53
65
|
private debug: boolean = false,
|
|
54
|
-
private outputTarget: TCompileOutputTarget = mode === 'dev' ? 'dev' : 'bin'
|
|
55
|
-
) {
|
|
56
|
-
|
|
57
|
-
}
|
|
66
|
+
private outputTarget: TCompileOutputTarget = mode === 'dev' ? 'dev' : 'bin',
|
|
67
|
+
) {}
|
|
58
68
|
|
|
59
69
|
public cleanup() {
|
|
60
70
|
const outputPath = app.outputPath(this.outputTarget);
|
|
61
71
|
const generatedPublicEntries = new Set(['app']);
|
|
72
|
+
const outputPublicPath = path.join(outputPath, 'public');
|
|
73
|
+
const preserveDevOutput = this.mode === 'dev' && this.outputTarget === 'dev';
|
|
62
74
|
|
|
63
|
-
fs.emptyDirSync(
|
|
64
|
-
fs.ensureDirSync( path.join(outputPath, 'public') )
|
|
65
|
-
const publicFiles = fs.readdirSync(app.paths.public);
|
|
66
|
-
for (const publicFile of publicFiles) {
|
|
67
|
-
if (generatedPublicEntries.has(publicFile))
|
|
68
|
-
continue;
|
|
75
|
+
if (!preserveDevOutput) fs.emptyDirSync(outputPath);
|
|
69
76
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
fs.symlinkSync(
|
|
73
|
-
path.join(app.paths.public, publicFile),
|
|
74
|
-
path.join(outputPath, 'public', publicFile)
|
|
75
|
-
);
|
|
76
|
-
// Prod: Symlink not always supported by CI / Containers solutions
|
|
77
|
-
else
|
|
78
|
-
fs.copySync(
|
|
79
|
-
path.join(app.paths.public, publicFile),
|
|
80
|
-
path.join(outputPath, 'public', publicFile)
|
|
81
|
-
);
|
|
82
|
-
}
|
|
77
|
+
fs.ensureDirSync(outputPublicPath);
|
|
78
|
+
this.syncPublicEntries(outputPublicPath, generatedPublicEntries, this.mode === 'dev');
|
|
83
79
|
}
|
|
84
80
|
/* FIX issue with npm link
|
|
85
81
|
When we install a module with npm link, this module's deps are not installed in the parent project scope
|
|
@@ -89,7 +85,7 @@ export default class Compiler {
|
|
|
89
85
|
*/
|
|
90
86
|
public fixNpmLinkIssues() {
|
|
91
87
|
const corePath = path.join(app.paths.root, '/node_modules/proteum');
|
|
92
|
-
if (!fs.lstatSync(
|
|
88
|
+
if (!fs.lstatSync(corePath).isSymbolicLink())
|
|
93
89
|
return console.info("Not fixing npm issue because proteum wasn't installed with npm link.");
|
|
94
90
|
|
|
95
91
|
this.debug && console.info(`Fix NPM link issues ...`);
|
|
@@ -102,191 +98,718 @@ export default class Compiler {
|
|
|
102
98
|
// Modules are installed locally and not glbally as with with the 5htp package from NPM.
|
|
103
99
|
// So we need to symbilnk the http-core node_modules in one of the parents of server.js.
|
|
104
100
|
// It avoids errors like: "Error: Cannot find module 'intl'"
|
|
105
|
-
|
|
101
|
+
this.ensureSymlinkSync(coreModules, path.join(outputPath, 'node_modules'));
|
|
106
102
|
|
|
107
|
-
// Same problem: when 5htp-core is installed via npm link,
|
|
103
|
+
// Same problem: when 5htp-core is installed via npm link,
|
|
108
104
|
// Typescript doesn't detect React and shows mission JSX errors
|
|
109
105
|
const preactCoreModule = path.join(coreModules, 'preact');
|
|
110
106
|
const preactAppModule = path.join(appModules, 'preact');
|
|
111
107
|
const reactAppModule = path.join(appModules, 'react');
|
|
112
108
|
|
|
113
|
-
if (!fs.existsSync( preactAppModule
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
if (!fs.existsSync(preactAppModule)) fs.symlinkSync(preactCoreModule, preactAppModule);
|
|
110
|
+
if (!fs.existsSync(reactAppModule)) fs.symlinkSync(path.join(preactCoreModule, 'compat'), reactAppModule);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private syncPublicEntries(outputPublicPath: string, generatedPublicEntries: Set<string>, useSymlinks: boolean) {
|
|
114
|
+
const publicFiles = new Set(
|
|
115
|
+
fs.readdirSync(app.paths.public).filter((publicFile) => !generatedPublicEntries.has(publicFile)),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
for (const existingPublicFile of fs.readdirSync(outputPublicPath)) {
|
|
119
|
+
if (generatedPublicEntries.has(existingPublicFile) || publicFiles.has(existingPublicFile)) continue;
|
|
120
|
+
|
|
121
|
+
fs.removeSync(path.join(outputPublicPath, existingPublicFile));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (const publicFile of publicFiles) {
|
|
125
|
+
const sourcePath = path.join(app.paths.public, publicFile);
|
|
126
|
+
const outputFilePath = path.join(outputPublicPath, publicFile);
|
|
127
|
+
|
|
128
|
+
if (useSymlinks) {
|
|
129
|
+
this.ensureSymlinkSync(sourcePath, outputFilePath);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (fs.existsSync(outputFilePath)) fs.removeSync(outputFilePath);
|
|
134
|
+
|
|
135
|
+
fs.copySync(sourcePath, outputFilePath);
|
|
136
|
+
}
|
|
117
137
|
}
|
|
118
138
|
|
|
119
|
-
private
|
|
139
|
+
private ensureSymlinkSync(targetPath: string, linkPath: string) {
|
|
140
|
+
fs.ensureDirSync(path.dirname(linkPath));
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const linkStats = fs.lstatSync(linkPath);
|
|
144
|
+
|
|
145
|
+
if (linkStats.isSymbolicLink()) {
|
|
146
|
+
const currentTarget = path.resolve(path.dirname(linkPath), fs.readlinkSync(linkPath));
|
|
147
|
+
if (currentTarget === path.resolve(targetPath)) return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fs.removeSync(linkPath);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
fs.symlinkSync(targetPath, linkPath);
|
|
156
|
+
}
|
|
120
157
|
|
|
121
|
-
|
|
158
|
+
private findServices(dir: string) {
|
|
159
|
+
const blacklist = ['node_modules', 'proteum'];
|
|
122
160
|
const files: string[] = [];
|
|
123
161
|
const dirents = fs.readdirSync(dir, { withFileTypes: true });
|
|
124
162
|
|
|
125
163
|
for (let dirent of dirents) {
|
|
126
|
-
|
|
127
164
|
let fileName = dirent.name;
|
|
128
165
|
let filePath = path.resolve(dir, fileName);
|
|
129
166
|
|
|
130
|
-
if (blacklist.includes(
|
|
131
|
-
continue;
|
|
167
|
+
if (blacklist.includes(fileName)) continue;
|
|
132
168
|
|
|
133
169
|
// Define is we should recursively find service in the current item
|
|
134
170
|
let iterate: boolean = false;
|
|
135
171
|
if (dirent.isSymbolicLink()) {
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
iterate = true;
|
|
141
|
-
|
|
142
|
-
} else if (dirent.isDirectory())
|
|
143
|
-
iterate = true;
|
|
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;
|
|
144
176
|
|
|
145
177
|
// Update the list of found services
|
|
146
178
|
if (iterate) {
|
|
147
|
-
files.push(
|
|
179
|
+
files.push(...this.findServices(filePath));
|
|
148
180
|
} else if (dirent.name === 'service.json') {
|
|
149
|
-
files.push(
|
|
181
|
+
files.push(path.dirname(filePath));
|
|
150
182
|
}
|
|
151
183
|
}
|
|
152
184
|
return files;
|
|
153
185
|
}
|
|
154
186
|
|
|
155
|
-
private
|
|
156
|
-
|
|
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
|
+
----------------------------------*/
|
|
157
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() {
|
|
158
675
|
// Index services
|
|
159
676
|
const searchDirs = [
|
|
160
677
|
// The less priority is the first
|
|
161
|
-
{
|
|
162
|
-
|
|
163
|
-
priority: -1,
|
|
164
|
-
root: path.join(cli.paths.core.root, 'server', 'services')
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
path: '@/server/services/',
|
|
168
|
-
priority: 0,
|
|
169
|
-
root: path.join(app.paths.root, 'server', 'services')
|
|
170
|
-
},
|
|
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') },
|
|
171
680
|
// Temp disabled because compile issue on vercel
|
|
172
681
|
//'': path.join(app.paths.root, 'node_modules'),
|
|
173
|
-
]
|
|
682
|
+
];
|
|
174
683
|
|
|
175
684
|
// Generate app class file
|
|
176
|
-
const servicesAvailable: {[id: string]: TServiceMetas} = {};
|
|
685
|
+
const servicesAvailable: { [id: string]: TServiceMetas } = {};
|
|
177
686
|
for (const searchDir of searchDirs) {
|
|
178
|
-
|
|
179
687
|
const services = this.findServices(searchDir.root);
|
|
180
688
|
|
|
181
689
|
for (const serviceDir of services) {
|
|
182
|
-
const metasFile = path.join(
|
|
690
|
+
const metasFile = path.join(serviceDir, 'service.json');
|
|
183
691
|
|
|
184
692
|
// The +1 is to remove the slash
|
|
185
|
-
const importationPath = searchDir.path + serviceDir.substring(
|
|
693
|
+
const importationPath = searchDir.path + serviceDir.substring(searchDir.root.length + 1);
|
|
186
694
|
|
|
187
695
|
const serviceMetas = require(metasFile);
|
|
188
696
|
|
|
189
|
-
servicesAvailable[
|
|
190
|
-
importationPath,
|
|
191
|
-
priority: searchDir.priority,
|
|
192
|
-
...serviceMetas,
|
|
193
|
-
};
|
|
697
|
+
servicesAvailable[serviceMetas.id] = { importationPath, priority: searchDir.priority, ...serviceMetas };
|
|
194
698
|
}
|
|
195
699
|
}
|
|
196
700
|
|
|
197
701
|
// Read app services
|
|
198
|
-
const imported: string[] = []
|
|
199
|
-
const referencedNames: {[serviceId: string]: string} = {} // ID to Name
|
|
702
|
+
const imported: string[] = [];
|
|
703
|
+
const referencedNames: { [serviceId: string]: string } = {}; // ID to Name
|
|
704
|
+
let serviceImportIndex = 0;
|
|
200
705
|
|
|
201
706
|
const refService = (serviceName: string, serviceConfig: any, level: number = 0): TRegisteredService => {
|
|
202
|
-
|
|
203
707
|
if (serviceConfig.refTo !== undefined) {
|
|
204
708
|
const refTo = serviceConfig.refTo;
|
|
205
709
|
return {
|
|
206
710
|
name: serviceName,
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
711
|
+
className: serviceName,
|
|
712
|
+
instanciation: (_parentRef, appRef = 'this') => `${appRef}.${refTo}`,
|
|
713
|
+
priority: 0,
|
|
714
|
+
};
|
|
210
715
|
}
|
|
211
716
|
|
|
212
|
-
const serviceMetas = servicesAvailable[
|
|
717
|
+
const serviceMetas = servicesAvailable[serviceConfig.id];
|
|
213
718
|
if (serviceMetas === undefined)
|
|
214
|
-
throw new Error(
|
|
719
|
+
throw new Error(
|
|
720
|
+
`Service ${serviceConfig.id} not found. Referenced services: ${Object.keys(servicesAvailable).join('\n')}`,
|
|
721
|
+
);
|
|
215
722
|
|
|
216
723
|
const referencedName = referencedNames[serviceConfig.id];
|
|
217
724
|
if (referencedName !== undefined)
|
|
218
725
|
throw new Error(`Service ${serviceConfig.id} is already setup as ${referencedName}`);
|
|
219
|
-
|
|
220
|
-
// Generate index & typings
|
|
221
|
-
imported.push(`import ${serviceMetas.name} from "${serviceMetas.importationPath}";`);
|
|
222
726
|
|
|
223
|
-
|
|
224
|
-
|
|
727
|
+
// Generate index & typings
|
|
728
|
+
const importIdentifier = `${serviceMetas.name}Class${serviceImportIndex++}`;
|
|
729
|
+
imported.push(`import ${importIdentifier} from "${serviceMetas.importationPath}";`);
|
|
225
730
|
|
|
226
|
-
|
|
731
|
+
if (serviceConfig.name !== undefined) referencedNames[serviceConfig.id] = serviceConfig.name;
|
|
227
732
|
|
|
733
|
+
const processConfig = (config: any, level: number = 0, appRef: string = 'this') => {
|
|
228
734
|
let propsStr = '';
|
|
229
735
|
for (const key in config) {
|
|
230
736
|
const value = config[key];
|
|
231
737
|
|
|
232
738
|
if (!value || typeof value !== 'object')
|
|
233
739
|
propsStr += `"${key}":${serialize(value, { space: 4 })},\n`;
|
|
234
|
-
|
|
235
740
|
// Reference to a service
|
|
236
|
-
else if (value.type === 'service.setup' || value.type === 'service.ref')
|
|
237
|
-
|
|
238
|
-
|
|
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';
|
|
239
744
|
// Recursion
|
|
240
745
|
else if (level <= 4 && !Array.isArray(value))
|
|
241
|
-
propsStr += `"${key}":` + processConfig(value, level + 1) + ',\n';
|
|
242
|
-
|
|
243
|
-
else
|
|
244
|
-
propsStr += `"${key}":${serialize(value, { space: 4 })},\n`;
|
|
245
|
-
|
|
746
|
+
propsStr += `"${key}":` + processConfig(value, level + 1, appRef) + ',\n';
|
|
747
|
+
else propsStr += `"${key}":${serialize(value, { space: 4 })},\n`;
|
|
246
748
|
}
|
|
247
749
|
|
|
248
750
|
return `{ ${propsStr} }`;
|
|
249
|
-
}
|
|
250
|
-
const config = processConfig(serviceConfig.config || {});
|
|
751
|
+
};
|
|
251
752
|
|
|
252
753
|
// Generate the service instance
|
|
253
|
-
const instanciation = (parentRef?: string) =>
|
|
254
|
-
|
|
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}(
|
|
255
762
|
${parentRef ? `${parentRef},` : ''}
|
|
256
|
-
${
|
|
257
|
-
|
|
258
|
-
)
|
|
763
|
+
${typedRouterConfig},
|
|
764
|
+
${appRef}
|
|
765
|
+
)`;
|
|
766
|
+
};
|
|
259
767
|
|
|
260
768
|
return {
|
|
261
769
|
id: serviceConfig.id,
|
|
262
770
|
name: serviceName,
|
|
263
771
|
instanciation,
|
|
264
|
-
className:
|
|
772
|
+
className: importIdentifier,
|
|
265
773
|
priority: serviceConfig.config?.priority || serviceMetas.priority || 0,
|
|
266
774
|
};
|
|
267
|
-
}
|
|
775
|
+
};
|
|
268
776
|
|
|
269
|
-
const servicesCode = Object.values(app.registered).map(
|
|
777
|
+
const servicesCode = Object.values(app.registered).map((s) => refService(s.name, s, 0));
|
|
270
778
|
const sortedServices = servicesCode.sort((a, b) => a.priority - b.priority);
|
|
271
779
|
|
|
272
780
|
// Define the app class identifier
|
|
273
781
|
const appClassIdentifier = app.identity.identifier;
|
|
274
|
-
const containerServices = app.containerServices.map(
|
|
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');
|
|
275
793
|
|
|
276
794
|
// @/client/.generated/services.d.ts
|
|
277
|
-
|
|
278
|
-
path.join(
|
|
279
|
-
`declare
|
|
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" {
|
|
280
800
|
|
|
281
801
|
import { ${appClassIdentifier} as ${appClassIdentifier}Client } from "@/client";
|
|
282
802
|
import ${appClassIdentifier}Server from "@/server/.generated/app";
|
|
283
803
|
|
|
284
804
|
export const Router: ${appClassIdentifier}Client['Router'];
|
|
285
805
|
|
|
286
|
-
${sortedServices
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
806
|
+
${sortedServices
|
|
807
|
+
.map((service) =>
|
|
808
|
+
service.name !== 'Router'
|
|
809
|
+
? `export const ${service.name}: ${appClassIdentifier}Server["${service.name}"];`
|
|
810
|
+
: '',
|
|
811
|
+
)
|
|
812
|
+
.join('\n')}
|
|
290
813
|
|
|
291
814
|
}
|
|
292
815
|
|
|
@@ -306,91 +829,96 @@ declare module '@common/errors' {
|
|
|
306
829
|
export type UpgradeRequired = import('@common/errors/index').UpgradeRequired<FeatureKeys>;
|
|
307
830
|
}
|
|
308
831
|
|
|
309
|
-
declare module '@request' {
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
|
|
313
832
|
declare namespace preact.JSX {
|
|
314
833
|
interface HTMLAttributes {
|
|
315
834
|
src?: string;
|
|
316
835
|
}
|
|
317
836
|
}
|
|
318
|
-
|
|
837
|
+
`,
|
|
319
838
|
);
|
|
320
839
|
|
|
321
840
|
// @/client/.generated/context.ts
|
|
322
|
-
|
|
323
|
-
path.join(
|
|
324
|
-
`// TODO: move it into core (but how to make sure usecontext returns ${appClassIdentifier}'s context ?)
|
|
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 ?)
|
|
325
844
|
import React from 'react';
|
|
326
845
|
|
|
327
|
-
import type ${appClassIdentifier}
|
|
328
|
-
import type { TRouterContext as TServerRouterRequestContext } from '@server/services/router/response';
|
|
329
|
-
import type { TRouterContext as TClientRouterRequestContext } from '@client/services/router/response';
|
|
330
|
-
import type ${appClassIdentifier}Client from '.';
|
|
846
|
+
import type ${appClassIdentifier}Client from '@/client/index';
|
|
331
847
|
|
|
332
|
-
|
|
333
|
-
// (it gets ClientApplication instead of ${appClassIdentifier}Client)
|
|
334
|
-
type ClientRequestContext = TClientRouterRequestContext<${appClassIdentifier}Client["Router"], ${appClassIdentifier}Client>;
|
|
335
|
-
type ServerRequestContext = TServerRouterRequestContext<${appClassIdentifier}Server["Router"]>
|
|
336
|
-
type UniversalServices = ClientRequestContext | ServerRequestContext
|
|
337
|
-
|
|
338
|
-
// Non-universla services are flagged as potentially undefined
|
|
339
|
-
export type ClientContext = (
|
|
340
|
-
UniversalServices
|
|
341
|
-
&
|
|
342
|
-
Partial<Omit<ClientRequestContext, keyof UniversalServices>>
|
|
343
|
-
&
|
|
344
|
-
{
|
|
345
|
-
Router: ${appClassIdentifier}Client["Router"],
|
|
346
|
-
}
|
|
347
|
-
)
|
|
848
|
+
export type ClientContext = ${appClassIdentifier}Client["Router"]["context"];
|
|
348
849
|
|
|
349
850
|
export const ReactClientContext = React.createContext<ClientContext>({} as ClientContext);
|
|
350
|
-
export default (): ClientContext => React.useContext<ClientContext>(ReactClientContext)
|
|
851
|
+
export default (): ClientContext => React.useContext<ClientContext>(ReactClientContext);`,
|
|
852
|
+
);
|
|
351
853
|
|
|
352
854
|
// @/common/.generated/services.d.ts
|
|
353
|
-
|
|
354
|
-
path.join(
|
|
355
|
-
`declare
|
|
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' {
|
|
356
860
|
export * from '@/var/prisma/index';
|
|
357
|
-
}
|
|
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
|
+
`,
|
|
358
869
|
);
|
|
359
870
|
|
|
360
871
|
// @/server/.generated/app.ts
|
|
361
|
-
|
|
362
|
-
path.join(
|
|
363
|
-
`
|
|
872
|
+
writeIfChanged(
|
|
873
|
+
path.join(app.paths.server.generated, 'app.ts'),
|
|
874
|
+
`
|
|
364
875
|
import { Application } from '@server/app/index';
|
|
365
876
|
import { ServicesContainer } from '@server/app/service/container';
|
|
366
877
|
|
|
367
878
|
${imported.join('\n')}
|
|
368
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
|
+
|
|
369
890
|
export default class ${appClassIdentifier} extends Application<ServicesContainer, CurrentUser> {
|
|
370
891
|
|
|
371
892
|
// Make sure the services typigs are reflecting the config and referring to the app
|
|
372
|
-
${sortedServices
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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}": {
|
|
379
905
|
name: "${service.name}",
|
|
380
906
|
priority: ${service.priority},
|
|
381
|
-
start: () => ${service.
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
|
|
907
|
+
start: () => create${service.className}(this)
|
|
908
|
+
}`,
|
|
909
|
+
)
|
|
910
|
+
.join(',\n')}
|
|
911
|
+
};
|
|
385
912
|
}
|
|
386
913
|
|
|
387
914
|
|
|
388
|
-
|
|
915
|
+
`,
|
|
916
|
+
);
|
|
389
917
|
|
|
390
918
|
// @/server/.generated/services.d.ts
|
|
391
|
-
|
|
392
|
-
path.join(
|
|
393
|
-
`type InstalledServices = import('
|
|
919
|
+
writeIfChanged(
|
|
920
|
+
path.join(app.paths.server.generated, 'services.d.ts'),
|
|
921
|
+
`type InstalledServices = Record<string, import('@server/app/service').AnyService>;
|
|
394
922
|
|
|
395
923
|
declare type ${appClassIdentifier} = import("@/server/.generated/app").default;
|
|
396
924
|
|
|
@@ -399,9 +927,9 @@ declare module '@cli/app' {
|
|
|
399
927
|
type TSetupConfig<TConfig> =
|
|
400
928
|
TConfig extends (...args: any[]) => any ? TConfig
|
|
401
929
|
: TConfig extends Array<infer TItem> ? Array<TSetupConfig<TItem>>
|
|
402
|
-
: TConfig extends object ? {
|
|
403
|
-
[K in keyof TConfig]
|
|
404
|
-
}
|
|
930
|
+
: TConfig extends object ? ({
|
|
931
|
+
[K in keyof TConfig]?: TSetupConfig<TConfig[K]> | TServiceSetup | TServiceRef
|
|
932
|
+
} & Record<string, unknown>)
|
|
405
933
|
: TConfig;
|
|
406
934
|
|
|
407
935
|
type App = {
|
|
@@ -461,26 +989,6 @@ declare module '@server/app' {
|
|
|
461
989
|
export = foo;
|
|
462
990
|
}
|
|
463
991
|
|
|
464
|
-
declare module '@request' {
|
|
465
|
-
import type { TRouterContext } from '@server/services/router/response';
|
|
466
|
-
const routerContext: TRouterContext<${appClassIdentifier}["Router"]>;
|
|
467
|
-
export = routerContext;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
declare module '@models' {
|
|
471
|
-
import { Prisma, PrismaClient } from '@/var/prisma/index';
|
|
472
|
-
|
|
473
|
-
type ModelNames = Prisma.ModelName;
|
|
474
|
-
|
|
475
|
-
type ModelDelegates = {
|
|
476
|
-
[K in ModelNames]: PrismaClient[Uncapitalize<K>];
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
const models: ModelDelegates;
|
|
480
|
-
|
|
481
|
-
export = models;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
992
|
declare module '@common/errors' {
|
|
485
993
|
|
|
486
994
|
export * from '@common/errors/index';
|
|
@@ -495,56 +1003,73 @@ declare module '@common/errors' {
|
|
|
495
1003
|
|
|
496
1004
|
declare module '@models/types' {
|
|
497
1005
|
export * from '@/var/prisma/index';
|
|
498
|
-
}
|
|
1006
|
+
}`,
|
|
499
1007
|
);
|
|
500
1008
|
}
|
|
501
1009
|
|
|
502
1010
|
private async warmupApp() {
|
|
503
|
-
|
|
504
1011
|
await app.warmup();
|
|
1012
|
+
}
|
|
505
1013
|
|
|
1014
|
+
private async refreshGeneratedArtifacts() {
|
|
1015
|
+
if (!this.refreshingGeneratedArtifacts) {
|
|
1016
|
+
this.refreshingGeneratedArtifacts = (async () => {
|
|
1017
|
+
this.indexServices();
|
|
1018
|
+
this.generateControllerModules();
|
|
1019
|
+
this.generateRoutingModules();
|
|
1020
|
+
})().finally(() => {
|
|
1021
|
+
this.refreshingGeneratedArtifacts = undefined;
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
await this.refreshingGeneratedArtifacts;
|
|
506
1026
|
}
|
|
507
1027
|
|
|
508
1028
|
public async refreshGeneratedTypings() {
|
|
509
|
-
|
|
510
1029
|
await this.warmupApp();
|
|
1030
|
+
await this.refreshGeneratedArtifacts();
|
|
1031
|
+
}
|
|
511
1032
|
|
|
512
|
-
|
|
513
|
-
|
|
1033
|
+
public consumeRecentCompilationResults() {
|
|
1034
|
+
const recentCompilationResults = { ...this.recentCompilationResults };
|
|
1035
|
+
this.recentCompilationResults = {};
|
|
1036
|
+
return recentCompilationResults;
|
|
514
1037
|
}
|
|
515
1038
|
|
|
516
1039
|
public async create() {
|
|
517
|
-
|
|
518
1040
|
await this.warmupApp();
|
|
519
1041
|
|
|
520
1042
|
this.cleanup();
|
|
521
1043
|
|
|
522
1044
|
this.fixNpmLinkIssues();
|
|
523
|
-
|
|
524
|
-
this.indexServices();
|
|
1045
|
+
await this.refreshGeneratedArtifacts();
|
|
525
1046
|
|
|
526
1047
|
// Create compilers
|
|
527
|
-
const multiCompiler =
|
|
528
|
-
|
|
529
|
-
|
|
1048
|
+
const multiCompiler = rspack([
|
|
1049
|
+
createServerConfig(app, this.mode, this.outputTarget),
|
|
1050
|
+
createClientConfig(app, this.mode, this.outputTarget),
|
|
530
1051
|
]);
|
|
531
1052
|
|
|
532
1053
|
for (const compiler of multiCompiler.compilers) {
|
|
533
|
-
|
|
534
1054
|
const name = compiler.name;
|
|
535
|
-
if (name === undefined)
|
|
536
|
-
throw new Error(`A name must be specified to each compiler.`);
|
|
1055
|
+
if (name === undefined) throw new Error(`A name must be specified to each compiler.`);
|
|
537
1056
|
|
|
538
1057
|
let timeStart = new Date();
|
|
539
1058
|
|
|
540
|
-
let finished: (
|
|
541
|
-
this.compiling[name] = new Promise((resolve) => finished = resolve);
|
|
1059
|
+
let finished: () => void;
|
|
1060
|
+
this.compiling[name] = new Promise((resolve) => (finished = resolve));
|
|
1061
|
+
|
|
1062
|
+
compiler.hooks.beforeRun.tapPromise(name, () => this.refreshGeneratedArtifacts());
|
|
1063
|
+
compiler.hooks.watchRun.tapPromise(name, () => this.refreshGeneratedArtifacts());
|
|
542
1064
|
|
|
543
1065
|
compiler.hooks.compile.tap(name, (compilation) => {
|
|
544
|
-
|
|
545
|
-
this.callbacks.before && this.callbacks.before( compiler );
|
|
1066
|
+
this.callbacks.before && this.callbacks.before(compiler);
|
|
546
1067
|
|
|
547
|
-
this.
|
|
1068
|
+
this.recentModifiedFiles[name] = [...(compiler.modifiedFiles ? [...compiler.modifiedFiles] : [])].map(
|
|
1069
|
+
(filepath) => normalizePath(path.resolve(filepath)),
|
|
1070
|
+
);
|
|
1071
|
+
|
|
1072
|
+
this.compiling[name] = new Promise((resolve) => (finished = resolve));
|
|
548
1073
|
|
|
549
1074
|
timeStart = new Date();
|
|
550
1075
|
console.info(`[${name}] Compiling ...`);
|
|
@@ -552,22 +1077,29 @@ declare module '@models/types' {
|
|
|
552
1077
|
|
|
553
1078
|
/* TODO: Ne pas résoudre la promise tant que la recompilation des données indexées (icones, identité, ...)
|
|
554
1079
|
n'a pas été achevée */
|
|
555
|
-
compiler.hooks.done.tap(name, stats => {
|
|
1080
|
+
compiler.hooks.done.tap(name, (stats) => {
|
|
1081
|
+
const compilationSucceeded = !stats.hasErrors();
|
|
1082
|
+
this.recentCompilationResults[name] = {
|
|
1083
|
+
succeeded: compilationSucceeded,
|
|
1084
|
+
hash: typeof stats.hash === 'string' ? stats.hash : undefined,
|
|
1085
|
+
modifiedFiles: this.recentModifiedFiles[name] || [],
|
|
1086
|
+
};
|
|
556
1087
|
|
|
557
1088
|
// Shiow status
|
|
558
1089
|
const timeEnd = new Date();
|
|
559
1090
|
const time = timeEnd.getTime() - timeStart.getTime();
|
|
560
|
-
if (
|
|
561
|
-
|
|
1091
|
+
if (!compilationSucceeded) {
|
|
562
1092
|
console.info(stats.toString(compiler.options.stats));
|
|
563
1093
|
console.error(`[${name}] Failed to compile after ${time} ms`);
|
|
564
1094
|
|
|
565
1095
|
// Exit process with code 0, so the CI container can understand building failed
|
|
566
1096
|
// Only in prod, because in dev, we want the compiler watcher continue running
|
|
567
|
-
if (this.mode === 'prod')
|
|
568
|
-
process.exit(0);
|
|
569
|
-
|
|
1097
|
+
if (this.mode === 'prod') process.exit(0);
|
|
570
1098
|
} else {
|
|
1099
|
+
if (name === 'client') {
|
|
1100
|
+
writeClientManifest(stats, app.outputPath(this.outputTarget));
|
|
1101
|
+
}
|
|
1102
|
+
|
|
571
1103
|
this.debug && console.info(stats.toString(compiler.options.stats));
|
|
572
1104
|
console.info(`[${name}] Finished compilation after ${time} ms`);
|
|
573
1105
|
}
|
|
@@ -579,7 +1111,5 @@ declare module '@models/types' {
|
|
|
579
1111
|
}
|
|
580
1112
|
|
|
581
1113
|
return multiCompiler;
|
|
582
|
-
|
|
583
1114
|
}
|
|
584
|
-
|
|
585
1115
|
}
|