proteum 1.0.2 → 2.0.0-1

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 (185) hide show
  1. package/AGENTS.md +101 -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 +13 -11
  11. package/cli/app/index.ts +74 -82
  12. package/cli/bin.js +1 -1
  13. package/cli/commands/build.ts +51 -14
  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 +189 -64
  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 +18 -0
  22. package/cli/commands/typecheck.ts +18 -0
  23. package/cli/compiler/client/identite.ts +80 -53
  24. package/cli/compiler/client/index.ts +139 -213
  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 +11 -22
  31. package/cli/compiler/common/generatedRouteModules.ts +368 -0
  32. package/cli/compiler/common/index.ts +31 -65
  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 +781 -230
  37. package/cli/compiler/server/index.ts +59 -75
  38. package/cli/compiler/writeIfChanged.ts +21 -0
  39. package/cli/index.ts +71 -72
  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/doc/TODO.md +1 -1
  95. package/eslint.js +62 -0
  96. package/package.json +14 -49
  97. package/prettier.config.cjs +9 -0
  98. package/scripts/cleanup-generated-controllers.ts +62 -0
  99. package/scripts/fix-reference-app-typing.ts +490 -0
  100. package/scripts/refactor-client-app-imports.ts +244 -0
  101. package/scripts/refactor-client-pages.ts +587 -0
  102. package/scripts/refactor-server-controllers.ts +470 -0
  103. package/scripts/refactor-server-runtime-aliases.ts +360 -0
  104. package/scripts/restore-client-app-import-files.ts +41 -0
  105. package/scripts/restore-files-from-git-head.ts +20 -0
  106. package/scripts/update-codex-agents.ts +35 -0
  107. package/server/app/commands.ts +35 -64
  108. package/server/app/container/config.ts +48 -59
  109. package/server/app/container/console/index.ts +202 -248
  110. package/server/app/container/index.ts +33 -71
  111. package/server/app/controller/index.ts +61 -0
  112. package/server/app/index.ts +39 -105
  113. package/server/app/service/container.ts +41 -42
  114. package/server/app/service/index.ts +120 -147
  115. package/server/context.ts +1 -1
  116. package/server/index.ts +25 -1
  117. package/server/services/auth/index.ts +75 -115
  118. package/server/services/auth/router/index.ts +31 -32
  119. package/server/services/auth/router/request.ts +14 -16
  120. package/server/services/cron/CronTask.ts +13 -26
  121. package/server/services/cron/index.ts +14 -36
  122. package/server/services/disks/driver.ts +40 -58
  123. package/server/services/disks/drivers/local/index.ts +79 -90
  124. package/server/services/disks/drivers/s3/index.ts +116 -163
  125. package/server/services/disks/index.ts +23 -38
  126. package/server/services/email/index.ts +45 -104
  127. package/server/services/email/utils.ts +14 -27
  128. package/server/services/fetch/index.ts +53 -85
  129. package/server/services/prisma/Facet.ts +39 -91
  130. package/server/services/prisma/index.ts +74 -110
  131. package/server/services/router/generatedRuntime.ts +29 -0
  132. package/server/services/router/http/index.ts +78 -73
  133. package/server/services/router/http/multipart.ts +19 -42
  134. package/server/services/router/index.ts +378 -365
  135. package/server/services/router/request/api.ts +26 -25
  136. package/server/services/router/request/index.ts +44 -51
  137. package/server/services/router/request/service.ts +7 -11
  138. package/server/services/router/request/validation/zod.ts +111 -148
  139. package/server/services/router/response/index.ts +110 -125
  140. package/server/services/router/response/mask/Filter.ts +31 -72
  141. package/server/services/router/response/mask/index.ts +8 -15
  142. package/server/services/router/response/mask/selecteurs.ts +11 -25
  143. package/server/services/router/response/page/clientManifest.ts +25 -0
  144. package/server/services/router/response/page/document.tsx +199 -127
  145. package/server/services/router/response/page/index.tsx +89 -94
  146. package/server/services/router/service.ts +13 -15
  147. package/server/services/schema/index.ts +17 -26
  148. package/server/services/schema/request.ts +19 -33
  149. package/server/services/schema/router/index.ts +8 -11
  150. package/server/services/security/encrypt/aes/index.ts +15 -35
  151. package/server/utils/slug.ts +29 -35
  152. package/skills/clean-project-code/SKILL.md +63 -0
  153. package/skills/clean-project-code/agents/openai.yaml +4 -0
  154. package/tsconfig.common.json +4 -3
  155. package/tsconfig.json +4 -1
  156. package/types/aliases.d.ts +17 -21
  157. package/types/controller-input.test.ts +48 -0
  158. package/types/express-extra.d.ts +6 -0
  159. package/types/global/constants.d.ts +13 -0
  160. package/types/global/express-extra.d.ts +6 -0
  161. package/types/global/modules.d.ts +13 -16
  162. package/types/global/utils.d.ts +17 -49
  163. package/types/global/vendors.d.ts +62 -0
  164. package/types/icons.d.ts +65 -1
  165. package/types/uuid.d.ts +3 -0
  166. package/types/vendors.d.ts +62 -0
  167. package/cli/compiler/common/babel/index.ts +0 -170
  168. package/cli/compiler/common/babel/plugins/index.ts +0 -0
  169. package/cli/compiler/common/babel/plugins/services.ts +0 -586
  170. package/cli/compiler/common/babel/routes/imports.ts +0 -127
  171. package/cli/compiler/common/babel/routes/routes.ts +0 -1130
  172. package/client/services/captcha/index.ts +0 -67
  173. package/client/services/socket/index.ts +0 -147
  174. package/common/data/rte/nodes.ts +0 -83
  175. package/common/data/stats.ts +0 -90
  176. package/common/utils/rte.ts +0 -183
  177. package/server/services/auth/old.ts +0 -277
  178. package/server/services/cache/commands.ts +0 -41
  179. package/server/services/cache/index.ts +0 -297
  180. package/server/services/cache/service.json +0 -6
  181. package/server/services/socket/index.ts +0 -162
  182. package/server/services/socket/scope.ts +0 -226
  183. package/server/services/socket/service.json +0 -6
  184. package/server/services_old/SocketClient.ts +0 -92
  185. 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,32 +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'
