proteum 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/AGENTS.md +44 -98
  2. package/README.md +143 -10
  3. package/agents/framework/AGENTS.md +146 -886
  4. package/agents/project/AGENTS.md +73 -127
  5. package/agents/project/client/AGENTS.md +22 -93
  6. package/agents/project/client/pages/AGENTS.md +24 -26
  7. package/agents/project/server/routes/AGENTS.md +10 -8
  8. package/agents/project/server/services/AGENTS.md +22 -159
  9. package/agents/project/tests/AGENTS.md +11 -8
  10. package/cli/app/config.ts +7 -20
  11. package/cli/bin.js +8 -0
  12. package/cli/commands/command.ts +243 -0
  13. package/cli/commands/commandLocalRunner.js +198 -0
  14. package/cli/commands/create.ts +5 -0
  15. package/cli/commands/deploy/web.ts +1 -2
  16. package/cli/commands/dev.ts +98 -2
  17. package/cli/commands/doctor.ts +8 -74
  18. package/cli/commands/explain.ts +8 -186
  19. package/cli/commands/init.ts +2 -94
  20. package/cli/commands/trace.ts +228 -0
  21. package/cli/compiler/artifacts/commands.ts +217 -0
  22. package/cli/compiler/artifacts/manifest.ts +35 -21
  23. package/cli/compiler/artifacts/services.ts +300 -1
  24. package/cli/compiler/client/index.ts +43 -8
  25. package/cli/compiler/common/commands.ts +175 -0
  26. package/cli/compiler/common/index.ts +1 -1
  27. package/cli/compiler/common/proteumManifest.ts +15 -114
  28. package/cli/compiler/index.ts +25 -2
  29. package/cli/compiler/server/index.ts +31 -6
  30. package/cli/index.ts +1 -4
  31. package/cli/paths.ts +16 -1
  32. package/cli/presentation/commands.ts +104 -14
  33. package/cli/presentation/devSession.ts +22 -3
  34. package/cli/presentation/proteum_logo_400x400_square_icon.txt +400 -0
  35. package/cli/runtime/commands.ts +121 -4
  36. package/cli/scaffold/index.ts +720 -0
  37. package/cli/scaffold/templates.ts +344 -0
  38. package/cli/scaffold/types.ts +26 -0
  39. package/cli/tsconfig.json +4 -1
  40. package/cli/utils/check.ts +1 -1
  41. package/client/app/component.tsx +13 -9
  42. package/client/dev/profiler/index.tsx +2511 -0
  43. package/client/dev/profiler/noop.tsx +5 -0
  44. package/client/dev/profiler/runtime.noop.ts +116 -0
  45. package/client/dev/profiler/runtime.ts +840 -0
  46. package/client/services/router/components/router.tsx +30 -2
  47. package/client/services/router/index.tsx +27 -3
  48. package/client/services/router/request/api.ts +133 -17
  49. package/commands/proteum/diagnostics.ts +11 -0
  50. package/common/dev/commands.ts +50 -0
  51. package/common/dev/diagnostics.ts +298 -0
  52. package/common/dev/profiler.ts +92 -0
  53. package/common/dev/proteumManifest.ts +135 -0
  54. package/common/dev/requestTrace.ts +115 -0
  55. package/common/env/proteumEnv.ts +284 -0
  56. package/common/router/index.ts +4 -22
  57. package/docs/dev-commands.md +93 -0
  58. package/docs/diagnostics.md +88 -0
  59. package/docs/request-tracing.md +132 -0
  60. package/eslint.js +11 -6
  61. package/package.json +3 -3
  62. package/server/app/commands.ts +35 -370
  63. package/server/app/commandsManager.ts +393 -0
  64. package/server/app/container/config.ts +11 -49
  65. package/server/app/container/console/index.ts +2 -3
  66. package/server/app/container/index.ts +5 -2
  67. package/server/app/container/trace/index.ts +364 -0
  68. package/server/app/devCommands.ts +192 -0
  69. package/server/app/devDiagnostics.ts +53 -0
  70. package/server/app/index.ts +29 -6
  71. package/server/index.ts +0 -1
  72. package/server/services/auth/index.ts +525 -61
  73. package/server/services/auth/router/index.ts +106 -7
  74. package/server/services/cron/CronTask.ts +73 -5
  75. package/server/services/cron/index.ts +34 -11
  76. package/server/services/fetch/index.ts +3 -10
  77. package/server/services/prisma/index.ts +66 -4
  78. package/server/services/router/http/index.ts +173 -6
  79. package/server/services/router/index.ts +200 -12
  80. package/server/services/router/request/api.ts +30 -1
  81. package/server/services/router/response/index.ts +83 -10
  82. package/server/services/router/response/page/document.tsx +16 -0
  83. package/server/services/router/response/page/index.tsx +27 -1
  84. package/skills/clean-project-code/SKILL.md +7 -2
  85. package/test-results/.last-run.json +4 -0
  86. package/types/aliases.d.ts +6 -0
  87. package/types/global/utils.d.ts +7 -14
  88. package/Rte.zip +0 -0
  89. package/agents/project/agents.md.zip +0 -0
  90. package/doc/TODO.md +0 -71
  91. package/doc/front/router.md +0 -27
  92. package/doc/workspace/workspace.png +0 -0
  93. package/doc/workspace/workspace2.png +0 -0
  94. package/doc/workspace/workspace_26.01.22.png +0 -0
  95. package/server/services/router/http/session.ts.old +0 -40
