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.
Files changed (184) hide show
  1. package/AGENTS.md +92 -0
  2. package/agents/codex/AGENTS.md +95 -0
  3. package/agents/codex/CODING_STYLE.md +71 -0
  4. package/agents/codex/agents.md.zip +0 -0
  5. package/agents/codex/client/AGENTS.md +102 -0
  6. package/agents/codex/client/pages/AGENTS.md +35 -0
  7. package/agents/codex/server/routes/AGENTS.md +12 -0
  8. package/agents/codex/server/services/AGENTS.md +137 -0
  9. package/agents/codex/tests/AGENTS.md +8 -0
  10. package/cli/app/config.ts +12 -17
  11. package/cli/app/index.ts +59 -99
  12. package/cli/bin.js +1 -1
  13. package/cli/commands/build.ts +23 -12
  14. package/cli/commands/check.ts +19 -0
  15. package/cli/commands/deploy/app.ts +4 -8
  16. package/cli/commands/deploy/web.ts +16 -20
  17. package/cli/commands/dev.ts +185 -75
  18. package/cli/commands/devEvents.ts +106 -0
  19. package/cli/commands/init.ts +63 -57
  20. package/cli/commands/lint.ts +21 -0
  21. package/cli/commands/refresh.ts +6 -6
  22. package/cli/commands/typecheck.ts +18 -0
  23. package/cli/compiler/client/identite.ts +79 -49
  24. package/cli/compiler/client/index.ts +132 -214
  25. package/cli/compiler/common/bundleAnalysis.ts +94 -0
  26. package/cli/compiler/common/clientManifest.ts +67 -0
  27. package/cli/compiler/common/controllers.ts +288 -0
  28. package/cli/compiler/common/files/autres.ts +7 -18
  29. package/cli/compiler/common/files/images.ts +40 -37
  30. package/cli/compiler/common/files/style.ts +12 -25
  31. package/cli/compiler/common/generatedRouteModules.ts +368 -0
  32. package/cli/compiler/common/index.ts +29 -68
  33. package/cli/compiler/common/loaders/forbid-ssr-import.js +13 -0
  34. package/cli/compiler/common/rspackAliases.ts +13 -0
  35. package/cli/compiler/common/scripts.ts +37 -0
  36. package/cli/compiler/index.ts +764 -234
  37. package/cli/compiler/server/index.ts +52 -77
  38. package/cli/compiler/writeIfChanged.ts +21 -0
  39. package/cli/index.ts +65 -90
  40. package/cli/paths.ts +51 -57
  41. package/cli/print.ts +17 -11
  42. package/cli/tsconfig.json +5 -4
  43. package/cli/utils/agents.ts +100 -0
  44. package/cli/utils/check.ts +71 -0
  45. package/cli/utils/index.ts +1 -3
  46. package/cli/utils/keyboard.ts +8 -25
  47. package/cli/utils/runProcess.ts +30 -0
  48. package/client/app/component.tsx +29 -29
  49. package/client/app/index.ts +36 -57
  50. package/client/app/service.ts +7 -12
  51. package/client/app.tsconfig.json +2 -2
  52. package/client/components/Dialog/Manager.ssr.tsx +40 -0
  53. package/client/components/Dialog/Manager.tsx +119 -150
  54. package/client/components/Dialog/status.tsx +3 -3
  55. package/client/components/index.ts +1 -1
  56. package/client/components/types.d.ts +1 -3
  57. package/client/dev/hmr.ts +65 -0
  58. package/client/global.d.ts +2 -2
  59. package/client/hooks.ts +6 -9
  60. package/client/index.ts +2 -1
  61. package/client/islands/index.ts +7 -0
  62. package/client/islands/useDeferredModule.ts +199 -0
  63. package/client/pages/_layout/index.tsx +4 -12
  64. package/client/pages/useHeader.tsx +14 -21
  65. package/client/router.ts +27 -0
  66. package/client/services/router/components/Link.tsx +34 -27
  67. package/client/services/router/components/Page.tsx +6 -14
  68. package/client/services/router/components/router.ssr.tsx +36 -0
  69. package/client/services/router/components/router.tsx +63 -83
  70. package/client/services/router/index.tsx +185 -220
  71. package/client/services/router/request/api.ts +97 -119
  72. package/client/services/router/request/history.ts +2 -2
  73. package/client/services/router/request/index.ts +13 -12
  74. package/client/services/router/request/multipart.ts +72 -62
  75. package/client/services/router/response/index.tsx +68 -61
  76. package/client/services/router/response/page.ts +28 -32
  77. package/client/utils/dom.ts +17 -33
  78. package/common/app/index.ts +3 -3
  79. package/common/data/chaines/index.ts +22 -23
  80. package/common/data/dates.ts +35 -70
  81. package/common/data/markdown.ts +42 -39
  82. package/common/dev/serverHotReload.ts +26 -0
  83. package/common/errors/index.tsx +110 -142
  84. package/common/router/contracts.ts +29 -0
  85. package/common/router/index.ts +89 -108
  86. package/common/router/layouts.ts +34 -47
  87. package/common/router/pageSetup.ts +50 -0
  88. package/common/router/register.ts +53 -24
  89. package/common/router/request/api.ts +30 -36
  90. package/common/router/request/index.ts +2 -8
  91. package/common/router/response/index.ts +8 -15
  92. package/common/router/response/page.ts +70 -58
  93. package/common/utils.ts +1 -1
  94. package/eslint.js +62 -0
  95. package/package.json +12 -45
  96. package/prettier.config.cjs +9 -0
  97. package/scripts/cleanup-generated-controllers.ts +62 -0
  98. package/scripts/fix-reference-app-typing.ts +490 -0
  99. package/scripts/refactor-client-app-imports.ts +244 -0
  100. package/scripts/refactor-client-pages.ts +587 -0
  101. package/scripts/refactor-server-controllers.ts +470 -0
  102. package/scripts/refactor-server-runtime-aliases.ts +360 -0
  103. package/scripts/restore-client-app-import-files.ts +41 -0
  104. package/scripts/restore-files-from-git-head.ts +20 -0
  105. package/scripts/update-codex-agents.ts +35 -0
  106. package/server/app/commands.ts +35 -64
  107. package/server/app/container/config.ts +39 -69
  108. package/server/app/container/console/index.ts +202 -248
  109. package/server/app/container/index.ts +33 -71
  110. package/server/app/controller/index.ts +61 -0
  111. package/server/app/index.ts +39 -105
  112. package/server/app/service/container.ts +41 -42
  113. package/server/app/service/index.ts +120 -147
  114. package/server/context.ts +1 -1
  115. package/server/index.ts +25 -1
  116. package/server/services/auth/index.ts +75 -115
  117. package/server/services/auth/router/index.ts +31 -32
  118. package/server/services/auth/router/request.ts +14 -16
  119. package/server/services/cron/CronTask.ts +13 -26
  120. package/server/services/cron/index.ts +14 -36
  121. package/server/services/disks/driver.ts +40 -58
  122. package/server/services/disks/drivers/local/index.ts +79 -90
  123. package/server/services/disks/drivers/s3/index.ts +116 -163
  124. package/server/services/disks/index.ts +23 -38
  125. package/server/services/email/index.ts +45 -104
  126. package/server/services/email/utils.ts +14 -27
  127. package/server/services/fetch/index.ts +53 -85
  128. package/server/services/prisma/Facet.ts +39 -91
  129. package/server/services/prisma/index.ts +74 -110
  130. package/server/services/router/generatedRuntime.ts +29 -0
  131. package/server/services/router/http/index.ts +77 -72
  132. package/server/services/router/http/multipart.ts +19 -42
  133. package/server/services/router/index.ts +378 -365
  134. package/server/services/router/request/api.ts +26 -25
  135. package/server/services/router/request/index.ts +44 -51
  136. package/server/services/router/request/service.ts +7 -11
  137. package/server/services/router/request/validation/zod.ts +111 -148
  138. package/server/services/router/response/index.ts +110 -125
  139. package/server/services/router/response/mask/Filter.ts +31 -72
  140. package/server/services/router/response/mask/index.ts +8 -15
  141. package/server/services/router/response/mask/selecteurs.ts +11 -25
  142. package/server/services/router/response/page/clientManifest.ts +25 -0
  143. package/server/services/router/response/page/document.tsx +199 -127
  144. package/server/services/router/response/page/index.tsx +89 -94
  145. package/server/services/router/service.ts +13 -15
  146. package/server/services/schema/index.ts +17 -26
  147. package/server/services/schema/request.ts +19 -33
  148. package/server/services/schema/router/index.ts +8 -11
  149. package/server/services/security/encrypt/aes/index.ts +15 -35
  150. package/server/utils/slug.ts +29 -32
  151. package/skills/clean-project-code/SKILL.md +63 -0
  152. package/skills/clean-project-code/agents/openai.yaml +4 -0
  153. package/tsconfig.common.json +4 -3
  154. package/tsconfig.json +4 -1
  155. package/types/aliases.d.ts +17 -21
  156. package/types/controller-input.test.ts +48 -0
  157. package/types/express-extra.d.ts +6 -0
  158. package/types/global/constants.d.ts +1 -0
  159. package/types/global/express-extra.d.ts +6 -0
  160. package/types/global/modules.d.ts +13 -16
  161. package/types/global/utils.d.ts +17 -49
  162. package/types/global/vendors.d.ts +62 -0
  163. package/types/icons.d.ts +65 -1
  164. package/types/uuid.d.ts +3 -0
  165. package/types/vendors.d.ts +62 -0
  166. package/cli/compiler/common/babel/index.ts +0 -173
  167. package/cli/compiler/common/babel/plugins/index.ts +0 -0
  168. package/cli/compiler/common/babel/plugins/services.ts +0 -586
  169. package/cli/compiler/common/babel/routes/imports.ts +0 -127
  170. package/cli/compiler/common/babel/routes/routes.ts +0 -1170
  171. package/client/services/captcha/index.ts +0 -67
  172. package/client/services/socket/index.ts +0 -147
  173. package/common/data/rte/nodes.ts +0 -83
  174. package/common/data/stats.ts +0 -90
  175. package/common/utils/rte.ts +0 -183
  176. package/server/services/auth/old.ts +0 -277
  177. package/server/services/cache/commands.ts +0 -41
  178. package/server/services/cache/index.ts +0 -297
  179. package/server/services/cache/service.json +0 -6
  180. package/server/services/socket/index.ts +0 -162
  181. package/server/services/socket/scope.ts +0 -226
  182. package/server/services/socket/service.json +0 -6
  183. package/server/services_old/SocketClient.ts +0 -92
  184. package/server/services_old/Token.old.ts +0 -97