33
+ export type TCompileMode = 'dev' | 'prod';
34
+ export type TCompileOutputTarget = 'dev' | 'bin';
37
35
 
38
36
  /*----------------------------------
39
37
  - BASE CONFIG
40
38
  ----------------------------------*/
41
39
 
42
- export default function createCommonConfig(
43
- app: App,
44
- side: TAppSide,
40
+ export default function createCommonConfig(
41
+ app: App,
42
+ side: TAppSide,
45
43
  mode: TCompileMode,
46
- ): webpack.Configuration {
47
-
44
+ outputTarget: TCompileOutputTarget = mode === 'dev' ? 'dev' : 'bin',
45
+ ): Configuration {
48
46
  const dev = mode === 'dev';
49
- const config: webpack.Configuration = {
50
-
47
+ const enableFilesystemCache = dev ? cli.args.cache !== false : cli.args.cache === true;
48
+ const config: Configuration = {
51
49
  // Project root
52
50
  context: app.paths.root,
53
51
 
@@ -66,16 +64,13 @@ export default function createCommonConfig(
66
64
  },
67
65
 
68
66
  plugins: [
69
-
70
- // https://webpack.js.org/plugins/define-plugin/
71
- new webpack.DefinePlugin({
72
-
67
+ new rspack.DefinePlugin({
73
68
  // Flags
74
69
  __DEV__: dev,
75
70
  SERVER: side === 'server',
76
71
 
77
72
  // Core
78
- CORE_VERSION: JSON.stringify( cli.packageJson.version ),
73
+ CORE_VERSION: JSON.stringify(cli.packageJson.version),
79
74
  CORE_PATH: JSON.stringify(cli.paths.core.root),
80
75
 
81
76
  // Application
@@ -83,29 +78,15 @@ export default function createCommonConfig(
83
78
  BUILD_ID: JSON.stringify(app.buildId),
84
79
  APP_PATH: JSON.stringify(app.paths.root),
85
80
  APP_NAME: JSON.stringify(app.identity.web.title),
86
-
81
+ APP_OUTPUT_DIR: JSON.stringify(path.basename(app.outputPath(outputTarget))),
82
+ PROTEUM_DEV_EVENT_PORT: JSON.stringify(dev ? (app.devEventPort ?? null) : null),
83
+ PROTEUM_ROUTER_PORT_OVERRIDE: JSON.stringify(app.routerPortOverride ?? null),
87
84
  }),
88
85
 
89
- ...(side === 'client' && cli.args.analyze ? [
90
-
91
- new BundleAnalyzerPlugin({
92
- defaultSizes: 'stat',
93
- openAnalyzer: false
94
- }),
95
-
96
- ] : []),
97
-
98
- ...(dev ? [
99
-
100
- // HMR
101
- //new webpack.HotModuleReplacementPlugin()
102
-
103
- ] : []),
104
-
86
+ ...(dev ? [] : []),
105
87
  ],
106
88
 
107
89
  resolve: {
108
-
109
90
  // Empêche le remplatcement des chemins vers les liens symboliques par leur vrai chemin
110
91
  // Permet de conserver le chemin des packages enregistrés via npm link
111
92
  // Equivalent tsconfig: preserveSymlinks: true
@@ -121,40 +102,25 @@ export default function createCommonConfig(
121
102
  // our own hints via the FileSizeReporter
122
103
  performance: false,
123
104
 
124
- // Don't attempt to continue if there are any errors.
105
+ // Smoke builds should fail immediately on the first compilation error.
125
106
  bail: !dev,
126
107
 
127
- // Persistent cache speeds up cold starts and incremental rebuilds.
128
- cache: /*(dev || cli.args.cache === true) ? {
129
- type: 'filesystem',
130
- cacheDirectory: path.join(app.paths.cache, 'webpack', side),
131
- compression: false,
132
- buildDependencies: {
133
- config: [__filename],
134
- },
135
- } : */false,
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,
136
117
 
137
118
  // Increase compilation performance
138
119
  profile: false,
139
120
 
140
- // Pour bundle-stats
141
- // https://github.com/relative-ci/bundle-stats/tree/master/packages/cli#webpack-configuration
142
- stats: {
143
- cached: dev,
144
- cachedAssets: dev,
145
- chunks: dev,
146
- chunkModules: dev,
147
- colors: true,
148
- hash: dev,
149
- modules: dev,
150
- reasons: dev,
151
- timings: true,
152
- version: dev,
153
- errorDetails: true
154
- },
155
-
156
- }
121
+ // Keep output compact while preserving timing and error context.
122
+ stats: { colors: true, errors: true, errorDetails: true, timings: true, warnings: true },
123
+ };
157
124
 
158
125
  return config;
159
-
160
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
+ };