@@ -27,6 +27,24 @@ const hmrClientEntry = path.join(cli.paths.core.root, 'client', 'dev', 'hmr.ts')
27
27
  const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
28
28
  const resolveFromAppOrCore = (app: App, request: string) =>
29
29
  require.resolve(request, { paths: [app.paths.root, cli.paths.core.root] });
30
+ const rewriteFrameworkAliasTargets = (app: App, aliases: Record<string, string | string[]>) => {
31
+ const installedCoreRoot = normalizeModulePath(path.join(app.paths.root, 'node_modules', 'proteum'));
32
+ const activeCoreRoot = normalizeModulePath(cli.paths.core.root);
33
+
34
+ if (installedCoreRoot === activeCoreRoot) return aliases;
35
+
36
+ const rewriteCandidate = (candidate: string) =>
37
+ normalizeModulePath(candidate).startsWith(installedCoreRoot + '/')
38
+ ? activeCoreRoot + normalizeModulePath(candidate).substring(installedCoreRoot.length)
39
+ : candidate;
40
+
41
+ return Object.fromEntries(
42
+ Object.entries(aliases).map(([alias, value]) => [
43
+ alias,
44
+ Array.isArray(value) ? value.map(rewriteCandidate) : rewriteCandidate(value),
45
+ ]),
46
+ );
47
+ };
30
48
 