@@ -0,0 +1,368 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import ts from 'typescript';
4
+
5
+ import writeIfChanged from '../writeIfChanged';
6
+
7
+ type TRouteSide = 'client' | 'server';
8
+ type TRouteRuntime = 'client' | 'server';
9
+
10
+ type TImportedService = { importedName: string; localName: string };
11
+
12
+ type TRouteDefinition = { args: ts.NodeArray<ts.Expression>; methodName: string; serviceLocalName: string };
13
+
14
+ type TGeneratedClientRouteModuleOptions = { chunkId: string; filepath: string };
15
+
16
+ type TWriteGeneratedRouteModuleOptions = {
17
+ outputFilepath: string;
18
+ runtime: TRouteRuntime;
19
+ side: TRouteSide;
20
+ sourceFilepath: string;
21
+ clientRoute?: TGeneratedClientRouteModuleOptions;
22
+ routeSourceFilepaths?: Set<string>;
23
+ };
24
+
25
+ const clientRouterImportSources = new Set(['@client/router', '@/client/router']);
26
+ const routerMethods = new Set(['page', 'error', 'get', 'post', 'put', 'delete', 'patch']);
27
+
28
+ const parseSourceFile = (filepath: string, code: string) =>
29
+ ts.createSourceFile(
30
+ filepath,
31
+ code,
32
+ ts.ScriptTarget.Latest,
33
+ true,
34
+ filepath.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
35
+ );
36
+
37
+ const normalizeFilepath = (value: string) => path.resolve(value).replace(/\\/g, '/');
38
+
39
+ const getNodeText = (sourceFile: ts.SourceFile, node: ts.Node) =>
40
+ sourceFile.text.slice(node.getStart(sourceFile), node.getEnd());
41
+
42
+ const routeModuleExtensions = ['.ts', '.tsx', '.js', '.jsx'];
43
+
44
+ const resolveRouteImport = (sourceFilepath: string, moduleSpecifier: string, routeSourceFilepaths?: Set<string>) => {
45
+ if (!routeSourceFilepaths || !moduleSpecifier.startsWith('.')) return undefined;
46
+
47
+ const absoluteBasePath = path.resolve(path.dirname(sourceFilepath), moduleSpecifier);
48
+ const candidates = [
49
+ absoluteBasePath,
50
+ ...routeModuleExtensions.map((extension) => absoluteBasePath + extension),
51
+ ...routeModuleExtensions.map((extension) => path.join(absoluteBasePath, `index${extension}`)),
52
+ ];
53
+
54
+ return candidates.find((candidate) => routeSourceFilepaths.has(normalizeFilepath(candidate)));
55
+ };
56
+
57
+ const addImportedService = (importedServices: TImportedService[], importedName: string, localName: string) => {
58
+ if (
59
+ importedServices.some(
60
+ (importedService) =>
61
+ importedService.importedName === importedName && importedService.localName === localName,
62
+ )
63
+ ) {
64
+ return;
65
+ }
66
+
67
+ importedServices.push({ importedName, localName });
68
+ };
69
+
70
+ const collectImportedServices = (
71
+ sourceFile: ts.SourceFile,
72
+ side: TRouteSide,
73
+ stripRanges: Array<{ start: number; end: number }>,
74
+ ) => {
75
+ const importedServices: TImportedService[] = [];
76
+
77
+ for (const statement of sourceFile.statements) {
78
+ if (!ts.isImportDeclaration(statement)) continue;
79
+ if (!statement.importClause) continue;
80
+ if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
81
+
82
+ const source = statement.moduleSpecifier.text;
83
+ const isClientRouterImport = side === 'client' && clientRouterImportSources.has(source);
84
+
85
+ if (source !== '@app' && !isClientRouterImport) continue;
86
+
87
+ if (isClientRouterImport && statement.importClause.name) {
88
+ addImportedService(importedServices, 'Router', statement.importClause.name.text);
89
+ }
90
+
91
+ for (const specifier of statement.importClause.namedBindings
92
+ ? ts.isNamedImports(statement.importClause.namedBindings)
93
+ ? statement.importClause.namedBindings.elements
94
+ : []
95
+ : []) {
96
+ addImportedService(
97
+ importedServices,
98
+ specifier.propertyName?.text || specifier.name.text,
99
+ specifier.name.text,
100
+ );
101
+ }
102
+
103
+ stripRanges.push({ start: statement.getStart(sourceFile), end: statement.getEnd() });
104
+ }
105
+
106
+ return importedServices;
107
+ };
108
+
109
+ const collectNestedRouteImports = (
110
+ sourceFile: ts.SourceFile,
111
+ sourceFilepath: string,
112
+ routeSourceFilepaths: Set<string> | undefined,
113
+ stripRanges: Array<{ start: number; end: number }>,
114
+ ) => {
115
+ for (const statement of sourceFile.statements) {
116
+ if (!ts.isImportDeclaration(statement)) continue;
117
+ if (statement.importClause) continue;
118
+ if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
119
+
120
+ const routeImportPath = resolveRouteImport(
121
+ sourceFilepath,
122
+ statement.moduleSpecifier.text,
123
+ routeSourceFilepaths,
124
+ );
125
+
126
+ if (!routeImportPath) continue;
127
+
128
+ stripRanges.push({ start: statement.getStart(sourceFile), end: statement.getEnd() });
129
+ }
130
+ };
131
+
132
+ const collectRouteDefinitions = (
133
+ sourceFile: ts.SourceFile,
134
+ importedServices: TImportedService[],
135
+ stripRanges: Array<{ start: number; end: number }>,
136
+ ) => {
137
+ const importedServiceNames = new Set(importedServices.map((importedService) => importedService.localName));
138
+ const definitions: TRouteDefinition[] = [];
139
+
140
+ for (const statement of sourceFile.statements) {
141
+ if (!ts.isExpressionStatement(statement)) continue;
142
+ if (!ts.isCallExpression(statement.expression)) continue;
143
+ if (!ts.isPropertyAccessExpression(statement.expression.expression)) continue;
144
+
145
+ const callee = statement.expression.expression;
146
+ if (!ts.isIdentifier(callee.expression)) continue;
147
+ if (!routerMethods.has(callee.name.text)) continue;
148
+ if (!importedServiceNames.has(callee.expression.text)) continue;
149
+
150
+ definitions.push({
151
+ args: statement.expression.arguments,
152
+ methodName: callee.name.text,
153
+ serviceLocalName: callee.expression.text,
154
+ });
155
+
156
+ stripRanges.push({ start: statement.getStart(sourceFile), end: statement.getEnd() });
157
+ }
158
+
159
+ return definitions;
160
+ };
161
+
162
+ const buildRemainingSource = (sourceFile: ts.SourceFile, stripRanges: Array<{ start: number; end: number }>) => {
163
+ const sortedRanges = stripRanges.sort((a, b) => a.start - b.start);
164
+ const chunks: string[] = [];
165
+ let cursor = 0;
166
+
167
+ for (const range of sortedRanges) {
168
+ if (cursor < range.start) chunks.push(sourceFile.text.slice(cursor, range.start));
169
+ cursor = Math.max(cursor, range.end);
170
+ }
171
+
172
+ if (cursor < sourceFile.text.length) {
173
+ chunks.push(sourceFile.text.slice(cursor));
174
+ }
175
+
176
+ return chunks.join('').trim();
177
+ };
178
+
179
+ const normalizeRelativeImportPath = (value: string) => (value.startsWith('.') ? value : `./${value}`);
180
+
181
+ const rebaseRelativeModuleSpecifiers = (code: string, outputFilepath: string, sourceFilepath: string) => {
182
+ const outputDir = path.dirname(outputFilepath);
183
+ const sourceDir = path.dirname(sourceFilepath);
184
+ const sourceFile = parseSourceFile(outputFilepath, code);
185
+ const replacements: Array<{ start: number; end: number; value: string }> = [];
186
+
187
+ const addReplacement = (literal: ts.StringLiteralLike) => {
188
+ if (!literal.text.startsWith('.')) return;
189
+
190
+ const absoluteTarget = path.resolve(sourceDir, literal.text);
191
+ const nextRelativePath = normalizeRelativeImportPath(
192
+ path.relative(outputDir, absoluteTarget).replace(/\\/g, '/'),
193
+ );
194
+
195
+ replacements.push({
196
+ start: literal.getStart(sourceFile),
197
+ end: literal.getEnd(),
198
+ value: JSON.stringify(nextRelativePath),
199
+ });
200
+ };
201
+
202
+ const visit = (node: ts.Node) => {
203
+ if (
204
+ (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
205
+ node.moduleSpecifier &&
206
+ ts.isStringLiteral(node.moduleSpecifier)
207
+ ) {
208
+ addReplacement(node.moduleSpecifier);
209
+ }
210
+
211
+ if (
212
+ ts.isCallExpression(node) &&
213
+ node.arguments.length > 0 &&
214
+ ts.isStringLiteral(node.arguments[0]) &&
215
+ (node.expression.kind === ts.SyntaxKind.ImportKeyword ||
216
+ (ts.isIdentifier(node.expression) && node.expression.text === 'require'))
217
+ ) {
218
+ addReplacement(node.arguments[0]);
219
+ }
220
+
221
+ if (
222
+ ts.isImportTypeNode(node) &&
223
+ ts.isLiteralTypeNode(node.argument) &&
224
+ ts.isStringLiteral(node.argument.literal)
225
+ ) {
226
+ addReplacement(node.argument.literal);
227
+ }
228
+
229
+ ts.forEachChild(node, visit);
230
+ };
231
+
232
+ ts.forEachChild(sourceFile, visit);
233
+
234
+ return replacements
235
+ .sort((a, b) => b.start - a.start)
236
+ .reduce(
237
+ (currentCode, replacement) =>
238
+ currentCode.slice(0, replacement.start) + replacement.value + currentCode.slice(replacement.end),
239
+ code,
240
+ );
241
+ };
242
+
243
+ const buildDestructuring = (importedServices: TImportedService[]) => {
244
+ const parts = importedServices.map(({ importedName, localName }) =>
245
+ importedName === localName ? importedName : `${importedName}: ${localName}`,
246
+ );
247
+
248
+ return `const { ${parts.join(', ')} } = app;`;
249
+ };
250
+
251
+ const buildClientRegisterArgs = (
252
+ sourceFile: ts.SourceFile,
253
+ definition: TRouteDefinition,
254
+ clientRoute: TGeneratedClientRouteModuleOptions,
255
+ ) => {
256
+ const [, ...routeArgs] = [...definition.args];
257
+ const injectedOptions = `{ id: ${JSON.stringify(clientRoute.chunkId)}, filepath: ${JSON.stringify(clientRoute.filepath)} }`;
258
+
259
+ if (routeArgs.length === 1) {
260
+ return [injectedOptions, getNodeText(sourceFile, routeArgs[0])];
261
+ }
262
+
263
+ if (routeArgs.length === 2) {
264
+ if (ts.isObjectLiteralExpression(routeArgs[0])) {
265
+ return [
266
+ `{ ...(${getNodeText(sourceFile, routeArgs[0])}), id: ${JSON.stringify(clientRoute.chunkId)}, filepath: ${JSON.stringify(clientRoute.filepath)} }`,
267
+ getNodeText(sourceFile, routeArgs[1]),
268
+ ];
269
+ }
270
+
271
+ return [injectedOptions, getNodeText(sourceFile, routeArgs[0]), getNodeText(sourceFile, routeArgs[1])];
272
+ }
273
+
274
+ if (routeArgs.length === 3 && ts.isObjectLiteralExpression(routeArgs[0])) {
275
+ return [
276
+ `{ ...(${getNodeText(sourceFile, routeArgs[0])}), id: ${JSON.stringify(clientRoute.chunkId)}, filepath: ${JSON.stringify(clientRoute.filepath)} }`,
277
+ getNodeText(sourceFile, routeArgs[1]),
278
+ getNodeText(sourceFile, routeArgs[2]),
279
+ ];
280
+ }
281
+
282
+ throw new Error(
283
+ `Unsupported client route signature in ${sourceFile.fileName}. Expected Router.page/error with 2-4 arguments.`,
284
+ );
285
+ };
286
+
287
+ const buildRegisterStatements = (
288
+ sourceFile: ts.SourceFile,
289
+ side: TRouteSide,
290
+ definitions: TRouteDefinition[],
291
+ clientRoute?: TGeneratedClientRouteModuleOptions,
292
+ ) => {
293
+ if (side === 'client') {
294
+ if (!clientRoute) {
295
+ throw new Error(`Missing client route metadata for ${sourceFile.fileName}.`);
296
+ }
297
+
298
+ if (definitions.length !== 1) {
299
+ throw new Error(
300
+ `Frontend route definition files can contain only one route definition. ${definitions.length} were found in ${sourceFile.fileName}.`,
301
+ );
302
+ }
303
+
304
+ const definition = definitions[0];
305
+ const [routePath, ...routeArgs] = [...definition.args];
306
+ const finalArgs = [
307
+ getNodeText(sourceFile, routePath),
308
+ ...buildClientRegisterArgs(sourceFile, definition, clientRoute),
309
+ ];
310
+
311
+ return [`return ${definition.serviceLocalName}.${definition.methodName}(${finalArgs.join(', ')});`];
312
+ }
313
+
314
+ return definitions.map((definition) => {
315
+ const args = [...definition.args].map((arg) => getNodeText(sourceFile, arg));
316
+
317
+ return `${definition.serviceLocalName}.${definition.methodName}(${args.join(', ')});`;
318
+ });
319
+ };
320
+
321
+ export const getGeneratedRouteModuleFilepath = (generatedRoot: string, sourceRoot: string, sourceFilepath: string) =>
322
+ path.join(generatedRoot, 'route-modules', path.relative(sourceRoot, sourceFilepath));
323
+
324
+ export const writeGeneratedRouteModule = ({
325
+ outputFilepath,
326
+ runtime,
327
+ side,
328
+ sourceFilepath,
329
+ clientRoute,
330
+ routeSourceFilepaths,
331
+ }: TWriteGeneratedRouteModuleOptions) => {
332
+ const code = fs.readFileSync(sourceFilepath, 'utf8');
333
+ const sourceFile = parseSourceFile(sourceFilepath, code);
334
+ const stripRanges: Array<{ start: number; end: number }> = [];
335
+ const importedServices = collectImportedServices(sourceFile, side, stripRanges);
336
+ collectNestedRouteImports(sourceFile, sourceFilepath, routeSourceFilepaths, stripRanges);
337
+ const definitions = collectRouteDefinitions(sourceFile, importedServices, stripRanges);
338
+
339
+ if (definitions.length === 0) {
340
+ throw new Error(`No route definitions were found in ${sourceFilepath}.`);
341
+ }
342
+
343
+ const remainingSource = rebaseRelativeModuleSpecifiers(
344
+ buildRemainingSource(sourceFile, stripRanges),
345
+ outputFilepath,
346
+ sourceFilepath,
347
+ );
348
+ const registerStatements = buildRegisterStatements(sourceFile, side, definitions, clientRoute);
349
+ const runtimeAppImportPath = runtime === 'client' ? '@/client/index' : '@/server/.generated/app';
350
+
351
+ const content = `/*----------------------------------
352
+ - GENERATED FILE
353
+ ----------------------------------*/
354
+
355
+ // This file is generated by Proteum from ${path.relative(process.cwd(), sourceFilepath).replace(/\\/g, '/')}.
356
+ // Do not edit it manually.
357
+
358
+ import type __GeneratedRouteApp from ${JSON.stringify(runtimeAppImportPath)};
359
+
360
+ ${remainingSource}
361
+ ${remainingSource ? '\n' : ''}export const __register = (app: __GeneratedRouteApp) => {
362
+ ${buildDestructuring(importedServices)}
363
+ ${registerStatements.join('\n ')}
364
+ };
365
+ `;
366
+
367
+ return writeIfChanged(outputFilepath, content);
368
+ };
@@ -3,12 +3,9 @@
3
3
  ----------------------------------*/
4
4
 
5
5
  // npm
6
- import webpack from 'webpack';
7
6
  import dayjs from 'dayjs';
8
7
  import path from 'path';
9
-
10
- // Plugins
11
- const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
8
+ import { rspack, type Configuration } from '@rspack/core';
12
9
 
13
10
  // Core
14
11
  import cli from '../..';
@@ -22,35 +19,33 @@ import type { TAppSide } from '../../app';
22
19
  ----------------------------------*/
23
20
 
24
21
  export const regex = {
25
- scripts: /\.(ts|tsx)$/,
22
+ scripts: /\.(ts|tsx|js|jsx)$/,
26
23
  style: /\.(css|less|scss)$/,
27
24
  images: /\.(bmp|gif|jpg|jpeg|png|ico|svg)$/, // SVG gérés par SVGR
28
25
  fonts: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
29
26
  staticAssetName: /*isDebug ? '[name].[ext].[hash:8]' :*/ '[hash:8][ext]',
30
- }
27
+ };
31
28
 
