proteum 2.1.9 → 2.2.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/.codex/environments/environment.toml +11 -0
- package/AGENTS.md +25 -11
- package/README.md +19 -9
- package/agents/project/AGENTS.md +165 -120
- package/agents/project/CODING_STYLE.md +1 -1
- package/agents/project/app-root/AGENTS.md +16 -0
- package/agents/project/client/AGENTS.md +5 -5
- package/agents/project/client/pages/AGENTS.md +13 -13
- package/agents/project/diagnostics.md +19 -10
- package/agents/project/optimizations.md +5 -6
- package/agents/project/root/AGENTS.md +295 -0
- package/agents/project/server/routes/AGENTS.md +2 -2
- package/agents/project/server/services/AGENTS.md +4 -2
- package/agents/project/tests/AGENTS.md +2 -2
- package/cli/app/index.ts +31 -7
- package/cli/commands/configure.ts +226 -0
- package/cli/commands/dev.ts +0 -2
- package/cli/commands/diagnose.ts +33 -1
- package/cli/commands/explain.ts +1 -1
- package/cli/commands/migrate.ts +51 -0
- package/cli/commands/orient.ts +169 -0
- package/cli/commands/perf.ts +8 -1
- package/cli/commands/verify.ts +1003 -49
- package/cli/compiler/artifacts/manifest.ts +4 -4
- package/cli/compiler/artifacts/routing.ts +2 -2
- package/cli/compiler/artifacts/services.ts +12 -3
- package/cli/compiler/client/index.ts +65 -19
- package/cli/compiler/common/files/style.ts +47 -2
- package/cli/compiler/common/generatedRouteModules.ts +31 -38
- package/cli/compiler/common/index.ts +10 -0
- package/cli/compiler/common/proteumManifest.ts +1 -0
- package/cli/compiler/server/index.ts +34 -9
- package/cli/context.ts +6 -1
- package/cli/index.ts +7 -8
- package/cli/migrate/pageContract.ts +516 -0
- package/cli/paths.ts +47 -6
- package/cli/presentation/commands.ts +100 -10
- package/cli/presentation/devSession.ts +4 -6
- package/cli/presentation/help.ts +2 -2
- package/cli/presentation/ink.ts +10 -5
- package/cli/presentation/welcome.ts +2 -4
- package/cli/runtime/commands.ts +94 -1
- package/cli/scaffold/index.ts +2 -2
- package/cli/scaffold/templates.ts +4 -2
- package/cli/utils/agents.ts +273 -58
- package/client/dev/profiler/index.tsx +3 -2
- package/client/router.ts +10 -2
- package/client/services/router/index.tsx +6 -22
- package/common/dev/connect.ts +20 -4
- package/common/dev/console.ts +7 -0
- package/common/dev/contractsDoctor.ts +354 -0
- package/common/dev/diagnostics.ts +10 -7
- package/common/dev/inspection.ts +830 -38
- package/common/dev/performance.ts +19 -5
- package/common/dev/profiler.ts +1 -0
- package/common/dev/proteumManifest.ts +5 -4
- package/common/dev/requestTrace.ts +12 -1
- package/common/router/contracts.ts +8 -11
- package/common/router/index.ts +2 -2
- package/common/router/pageData.ts +72 -0
- package/common/router/register.ts +10 -46
- package/common/router/response/page.ts +28 -16
- package/docs/dev-sessions.md +8 -4
- package/docs/diagnostics.md +77 -11
- package/docs/migrate-from-2.1.3.md +388 -0
- package/docs/request-tracing.md +25 -6
- package/package.json +6 -1
- package/scripts/update-codex-agents.ts +2 -2
- package/server/app/container/console/index.ts +11 -1
- package/server/app/container/trace/index.ts +117 -0
- package/server/app/devDiagnostics.ts +1 -1
- package/server/app/index.ts +5 -1
- package/server/services/auth/index.ts +9 -0
- package/server/services/router/index.ts +64 -14
- package/server/services/router/request/api.ts +7 -1
- package/server/services/router/response/index.ts +8 -28
- package/types/global/vendors.d.ts +12 -0
- package/types/vendors.d.ts +12 -0
- package/common/router/pageSetup.ts +0 -51
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import app from '../../app';
|
|
5
5
|
import cli from '../..';
|
|
6
6
|
import { inspectProteumEnv } from '../../../common/env/proteumEnv';
|
|
7
|
-
import {
|
|
7
|
+
import { reservedRouteOptionKeys, routeOptionKeys } from '../../../common/router/pageData';
|
|
8
8
|
import { getProjectInstructionGitignoreEntries } from '../../utils/agents';
|
|
9
9
|
import {
|
|
10
10
|
TProteumManifest,
|
|
@@ -303,7 +303,7 @@ export const writeCurrentProteumManifest = ({
|
|
|
303
303
|
});
|
|
304
304
|
|
|
305
305
|
const manifest: TProteumManifest = {
|
|
306
|
-
version:
|
|
306
|
+
version: 10,
|
|
307
307
|
app: {
|
|
308
308
|
root: normalizeAbsolutePath(app.paths.root),
|
|
309
309
|
coreRoot: normalizeAbsolutePath(cli.paths.core.root),
|
|
@@ -338,8 +338,8 @@ export const writeCurrentProteumManifest = ({
|
|
|
338
338
|
},
|
|
339
339
|
},
|
|
340
340
|
conventions: {
|
|
341
|
-
|
|
342
|
-
|
|
341
|
+
routeOptionKeys: [...routeOptionKeys],
|
|
342
|
+
reservedRouteOptionKeys: [...reservedRouteOptionKeys],
|
|
343
343
|
},
|
|
344
344
|
env: {
|
|
345
345
|
source: 'process.env',
|
|
@@ -59,7 +59,7 @@ const buildClientRouteManifestEntry = (filepath: string): TProteumManifestRoute
|
|
|
59
59
|
invalidOptionKeys: definition.invalidOptionKeys,
|
|
60
60
|
reservedOptionKeys: definition.reservedOptionKeys,
|
|
61
61
|
optionsRaw: definition.optionsRaw,
|
|
62
|
-
|
|
62
|
+
hasData: definition.hasData,
|
|
63
63
|
chunkId: pageChunk.chunkId,
|
|
64
64
|
chunkFilepath: normalizePath(pageChunk.filepath),
|
|
65
65
|
scope: 'app',
|
|
@@ -83,7 +83,7 @@ const buildServerRouteManifestEntries = (filepath: string) =>
|
|
|
83
83
|
invalidOptionKeys: definition.invalidOptionKeys,
|
|
84
84
|
reservedOptionKeys: definition.reservedOptionKeys,
|
|
85
85
|
optionsRaw: definition.optionsRaw,
|
|
86
|
-
|
|
86
|
+
hasData: definition.hasData,
|
|
87
87
|
scope: 'app',
|
|
88
88
|
}));
|
|
89
89
|
|
|
@@ -791,15 +791,24 @@ import type ${appClassIdentifier}Client from '@/client/index';
|
|
|
791
791
|
export type ClientContext = ${appClassIdentifier}Client["Router"]["context"];
|
|
792
792
|
|
|
793
793
|
type GlobalClientContextStore = typeof globalThis & {
|
|
794
|
-
__proteumClientContexts?: Record<string, React.Context<ClientContext>>;
|
|
794
|
+
__proteumClientContexts?: Record<string, React.Context<ClientContext | undefined>>;
|
|
795
795
|
};
|
|
796
796
|
|
|
797
797
|
const globalClientContextStore = globalThis as GlobalClientContextStore;
|
|
798
798
|
const clientContexts = (globalClientContextStore.__proteumClientContexts ??= {});
|
|
799
799
|
|
|
800
800
|
export const ReactClientContext =
|
|
801
|
-
clientContexts['${appClassIdentifier}'] ?? (clientContexts['${appClassIdentifier}'] = React.createContext<ClientContext>(
|
|
802
|
-
export default (): ClientContext =>
|
|
801
|
+
clientContexts['${appClassIdentifier}'] ?? (clientContexts['${appClassIdentifier}'] = React.createContext<ClientContext | undefined>(undefined));
|
|
802
|
+
export default (): ClientContext => {
|
|
803
|
+
const context = React.useContext<ClientContext | undefined>(ReactClientContext);
|
|
804
|
+
if (context) return context;
|
|
805
|
+
|
|
806
|
+
throw new Error(
|
|
807
|
+
'Proteum router context hook was called outside the App provider. This is a framework contract failure. ' +
|
|
808
|
+
'Likely fix: move the hook back under Router.page render ownership or pass the required values explicitly. ' +
|
|
809
|
+
'Re-check both SSR and client navigation after the fix.',
|
|
810
|
+
);
|
|
811
|
+
};`,
|
|
803
812
|
);
|
|
804
813
|
|
|
805
814
|
writeIfChanged(
|
|
@@ -21,23 +21,40 @@ import type { App } from '../../app';
|
|
|
21
21
|
|
|
22
22
|
const debug = false;
|
|
23
23
|
const ssrScriptPattern = /\.ssr\.(ts|tsx)$/;
|
|
24
|
-
const normalizedCoreRoot = cli.paths.framework.activeRoot.replace(/\\/g, '/');
|
|
25
|
-
const hmrClientEntry = path.join(cli.paths.core.root, 'client', 'dev', 'hmr.ts');
|
|
26
|
-
|
|
27
24
|
const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
|
|
28
|
-
const
|
|
29
|
-
const rewriteFrameworkAliasTargets = (app: App, aliases: Record<string, string | string[]>) => {
|
|
25
|
+
const getFrameworkSourceRoot = () => {
|
|
30
26
|
const installedCoreRoot = cli.paths.framework.installedRoot
|
|
31
27
|
? normalizeModulePath(cli.paths.framework.installedRoot)
|
|
32
28
|
: undefined;
|
|
33
29
|
const activeCoreRoot = normalizeModulePath(cli.paths.framework.activeRoot);
|
|
34
30
|
|
|
35
|
-
if (
|
|
31
|
+
if (installedCoreRoot && activeCoreRoot.includes('/node_modules/')) {
|
|
32
|
+
return installedCoreRoot;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return activeCoreRoot;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const resolveFromAppOrCore = (_app: App, request: string) => cli.paths.resolveRequest(request);
|
|
39
|
+
const rewriteFrameworkAliasTargets = (aliases: Record<string, string | string[]>) => {
|
|
40
|
+
const visibleFrameworkRoots = [
|
|
41
|
+
...cli.paths.getVisiblePackageInstallRoots('proteum'),
|
|
42
|
+
cli.paths.framework.installedRoot,
|
|
43
|
+
cli.paths.framework.activeRoot,
|
|
44
|
+
]
|
|
45
|
+
.filter((rootPath): rootPath is string => typeof rootPath === 'string' && rootPath !== '')
|
|
46
|
+
.map((rootPath) => normalizeModulePath(rootPath))
|
|
47
|
+
.filter((rootPath, index, list) => list.indexOf(rootPath) === index);
|
|
48
|
+
const frameworkSourceRoot = getFrameworkSourceRoot();
|
|
36
49
|
|
|
37
50
|
const rewriteCandidate = (candidate: string) =>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
visibleFrameworkRoots.reduce((nextCandidate, rootPath) => {
|
|
52
|
+
const normalizedCandidate = normalizeModulePath(nextCandidate);
|
|
53
|
+
|
|
54
|
+
return normalizedCandidate.startsWith(rootPath + '/')
|
|
55
|
+
? frameworkSourceRoot + normalizedCandidate.substring(rootPath.length)
|
|
56
|
+
: nextCandidate;
|
|
57
|
+
}, candidate);
|
|
41
58
|
|
|
42
59
|
return Object.fromEntries(
|
|
43
60
|
Object.entries(aliases).map(([alias, value]) => [
|
|
@@ -62,8 +79,29 @@ const isExternalVendorModule = (module: Module) => {
|
|
|
62
79
|
|
|
63
80
|
const isCoreSourceModule = (module: Module) => {
|
|
64
81
|
const modulePath = getModulePath(module);
|
|
82
|
+
const frameworkSourceRoot = getFrameworkSourceRoot();
|
|
83
|
+
|
|
84
|
+
return modulePath.startsWith(frameworkSourceRoot + '/') || modulePath.includes('/node_modules/proteum/');
|
|
85
|
+
};
|
|
86
|
+
const resolveLightningCssTargets = (app: App) => {
|
|
87
|
+
const browserslistConfig = app.packageJson.browserslist;
|
|
88
|
+
|
|
89
|
+
if (typeof browserslistConfig === 'string') return browserslistConfig;
|
|
90
|
+
|
|
91
|
+
if (Array.isArray(browserslistConfig) && browserslistConfig.every((target) => typeof target === 'string'))
|
|
92
|
+
return browserslistConfig;
|
|
93
|
+
|
|
94
|
+
if (!browserslistConfig || typeof browserslistConfig !== 'object') return undefined;
|
|
65
95
|
|
|
66
|
-
|
|
96
|
+
for (const env of ['production', 'defaults']) {
|
|
97
|
+
const targets = browserslistConfig[env];
|
|
98
|
+
|
|
99
|
+
if (typeof targets === 'string') return targets;
|
|
100
|
+
|
|
101
|
+
if (Array.isArray(targets) && targets.every((target) => typeof target === 'string')) return targets;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return undefined;
|
|
67
105
|
};
|
|
68
106
|
|
|
69
107
|
/*----------------------------------
|
|
@@ -77,8 +115,13 @@ export default function createCompiler(
|
|
|
77
115
|
logVerbose(`Creating compiler for client (${mode}).`);
|
|
78
116
|
const dev = mode === 'dev';
|
|
79
117
|
const outputPath = app.outputPath(outputTarget);
|
|
80
|
-
const
|
|
118
|
+
const frameworkSourceRoot = getFrameworkSourceRoot();
|
|
119
|
+
const frameworkRoots = [frameworkSourceRoot, ...cli.paths.getFrameworkRoots()].filter(
|
|
120
|
+
(rootPath, index, list) => list.indexOf(rootPath) === index,
|
|
121
|
+
);
|
|
81
122
|
const transpileModuleDirectories = app.transpileModuleDirectories;
|
|
123
|
+
const lightningCssTargets = resolveLightningCssTargets(app);
|
|
124
|
+
const hmrClientEntry = path.join(frameworkSourceRoot, 'client', 'dev', 'hmr.ts');
|
|
82
125
|
|
|
83
126
|
const commonConfig = createCommonConfig(app, 'client', mode, outputTarget);
|
|
84
127
|
|
|
@@ -94,13 +137,14 @@ export default function createCompiler(
|
|
|
94
137
|
|
|
95
138
|
// Convert tsconfig paths into bundler aliases.
|
|
96
139
|
const { aliases } = app.aliases.client.forWebpack({ modulesPath: cli.paths.framework.appNodeModulesRoot });
|
|
97
|
-
const resolvedAliases = rewriteFrameworkAliasTargets(
|
|
140
|
+
const resolvedAliases = rewriteFrameworkAliasTargets(aliases);
|
|
98
141
|
|
|
99
142
|
// We're not supposed in any case to import server libs from client
|
|
100
143
|
delete resolvedAliases['@server'];
|
|
101
144
|
delete resolvedAliases['@/server'];
|
|
102
145
|
const rspackAliases = toRspackAliases(resolvedAliases);
|
|
103
|
-
rspackAliases['
|
|
146
|
+
rspackAliases['proteum'] = frameworkSourceRoot;
|
|
147
|
+
rspackAliases['@/client/router$'] = frameworkSourceRoot + '/client/router.ts';
|
|
104
148
|
rspackAliases['preact/jsx-runtime$'] = resolveFromAppOrCore(app, 'preact/jsx-runtime');
|
|
105
149
|
rspackAliases['react/jsx-runtime$'] = resolveFromAppOrCore(app, 'preact/jsx-runtime');
|
|
106
150
|
rspackAliases['react/jsx-dev-runtime$'] = resolveFromAppOrCore(app, 'preact/jsx-dev-runtime');
|
|
@@ -113,8 +157,8 @@ export default function createCompiler(
|
|
|
113
157
|
target: 'web',
|
|
114
158
|
entry: {
|
|
115
159
|
client: dev
|
|
116
|
-
? [hmrClientEntry,
|
|
117
|
-
: [
|
|
160
|
+
? [hmrClientEntry, frameworkSourceRoot + '/client/index.ts']
|
|
161
|
+
: [frameworkSourceRoot + '/client/index.ts'],
|
|
118
162
|
},
|
|
119
163
|
|
|
120
164
|
output: {
|
|
@@ -168,7 +212,7 @@ export default function createCompiler(
|
|
|
168
212
|
...transpileModuleDirectories,
|
|
169
213
|
],
|
|
170
214
|
loader: path.join(
|
|
171
|
-
|
|
215
|
+
frameworkSourceRoot,
|
|
172
216
|
'cli',
|
|
173
217
|
'compiler',
|
|
174
218
|
'common',
|
|
@@ -229,11 +273,11 @@ export default function createCompiler(
|
|
|
229
273
|
: [
|
|
230
274
|
new rspack.NormalModuleReplacementPlugin(
|
|
231
275
|
/^@client\/dev\/profiler$/,
|
|
232
|
-
|
|
276
|
+
frameworkSourceRoot + '/client/dev/profiler/noop.tsx',
|
|
233
277
|
),
|
|
234
278
|
new rspack.NormalModuleReplacementPlugin(
|
|
235
279
|
/^@client\/dev\/profiler\/runtime$/,
|
|
236
|
-
|
|
280
|
+
frameworkSourceRoot + '/client/dev/profiler/runtime.noop.ts',
|
|
237
281
|
),
|
|
238
282
|
]),
|
|
239
283
|
|
|
@@ -297,7 +341,9 @@ export default function createCompiler(
|
|
|
297
341
|
removeAvailableModules: true,
|
|
298
342
|
minimizer: [
|
|
299
343
|
new rspack.SwcJsMinimizerRspackPlugin({}),
|
|
300
|
-
new rspack.LightningCssMinimizerRspackPlugin({
|
|
344
|
+
new rspack.LightningCssMinimizerRspackPlugin({
|
|
345
|
+
...(lightningCssTargets ? { minimizerOptions: { targets: lightningCssTargets } } : {}),
|
|
346
|
+
}),
|
|
301
347
|
],
|
|
302
348
|
nodeEnv: 'production',
|
|
303
349
|
sideEffects: true,
|
|
@@ -2,7 +2,7 @@ import path from 'path';
|
|
|
2
2
|
|
|
3
3
|
// Plugons
|
|
4
4
|
import { rspack } from '@rspack/core';
|
|
5
|
-
import type { Root } from 'postcss';
|
|
5
|
+
import type { Declaration, Root } from 'postcss';
|
|
6
6
|
|
|
7
7
|
import type { App } from '../../../app';
|
|
8
8
|
|
|
@@ -11,6 +11,17 @@ const normalizePath = (value: string) => path.resolve(value).replace(/\\/g, '/')
|
|
|
11
11
|
const isPathInsideDirectory = (filepath: string, directory: string) =>
|
|
12
12
|
filepath === directory || filepath.startsWith(directory + '/');
|
|
13
13
|
|
|
14
|
+
const VENDOR_PROPERTY_PREFIXES = ['-webkit-', '-moz-', '-ms-', '-o-'] as const;
|
|
15
|
+
const getVendorlessProperty = (property: string) => {
|
|
16
|
+
for (const prefix of VENDOR_PROPERTY_PREFIXES) {
|
|
17
|
+
if (property.startsWith(prefix)) return property.slice(prefix.length);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return property;
|
|
21
|
+
};
|
|
22
|
+
const isVendorPrefixedProperty = (property: string) =>
|
|
23
|
+
VENDOR_PROPERTY_PREFIXES.some((prefix) => property.startsWith(prefix));
|
|
24
|
+
|
|
14
25
|
const createTailwindTranspileSourcesPlugin = (app: App) => {
|
|
15
26
|
const appRoot = normalizePath(app.paths.root);
|
|
16
27
|
const transpileSourceDirectories = Array.from(
|
|
@@ -50,9 +61,42 @@ const createTailwindTranspileSourcesPlugin = (app: App) => {
|
|
|
50
61
|
};
|
|
51
62
|
};
|
|
52
63
|
|
|
64
|
+
const createVendorPropertyOrderPlugin = () => {
|
|
65
|
+
return {
|
|
66
|
+
postcssPlugin: 'proteum-normalize-vendor-property-order',
|
|
67
|
+
Once(root: Root) {
|
|
68
|
+
root.walkRules((rule) => {
|
|
69
|
+
const declarations = (rule.nodes || []).filter((node): node is Declaration => node.type === 'decl');
|
|
70
|
+
|
|
71
|
+
for (const declaration of declarations) {
|
|
72
|
+
if (isVendorPrefixedProperty(declaration.prop)) continue;
|
|
73
|
+
|
|
74
|
+
const property = declaration.prop;
|
|
75
|
+
let nextNode = declaration.next();
|
|
76
|
+
|
|
77
|
+
// LightningCSS canonicalizes property aliases based on source order.
|
|
78
|
+
// Keep prefixed declarations before the standard property so target-aware
|
|
79
|
+
// minification preserves the right output for both old and modern browsers.
|
|
80
|
+
while (nextNode && nextNode.type === 'decl' && getVendorlessProperty(nextNode.prop) === property) {
|
|
81
|
+
const currentNextNode = nextNode;
|
|
82
|
+
nextNode = currentNextNode.next();
|
|
83
|
+
|
|
84
|
+
if (!isVendorPrefixedProperty(currentNextNode.prop)) continue;
|
|
85
|
+
|
|
86
|
+
const reorderedDeclaration = currentNextNode.clone();
|
|
87
|
+
currentNextNode.remove();
|
|
88
|
+
rule.insertBefore(declaration, reorderedDeclaration);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
53
96
|
module.exports = (app: App, dev: boolean, _client: boolean) => {
|
|
54
97
|
const enableSourceMaps = dev;
|
|
55
98
|
const tailwindTranspileSourcesPlugin = createTailwindTranspileSourcesPlugin(app);
|
|
99
|
+
const vendorPropertyOrderPlugin = createVendorPropertyOrderPlugin();
|
|
56
100
|
|
|
57
101
|
return [
|
|
58
102
|
// Keep CSS delivery identical in dev and prod: extract files so SSR links stylesheets in both modes.
|
|
@@ -86,9 +130,10 @@ module.exports = (app: App, dev: boolean, _client: boolean) => {
|
|
|
86
130
|
// process is launched from another working directory (e.g. Docker).
|
|
87
131
|
base: app.paths.root,
|
|
88
132
|
|
|
89
|
-
// Avoid double-minifying:
|
|
133
|
+
// Avoid double-minifying: Rspack already runs LightningCssMinimizerRspackPlugin in prod.
|
|
90
134
|
optimize: false,
|
|
91
135
|
}),
|
|
136
|
+
vendorPropertyOrderPlugin,
|
|
92
137
|
///* Tailwind V3 */require('tailwindcss'),
|
|
93
138
|
require('autoprefixer'),
|
|
94
139
|
],
|
|
@@ -2,7 +2,7 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import ts from 'typescript';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { getRouteOptionKey } from '../../../common/router/pageData';
|
|
6
6
|
import writeIfChanged from '../writeIfChanged';
|
|
7
7
|
|
|
8
8
|
type TRouteSide = 'client' | 'server';
|
|
@@ -33,7 +33,7 @@ export type TIndexedRouteDefinition = {
|
|
|
33
33
|
invalidOptionKeys: string[];
|
|
34
34
|
reservedOptionKeys: string[];
|
|
35
35
|
optionsRaw?: string;
|
|
36
|
-
|
|
36
|
+
hasData: boolean;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
type TGeneratedClientRouteModuleOptions = { chunkId: string };
|
|
@@ -206,7 +206,7 @@ const getRouteOptionMetadata = (node: ts.ObjectLiteralExpression | undefined) =>
|
|
|
206
206
|
|
|
207
207
|
for (const optionKey of optionKeys) {
|
|
208
208
|
try {
|
|
209
|
-
const normalizedOptionKey =
|
|
209
|
+
const normalizedOptionKey = getRouteOptionKey(optionKey);
|
|
210
210
|
|
|
211
211
|
if (normalizedOptionKey) {
|
|
212
212
|
normalizedOptionKeys.push(normalizedOptionKey);
|
|
@@ -438,29 +438,33 @@ const buildDestructuring = (importedServices: TImportedService[]) => {
|
|
|
438
438
|
const getClientRouteSignature = (sourceFile: ts.SourceFile, definition: TRouteDefinition) => {
|
|
439
439
|
const [, ...routeArgs] = [...definition.args];
|
|
440
440
|
|
|
441
|
-
if (
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
441
|
+
if (definition.methodName === 'error') {
|
|
442
|
+
if (routeArgs.length === 2) {
|
|
443
|
+
return {
|
|
444
|
+
hasData: false,
|
|
445
|
+
optionsExpression: routeArgs[0],
|
|
446
|
+
optionsArg: ts.isObjectLiteralExpression(routeArgs[0]) ? routeArgs[0] : undefined,
|
|
447
|
+
renderArg: routeArgs[1],
|
|
448
|
+
};
|
|
448
449
|
}
|
|
449
450
|
|
|
450
|
-
|
|
451
|
+
throw new Error(
|
|
452
|
+
`Unsupported client error route signature in ${sourceFile.fileName}. Expected Router.error(code, options, render).`,
|
|
453
|
+
);
|
|
451
454
|
}
|
|
452
455
|
|
|
453
|
-
if (routeArgs.length === 3
|
|
456
|
+
if (routeArgs.length === 3) {
|
|
454
457
|
return {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
+
hasData: routeArgs[1].kind !== ts.SyntaxKind.NullKeyword,
|
|
459
|
+
optionsExpression: routeArgs[0],
|
|
460
|
+
optionsArg: ts.isObjectLiteralExpression(routeArgs[0]) ? routeArgs[0] : undefined,
|
|
461
|
+
dataArg: routeArgs[1],
|
|
458
462
|
renderArg: routeArgs[2],
|
|
459
463
|
};
|
|
460
464
|
}
|
|
461
465
|
|
|
462
466
|
throw new Error(
|
|
463
|
-
`Unsupported client route signature in ${sourceFile.fileName}. Expected Router.page
|
|
467
|
+
`Unsupported client page route signature in ${sourceFile.fileName}. Expected Router.page(path, options, data, render).`,
|
|
464
468
|
);
|
|
465
469
|
};
|
|
466
470
|
|
|
@@ -469,30 +473,23 @@ const buildClientRegisterArgs = (
|
|
|
469
473
|
definition: TRouteDefinition,
|
|
470
474
|
clientRoute: TGeneratedClientRouteModuleOptions,
|
|
471
475
|
) => {
|
|
472
|
-
const {
|
|
476
|
+
const { optionsExpression, renderArg } = getClientRouteSignature(sourceFile, definition);
|
|
473
477
|
const sourceLocation = getNodeLocation(sourceFile, definition.callExpression);
|
|
474
478
|
const injectedOptions = buildInjectedRouteMetadata(sourceFile.fileName, sourceLocation, [
|
|
475
479
|
`id: ${JSON.stringify(clientRoute.chunkId)}`,
|
|
476
480
|
]);
|
|
477
481
|
|
|
478
|
-
if (
|
|
479
|
-
return [injectedOptions, getNodeText(sourceFile, renderArg)];
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (optionsArg && !setupArg) {
|
|
482
|
+
if (definition.methodName === 'error') {
|
|
483
483
|
return [
|
|
484
|
-
`{ ...(${getNodeText(sourceFile,
|
|
484
|
+
`{ ...(${getNodeText(sourceFile, optionsExpression)}), ...${injectedOptions} }`,
|
|
485
485
|
getNodeText(sourceFile, renderArg),
|
|
486
486
|
];
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
-
|
|
490
|
-
return [injectedOptions, getNodeText(sourceFile, setupArg), getNodeText(sourceFile, renderArg)];
|
|
491
|
-
}
|
|
492
|
-
|
|
489
|
+
const { dataArg } = getClientRouteSignature(sourceFile, definition);
|
|
493
490
|
return [
|
|
494
|
-
`{ ...(${getNodeText(sourceFile,
|
|
495
|
-
getNodeText(sourceFile,
|
|
491
|
+
`{ ...(${getNodeText(sourceFile, optionsExpression)}), ...${injectedOptions} }`,
|
|
492
|
+
getNodeText(sourceFile, dataArg!),
|
|
496
493
|
getNodeText(sourceFile, renderArg),
|
|
497
494
|
];
|
|
498
495
|
};
|
|
@@ -604,10 +601,8 @@ export const indexRouteDefinitions = ({ side, sourceFilepath }: { side: TRouteSi
|
|
|
604
601
|
normalizedOptionKeys: optionMetadata.normalizedOptionKeys,
|
|
605
602
|
invalidOptionKeys: optionMetadata.invalidOptionKeys,
|
|
606
603
|
reservedOptionKeys: optionMetadata.reservedOptionKeys,
|
|
607
|
-
optionsRaw: clientSignature.
|
|
608
|
-
|
|
609
|
-
: undefined,
|
|
610
|
-
hasSetup: clientSignature.hasSetup,
|
|
604
|
+
optionsRaw: getNodeText(sourceFile, clientSignature.optionsExpression),
|
|
605
|
+
hasData: false,
|
|
611
606
|
}
|
|
612
607
|
: {
|
|
613
608
|
methodName: definition.methodName,
|
|
@@ -627,10 +622,8 @@ export const indexRouteDefinitions = ({ side, sourceFilepath }: { side: TRouteSi
|
|
|
627
622
|
normalizedOptionKeys: optionMetadata.normalizedOptionKeys,
|
|
628
623
|
invalidOptionKeys: optionMetadata.invalidOptionKeys,
|
|
629
624
|
reservedOptionKeys: optionMetadata.reservedOptionKeys,
|
|
630
|
-
optionsRaw: clientSignature.
|
|
631
|
-
|
|
632
|
-
: undefined,
|
|
633
|
-
hasSetup: clientSignature.hasSetup,
|
|
625
|
+
optionsRaw: getNodeText(sourceFile, clientSignature.optionsExpression),
|
|
626
|
+
hasData: clientSignature.hasData,
|
|
634
627
|
};
|
|
635
628
|
}
|
|
636
629
|
|
|
@@ -659,7 +652,7 @@ export const indexRouteDefinitions = ({ side, sourceFilepath }: { side: TRouteSi
|
|
|
659
652
|
invalidOptionKeys: optionMetadata.invalidOptionKeys,
|
|
660
653
|
reservedOptionKeys: optionMetadata.reservedOptionKeys,
|
|
661
654
|
optionsRaw: optionsArg ? getNodeText(sourceFile, optionsArg) : undefined,
|
|
662
|
-
|
|
655
|
+
hasData: false,
|
|
663
656
|
};
|
|
664
657
|
});
|
|
665
658
|
};
|
|
@@ -45,7 +45,17 @@ export default function createCommonConfig(
|
|
|
45
45
|
): Configuration {
|
|
46
46
|
const dev = mode === 'dev';
|
|
47
47
|
const enableFilesystemCache = dev ? cli.args.cache !== false : cli.args.cache === true;
|
|
48
|
+
const frameworkPackageRoots = [cli.paths.framework.installedRoot, cli.paths.framework.activeRoot].filter(
|
|
49
|
+
(rootPath, index, list): rootPath is string => typeof rootPath === 'string' && list.indexOf(rootPath) === index,
|
|
50
|
+
);
|
|
51
|
+
const visibleNodeModulesRoots = [
|
|
52
|
+
...cli.paths.getVisibleNodeModulesRootsForPath(app.paths.root),
|
|
53
|
+
...frameworkPackageRoots.flatMap((rootPath) => cli.paths.getVisibleNodeModulesRootsForPath(rootPath)),
|
|
54
|
+
...cli.paths.getVisibleNodeModulesRootsForPath(cli.paths.core.cli),
|
|
55
|
+
].filter((moduleRoot, index, list) => list.indexOf(moduleRoot) === index);
|
|
48
56
|
const loaderModuleRoots = [
|
|
57
|
+
...visibleNodeModulesRoots,
|
|
58
|
+
...frameworkPackageRoots.map((rootPath) => path.join(rootPath, 'node_modules')),
|
|
49
59
|
cli.paths.framework.appNodeModulesRoot,
|
|
50
60
|
cli.paths.framework.frameworkNodeModulesRoot,
|
|
51
61
|
path.join(cli.paths.core.cli, 'node_modules'),
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
5
|
// Npm
|
|
6
|
+
import path from 'path';
|
|
6
7
|
import { type Configuration } from '@rspack/core';
|
|
7
8
|
|
|
8
9
|
// Core
|
|
@@ -50,18 +51,38 @@ const getDevGeneratedRuntimeEntries = (app: App) => ({
|
|
|
50
51
|
__proteum_dev_controllers: [app.paths.server.generated + '/controllers.ts'],
|
|
51
52
|
});
|
|
52
53
|
const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
|
|
53
|
-
const
|
|
54
|
+
const getFrameworkSourceRoot = () => {
|
|
54
55
|
const installedCoreRoot = cli.paths.framework.installedRoot
|
|
55
56
|
? normalizeModulePath(cli.paths.framework.installedRoot)
|
|
56
57
|
: undefined;
|
|
57
58
|
const activeCoreRoot = normalizeModulePath(cli.paths.framework.activeRoot);
|
|
58
59
|
|
|
59
|
-
if (
|
|
60
|
+
if (installedCoreRoot && activeCoreRoot.includes('/node_modules/')) {
|
|
61
|
+
return installedCoreRoot;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return activeCoreRoot;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const rewriteFrameworkAliasTargets = (aliases: Record<string, string | string[]>) => {
|
|
68
|
+
const visibleFrameworkRoots = [
|
|
69
|
+
...cli.paths.getVisiblePackageInstallRoots('proteum'),
|
|
70
|
+
cli.paths.framework.installedRoot,
|
|
71
|
+
cli.paths.framework.activeRoot,
|
|
72
|
+
]
|
|
73
|
+
.filter((rootPath): rootPath is string => typeof rootPath === 'string' && rootPath !== '')
|
|
74
|
+
.map((rootPath) => normalizeModulePath(rootPath))
|
|
75
|
+
.filter((rootPath, index, list) => list.indexOf(rootPath) === index);
|
|
76
|
+
const frameworkSourceRoot = getFrameworkSourceRoot();
|
|
60
77
|
|
|
61
78
|
const rewriteCandidate = (candidate: string) =>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
visibleFrameworkRoots.reduce((nextCandidate, rootPath) => {
|
|
80
|
+
const normalizedCandidate = normalizeModulePath(nextCandidate);
|
|
81
|
+
|
|
82
|
+
return normalizedCandidate.startsWith(rootPath + '/')
|
|
83
|
+
? frameworkSourceRoot + normalizedCandidate.substring(rootPath.length)
|
|
84
|
+
: nextCandidate;
|
|
85
|
+
}, candidate);
|
|
65
86
|
|
|
66
87
|
return Object.fromEntries(
|
|
67
88
|
Object.entries(aliases).map(([alias, value]) => [
|
|
@@ -82,18 +103,22 @@ export default function createCompiler(
|
|
|
82
103
|
debug && console.info(`Creating compiler for server (${mode}).`);
|
|
83
104
|
const dev = mode === 'dev';
|
|
84
105
|
const outputPath = app.outputPath(outputTarget);
|
|
85
|
-
const
|
|
106
|
+
const frameworkSourceRoot = getFrameworkSourceRoot();
|
|
107
|
+
const frameworkRoots = [frameworkSourceRoot, ...cli.paths.getFrameworkRoots()].filter(
|
|
108
|
+
(rootPath, index, list) => list.indexOf(rootPath) === index,
|
|
109
|
+
);
|
|
86
110
|
const transpileModuleDirectories = app.transpileModuleDirectories;
|
|
87
111
|
|
|
88
112
|
const commonConfig = createCommonConfig(app, 'server', mode, outputTarget);
|
|
89
113
|
const { aliases } = app.aliases.server.forWebpack({ modulesPath: cli.paths.framework.appNodeModulesRoot });
|
|
90
|
-
const resolvedAliases = rewriteFrameworkAliasTargets(
|
|
114
|
+
const resolvedAliases = rewriteFrameworkAliasTargets(aliases);
|
|
91
115
|
|
|
92
116
|
// We're not supposed in any case to import client services from server
|
|
93
117
|
delete resolvedAliases['@client/services'];
|
|
94
118
|
delete resolvedAliases['@/client/services'];
|
|
95
119
|
const rspackAliases = toRspackAliases(resolvedAliases);
|
|
96
|
-
rspackAliases['
|
|
120
|
+
rspackAliases['proteum'] = frameworkSourceRoot;
|
|
121
|
+
rspackAliases['@/client/router$'] = frameworkSourceRoot + '/client/router.ts';
|
|
97
122
|
|
|
98
123
|
debug &&
|
|
99
124
|
console.log(
|
|
@@ -108,7 +133,7 @@ export default function createCompiler(
|
|
|
108
133
|
name: 'server',
|
|
109
134
|
target: 'node',
|
|
110
135
|
entry: {
|
|
111
|
-
server: [
|
|
136
|
+
server: [path.join(frameworkSourceRoot, 'server', 'index.ts')],
|
|
112
137
|
...(dev ? getDevGeneratedRuntimeEntries(app) : {}),
|
|
113
138
|
},
|
|
114
139
|
|
package/cli/context.ts
CHANGED
|
@@ -24,7 +24,12 @@ export class CLIContext {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
public setArgs(args: TArgsObject = {}) {
|
|
27
|
-
|
|
27
|
+
const workdir =
|
|
28
|
+
typeof args.workdir === 'string' && args.workdir.trim().length > 0 ? args.workdir.trim() : process.cwd();
|
|
29
|
+
|
|
30
|
+
this.args = { ...args, workdir };
|
|
31
|
+
this.paths = new Paths(workdir, this.paths.core.root);
|
|
32
|
+
this.paths.applyAliases();
|
|
28
33
|
this.verbose = this.args.verbose === true;
|
|
29
34
|
this.debug = this.verbose;
|
|
30
35
|
}
|
package/cli/index.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { createCli, registeredCommands } from './runtime/commands';
|
|
|
12
12
|
|
|
13
13
|
const formatInvocation = (argv: string[]) => ['proteum', ...argv].join(' ').trim();
|
|
14
14
|
|
|
15
|
+
const sharedWelcomeBannerCommands = new Set(['build', 'dev']);
|
|
16
|
+
|
|
15
17
|
const shouldRenderSharedWelcomeBanner = ({
|
|
16
18
|
argv,
|
|
17
19
|
helpRequestKind,
|
|
@@ -19,15 +21,12 @@ const shouldRenderSharedWelcomeBanner = ({
|
|
|
19
21
|
argv: string[];
|
|
20
22
|
helpRequestKind: 'none' | 'overview' | 'command';
|
|
21
23
|
}) => {
|
|
22
|
-
if (helpRequestKind !== 'none') return
|
|
23
|
-
if (argv
|
|
24
|
-
|
|
25
|
-
if (argv.length === 1) return false;
|
|
26
|
-
|
|
27
|
-
const action = argv[1];
|
|
28
|
-
if (action.startsWith('-')) return false;
|
|
24
|
+
if (helpRequestKind !== 'none') return false;
|
|
25
|
+
if (argv.length !== 1) return false;
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
const command = argv[0];
|
|
28
|
+
if (!command || !sharedWelcomeBannerCommands.has(command)) return false;
|
|
29
|
+
return true;
|
|
31
30
|
};
|
|
32
31
|
|
|
33
32
|
export const runCli = async (argv: string[] = process.argv.slice(2)) => {
|