proteum 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +101 -0
- package/agents/codex/AGENTS.md +95 -0
- package/agents/codex/CODING_STYLE.md +71 -0
- package/agents/codex/agents.md.zip +0 -0
- package/agents/codex/client/AGENTS.md +102 -0
- package/agents/codex/client/pages/AGENTS.md +35 -0
- package/agents/codex/server/routes/AGENTS.md +12 -0
- package/agents/codex/server/services/AGENTS.md +137 -0
- package/agents/codex/tests/AGENTS.md +8 -0
- package/cli/app/config.ts +13 -11
- package/cli/app/index.ts +74 -82
- package/cli/bin.js +1 -1
- package/cli/commands/build.ts +51 -14
- package/cli/commands/check.ts +19 -0
- package/cli/commands/deploy/app.ts +4 -8
- package/cli/commands/deploy/web.ts +16 -20
- package/cli/commands/dev.ts +189 -64
- package/cli/commands/devEvents.ts +106 -0
- package/cli/commands/init.ts +63 -57
- package/cli/commands/lint.ts +21 -0
- package/cli/commands/refresh.ts +18 -0
- package/cli/commands/typecheck.ts +18 -0
- package/cli/compiler/client/identite.ts +80 -53
- package/cli/compiler/client/index.ts +139 -213
- package/cli/compiler/common/bundleAnalysis.ts +94 -0
- package/cli/compiler/common/clientManifest.ts +67 -0
- package/cli/compiler/common/controllers.ts +288 -0
- package/cli/compiler/common/files/autres.ts +7 -18
- package/cli/compiler/common/files/images.ts +40 -37
- package/cli/compiler/common/files/style.ts +11 -22
- package/cli/compiler/common/generatedRouteModules.ts +368 -0
- package/cli/compiler/common/index.ts +31 -65
- package/cli/compiler/common/loaders/forbid-ssr-import.js +13 -0
- package/cli/compiler/common/rspackAliases.ts +13 -0
- package/cli/compiler/common/scripts.ts +37 -0
- package/cli/compiler/index.ts +781 -230
- package/cli/compiler/server/index.ts +59 -75
- package/cli/compiler/writeIfChanged.ts +21 -0
- package/cli/index.ts +71 -72
- package/cli/paths.ts +51 -57
- package/cli/print.ts +17 -11
- package/cli/tsconfig.json +5 -4
- package/cli/utils/agents.ts +100 -0
- package/cli/utils/check.ts +71 -0
- package/cli/utils/index.ts +1 -3
- package/cli/utils/keyboard.ts +8 -25
- package/cli/utils/runProcess.ts +30 -0
- package/client/app/component.tsx +29 -29
- package/client/app/index.ts +36 -57
- package/client/app/service.ts +7 -12
- package/client/app.tsconfig.json +2 -2
- package/client/components/Dialog/Manager.ssr.tsx +40 -0
- package/client/components/Dialog/Manager.tsx +119 -150
- package/client/components/Dialog/status.tsx +3 -3
- package/client/components/index.ts +1 -1
- package/client/components/types.d.ts +1 -3
- package/client/dev/hmr.ts +65 -0
- package/client/global.d.ts +2 -2
- package/client/hooks.ts +6 -9
- package/client/index.ts +2 -1
- package/client/islands/index.ts +7 -0
- package/client/islands/useDeferredModule.ts +199 -0
- package/client/pages/_layout/index.tsx +4 -12
- package/client/pages/useHeader.tsx +14 -21
- package/client/router.ts +27 -0
- package/client/services/router/components/Link.tsx +34 -27
- package/client/services/router/components/Page.tsx +6 -14
- package/client/services/router/components/router.ssr.tsx +36 -0
- package/client/services/router/components/router.tsx +63 -83
- package/client/services/router/index.tsx +185 -220
- package/client/services/router/request/api.ts +97 -119
- package/client/services/router/request/history.ts +2 -2
- package/client/services/router/request/index.ts +13 -12
- package/client/services/router/request/multipart.ts +72 -62
- package/client/services/router/response/index.tsx +68 -61
- package/client/services/router/response/page.ts +28 -32
- package/client/utils/dom.ts +17 -33
- package/common/app/index.ts +3 -3
- package/common/data/chaines/index.ts +22 -23
- package/common/data/dates.ts +35 -70
- package/common/data/markdown.ts +42 -39
- package/common/dev/serverHotReload.ts +26 -0
- package/common/errors/index.tsx +110 -142
- package/common/router/contracts.ts +29 -0
- package/common/router/index.ts +89 -108
- package/common/router/layouts.ts +34 -47
- package/common/router/pageSetup.ts +50 -0
- package/common/router/register.ts +53 -24
- package/common/router/request/api.ts +30 -36
- package/common/router/request/index.ts +2 -8
- package/common/router/response/index.ts +8 -15
- package/common/router/response/page.ts +70 -58
- package/common/utils.ts +1 -1
- package/doc/TODO.md +1 -1
- package/eslint.js +62 -0
- package/package.json +12 -47
- package/prettier.config.cjs +9 -0
- package/scripts/cleanup-generated-controllers.ts +62 -0
- package/scripts/fix-reference-app-typing.ts +490 -0
- package/scripts/refactor-client-app-imports.ts +244 -0
- package/scripts/refactor-client-pages.ts +587 -0
- package/scripts/refactor-server-controllers.ts +470 -0
- package/scripts/refactor-server-runtime-aliases.ts +360 -0
- package/scripts/restore-client-app-import-files.ts +41 -0
- package/scripts/restore-files-from-git-head.ts +20 -0
- package/scripts/update-codex-agents.ts +35 -0
- package/server/app/commands.ts +35 -64
- package/server/app/container/config.ts +48 -59
- package/server/app/container/console/index.ts +202 -248
- package/server/app/container/index.ts +33 -71
- package/server/app/controller/index.ts +61 -0
- package/server/app/index.ts +39 -105
- package/server/app/service/container.ts +41 -42
- package/server/app/service/index.ts +120 -147
- package/server/context.ts +1 -1
- package/server/index.ts +25 -1
- package/server/services/auth/index.ts +75 -115
- package/server/services/auth/router/index.ts +31 -32
- package/server/services/auth/router/request.ts +14 -16
- package/server/services/cron/CronTask.ts +13 -26
- package/server/services/cron/index.ts +14 -36
- package/server/services/disks/driver.ts +40 -58
- package/server/services/disks/drivers/local/index.ts +79 -90
- package/server/services/disks/drivers/s3/index.ts +116 -163
- package/server/services/disks/index.ts +23 -38
- package/server/services/email/index.ts +45 -104
- package/server/services/email/utils.ts +14 -27
- package/server/services/fetch/index.ts +53 -85
- package/server/services/prisma/Facet.ts +39 -91
- package/server/services/prisma/index.ts +74 -110
- package/server/services/router/generatedRuntime.ts +29 -0
- package/server/services/router/http/index.ts +78 -73
- package/server/services/router/http/multipart.ts +19 -42
- package/server/services/router/index.ts +378 -365
- package/server/services/router/request/api.ts +26 -25
- package/server/services/router/request/index.ts +44 -51
- package/server/services/router/request/service.ts +7 -11
- package/server/services/router/request/validation/zod.ts +111 -148
- package/server/services/router/response/index.ts +110 -125
- package/server/services/router/response/mask/Filter.ts +31 -72
- package/server/services/router/response/mask/index.ts +8 -15
- package/server/services/router/response/mask/selecteurs.ts +11 -25
- package/server/services/router/response/page/clientManifest.ts +25 -0
- package/server/services/router/response/page/document.tsx +199 -127
- package/server/services/router/response/page/index.tsx +89 -94
- package/server/services/router/service.ts +13 -15
- package/server/services/schema/index.ts +17 -26
- package/server/services/schema/request.ts +19 -33
- package/server/services/schema/router/index.ts +8 -11
- package/server/services/security/encrypt/aes/index.ts +15 -35
- package/server/utils/slug.ts +29 -35
- package/skills/clean-project-code/SKILL.md +63 -0
- package/skills/clean-project-code/agents/openai.yaml +4 -0
- package/tsconfig.common.json +4 -3
- package/tsconfig.json +4 -1
- package/types/aliases.d.ts +17 -21
- package/types/controller-input.test.ts +48 -0
- package/types/express-extra.d.ts +6 -0
- package/types/global/constants.d.ts +13 -0
- package/types/global/express-extra.d.ts +6 -0
- package/types/global/modules.d.ts +13 -16
- package/types/global/utils.d.ts +17 -49
- package/types/global/vendors.d.ts +62 -0
- package/types/icons.d.ts +65 -1
- package/types/uuid.d.ts +3 -0
- package/types/vendors.d.ts +62 -0
- package/cli/compiler/common/babel/index.ts +0 -170
- package/cli/compiler/common/babel/plugins/index.ts +0 -0
- package/cli/compiler/common/babel/plugins/services.ts +0 -586
- package/cli/compiler/common/babel/routes/imports.ts +0 -127
- package/cli/compiler/common/babel/routes/routes.ts +0 -1130
- package/client/services/captcha/index.ts +0 -67
- package/client/services/socket/index.ts +0 -147
- package/common/data/rte/nodes.ts +0 -83
- package/common/data/stats.ts +0 -90
- package/common/utils/rte.ts +0 -183
- package/server/services/auth/old.ts +0 -277
- package/server/services/cache/commands.ts +0 -41
- package/server/services/cache/index.ts +0 -297
- package/server/services/cache/service.json +0 -6
- package/server/services/socket/index.ts +0 -162
- package/server/services/socket/scope.ts +0 -226
- package/server/services/socket/service.json +0 -6
- package/server/services_old/SocketClient.ts +0 -92
- package/server/services_old/Token.old.ts +0 -97
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import type { RspackPluginInstance } from '@rspack/core';
|
|
4
|
+
|
|
5
|
+
import cli from '../..';
|
|
6
|
+
import type { App } from '../../app';
|
|
7
|
+
|
|
8
|
+
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
|
9
|
+
|
|
10
|
+
export type TBundleAnalysisReportPaths = { reportPath: string; statsPath: string };
|
|
11
|
+
|
|
12
|
+
export const isBundleAnalysisEnabled = () => cli.args.analyze === true;
|
|
13
|
+
|
|
14
|
+
export const getClientBundleAnalysisReportPaths = (
|
|
15
|
+
app: App,
|
|
16
|
+
outputTarget: 'dev' | 'bin',
|
|
17
|
+
): TBundleAnalysisReportPaths => {
|
|
18
|
+
const reportDir = path.join(app.outputPath(outputTarget), 'bundle-analysis');
|
|
19
|
+
|
|
20
|
+
return { reportPath: path.join(reportDir, 'client.html'), statsPath: path.join(reportDir, 'client-stats.json') };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const createClientBundleAnalysisPlugins = (app: App, outputTarget: 'dev' | 'bin'): RspackPluginInstance[] => {
|
|
24
|
+
if (!isBundleAnalysisEnabled()) return [];
|
|
25
|
+
|
|
26
|
+
const { reportPath, statsPath } = getClientBundleAnalysisReportPaths(app, outputTarget);
|
|
27
|
+
|
|
28
|
+
fs.ensureDirSync(path.dirname(reportPath));
|
|
29
|
+
|
|
30
|
+
return [
|
|
31
|
+
new BundleAnalyzerPlugin({
|
|
32
|
+
analyzerMode: 'static',
|
|
33
|
+
openAnalyzer: false,
|
|
34
|
+
defaultSizes: 'parsed',
|
|
35
|
+
reportFilename: reportPath,
|
|
36
|
+
generateStatsFile: true,
|
|
37
|
+
statsFilename: statsPath,
|
|
38
|
+
logLevel: 'info',
|
|
39
|
+
}),
|
|
40
|
+
];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const sleep = (delayMs: number) => new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
44
|
+
|
|
45
|
+
const isJsonFileComplete = (filepath: string) => {
|
|
46
|
+
const fd = fs.openSync(filepath, 'r');
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const { size } = fs.fstatSync(fd);
|
|
50
|
+
if (size === 0) return false;
|
|
51
|
+
|
|
52
|
+
const readLength = Math.min(512, size);
|
|
53
|
+
const buffer = Buffer.alloc(readLength);
|
|
54
|
+
fs.readSync(fd, buffer, 0, readLength, size - readLength);
|
|
55
|
+
|
|
56
|
+
const tail = buffer.toString('utf8').trimEnd();
|
|
57
|
+
if (!tail.endsWith('}')) return false;
|
|
58
|
+
|
|
59
|
+
JSON.parse(fs.readFileSync(filepath, 'utf8'));
|
|
60
|
+
return true;
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
} finally {
|
|
64
|
+
fs.closeSync(fd);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const waitForClientBundleAnalysisArtifacts = async (
|
|
69
|
+
app: App,
|
|
70
|
+
outputTarget: 'dev' | 'bin',
|
|
71
|
+
timeoutMs: number = 30000,
|
|
72
|
+
) => {
|
|
73
|
+
const startedAt = Date.now();
|
|
74
|
+
const { reportPath, statsPath } = getClientBundleAnalysisReportPaths(app, outputTarget);
|
|
75
|
+
let previousStatsSize = -1;
|
|
76
|
+
|
|
77
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
78
|
+
const reportExists = fs.existsSync(reportPath);
|
|
79
|
+
const statsExists = fs.existsSync(statsPath);
|
|
80
|
+
|
|
81
|
+
if (reportExists && statsExists) {
|
|
82
|
+
const { size } = fs.statSync(statsPath);
|
|
83
|
+
const sizeStable = size > 0 && size === previousStatsSize;
|
|
84
|
+
|
|
85
|
+
if (sizeStable && isJsonFileComplete(statsPath)) return;
|
|
86
|
+
|
|
87
|
+
previousStatsSize = size;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await sleep(250);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
throw new Error(`Timed out waiting for bundle analysis artifacts to complete: ${reportPath}, ${statsPath}`);
|
|
94
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import type { Stats } from '@rspack/core';
|
|
4
|
+
|
|
5
|
+
export type TClientManifestChunkAssets = { assets: string[]; css: string[]; js: string[] };
|
|
6
|
+
|
|
7
|
+
export type TClientBuildManifest = {
|
|
8
|
+
publicPath: string;
|
|
9
|
+
entries: Record<string, TClientManifestChunkAssets>;
|
|
10
|
+
chunks: Record<string, TClientManifestChunkAssets>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const filterAsset = (asset: string) => !asset.endsWith('.map');
|
|
14
|
+
|
|
15
|
+
const classifyAssets = (assets: string[]): TClientManifestChunkAssets => {
|
|
16
|
+
const filtered = assets.filter(filterAsset);
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
assets: filtered,
|
|
20
|
+
css: filtered.filter((asset) => asset.endsWith('.css')),
|
|
21
|
+
js: filtered.filter((asset) => asset.endsWith('.js')),
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const normalizePublicPath = (publicPath?: string) => {
|
|
26
|
+
if (!publicPath || publicPath === 'auto') return '/public/';
|
|
27
|
+
|
|
28
|
+
return publicPath.endsWith('/') ? publicPath : `${publicPath}/`;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const getClientManifestPath = (outputPath: string) => path.join(outputPath, 'client-manifest.json');
|
|
32
|
+
|
|
33
|
+
export const writeClientManifest = (stats: Stats, outputPath: string) => {
|
|
34
|
+
const statsJson = stats.toJson({
|
|
35
|
+
assets: true,
|
|
36
|
+
assetsByChunkName: true,
|
|
37
|
+
entrypoints: true,
|
|
38
|
+
namedChunkGroups: true,
|
|
39
|
+
publicPath: true,
|
|
40
|
+
});
|
|
41
|
+
const publicPath = normalizePublicPath(statsJson.publicPath);
|
|
42
|
+
|
|
43
|
+
const buildChunkAssets = (chunkGroup: { assets?: Array<string | { name?: string | null }> } | undefined) => {
|
|
44
|
+
const assets = (chunkGroup?.assets || [])
|
|
45
|
+
.map((asset) => (typeof asset === 'string' ? asset : asset.name || ''))
|
|
46
|
+
.filter(Boolean);
|
|
47
|
+
|
|
48
|
+
return classifyAssets(assets);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const entries = Object.fromEntries(
|
|
52
|
+
Object.entries(statsJson.entrypoints || {}).map(([name, chunkGroup]) => [name, buildChunkAssets(chunkGroup)]),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const chunks = Object.fromEntries(
|
|
56
|
+
Object.entries(statsJson.namedChunkGroups || {}).map(([name, chunkGroup]) => [
|
|
57
|
+
name,
|
|
58
|
+
buildChunkAssets(chunkGroup),
|
|
59
|
+
]),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const manifest: TClientBuildManifest = { publicPath, entries, chunks };
|
|
63
|
+
|
|
64
|
+
fs.writeJsonSync(getClientManifestPath(outputPath), manifest, { spaces: 2 });
|
|
65
|
+
|
|
66
|
+
return manifest;
|
|
67
|
+
};
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import ts from 'typescript';
|
|
9
|
+
|
|
10
|
+
/*----------------------------------
|
|
11
|
+
- TYPES
|
|
12
|
+
----------------------------------*/
|
|
13
|
+
|
|
14
|
+
export type TControllerMethodMeta = { name: string; inputCallsCount: number; routePath: string };
|
|
15
|
+
|
|
16
|
+
export type TControllerFileMeta = {
|
|
17
|
+
importPath: string;
|
|
18
|
+
filepath: string;
|
|
19
|
+
className: string;
|
|
20
|
+
routeBasePath: string;
|
|
21
|
+
methods: TControllerMethodMeta[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type TControllerServiceRoot = { alias: string; dir: string };
|
|
25
|
+
|
|
26
|
+
type TControllerSearchDir = { importPrefix: string; root: string; serviceRoots?: TControllerServiceRoot[] };
|
|
27
|
+
|
|
28
|
+
/*----------------------------------
|
|
29
|
+
- HELPERS
|
|
30
|
+
----------------------------------*/
|
|
31
|
+
|
|
32
|
+
const getControllerSegments = (relativePath: string) => {
|
|
33
|
+
const segments = relativePath
|
|
34
|
+
.replace(/\.controller\.ts$/, '')
|
|
35
|
+
.split('/')
|
|
36
|
+
.filter(Boolean);
|
|
37
|
+
|
|
38
|
+
if (segments.length > 1 && segments[segments.length - 1] === segments[segments.length - 2]) {
|
|
39
|
+
segments.pop();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return segments;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const getControllerBasePathFromFilepath = (filepath: string, root: string, serviceRoots: TControllerServiceRoot[] = []) => {
|
|
46
|
+
const normalizedFilepath = filepath.replace(/\\/g, '/');
|
|
47
|
+
const serviceRoot = serviceRoots
|
|
48
|
+
.filter((candidate) => normalizedFilepath.startsWith(candidate.dir.replace(/\\/g, '/') + '/'))
|
|
49
|
+
.sort((a, b) => b.dir.length - a.dir.length)[0];
|
|
50
|
+
|
|
51
|
+
if (!serviceRoot) {
|
|
52
|
+
return getControllerSegments(path.relative(root, filepath).replace(/\\/g, '/')).join('/');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const segments = getControllerSegments(path.relative(serviceRoot.dir, filepath).replace(/\\/g, '/'));
|
|
56
|
+
|
|
57
|
+
if (segments[0]?.toLowerCase() === serviceRoot.alias.toLowerCase()) {
|
|
58
|
+
segments.shift();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return [serviceRoot.alias, ...segments].filter(Boolean).join('/');
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const getGeneratedClassName = (filepath: string) => {
|
|
65
|
+
const filename = path.basename(filepath, '.ts').replace(/[^A-Za-z0-9_$]+/g, '_');
|
|
66
|
+
const normalized = filename.length ? filename : 'Controller';
|
|
67
|
+
|
|
68
|
+
return normalized[0].toUpperCase() + normalized.substring(1);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const buildImportPath = (searchDir: TControllerSearchDir, filepath: string) =>
|
|
72
|
+
searchDir.importPrefix + path.relative(searchDir.root, filepath).replace(/\\/g, '/').replace(/\.ts$/, '');
|
|
73
|
+
|
|
74
|
+
const findControllerFiles = (dir: string): string[] => {
|
|
75
|
+
if (!fs.existsSync(dir)) return [];
|
|
76
|
+
|
|
77
|
+
const files: string[] = [];
|
|
78
|
+
|
|
79
|
+
for (const dirent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
80
|
+
const filepath = path.join(dir, dirent.name);
|
|
81
|
+
|
|
82
|
+
if (dirent.isDirectory()) {
|
|
83
|
+
files.push(...findControllerFiles(filepath));
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!dirent.isFile()) continue;
|
|
88
|
+
if (!dirent.name.endsWith('.controller.ts')) continue;
|
|
89
|
+
|
|
90
|
+
files.push(filepath);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return files;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const parseSourceFile = (filepath: string, code: string) =>
|
|
97
|
+
ts.createSourceFile(
|
|
98
|
+
filepath,
|
|
99
|
+
code,
|
|
100
|
+
ts.ScriptTarget.Latest,
|
|
101
|
+
true,
|
|
102
|
+
filepath.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const hasModifier = (node: ts.Node, kind: ts.SyntaxKind) =>
|
|
106
|
+
!!node.modifiers?.some((modifier) => modifier.kind === kind);
|
|
107
|
+
|
|
108
|
+
const getDefaultExportClass = (sourceFile: ts.SourceFile) => {
|
|
109
|
+
const classes = new Map<string, ts.ClassDeclaration>();
|
|
110
|
+
|
|
111
|
+
for (const statement of sourceFile.statements) {
|
|
112
|
+
if (ts.isClassDeclaration(statement) && statement.name) {
|
|
113
|
+
classes.set(statement.name.text, statement);
|
|
114
|
+
|
|
115
|
+
if (hasModifier(statement, ts.SyntaxKind.DefaultKeyword)) {
|
|
116
|
+
return statement;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const statement of sourceFile.statements) {
|
|
122
|
+
if (!ts.isExportAssignment(statement) || statement.isExportEquals) continue;
|
|
123
|
+
|
|
124
|
+
if (ts.isIdentifier(statement.expression)) {
|
|
125
|
+
return classes.get(statement.expression.text);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return undefined;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const getExportedString = (sourceFile: ts.SourceFile, exportName: string) => {
|
|
133
|
+
for (const statement of sourceFile.statements) {
|
|
134
|
+
if (!ts.isVariableStatement(statement)) continue;
|
|
135
|
+
if (!hasModifier(statement, ts.SyntaxKind.ExportKeyword)) continue;
|
|
136
|
+
|
|
137
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
138
|
+
if (!ts.isIdentifier(declaration.name)) continue;
|
|
139
|
+
if (declaration.name.text !== exportName) continue;
|
|
140
|
+
if (!declaration.initializer || !ts.isStringLiteral(declaration.initializer)) continue;
|
|
141
|
+
|
|
142
|
+
return declaration.initializer.text;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return undefined;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const countInputCalls = (method: ts.MethodDeclaration) => {
|
|
150
|
+
let inputCallsCount = 0;
|
|
151
|
+
|
|
152
|
+
const visit = (node: ts.Node) => {
|
|
153
|
+
if (
|
|
154
|
+
ts.isCallExpression(node) &&
|
|
155
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
156
|
+
node.expression.expression.kind === ts.SyntaxKind.ThisKeyword &&
|
|
157
|
+
node.expression.name.text === 'input'
|
|
158
|
+
) {
|
|
159
|
+
inputCallsCount++;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
ts.forEachChild(node, visit);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
if (method.body) ts.forEachChild(method.body, visit);
|
|
166
|
+
|
|
167
|
+
return inputCallsCount;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/*----------------------------------
|
|
171
|
+
- EXPORTS
|
|
172
|
+
----------------------------------*/
|
|
173
|
+
|
|
174
|
+
export const indexControllers = (searchDirs: TControllerSearchDir[]) => {
|
|
175
|
+
const controllers: TControllerFileMeta[] = [];
|
|
176
|
+
|
|
177
|
+
for (const searchDir of searchDirs) {
|
|
178
|
+
const controllerFiles = findControllerFiles(searchDir.root);
|
|
179
|
+
|
|
180
|
+
for (const filepath of controllerFiles.sort((a, b) => a.localeCompare(b))) {
|
|
181
|
+
const code = fs.readFileSync(filepath, 'utf8');
|
|
182
|
+
const sourceFile = parseSourceFile(filepath, code);
|
|
183
|
+
|
|
184
|
+
const controllerPathOverride = getExportedString(sourceFile, 'controllerPath');
|
|
185
|
+
const defaultClass = getDefaultExportClass(sourceFile);
|
|
186
|
+
|
|
187
|
+
if (!defaultClass) continue;
|
|
188
|
+
|
|
189
|
+
const className = defaultClass.name?.text || getGeneratedClassName(filepath);
|
|
190
|
+
const routeBasePath =
|
|
191
|
+
controllerPathOverride ||
|
|
192
|
+
getControllerBasePathFromFilepath(filepath, searchDir.root, searchDir.serviceRoots || []);
|
|
193
|
+
const methods: TControllerMethodMeta[] = [];
|
|
194
|
+
|
|
195
|
+
for (const member of defaultClass.members) {
|
|
196
|
+
if (!ts.isMethodDeclaration(member)) continue;
|
|
197
|
+
if (!member.body) continue;
|
|
198
|
+
if (!member.name || !ts.isIdentifier(member.name)) continue;
|
|
199
|
+
|
|
200
|
+
const methodName = member.name.text;
|
|
201
|
+
const inputCallsCount = countInputCalls(member);
|
|
202
|
+
|
|
203
|
+
if (inputCallsCount > 1) {
|
|
204
|
+
throw new Error(`${filepath}#${methodName} uses this.input() more than once.`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
methods.push({
|
|
208
|
+
name: methodName,
|
|
209
|
+
inputCallsCount,
|
|
210
|
+
routePath: [routeBasePath, methodName].filter(Boolean).join('/'),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!methods.length) continue;
|
|
215
|
+
|
|
216
|
+
controllers.push({
|
|
217
|
+
filepath,
|
|
218
|
+
importPath: buildImportPath(searchDir, filepath),
|
|
219
|
+
className,
|
|
220
|
+
routeBasePath,
|
|
221
|
+
methods,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return controllers.sort((a, b) => a.filepath.localeCompare(b.filepath));
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export const generateControllerClientTree = (controllers: TControllerFileMeta[]) => {
|
|
230
|
+
const root: Record<string, any> = {};
|
|
231
|
+
|
|
232
|
+
const insert = (segments: string[], valueFactory: () => string) => {
|
|
233
|
+
let cursor = root;
|
|
234
|
+
|
|
235
|
+
for (let i = 0; i < segments.length; i++) {
|
|
236
|
+
const segment = segments[i];
|
|
237
|
+
const isLeaf = i === segments.length - 1;
|
|
238
|
+
|
|
239
|
+
if (isLeaf) {
|
|
240
|
+
cursor[segment] = valueFactory();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
cursor[segment] = cursor[segment] || {};
|
|
245
|
+
cursor = cursor[segment];
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
for (const controller of controllers) {
|
|
250
|
+
for (const method of controller.methods) {
|
|
251
|
+
insert(method.routePath.split('/'), () =>
|
|
252
|
+
JSON.stringify({
|
|
253
|
+
importPath: controller.importPath,
|
|
254
|
+
className: controller.className,
|
|
255
|
+
methodName: method.name,
|
|
256
|
+
routePath: '/api/' + method.routePath,
|
|
257
|
+
hasInput: method.inputCallsCount > 0,
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return root;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
export const printControllerTree = (
|
|
267
|
+
tree: Record<string, any>,
|
|
268
|
+
renderLeaf: (leaf: string) => string,
|
|
269
|
+
indentLevel: number = 1,
|
|
270
|
+
) => {
|
|
271
|
+
const indent = ' '.repeat(indentLevel);
|
|
272
|
+
const lines: string[] = ['{'];
|
|
273
|
+
|
|
274
|
+
for (const key of Object.keys(tree).sort((a, b) => a.localeCompare(b))) {
|
|
275
|
+
const value = tree[key];
|
|
276
|
+
|
|
277
|
+
if (typeof value === 'string') {
|
|
278
|
+
lines.push(`${indent}${JSON.stringify(key)}: ${renderLeaf(value)},`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
lines.push(`${indent}${JSON.stringify(key)}: ${printControllerTree(value, renderLeaf, indentLevel + 1)},`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
lines.push(`${' '.repeat(indentLevel - 1)}}`);
|
|
286
|
+
|
|
287
|
+
return lines.join('\n');
|
|
288
|
+
};
|
|
@@ -2,38 +2,27 @@ import { staticAssetName } from '../../../paths';
|
|
|
2
2
|
|
|
3
3
|
import type App from '../../../app';
|
|
4
4
|
|
|
5
|
-
module.exports = (app: App, dev: boolean, client: boolean) =>
|
|
6
|
-
|
|
5
|
+
module.exports = (app: App, dev: boolean, client: boolean) => [
|
|
7
6
|
// Allow to use ?raw at the end of the module path to iport the raw content only
|
|
8
7
|
// Example: import VisualParserSource from './Parsers/visual.js?raw';
|
|
9
|
-
{
|
|
10
|
-
resourceQuery: /raw/,
|
|
11
|
-
type: 'asset/source',
|
|
12
|
-
},
|
|
8
|
+
{ resourceQuery: /raw/, type: 'asset/source' },
|
|
13
9
|
|
|
14
10
|
// Client uniquement: Retourne le fichier correspondant au fichier dans le dossier public
|
|
15
11
|
{
|
|
16
12
|
test: /\.(xml|ico|wav|mp3)$/,
|
|
17
13
|
//loader: 'file-loader',
|
|
18
14
|
type: 'asset/resource',
|
|
19
|
-
generator: {
|
|
20
|
-
filename: staticAssetName
|
|
21
|
-
}
|
|
15
|
+
generator: { filename: staticAssetName },
|
|
22
16
|
},
|
|
23
17
|
|
|
24
18
|
// Texte brut
|
|
25
|
-
{
|
|
26
|
-
type: 'asset/source',
|
|
27
|
-
test: /\.(md|hbs|sql|txt|csv|html)$/,
|
|
28
|
-
},
|
|
19
|
+
{ type: 'asset/source', test: /\.(md|hbs|sql|txt|csv|html)$/ },
|
|
29
20
|
|
|
30
21
|
// Polices dans un fichier distinc dans le dossier dédié
|
|
31
22
|
{
|
|
32
23
|
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
|
|
33
24
|
//loader: 'file-loader',
|
|
34
25
|
type: 'asset/resource',
|
|
35
|
-
generator: {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
])
|
|
26
|
+
generator: { filename: 'fonts/[name].[ext]' },
|
|
27
|
+
},
|
|
28
|
+
];
|
|
@@ -1,42 +1,45 @@
|
|
|
1
1
|
import { staticAssetName } from '../../../paths';
|
|
2
|
-
import type
|
|
2
|
+
import type { RuleSetRule } from '@rspack/core';
|
|
3
|
+
import type App from '../../../app';
|
|
3
4
|
|
|
4
|
-
module.exports = (app: App, dev: boolean, client: boolean):
|
|
5
|
+
module.exports = (app: App, dev: boolean, client: boolean): RuleSetRule[] => {
|
|
6
|
+
return [
|
|
7
|
+
{
|
|
8
|
+
test: /\.(bmp|gif|png|jpe?g|ico|svg|webp)$/i,
|
|
9
|
+
type: 'asset',
|
|
10
|
+
parser: {
|
|
11
|
+
dataUrlCondition: {
|
|
12
|
+
// < 4kb = importation inline
|
|
13
|
+
// > 4kb = référence à l'url
|
|
14
|
+
maxSize: 4 * 1024, // 4kb
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
5
18
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
...(dev
|
|
20
|
+
? []
|
|
21
|
+
: [
|
|
22
|
+
{
|
|
23
|
+
test: /\.(jpg|jpeg|png)$/i,
|
|
24
|
+
type: 'javascript/auto',
|
|
25
|
+
use: [
|
|
26
|
+
{
|
|
27
|
+
loader: 'responsive-loader',
|
|
28
|
+
options: {
|
|
29
|
+
sizes: [320, 480, 640, 768, 1024, 1300],
|
|
30
|
+
placeholder: true,
|
|
31
|
+
placeholderSize: 20,
|
|
32
|
+
quality: 100,
|
|
33
|
+
publicPath: '/public',
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
// Triggers error
|
|
36
|
+
// cacheDirectory: true,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
]),
|
|
19
42
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
use: [{
|
|
24
|
-
loader: "responsive-loader",
|
|
25
|
-
options: {
|
|
26
|
-
sizes: [320, 480, 640, 768, 1024, 1300],
|
|
27
|
-
placeholder: true,
|
|
28
|
-
placeholderSize: 20,
|
|
29
|
-
quality: 100,
|
|
30
|
-
publicPath: '/public',
|
|
31
|
-
|
|
32
|
-
// Triggers error
|
|
33
|
-
// cacheDirectory: true,
|
|
34
|
-
}
|
|
35
|
-
}],
|
|
36
|
-
}]),
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
test: /\.(webm|mp4|avi|mpk|mov|mkv)$/,
|
|
40
|
-
type: 'asset/resource',
|
|
41
|
-
},]
|
|
42
|
-
}
|
|
43
|
+
{ test: /\.(webm|mp4|avi|mpk|mov|mkv)$/, type: 'asset/resource' },
|
|
44
|
+
];
|
|
45
|
+
};
|
|
@@ -1,36 +1,26 @@
|
|
|
1
1
|
// Plugons
|
|
2
|
-
import
|
|
2
|
+
import { rspack } from '@rspack/core';
|
|
3
3
|
|
|
4
4
|
import type { App } from '../../../app';
|
|
5
5
|
|
|
6
|
-
module.exports = (app: App, dev:
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
module.exports = (app: App, dev: boolean, client: boolean) => {
|
|
7
|
+
const enableSourceMaps = dev;
|
|
8
|
+
const useStyleLoader = client && dev;
|
|
9
9
|
|
|
10
10
|
return [
|
|
11
|
-
|
|
12
11
|
// Apply PostCSS plugins including autoprefixer
|
|
13
|
-
{
|
|
14
|
-
loader: MiniCssExtractPlugin.loader
|
|
15
|
-
},
|
|
12
|
+
useStyleLoader ? { loader: 'style-loader' } : { loader: rspack.CssExtractRspackPlugin.loader },
|
|
16
13
|
|
|
17
14
|
// Process external/third-party styles
|
|
18
|
-
{
|
|
19
|
-
exclude: [app.paths.root],
|
|
20
|
-
loader: 'css-loader',
|
|
21
|
-
options: {
|
|
22
|
-
sourceMap: dev
|
|
23
|
-
},
|
|
24
|
-
},
|
|
15
|
+
{ exclude: [app.paths.root], loader: 'css-loader', options: { sourceMap: enableSourceMaps } },
|
|
25
16
|
|
|
26
17
|
// Process internal/project styles (from root folder)
|
|
27
18
|
{
|
|
28
19
|
include: [app.paths.root],
|
|
29
20
|
loader: 'css-loader',
|
|
30
21
|
options: {
|
|
31
|
-
// CSS Loader https://github.com/webpack/css-loader
|
|
32
22
|
importLoaders: 1, // let postcss run on @imports
|
|
33
|
-
sourceMap:
|
|
23
|
+
sourceMap: enableSourceMaps,
|
|
34
24
|
},
|
|
35
25
|
},
|
|
36
26
|
|
|
@@ -40,7 +30,7 @@ module.exports = (app: App, dev: Boolean, client: boolean) => {
|
|
|
40
30
|
options: {
|
|
41
31
|
postcssOptions: {
|
|
42
32
|
plugins: [
|
|
43
|
-
/* Tailwind V4 */require('@tailwindcss/postcss')({
|
|
33
|
+
/* Tailwind V4 */ require('@tailwindcss/postcss')({
|
|
44
34
|
// Ensure Tailwind scans the application sources even if the build
|
|
45
35
|
// process is launched from another working directory (e.g. Docker).
|
|
46
36
|
base: app.paths.root,
|
|
@@ -68,13 +58,12 @@ module.exports = (app: App, dev: Boolean, client: boolean) => {
|
|
|
68
58
|
// https://lesscss.org/usage/#less-options-math
|
|
69
59
|
math: 'always',
|
|
70
60
|
},
|
|
71
|
-
}
|
|
61
|
+
},
|
|
72
62
|
},
|
|
73
63
|
|
|
74
64
|
/*{
|
|
75
65
|
test: /\.scss/,
|
|
76
66
|
loader: process.env.framework + '/node_modules/sass-loader',
|
|
77
67
|
}*/
|
|
78
|
-
]
|
|
79
|
-
|
|
80
|
-
}
|
|
68
|
+
];
|
|
69
|
+
};
|