@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 crypto = __importStar(require("crypto"));
42
- const parser_1 = require("@babel/parser");
43
- const traverse_1 = __importDefault(require("@babel/traverse"));
44
- const t = __importStar(require("@babel/types"));
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 = this.normalizeBasePath(options.basePath ?? envBasePath);
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
- const routes = this.parseRoutes();
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
- this.log('warn', '⚠️ 路由解析失败,使用默认路由:', error.message);
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
- this.log('warn', `⚠️ App.tsx 文件不存在: ${appFilePath}`);
88
+ (0, devtool_kits_1.routeParserLog)('warn', `⚠️ App.tsx 文件不存在: ${appFilePath}`);
124
89
  return false;
125
90
  }
126
- const currentHash = this.calculateFileHash(appFilePath);
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
- this.log('warn', '⚠️ 检查文件变更时出错:', error.message);
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-alpha.0",
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.19",
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",