32
29
  /*----------------------------------
33
30
  - TYPES
34
31
  ----------------------------------*/
35
32
 
36
- export type TCompileMode = 'dev' | 'prod'
37
- export type TCompileOutputTarget = 'dev' | 'bin'
33
+ export type TCompileMode = 'dev' | 'prod';
34
+ export type TCompileOutputTarget = 'dev' | 'bin';
38
35
 
39
36
  /*----------------------------------
40
37
  - BASE CONFIG
41
38
  ----------------------------------*/
42
39
 
43
- export default function createCommonConfig(
44
- app: App,
45
- side: TAppSide,
40
+ export default function createCommonConfig(
41
+ app: App,
42
+ side: TAppSide,
46
43
  mode: TCompileMode,
47
44
  outputTarget: TCompileOutputTarget = mode === 'dev' ? 'dev' : 'bin',
48
- ): webpack.Configuration {
49
-
45
+ ): Configuration {
50
46
  const dev = mode === 'dev';
51
- const buildDev = dev && outputTarget === 'bin';
52
- const config: webpack.Configuration = {
53
-
47
+ const enableFilesystemCache = dev ? cli.args.cache !== false : cli.args.cache === true;
48
+ const config: Configuration = {
54
49
  // Project root
55
50
  context: app.paths.root,
56
51
 
@@ -69,16 +64,13 @@ export default function createCommonConfig(
69
64
  },
70
65
 
71
66
  plugins: [
72
-
73
- // https://webpack.js.org/plugins/define-plugin/
74
- new webpack.DefinePlugin({
75
-
67
+ new rspack.DefinePlugin({
76
68
  // Flags
77
69
  __DEV__: dev,
78
70
  SERVER: side === 'server',
79
71
 
80
72
  // Core
81
- CORE_VERSION: JSON.stringify( cli.packageJson.version ),
73
+ CORE_VERSION: JSON.stringify(cli.packageJson.version),
82
74
  CORE_PATH: JSON.stringify(cli.paths.core.root),
83
75
 
84
76
  // Application
@@ -87,30 +79,14 @@ export default function createCommonConfig(
87
79
  APP_PATH: JSON.stringify(app.paths.root),
88
80
  APP_NAME: JSON.stringify(app.identity.web.title),
89
81
  APP_OUTPUT_DIR: JSON.stringify(path.basename(app.outputPath(outputTarget))),
82
+ PROTEUM_DEV_EVENT_PORT: JSON.stringify(dev ? (app.devEventPort ?? null) : null),
90
83
  PROTEUM_ROUTER_PORT_OVERRIDE: JSON.stringify(app.routerPortOverride ?? null),
91
-
92
84
  }),
93
85
 
94
- ...(side === 'client' && cli.args.analyze ? [
95
-
96
- new BundleAnalyzerPlugin({
97
- defaultSizes: 'stat',
98
- openAnalyzer: false
99
- }),
100
-
101
- ] : []),
102
-
103
- ...(dev ? [
104
-
105
- // HMR
106
- //new webpack.HotModuleReplacementPlugin()
107
-
108
- ] : []),
109
-
86
+ ...(dev ? [] : []),
110
87
  ],
111
88
 
112
89
  resolve: {
113
-
114
90
  // Empêche le remplatcement des chemins vers les liens symboliques par leur vrai chemin
115
91
  // Permet de conserver le chemin des packages enregistrés via npm link
116
92
  // Equivalent tsconfig: preserveSymlinks: true
@@ -127,39 +103,24 @@ export default function createCommonConfig(
127
103
  performance: false,
128
104
 
129
105
  // Smoke builds should fail immediately on the first compilation error.
130
- bail: buildDev || !dev,
131
-
132
- // Persistent cache speeds up repeated local build-dev invocations.
133
- cache: (buildDev || cli.args.cache === true) ? {
134
- type: 'filesystem',
135
- cacheDirectory: path.join(app.paths.cache, 'webpack', side, buildDev ? 'build-dev' : mode),
136
- compression: false,
137
- buildDependencies: {
138
- config: [__filename],
139
- },
140
- } : false,
106
+ bail: !dev,
107
+
108
+ // Persistent cache speeds up repeated local dev and prod build invocations.
109
+ cache: enableFilesystemCache
110
+ ? {
111
+ type: 'filesystem',
112
+ cacheDirectory: path.join(app.paths.cache, 'rspack', side, mode),
113
+ compression: false,
114
+ buildDependencies: { config: [__filename] },
115
+ }
116
+ : false,
141
117
 
142
118
  // Increase compilation performance
143
119
  profile: false,
144
120
 
145
- // Pour bundle-stats
146
- // https://github.com/relative-ci/bundle-stats/tree/master/packages/cli#webpack-configuration
147
- stats: {
148
- cached: dev,
149
- cachedAssets: dev,
150
- chunks: dev,
151
- chunkModules: dev,
152
- colors: true,
153
- hash: dev,
154
- modules: dev,
155
- reasons: dev,
156
- timings: true,
157
- version: dev,
158
- errorDetails: true
159
- },
160
-
161
- }
121
+ // Keep output compact while preserving timing and error context.
122
+ stats: { colors: true, errors: true, errorDetails: true, timings: true, warnings: true },
123
+ };
162
124
 
163
125
  return config;
164
-
165
126
  }
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ module.exports = function forbidSsrImportLoader() {
4
+ const resourcePath = this.resourcePath || this.request || '<unknown>';
5
+
6
+ throw new Error(
7
+ [
8
+ 'SSR-only module imported into the client bundle:',
9
+ resourcePath,
10
+ 'Provide the browser implementation without the ".ssr" suffix and keep SSR-specific logic in "*.ssr.tsx" files.',
11
+ ].join('\n'),
12
+ );
13
+ };
@@ -0,0 +1,13 @@
1
+ import fs from 'fs-extra';
2
+
3
+ type TWebpackAliases = Record<string, string | string[]>;
4
+
5
+ export const toRspackAliases = (aliases: TWebpackAliases) =>
6
+ Object.fromEntries(
7
+ Object.entries(aliases).map(([alias, value]) => {
8
+ const candidates = Array.isArray(value) ? value : [value];
9
+ const selectedCandidate = candidates.find((candidate) => fs.existsSync(candidate)) || candidates[0];
10
+
11
+ return [alias, selectedCandidate];
12
+ }),
13
+ );
@@ -0,0 +1,37 @@
1
+ import type { App, TAppSide } from '@cli/app';
2
+ import type { RuleSetRule } from '@rspack/core';
3
+
4
+ type TScriptRuleOptions = { app: App; side: TAppSide; dev: boolean };
5
+
6
+ const shouldExcludeNodeModule = (filePath: string) => {
7
+ if (!filePath.includes('node_modules')) return false;
8
+
9
+ if (filePath.includes('node_modules/proteum') && !filePath.includes('node_modules/proteum/node_modules')) {
10
+ return false;
11
+ }
12
+
13
+ return true;
14
+ };
15
+
16
+ const getSwcTarget = (side: TAppSide) => (side === 'client' ? 'es2022' : 'es2021');
17
+
18
+ module.exports = ({ side, dev }: TScriptRuleOptions): RuleSetRule[] => {
19
+ return [
20
+ {
21
+ loader: 'builtin:swc-loader',
22
+ type: 'javascript/auto',
23
+ exclude: shouldExcludeNodeModule,
24
+ options: {
25
+ sourceMaps: true,
26
+ jsc: {
27
+ target: getSwcTarget(side),
28
+ loose: true,
29
+ parser: { syntax: 'typescript', tsx: true, decorators: false, dynamicImport: true },
30
+ transform: {
31
+ react: { runtime: 'automatic', importSource: 'preact', development: dev, refresh: false },
32
+ },
33
+ },
34
+ },
35
+ },
36
+ ];
37
+ };