@lark-apaas/fullstack-rspack-preset 1.0.50-alpha.0 → 1.0.50
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/lib/preset.js
CHANGED
|
@@ -54,6 +54,25 @@ function createRecommendRspackConfig(options) {
|
|
|
54
54
|
const staticAssetsPlugin = new static_assets_plugin_1.default({ clientBasePath, rootDir });
|
|
55
55
|
// 优先从环境变量获取 RELEASE_ID,否则使用时间戳作为兜底
|
|
56
56
|
const releaseId = process.env.RELEASE_ID || new Date().toISOString();
|
|
57
|
+
// 读取构建前生成的路由定义 JSON,供 DefinePlugin 注入到 bundle
|
|
58
|
+
// build.sh 在 client 构建之前已将 page-routes.json / api-routes.json 写入 dist/
|
|
59
|
+
// 独立于 needRoutes(needRoutes 仅控制 RouteParserPlugin),只要文件存在就注入
|
|
60
|
+
let pageRouteDefinitions = [];
|
|
61
|
+
let apiRouteDefinitions = [];
|
|
62
|
+
try {
|
|
63
|
+
const pageRoutesPath = path_1.default.resolve(rootDir, 'dist/page-routes.json');
|
|
64
|
+
if (fs_1.default.existsSync(pageRoutesPath)) {
|
|
65
|
+
pageRouteDefinitions = JSON.parse(fs_1.default.readFileSync(pageRoutesPath, 'utf-8'));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch { /* ignore */ }
|
|
69
|
+
try {
|
|
70
|
+
const apiRoutesPath = path_1.default.resolve(rootDir, 'dist/api-routes.json');
|
|
71
|
+
if (fs_1.default.existsSync(apiRoutesPath)) {
|
|
72
|
+
apiRouteDefinitions = JSON.parse(fs_1.default.readFileSync(apiRoutesPath, 'utf-8'));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch { /* ignore */ }
|
|
57
76
|
return {
|
|
58
77
|
mode: isDev ? 'development' : 'production',
|
|
59
78
|
cache: false,
|
|
@@ -204,6 +223,9 @@ function createRecommendRspackConfig(options) {
|
|
|
204
223
|
'process.env.BUILD_TOOL': JSON.stringify('rspack'),
|
|
205
224
|
// 模板 flags 配置
|
|
206
225
|
'process.env.APP_FLAGS': JSON.stringify(appFlags),
|
|
226
|
+
// 路由定义,供客户端匹配 referer_path 和 api
|
|
227
|
+
'process.env.__PAGE_ROUTE_DEFINITIONS__': JSON.stringify(JSON.stringify(pageRouteDefinitions)),
|
|
228
|
+
'process.env.__API_ROUTE_DEFINITIONS__': JSON.stringify(JSON.stringify(apiRouteDefinitions)),
|
|
207
229
|
// 解决 window 未定义问题
|
|
208
230
|
'typeof window': JSON.stringify('object'),
|
|
209
231
|
window: 'globalThis',
|
|
@@ -238,7 +260,7 @@ function createRecommendRspackConfig(options) {
|
|
|
238
260
|
!isDev && new polyfill_plugin_1.default(),
|
|
239
261
|
// 静态资源插件 - 支持 @shared/static/* 导入
|
|
240
262
|
staticAssetsPlugin,
|
|
241
|
-
//
|
|
263
|
+
// 解析路由(dev + prod 均生成 routes.json 和 page-routes.json)
|
|
242
264
|
needRoutes &&
|
|
243
265
|
new route_parser_plugin_1.default({
|
|
244
266
|
appPath: './client/src/app.tsx',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { normalizeBasePath, parseRoutesFromFile } from '@lark-apaas/devtool-kits';
|
|
1
2
|
interface RouteParserPluginOptions {
|
|
2
3
|
appPath?: string;
|
|
3
4
|
outputPath?: string;
|
|
@@ -8,15 +9,7 @@ declare class RouteParserPlugin {
|
|
|
8
9
|
private lastAppPathHash;
|
|
9
10
|
private cachedRoutes;
|
|
10
11
|
constructor(options?: RouteParserPluginOptions);
|
|
11
|
-
private normalizeBasePath;
|
|
12
|
-
private log;
|
|
13
12
|
apply(compiler: any): void;
|
|
14
13
|
private shouldRegenerateRoutes;
|
|
15
|
-
private calculateFileHash;
|
|
16
|
-
private parseRoutes;
|
|
17
|
-
private isRouteComponent;
|
|
18
|
-
private extractRouteInfo;
|
|
19
|
-
private buildFullPath;
|
|
20
|
-
private evaluateTemplateLiteral;
|
|
21
14
|
}
|
|
22
15
|
export default RouteParserPlugin;
|
|
@@ -32,68 +32,33 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseRoutesFromFile = exports.normalizeBasePath = void 0;
|
|
39
37
|
const fs = __importStar(require("fs"));
|
|
40
38
|
const path = __importStar(require("path"));
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
const devtool_kits_1 = require("@lark-apaas/devtool-kits");
|
|
40
|
+
// Re-export for backward compatibility
|
|
41
|
+
var devtool_kits_2 = require("@lark-apaas/devtool-kits");
|
|
42
|
+
Object.defineProperty(exports, "normalizeBasePath", { enumerable: true, get: function () { return devtool_kits_2.normalizeBasePath; } });
|
|
43
|
+
Object.defineProperty(exports, "parseRoutesFromFile", { enumerable: true, get: function () { return devtool_kits_2.parseRoutesFromFile; } });
|
|
45
44
|
class RouteParserPlugin {
|
|
46
45
|
constructor(options = {}) {
|
|
47
46
|
this.lastAppPathHash = null;
|
|
48
47
|
this.cachedRoutes = null;
|
|
49
|
-
// 从环境变量读取 basePath,并规范化(确保以 / 开头,不以 / 结尾)
|
|
50
48
|
const envBasePath = process.env.CLIENT_BASE_PATH || '';
|
|
51
|
-
const normalizedBasePath =
|
|
49
|
+
const normalizedBasePath = (0, devtool_kits_1.normalizeBasePath)(options.basePath ?? envBasePath);
|
|
52
50
|
this.options = {
|
|
53
51
|
appPath: options.appPath || './client/src/app.tsx',
|
|
54
52
|
outputPath: options.outputPath || './dist/client/routes.json',
|
|
55
53
|
basePath: normalizedBasePath,
|
|
56
54
|
};
|
|
57
55
|
}
|
|
58
|
-
normalizeBasePath(basePath) {
|
|
59
|
-
if (!basePath || basePath === '/') {
|
|
60
|
-
return '';
|
|
61
|
-
}
|
|
62
|
-
// 确保以 / 开头
|
|
63
|
-
let normalized = basePath.startsWith('/') ? basePath : `/${basePath}`;
|
|
64
|
-
// 确保不以 / 结尾
|
|
65
|
-
if (normalized.endsWith('/')) {
|
|
66
|
-
normalized = normalized.slice(0, -1);
|
|
67
|
-
}
|
|
68
|
-
return normalized;
|
|
69
|
-
}
|
|
70
|
-
log(level, message, ...args) {
|
|
71
|
-
const prefix = '[route-parser]';
|
|
72
|
-
const logMessage = `${prefix} ${message}`;
|
|
73
|
-
switch (level) {
|
|
74
|
-
case 'log':
|
|
75
|
-
console.log(logMessage, ...args);
|
|
76
|
-
break;
|
|
77
|
-
case 'warn':
|
|
78
|
-
console.warn(logMessage, ...args);
|
|
79
|
-
break;
|
|
80
|
-
case 'error':
|
|
81
|
-
console.error(logMessage, ...args);
|
|
82
|
-
break;
|
|
83
|
-
case 'info':
|
|
84
|
-
console.info(logMessage, ...args);
|
|
85
|
-
break;
|
|
86
|
-
default:
|
|
87
|
-
console.log(logMessage, ...args);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
56
|
apply(compiler) {
|
|
91
57
|
const pluginName = 'RouteParserPlugin';
|
|
92
58
|
compiler.hooks.emit.tapAsync(pluginName, (compilation, callback) => {
|
|
93
59
|
try {
|
|
94
60
|
if (this.shouldRegenerateRoutes()) {
|
|
95
|
-
|
|
96
|
-
this.cachedRoutes = routes;
|
|
61
|
+
this.cachedRoutes = (0, devtool_kits_1.parseRoutesFromFile)(this.options.appPath, this.options.basePath);
|
|
97
62
|
}
|
|
98
63
|
const routesJson = JSON.stringify(this.cachedRoutes, null, 2);
|
|
99
64
|
compilation.assets['routes.json'] = {
|
|
@@ -103,7 +68,7 @@ class RouteParserPlugin {
|
|
|
103
68
|
callback();
|
|
104
69
|
}
|
|
105
70
|
catch (error) {
|
|
106
|
-
|
|
71
|
+
(0, devtool_kits_1.routeParserLog)('warn', '⚠️ 路由解析失败,使用默认路由:', error.message);
|
|
107
72
|
const { basePath } = this.options;
|
|
108
73
|
const defaultPath = basePath ? `${basePath}/` : '/';
|
|
109
74
|
const defaultRoutes = [{ path: defaultPath }];
|
|
@@ -120,10 +85,10 @@ class RouteParserPlugin {
|
|
|
120
85
|
try {
|
|
121
86
|
const appFilePath = path.resolve(process.cwd(), this.options.appPath);
|
|
122
87
|
if (!fs.existsSync(appFilePath)) {
|
|
123
|
-
|
|
88
|
+
(0, devtool_kits_1.routeParserLog)('warn', `⚠️ App.tsx 文件不存在: ${appFilePath}`);
|
|
124
89
|
return false;
|
|
125
90
|
}
|
|
126
|
-
const currentHash =
|
|
91
|
+
const currentHash = (0, devtool_kits_1.calculateFileHash)(appFilePath);
|
|
127
92
|
if (this.lastAppPathHash === currentHash && this.cachedRoutes) {
|
|
128
93
|
return false;
|
|
129
94
|
}
|
|
@@ -131,164 +96,9 @@ class RouteParserPlugin {
|
|
|
131
96
|
return true;
|
|
132
97
|
}
|
|
133
98
|
catch (error) {
|
|
134
|
-
|
|
99
|
+
(0, devtool_kits_1.routeParserLog)('warn', '⚠️ 检查文件变更时出错:', error.message);
|
|
135
100
|
return true;
|
|
136
101
|
}
|
|
137
102
|
}
|
|
138
|
-
calculateFileHash(filePath) {
|
|
139
|
-
try {
|
|
140
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
141
|
-
return crypto.createHash('md5').update(content).digest('hex');
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
this.log('warn', '⚠️ 计算文件哈希失败:', error.message);
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
parseRoutes() {
|
|
149
|
-
try {
|
|
150
|
-
const appFilePath = path.resolve(process.cwd(), this.options.appPath);
|
|
151
|
-
if (!fs.existsSync(appFilePath)) {
|
|
152
|
-
throw new Error(`App.tsx 文件不存在: ${appFilePath}`);
|
|
153
|
-
}
|
|
154
|
-
const sourceCode = fs.readFileSync(appFilePath, 'utf-8');
|
|
155
|
-
const ast = (0, parser_1.parse)(sourceCode, {
|
|
156
|
-
sourceType: 'module',
|
|
157
|
-
plugins: [
|
|
158
|
-
'jsx',
|
|
159
|
-
'typescript',
|
|
160
|
-
'decorators-legacy',
|
|
161
|
-
'classProperties',
|
|
162
|
-
'objectRestSpread',
|
|
163
|
-
'functionBind',
|
|
164
|
-
'exportDefaultFrom',
|
|
165
|
-
'exportNamespaceFrom',
|
|
166
|
-
'dynamicImport',
|
|
167
|
-
'nullishCoalescingOperator',
|
|
168
|
-
'optionalChaining'
|
|
169
|
-
]
|
|
170
|
-
});
|
|
171
|
-
const routeSet = new Set();
|
|
172
|
-
const routeStack = [];
|
|
173
|
-
const self = this;
|
|
174
|
-
(0, traverse_1.default)(ast, {
|
|
175
|
-
JSXElement: {
|
|
176
|
-
enter(path) {
|
|
177
|
-
const { openingElement } = path.node;
|
|
178
|
-
if (self.isRouteComponent(openingElement)) {
|
|
179
|
-
const routeInfo = self.extractRouteInfo(openingElement);
|
|
180
|
-
routeStack.push(routeInfo);
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
exit(path) {
|
|
184
|
-
const { openingElement } = path.node;
|
|
185
|
-
if (self.isRouteComponent(openingElement)) {
|
|
186
|
-
const currentRoute = routeStack.pop();
|
|
187
|
-
if (currentRoute && currentRoute.path === '*') {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
if (currentRoute && (currentRoute.path || currentRoute.index)) {
|
|
191
|
-
const fullPath = self.buildFullPath(routeStack, currentRoute);
|
|
192
|
-
if (fullPath) {
|
|
193
|
-
routeSet.add(fullPath);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
const { basePath } = this.options;
|
|
201
|
-
const routes = Array.from(routeSet).map(routePath => ({
|
|
202
|
-
path: basePath ? `${basePath}${routePath}` : routePath,
|
|
203
|
-
}));
|
|
204
|
-
// 默认路由也需要加上 basePath
|
|
205
|
-
const defaultPath = basePath ? `${basePath}/` : '/';
|
|
206
|
-
return routes.length > 0 ? routes : [{ path: defaultPath }];
|
|
207
|
-
}
|
|
208
|
-
catch (error) {
|
|
209
|
-
this.log('warn', '⚠️ 路由解析失败,使用默认路由:', error.message);
|
|
210
|
-
const { basePath } = this.options;
|
|
211
|
-
const defaultPath = basePath ? `${basePath}/` : '/';
|
|
212
|
-
return [{ path: defaultPath }];
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
isRouteComponent(openingElement) {
|
|
216
|
-
return (t.isJSXIdentifier(openingElement.name) &&
|
|
217
|
-
openingElement.name.name === 'Route');
|
|
218
|
-
}
|
|
219
|
-
extractRouteInfo(openingElement) {
|
|
220
|
-
const routeInfo = {};
|
|
221
|
-
openingElement.attributes.forEach((attr) => {
|
|
222
|
-
if (t.isJSXAttribute(attr)) {
|
|
223
|
-
const name = attr.name.name;
|
|
224
|
-
let value;
|
|
225
|
-
if (attr.value) {
|
|
226
|
-
if (t.isStringLiteral(attr.value)) {
|
|
227
|
-
value = attr.value.value;
|
|
228
|
-
}
|
|
229
|
-
else if (t.isJSXExpressionContainer(attr.value)) {
|
|
230
|
-
const expression = attr.value.expression;
|
|
231
|
-
if (t.isStringLiteral(expression)) {
|
|
232
|
-
value = expression.value;
|
|
233
|
-
}
|
|
234
|
-
else if (t.isTemplateLiteral(expression)) {
|
|
235
|
-
value = this.evaluateTemplateLiteral(expression);
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
value = true;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
value = true;
|
|
244
|
-
}
|
|
245
|
-
routeInfo[name] = value;
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
return routeInfo;
|
|
249
|
-
}
|
|
250
|
-
buildFullPath(routeStack, currentRoute) {
|
|
251
|
-
let fullPath = '';
|
|
252
|
-
for (let i = 0; i < routeStack.length; i++) {
|
|
253
|
-
if (routeStack[i].path) {
|
|
254
|
-
let parentPath = routeStack[i].path;
|
|
255
|
-
if (!parentPath.startsWith('/'))
|
|
256
|
-
parentPath = `/${parentPath}`;
|
|
257
|
-
if (parentPath.endsWith('/') && parentPath !== '/') {
|
|
258
|
-
parentPath = parentPath.slice(0, -1);
|
|
259
|
-
}
|
|
260
|
-
fullPath += parentPath === '/' ? '' : parentPath;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
if (currentRoute.index) {
|
|
264
|
-
return fullPath || '/';
|
|
265
|
-
}
|
|
266
|
-
else if (currentRoute.path) {
|
|
267
|
-
const routePath = currentRoute.path;
|
|
268
|
-
if (routePath === '*') {
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
if (!routePath.startsWith('/')) {
|
|
272
|
-
fullPath = `${fullPath}/${routePath}`;
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
fullPath = routePath;
|
|
276
|
-
}
|
|
277
|
-
if (fullPath === '')
|
|
278
|
-
fullPath = '/';
|
|
279
|
-
if (!fullPath.startsWith('/'))
|
|
280
|
-
fullPath = `/${fullPath}`;
|
|
281
|
-
return fullPath;
|
|
282
|
-
}
|
|
283
|
-
return null;
|
|
284
|
-
}
|
|
285
|
-
evaluateTemplateLiteral(templateLiteral) {
|
|
286
|
-
const quasis = templateLiteral.quasis;
|
|
287
|
-
const expressions = templateLiteral.expressions;
|
|
288
|
-
if (quasis.length === 1 && expressions.length === 0) {
|
|
289
|
-
return quasis[0].value.raw;
|
|
290
|
-
}
|
|
291
|
-
return quasis.map((q) => q.value.raw).join('');
|
|
292
|
-
}
|
|
293
103
|
}
|
|
294
104
|
exports.default = RouteParserPlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-rspack-preset",
|
|
3
|
-
"version": "1.0.50
|
|
3
|
+
"version": "1.0.50",
|
|
4
4
|
"files": [
|
|
5
5
|
"lib",
|
|
6
6
|
"patches",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@babel/parser": "^7.28.0",
|
|
32
32
|
"@babel/traverse": "^7.28.0",
|
|
33
33
|
"@babel/types": "^7.28.2",
|
|
34
|
-
"@lark-apaas/devtool-kits": "^1.2.
|
|
34
|
+
"@lark-apaas/devtool-kits": "^1.2.20",
|
|
35
35
|
"@lark-apaas/miaoda-inspector-babel-plugin": "^1.0.2",
|
|
36
36
|
"@lark-apaas/styled-jsx": "^1.0.1",
|
|
37
37
|
"@rspack/plugin-react-refresh": "^1.5.1",
|