31
49
  const getModulePath = (module: Module) => {
32
50
  const resource = typeof module.nameForCondition === 'function' ? module.nameForCondition() : undefined;
@@ -58,6 +76,8 @@ export default function createCompiler(
58
76
  logVerbose(`Creating compiler for client (${mode}).`);
59
77
  const dev = mode === 'dev';
60
78
  const outputPath = app.outputPath(outputTarget);
79
+ const installedCoreRoot = path.join(app.paths.root, 'node_modules', 'proteum');
80
+ const frameworkRoots = [cli.paths.core.root, installedCoreRoot];
61
81
 
62
82
  const commonConfig = createCommonConfig(app, 'client', mode, outputTarget);
63
83
 
@@ -73,11 +93,12 @@ export default function createCompiler(
73
93
 
74
94
  // Convert tsconfig paths into bundler aliases.
75
95
  const { aliases } = app.aliases.client.forWebpack({ modulesPath: app.paths.root + '/node_modules' });
96
+ const resolvedAliases = rewriteFrameworkAliasTargets(app, aliases);
76
97
 
77
98
  // We're not supposed in any case to import server libs from client
78
- delete aliases['@server'];
79
- delete aliases['@/server'];
80
- const rspackAliases = toRspackAliases(aliases);
99
+ delete resolvedAliases['@server'];
100
+ delete resolvedAliases['@/server'];
101
+ const rspackAliases = toRspackAliases(resolvedAliases);
81
102
  rspackAliases['@/client/router$'] = cli.paths.core.root + '/client/router.ts';
82
103
  rspackAliases['preact/jsx-runtime$'] = resolveFromAppOrCore(app, 'preact/jsx-runtime');
83
104
  rspackAliases['react/jsx-runtime$'] = resolveFromAppOrCore(app, 'preact/jsx-runtime');
@@ -133,16 +154,16 @@ export default function createCompiler(
133
154
  test: ssrScriptPattern,
134
155
  include: [
135
156
  app.paths.root + '/client',
136
- cli.paths.core.root + '/client',
137
157
  app.paths.client.generated,
138
158
 
139
159
  app.paths.root + '/common',
140
- cli.paths.core.root + '/common',
141
160
  app.paths.common.generated,
142
161
 
143
162
  app.paths.root + '/server',
144
- cli.paths.core.root + '/server',
145
163
  app.paths.server.generated,
164
+ ...frameworkRoots.map((rootPath) => rootPath + '/client'),
165
+ ...frameworkRoots.map((rootPath) => rootPath + '/common'),
166
+ ...frameworkRoots.map((rootPath) => rootPath + '/server'),
146
167
  ],
147
168
  loader: path.join(
148
169
  cli.paths.core.root,
@@ -157,17 +178,18 @@ export default function createCompiler(
157
178
  test: regex.scripts,
158
179
  include: [
159
180
  app.paths.root + '/client',
160
- cli.paths.core.root + '/client',
161
181
  app.paths.client.generated,
162
182
 
163
183
  app.paths.root + '/common',
164
- cli.paths.core.root + '/common',
165
184
  app.paths.common.generated,
166
185
 
167
186
  // Prisma 7 generates browser-safe TypeScript entrypoints under var/prisma.
168
187
  app.paths.root + '/var/prisma',
169
188
 
170
189
  app.paths.server.generated + '/models.ts',
190
+ ...frameworkRoots.map((rootPath) => rootPath + '/client'),
191
+ ...frameworkRoots.map((rootPath) => rootPath + '/common'),
192
+ ...frameworkRoots.map((rootPath) => rootPath + '/server'),
171
193
  ],
172
194
  rules: require('../common/scripts')({ app, side: 'client', dev }),
173
195
  },
@@ -199,6 +221,19 @@ export default function createCompiler(
199
221
  plugins: [
200
222
  ...(commonConfig.plugins || []),
201
223
 
224
+ ...(dev
225
+ ? []
226
+ : [
227
+ new rspack.NormalModuleReplacementPlugin(
228
+ /^@client\/dev\/profiler$/,
229
+ cli.paths.core.root + '/client/dev/profiler/noop.tsx',
230
+ ),
231
+ new rspack.NormalModuleReplacementPlugin(
232
+ /^@client\/dev\/profiler\/runtime$/,
233
+ cli.paths.core.root + '/client/dev/profiler/runtime.noop.ts',
234
+ ),
235
+ ]),
236
+
202
237
  // Extract CSS in dev too so SSR emits the same stylesheet links as production.
203
238
  new rspack.CssExtractRspackPlugin({}),
204
239
 
@@ -0,0 +1,175 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import ts from 'typescript';
4
+
5
+ export type TCommandSourceLocation = { line: number; column: number };
6
+
7
+ export type TCommandMethodMeta = {
8
+ name: string;
9
+ path: string;
10
+ sourceLocation: TCommandSourceLocation;
11
+ };
12
+
13
+ export type TCommandFileMeta = {
14
+ importPath: string;
15
+ filepath: string;
16
+ className: string;
17
+ commandBasePath: string;
18
+ methods: TCommandMethodMeta[];
19
+ };
20
+
21
+ type TCommandSearchDir = { importPrefix: string; root: string };
22
+
23
+ const getCommandSegments = (relativePath: string) => {
24
+ const segments = relativePath
25
+ .replace(/\.ts$/, '')
26
+ .split('/')
27
+ .filter(Boolean);
28
+
29
+ if (segments[segments.length - 1] === 'index') {
30
+ segments.pop();
31
+ }
32
+
33
+ return segments;
34
+ };
35
+
36
+ const getCommandBasePathFromFilepath = (filepath: string, root: string) =>
37
+ getCommandSegments(path.relative(root, filepath).replace(/\\/g, '/')).join('/');
38
+
39
+ const getGeneratedClassName = (filepath: string) => {
40
+ const filename = path.basename(filepath, '.ts').replace(/[^A-Za-z0-9_$]+/g, '_');
41
+ const normalized = filename.length ? filename : 'Commands';
42
+
43
+ return normalized[0].toUpperCase() + normalized.substring(1);
44
+ };
45
+
46
+ const buildImportPath = (searchDir: TCommandSearchDir, filepath: string) =>
47
+ searchDir.importPrefix + path.relative(searchDir.root, filepath).replace(/\\/g, '/').replace(/\.ts$/, '');
48
+
49
+ const findCommandFiles = (dir: string): string[] => {
50
+ if (!fs.existsSync(dir)) return [];
51
+
52
+ const files: string[] = [];
53
+
54
+ for (const dirent of fs.readdirSync(dir, { withFileTypes: true })) {
55
+ const filepath = path.join(dir, dirent.name);
56
+
57
+ if (dirent.isDirectory()) {
58
+ files.push(...findCommandFiles(filepath));
59
+ continue;
60
+ }
61
+
62
+ if (!dirent.isFile()) continue;
63
+ if (!dirent.name.endsWith('.ts')) continue;
64
+ if (dirent.name.endsWith('.d.ts')) continue;
65
+
66
+ files.push(filepath);
67
+ }
68
+
69
+ return files;
70
+ };
71
+
72
+ const parseSourceFile = (filepath: string, code: string) =>
73
+ ts.createSourceFile(
74
+ filepath,
75
+ code,
76
+ ts.ScriptTarget.Latest,
77
+ true,
78
+ filepath.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
79
+ );
80
+
81
+ const getNodeLocation = (sourceFile: ts.SourceFile, node: ts.Node): TCommandSourceLocation => {
82
+ const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
83
+
84
+ return { line: line + 1, column: character + 1 };
85
+ };
86
+
87
+ const hasModifier = (node: ts.Node, kind: ts.SyntaxKind) =>
88
+ !!node.modifiers?.some((modifier) => modifier.kind === kind);
89
+
90
+ const getDefaultExportClass = (sourceFile: ts.SourceFile) => {
91
+ const classes = new Map<string, ts.ClassDeclaration>();
92
+
93
+ for (const statement of sourceFile.statements) {
94
+ if (ts.isClassDeclaration(statement) && statement.name) {
95
+ classes.set(statement.name.text, statement);
96
+
97
+ if (hasModifier(statement, ts.SyntaxKind.DefaultKeyword)) {
98
+ return statement;
99
+ }
100
+ }
101
+ }
102
+
103
+ for (const statement of sourceFile.statements) {
104
+ if (!ts.isExportAssignment(statement) || statement.isExportEquals) continue;
105
+
106
+ if (ts.isIdentifier(statement.expression)) {
107
+ return classes.get(statement.expression.text);
108
+ }
109
+ }
110
+
111
+ return undefined;
112
+ };
113
+
114
+ const getExportedString = (sourceFile: ts.SourceFile, exportName: string) => {
115
+ for (const statement of sourceFile.statements) {
116
+ if (!ts.isVariableStatement(statement)) continue;
117
+ if (!hasModifier(statement, ts.SyntaxKind.ExportKeyword)) continue;
118
+
119
+ for (const declaration of statement.declarationList.declarations) {
120
+ if (!ts.isIdentifier(declaration.name)) continue;
121
+ if (declaration.name.text !== exportName) continue;
122
+ if (!declaration.initializer || !ts.isStringLiteral(declaration.initializer)) continue;
123
+
124
+ return declaration.initializer.text;
125
+ }
126
+ }
127
+
128
+ return undefined;
129
+ };
130
+
131
+ export const indexCommands = (searchDirs: TCommandSearchDir[]) => {
132
+ const commands: TCommandFileMeta[] = [];
133
+
134
+ for (const searchDir of searchDirs) {
135
+ const commandFiles = findCommandFiles(searchDir.root);
136
+
137
+ for (const filepath of commandFiles.sort((a, b) => a.localeCompare(b))) {
138
+ const code = fs.readFileSync(filepath, 'utf8');
139
+ const sourceFile = parseSourceFile(filepath, code);
140
+
141
+ const commandPathOverride = getExportedString(sourceFile, 'commandPath');
142
+ const defaultClass = getDefaultExportClass(sourceFile);
143
+
144
+ if (!defaultClass) continue;
145
+
146
+ const className = defaultClass.name?.text || getGeneratedClassName(filepath);
147
+ const commandBasePath = commandPathOverride || getCommandBasePathFromFilepath(filepath, searchDir.root);
148
+ const methods: TCommandMethodMeta[] = [];
149
+
150
+ for (const member of defaultClass.members) {
151
+ if (!ts.isMethodDeclaration(member)) continue;
152
+ if (!member.body) continue;
153
+ if (!member.name || !ts.isIdentifier(member.name)) continue;
154
+
155
+ methods.push({
156
+ name: member.name.text,
157
+ path: [commandBasePath, member.name.text].filter(Boolean).join('/'),
158
+ sourceLocation: getNodeLocation(sourceFile, member.name),
159
+ });
160
+ }
161
+
162
+ if (!methods.length) continue;
163
+
164
+ commands.push({
165
+ filepath,
166
+ importPath: buildImportPath(searchDir, filepath),
167
+ className,
168
+ commandBasePath,
169
+ methods,
170
+ });
171
+ }
172
+ }
173
+
174
+ return commands.sort((a, b) => a.filepath.localeCompare(b.filepath));
175
+ };
@@ -80,7 +80,7 @@ export default function createCommonConfig(
80
80
  APP_NAME: JSON.stringify(app.identity.web.title),
81
81
  APP_OUTPUT_DIR: JSON.stringify(path.basename(app.outputPath(outputTarget))),
82
82
  PROTEUM_DEV_EVENT_PORT: JSON.stringify(dev ? (app.devEventPort ?? null) : null),
83
- PROTEUM_ROUTER_PORT_OVERRIDE: JSON.stringify(app.routerPortOverride ?? null),
83
+ PROTEUM_PORT_OVERRIDE: JSON.stringify(app.routerPortOverride ?? null),
84
84
  }),
85
85
 
86
86
  ...(dev ? [] : []),
@@ -2,120 +2,21 @@ import path from 'path';
2
2
  import fs from 'fs-extra';
3
3
 
4
4
  import writeIfChanged from '../writeIfChanged';
5
-
6
- export type TProteumManifestScope = 'app' | 'framework';
7
- export type TProteumManifestSourceLocation = { line: number; column: number };
8
- export type TProteumManifestRouteTargetResolution = 'literal' | 'static-expression' | 'dynamic-expression';
9
- export type TProteumManifestDiagnosticLevel = 'warning' | 'error';
10
-
11
- export type TProteumManifestDiagnostic = {
12
- level: TProteumManifestDiagnosticLevel;
13
- code: string;
14
- message: string;
15
- filepath: string;
16
- sourceLocation?: TProteumManifestSourceLocation;
17
- relatedFilepaths?: string[];
18
- };
19
-
20
- export type TProteumManifestService = {
21
- kind: 'service' | 'ref';
22
- id?: string;
23
- registeredName: string;
24
- metaName?: string;
25
- parent: string;
26
- priority: number;
27
- importPath?: string;
28
- sourceDir?: string;
29
- metasFilepath?: string;
30
- refTo?: string;
31
- scope: TProteumManifestScope;
32
- };
33
-
34
- export type TProteumManifestController = {
35
- className: string;
36
- importPath: string;
37
- filepath: string;
38
- sourceLocation: TProteumManifestSourceLocation;
39
- routeBasePath: string;
40
- methodName: string;
41
- inputCallsCount: number;
42
- hasInput: boolean;
43
- routePath: string;
44
- httpPath: string;
45
- clientAccessor: string;
46
- scope: TProteumManifestScope;
47
- };
48
-
49
- export type TProteumManifestRoute = {
50
- kind: 'client-page' | 'client-error' | 'server-route';
51
- methodName: string;
52
- serviceLocalName: string;
53
- filepath: string;
54
- sourceLocation: TProteumManifestSourceLocation;
55
- targetResolution: TProteumManifestRouteTargetResolution;
56
- path?: string;
57
- pathRaw?: string;
58
- code?: number;
59
- codeRaw?: string;
60
- optionKeys: string[];
61
- normalizedOptionKeys: string[];
62
- invalidOptionKeys: string[];
63
- reservedOptionKeys: string[];
64
- optionsRaw?: string;
65
- hasSetup: boolean;
66
- chunkId?: string;
67
- chunkFilepath?: string;
68
- scope: TProteumManifestScope;
69
- };
70
-
71
- export type TProteumManifestLayout = {
72
- chunkId: string;
73
- filepath: string;
74
- importPath: string;
75
- depth: number;
76
- scope: TProteumManifestScope;
77
- };
78
-
79
- export type TProteumManifest = {
80
- version: 1;
81
- app: {
82
- root: string;
83
- coreRoot: string;
84
- identityFilepath: string;
85
- identity: {
86
- name: string;
87
- identifier: string;
88
- description: string;
89
- language?: string;
90
- locale?: string;
91
- title?: string;
92
- titleSuffix?: string;
93
- fullTitle?: string;
94
- webDescription?: string;
95
- version?: string;
96
- };
97
- };
98
- conventions: {
99
- routeSetupOptionKeys: string[];
100
- reservedRouteSetupKeys: string[];
101
- };
102
- env: {
103
- sourceFilepath: string;
104
- loadedTopLevelKeys: string[];
105
- requiredTopLevelKeys: string[];
106
- };
107
- services: {
108
- app: TProteumManifestService[];
109
- routerPlugins: TProteumManifestService[];
110
- };
111
- controllers: TProteumManifestController[];
112
- routes: {
113
- client: TProteumManifestRoute[];
114
- server: TProteumManifestRoute[];
115
- };
116
- layouts: TProteumManifestLayout[];
117
- diagnostics: TProteumManifestDiagnostic[];
118
- };
5
+ import type { TProteumManifest } from '@common/dev/proteumManifest';
6
+
7
+ export type {
8
+ TProteumManifest,
9
+ TProteumManifestCommand,
10
+ TProteumManifestController,
11
+ TProteumManifestDiagnostic,
12
+ TProteumManifestDiagnosticLevel,
13
+ TProteumManifestLayout,
14
+ TProteumManifestRoute,
15
+ TProteumManifestRouteTargetResolution,
16
+ TProteumManifestScope,
17
+ TProteumManifestService,
18
+ TProteumManifestSourceLocation,
19
+ } from '@common/dev/proteumManifest';
119
20
 
120
21
  export const getProteumManifestPath = (appRoot: string) => path.join(appRoot, '.proteum', 'manifest.json');
121
22
 
@@ -16,6 +16,7 @@ import { writeClientManifest } from './common/clientManifest';
16
16
  import { logVerbose } from '../runtime/verbose';
17
17
  import { createCompileReporter, type TCompileReporter } from '../presentation/compileReporter';
18
18
  import { generateRoutingArtifacts } from './artifacts/routing';
19
+ import { generateCommandArtifacts } from './artifacts/commands';
19
20
  import { generateControllerArtifacts } from './artifacts/controllers';
20
21
  import { generateServiceArtifacts } from './artifacts/services';
21
22
  import { writeCurrentProteumManifest } from './artifacts/manifest';
@@ -32,6 +33,7 @@ export default class Compiler {
32
33
  public compiling: { [compiler: string]: Promise<void> } = {};
33
34
  private recentCompilationResults: { [compiler: string]: TRecentCompilationResult } = {};
34
35
  private recentModifiedFiles: { [compiler: string]: string[] } = {};
36
+ private manualModifiedFiles: { [compiler: string]: Set<string> } = {};
35
37
  private refreshingGeneratedArtifacts?: Promise<void>;
36
38
  private compileReporter?: TCompileReporter;
37
39
 
@@ -140,11 +142,13 @@ export default class Compiler {
140
142
  this.refreshingGeneratedArtifacts = (async () => {
141
143
  const services = generateServiceArtifacts();
142
144
  const controllers = generateControllerArtifacts();
145
+ const commands = generateCommandArtifacts();
143
146
  const { clientRoutes, serverRoutes, layouts } = generateRoutingArtifacts();
144
147
 
145
148
  writeCurrentProteumManifest({
146
149
  services,
147
150
  controllers,
151
+ commands,
148
152
  routes: { client: clientRoutes, server: serverRoutes },
149
153
  layouts,
150
154
  });
@@ -172,6 +176,21 @@ export default class Compiler {
172
176
  return recentCompilationResults;
173
177
  }
174
178
 
179
+ public noteManualModifiedFiles(compilerNames: string[] | string, filepaths: string[]) {
180
+ const normalizedCompilerNames = Array.isArray(compilerNames) ? compilerNames : [compilerNames];
181
+ const normalizedFilepaths = filepaths.map((filepath) => normalizePath(path.resolve(filepath)));
182
+
183
+ for (const compilerName of normalizedCompilerNames) {
184
+ const pendingFiles = this.manualModifiedFiles[compilerName] || new Set<string>();
185
+
186
+ for (const filepath of normalizedFilepaths) {
187
+ pendingFiles.add(filepath);
188
+ }
189
+
190
+ this.manualModifiedFiles[compilerName] = pendingFiles;
191
+ }
192
+ }
193
+
175
194
  public async create() {
176
195
  await this.warmupApp();
177
196
 
@@ -204,9 +223,13 @@ export default class Compiler {
204
223
  compiler.hooks.compile.tap(name, (compilation) => {
205
224
  this.callbacks.before && this.callbacks.before(compiler);
206
225
 
207
- this.recentModifiedFiles[name] = [...(compiler.modifiedFiles ? [...compiler.modifiedFiles] : [])].map(
208
- (filepath) => normalizePath(path.resolve(filepath)),
226
+ const compilerModifiedFiles = [...(compiler.modifiedFiles ? [...compiler.modifiedFiles] : [])].map((filepath) =>
227
+ normalizePath(path.resolve(filepath)),
209
228
  );
229
+ const manualModifiedFiles = [...(this.manualModifiedFiles[name] || [])];
230
+ delete this.manualModifiedFiles[name];
231
+
232
+ this.recentModifiedFiles[name] = [...new Set([...compilerModifiedFiles, ...manualModifiedFiles])];
210
233
 
211
234
  this.compiling[name] = new Promise((resolve) => (finished = resolve));
212
235
 
@@ -49,6 +49,25 @@ const getDevGeneratedRuntimeEntries = (app: App) => ({
49
49
  __proteum_dev_routes: [app.paths.server.generated + '/routes.ts'],
50
50
  __proteum_dev_controllers: [app.paths.server.generated + '/controllers.ts'],
51
51
  });
52
+ const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
53
+ const rewriteFrameworkAliasTargets = (app: App, aliases: Record<string, string | string[]>) => {
54
+ const installedCoreRoot = normalizeModulePath(app.paths.root + '/node_modules/proteum');
55
+ const activeCoreRoot = normalizeModulePath(cli.paths.core.root);
56
+
57
+ if (installedCoreRoot === activeCoreRoot) return aliases;
58
+
59
+ const rewriteCandidate = (candidate: string) =>
60
+ normalizeModulePath(candidate).startsWith(installedCoreRoot + '/')
61
+ ? activeCoreRoot + normalizeModulePath(candidate).substring(installedCoreRoot.length)
62
+ : candidate;
63
+
64
+ return Object.fromEntries(
65
+ Object.entries(aliases).map(([alias, value]) => [
66
+ alias,
67
+ Array.isArray(value) ? value.map(rewriteCandidate) : rewriteCandidate(value),
68
+ ]),
69
+ );
70
+ };
52
71
 
53
72
  /*----------------------------------
54
73
  - CONFIG
@@ -61,14 +80,17 @@ export default function createCompiler(
61
80
  debug && console.info(`Creating compiler for server (${mode}).`);
62
81
  const dev = mode === 'dev';
63
82
  const outputPath = app.outputPath(outputTarget);
83
+ const installedCoreRoot = app.paths.root + '/node_modules/proteum';
84
+ const frameworkRoots = [cli.paths.core.root, installedCoreRoot];
64
85
 
65
86
  const commonConfig = createCommonConfig(app, 'server', mode, outputTarget);
66
87
  const { aliases } = app.aliases.server.forWebpack({ modulesPath: app.paths.root + '/node_modules' });
88
+ const resolvedAliases = rewriteFrameworkAliasTargets(app, aliases);
67
89
 
68
90
  // We're not supposed in any case to import client services from server
69
- delete aliases['@client/services'];
70
- delete aliases['@/client/services'];
71
- const rspackAliases = toRspackAliases(aliases);
91
+ delete resolvedAliases['@client/services'];
92
+ delete resolvedAliases['@/client/services'];
93
+ const rspackAliases = toRspackAliases(resolvedAliases);
72
94
  rspackAliases['@/client/router$'] = cli.paths.core.root + '/client/router.ts';
73
95
 
74
96
  debug &&
@@ -154,20 +176,23 @@ export default function createCompiler(
154
176
  test: regex.scripts,
155
177
  include: [
156
178
  app.paths.root + '/client',
157
- cli.paths.core.root + '/client',
158
179
  app.paths.client.generated,
159
180
 
160
181
  app.paths.root + '/common',
161
- cli.paths.core.root + '/common',
162
182
  app.paths.common.generated,
163
183
 
164
184
  // Prisma 7 generates TypeScript entrypoints under var/prisma.
165
185
  app.paths.root + '/var/prisma',
166
186
 
187
+ app.paths.root + '/commands',
188
+
167
189
  // Dossiers server uniquement pour le bundle server
168
190
  app.paths.root + '/server',
169
- cli.paths.core.root + '/server',
170
191
  app.paths.server.generated,
192
+ ...frameworkRoots.map((rootPath) => rootPath + '/commands'),
193
+ ...frameworkRoots.map((rootPath) => rootPath + '/client'),
194
+ ...frameworkRoots.map((rootPath) => rootPath + '/common'),
195
+ ...frameworkRoots.map((rootPath) => rootPath + '/server'),
171
196
 
172
197
  // Complle 5HTP modules so they can refer to the framework instance and aliases
173
198
  // Temp disabled because compile issue on vercel
package/cli/index.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  process.traceDeprecation = true;
2
2
 
3
- import fs from 'fs';
4
3
  import { Cli } from 'clipanion';
5
4
 
6
5
  import cli from './context';
@@ -9,12 +8,10 @@ import { renderCliOverview, renderCommandHelp, resolveCustomHelpRequest } from '
9
8
  import { normalizeHelpArgv, normalizeLegacyArgv } from './runtime/argv';
10
9
  import { createCli, registeredCommands } from './runtime/commands';
11
10
 
12
- const hasInitScaffold = () => fs.existsSync(`${cli.paths.core.cli}/skeleton`);
13
-
14
11
  export const runCli = async (argv: string[] = process.argv.slice(2)) => {
15
12
  const normalizedArgv = normalizeHelpArgv(normalizeLegacyArgv(argv), proteumCommandNames);
16
13
  const clipanion = createCli(String(cli.packageJson.version || ''));
17
- const initAvailable = hasInitScaffold();
14
+ const initAvailable = true;
18
15
  const helpRequest = resolveCustomHelpRequest(normalizedArgv);
19
16
 
20
17
  if (helpRequest.kind === 'overview') {
package/cli/paths.ts CHANGED
@@ -43,13 +43,28 @@ export const staticAssetName = /*isDebug ? '[name].[ext].[hash:8]' :*/ '[hash:8]
43
43
 
44
44
  const pathInfosDefaultOpts = { shortenExtensions: ['ts', 'js', 'tsx', 'jsx'], trimIndex: true };
45
45
 
46
+ const safeRealpath = (filepath: string) => {
47
+ try {
48
+ return fs.realpathSync(filepath);
49
+ } catch {
50
+ return path.resolve(filepath);
51
+ }
52
+ };
53
+
46
54
  const resolveCoreRoot = (appRoot: string) => {
55
+ const currentPackageRoot = path.resolve(__dirname, '..');
56
+ const currentBin = path.join(currentPackageRoot, 'cli', 'bin.js');
57
+ const invokedScript = process.argv[1] ? safeRealpath(process.argv[1]) : '';
58
+ const invokedCurrentPackage = invokedScript === safeRealpath(currentBin);
59
+
60
+ if (invokedCurrentPackage) return currentPackageRoot;
61
+
47
62
  const localInstall = path.join(appRoot, 'node_modules', 'proteum');
48
63
  if (fs.existsSync(localInstall)) return localInstall;
49
64
 
50
65
  // When running `npx`/global installs, there may be no local `node_modules/proteum` yet.
51
66
  // Fall back to the installed package root (this file lives in `<root>/cli`).
52
- return path.resolve(__dirname, '..');
67
+ return currentPackageRoot;
53
68
  };
54
69
 
55
70
  const normalizeImportPath = (value: string) => value.replace(/\\/g, '/');