@lark-apaas/fullstack-rspack-preset 1.0.32-alpha.3 → 1.0.32-alpha.30

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/index.js CHANGED
@@ -70,5 +70,6 @@ function createFullstackRspackConfig(overrides = {}) {
70
70
  publicPath,
71
71
  });
72
72
  // 5. 深度合并用户配置
73
+ // 注意:runtime 注入由 RuntimeInjectionPlugin 在编译时自动处理
73
74
  return (0, webpack_merge_1.merge)(baseConfig, overrides);
74
75
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * EChartsReact wrapper component
3
+ * - Automatically converts HSL colors to hex in option
4
+ * - Removes label.color from funnel series
5
+ */
6
+ export class EChartsReact extends React.Component<any, any, any> {
7
+ constructor(props: any);
8
+ constructor(props: any, context: any);
9
+ render(): React.CElement<import("echarts-for-react").EChartsReactProps, OriginalReactECharts>;
10
+ }
11
+ export * from "echarts-for-react";
12
+ export default EChartsReact;
13
+ import React from 'react';
14
+ import OriginalReactECharts from 'echarts-for-react';
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.EChartsReact = void 0;
21
+ const react_1 = __importDefault(require("react"));
22
+ const echarts_for_react_1 = __importDefault(require("echarts-for-react"));
23
+ const colorjs_io_1 = __importDefault(require("colorjs.io"));
24
+ /**
25
+ * Convert HSL/HSLA color to hex or rgba
26
+ */
27
+ function convertColorToHex(color) {
28
+ if (typeof color !== 'string')
29
+ return color;
30
+ const trimmed = color.trim();
31
+ if (!trimmed.startsWith('hsl')) {
32
+ return color;
33
+ }
34
+ try {
35
+ const colorObj = new colorjs_io_1.default(trimmed);
36
+ if (colorObj.alpha < 1) {
37
+ return colorObj.to('srgb').toString({ format: 'rgba' });
38
+ }
39
+ else {
40
+ return colorObj.to('srgb').toString({ format: 'hex' });
41
+ }
42
+ }
43
+ catch (error) {
44
+ console.warn(`Failed to convert color: ${color}`, error);
45
+ return color;
46
+ }
47
+ }
48
+ function convertColorValue(propValue, visited) {
49
+ if (typeof propValue === 'string') {
50
+ return convertColorToHex(propValue);
51
+ }
52
+ if (Array.isArray(propValue)) {
53
+ return propValue.map((item) => typeof item === 'string' ? convertColorToHex(item) : transformColors(item, visited));
54
+ }
55
+ return transformColors(propValue, visited);
56
+ }
57
+ /**
58
+ * Deep traverse object to transform all HSL colors in 'color' property
59
+ */
60
+ function transformColors(value, visited = new WeakSet()) {
61
+ if (value === null || value === undefined)
62
+ return value;
63
+ if (typeof value !== 'object')
64
+ return value;
65
+ if (visited.has(value))
66
+ return value;
67
+ if (value instanceof Date || value instanceof RegExp)
68
+ return value;
69
+ visited.add(value);
70
+ if (Array.isArray(value)) {
71
+ return value.map((item) => transformColors(item, visited));
72
+ }
73
+ const result = {};
74
+ for (const key in value) {
75
+ if (!Object.prototype.hasOwnProperty.call(value, key))
76
+ continue;
77
+ const propValue = value[key];
78
+ if (key === 'color') {
79
+ if (typeof propValue === 'function') {
80
+ result[key] = (...args) => convertColorValue(propValue(...args), visited);
81
+ }
82
+ else {
83
+ result[key] = convertColorValue(propValue, visited);
84
+ }
85
+ }
86
+ else {
87
+ result[key] = transformColors(propValue, visited);
88
+ }
89
+ }
90
+ return result;
91
+ }
92
+ /**
93
+ * Remove label.color and emphasis.label.color from funnel series
94
+ * to let ECharts use default contrast colors
95
+ */
96
+ function removeFunnelLabelColor(option) {
97
+ if (!Array.isArray(option.series))
98
+ return option;
99
+ const series = option.series.map((item) => {
100
+ if (item?.type !== 'funnel')
101
+ return item;
102
+ let result = { ...item };
103
+ // remove label.color
104
+ if (result.label && typeof result.label === 'object') {
105
+ const { color: _, ...labelRest } = result.label;
106
+ result = { ...result, label: labelRest };
107
+ }
108
+ // remove emphasis.label.color
109
+ const emphasis = result.emphasis;
110
+ if (emphasis?.label && typeof emphasis.label === 'object') {
111
+ const { color: _, ...emphasisLabelRest } = emphasis.label;
112
+ result = { ...result, emphasis: { ...emphasis, label: emphasisLabelRest } };
113
+ }
114
+ return result;
115
+ });
116
+ return { ...option, series };
117
+ }
118
+ /**
119
+ * EChartsReact wrapper component
120
+ * - Automatically converts HSL colors to hex in option
121
+ * - Removes label.color from funnel series
122
+ */
123
+ class EChartsReact extends react_1.default.Component {
124
+ render() {
125
+ const { option, ...rest } = this.props;
126
+ const transformedOption = option
127
+ ? removeFunnelLabelColor(transformColors(option))
128
+ : option;
129
+ return react_1.default.createElement(echarts_for_react_1.default, {
130
+ option: transformedOption,
131
+ ...rest,
132
+ });
133
+ }
134
+ }
135
+ exports.EChartsReact = EChartsReact;
136
+ __exportStar(require("echarts-for-react"), exports);
137
+ exports.default = EChartsReact;
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Mount echarts to window for debugging
3
+ */
4
+ export function registerEchartsToWindow(): void;
1
5
  export function resgisterEchartsTheme(): void;
2
6
  export { default as echarts } from "echarts";
3
7
  export * from "echarts";
@@ -10,21 +10,56 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
10
10
  if (k2 === undefined) k2 = k;
11
11
  o[k2] = m[k];
12
12
  }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
13
18
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
19
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
20
  };
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
16
38
  var __importDefault = (this && this.__importDefault) || function (mod) {
17
39
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
40
  };
19
41
  Object.defineProperty(exports, "__esModule", { value: true });
20
42
  exports.echarts = void 0;
43
+ exports.registerEchartsToWindow = registerEchartsToWindow;
21
44
  exports.resgisterEchartsTheme = resgisterEchartsTheme;
22
45
  var echarts_1 = require("echarts");
23
46
  Object.defineProperty(exports, "echarts", { enumerable: true, get: function () { return __importDefault(echarts_1).default; } });
24
47
  __exportStar(require("echarts"), exports);
48
+ const echarts = __importStar(require("echarts"));
25
49
  const echarts_2 = require("echarts");
26
50
  const registry_echarts_theme_1 = require("./registry_echarts_theme");
27
51
  let isRegistered = false;
52
+ let isWindowMounted = false;
53
+ /**
54
+ * Mount echarts to window for debugging
55
+ */
56
+ function registerEchartsToWindow() {
57
+ if (isWindowMounted || typeof globalThis.window === 'undefined') {
58
+ return;
59
+ }
60
+ isWindowMounted = true;
61
+ globalThis.echarts = echarts;
62
+ }
28
63
  function resgisterEchartsTheme() {
29
64
  // 注册 ud 主题,全局仅注册一次
30
65
  if (isRegistered) {
@@ -45,4 +80,5 @@ function resgisterEchartsTheme() {
45
80
  }
46
81
  }
47
82
  }
83
+ registerEchartsToWindow();
48
84
  resgisterEchartsTheme();
@@ -8,9 +8,7 @@ export declare function getCssVariable(varName: string, element?: HTMLElement):
8
8
  export declare function generateEChartsTheme(element?: HTMLElement): {
9
9
  color: string[];
10
10
  backgroundColor: string;
11
- textStyle: {
12
- color: string;
13
- };
11
+ textStyle: {};
14
12
  title: {
15
13
  textStyle: {
16
14
  color: string;
@@ -352,9 +350,7 @@ export declare function generateEChartsTheme(element?: HTMLElement): {
352
350
  export declare function getShadcnEChartsTheme(): {
353
351
  color: string[];
354
352
  backgroundColor: string;
355
- textStyle: {
356
- color: string;
357
- };
353
+ textStyle: {};
358
354
  title: {
359
355
  textStyle: {
360
356
  color: string;
@@ -44,7 +44,7 @@ function generateEChartsTheme(element = document.body) {
44
44
  color: chartColors,
45
45
  backgroundColor: 'transparent',
46
46
  textStyle: {
47
- color: foreground,
47
+ // color: foreground,
48
48
  },
49
49
  title: {
50
50
  textStyle: {
@@ -129,6 +129,25 @@ function removeAllChildren(element, skip) {
129
129
  element.removeChild(childList[i]);
130
130
  }
131
131
  }
132
+ function getParentOriginFromParams() {
133
+ try {
134
+ var params = new URLSearchParams(window.location.search);
135
+ var origin = params.get('__parentOrigin');
136
+ if (origin) {
137
+ sessionStorage.setItem('__parentOrigin', origin);
138
+ return origin;
139
+ }
140
+ }
141
+ catch (e) {
142
+ // ignore
143
+ }
144
+ try {
145
+ return sessionStorage.getItem('__parentOrigin') || '';
146
+ }
147
+ catch (e) {
148
+ return '';
149
+ }
150
+ }
132
151
  function getLegacyParentOrigin() {
133
152
  const currentOrigin = window.location?.origin || '';
134
153
  if (currentOrigin.includes('force.feishuapp.net')) {
@@ -150,7 +169,7 @@ function getLegacyParentOrigin() {
150
169
  }
151
170
  function sendPostMessage(message, targetOrigin) {
152
171
  try {
153
- const parentOrigin = process?.env?.FORCE_FRAMEWORK_DOMAIN_MAIN || getLegacyParentOrigin();
172
+ const parentOrigin = getParentOriginFromParams() || process?.env?.FORCE_FRAMEWORK_DOMAIN_MAIN || getLegacyParentOrigin();
154
173
  const origin = targetOrigin || parentOrigin;
155
174
  window.parent.postMessage(message, origin);
156
175
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * iOS 兼容性 Polyfills
3
+ *
4
+ * 使用 core-js 提供 iOS 15.4 以下版本需要的 polyfill
5
+ * 此文件会被单独打包成 polyfills.js,按需加载
6
+ */
7
+ import 'core-js/actual/array/at';
8
+ import 'core-js/actual/string/at';
9
+ import 'core-js/actual/array/find-last';
10
+ import 'core-js/actual/array/find-last-index';
11
+ import 'core-js/actual/object/has-own';
12
+ import 'core-js/actual/promise/any';
13
+ import 'core-js/actual/aggregate-error';
14
+ import 'core-js/actual/structured-clone';
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * iOS 兼容性 Polyfills
4
+ *
5
+ * 使用 core-js 提供 iOS 15.4 以下版本需要的 polyfill
6
+ * 此文件会被单独打包成 polyfills.js,按需加载
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ // ECMAScript polyfills (core-js)
10
+ require("core-js/actual/array/at");
11
+ require("core-js/actual/string/at");
12
+ require("core-js/actual/array/find-last");
13
+ require("core-js/actual/array/find-last-index");
14
+ require("core-js/actual/object/has-own");
15
+ require("core-js/actual/promise/any");
16
+ require("core-js/actual/aggregate-error");
17
+ require("core-js/actual/structured-clone");
18
+ // Web Crypto API polyfill (不在 core-js 范围内)
19
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID !== 'function') {
20
+ Object.defineProperty(crypto, 'randomUUID', {
21
+ value: function randomUUID() {
22
+ const bytes = new Uint8Array(16);
23
+ crypto.getRandomValues(bytes);
24
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
25
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
26
+ let hex = '';
27
+ for (let i = 0; i < 16; i++) {
28
+ hex += (bytes[i] < 16 ? '0' : '') + bytes[i].toString(16);
29
+ }
30
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
31
+ },
32
+ writable: true,
33
+ configurable: true,
34
+ });
35
+ }
package/lib/preset.js CHANGED
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createRecommendRspackConfig = createRecommendRspackConfig;
7
+ const fs_1 = __importDefault(require("fs"));
7
8
  const path_1 = __importDefault(require("path"));
8
9
  const core_1 = __importDefault(require("@rspack/core"));
9
10
  const devtool_kits_1 = require("@lark-apaas/devtool-kits");
@@ -13,6 +14,10 @@ const route_parser_plugin_1 = __importDefault(require("./rspack-plugins/route-pa
13
14
  const slardar_performance_monitor_plugin_1 = __importDefault(require("./rspack-plugins/slardar-performance-monitor-plugin"));
14
15
  const view_context_injection_plugin_1 = __importDefault(require("./rspack-plugins/view-context-injection-plugin"));
15
16
  const og_meta_injection_plugin_1 = __importDefault(require("./rspack-plugins/og-meta-injection-plugin"));
17
+ const runtime_injection_plugin_1 = __importDefault(require("./rspack-plugins/runtime-injection-plugin"));
18
+ const css_legacy_plugin_1 = __importDefault(require("./rspack-plugins/css-legacy-plugin"));
19
+ const polyfill_plugin_1 = __importDefault(require("./rspack-plugins/polyfill-plugin"));
20
+ const static_assets_plugin_1 = __importDefault(require("./rspack-plugins/static-assets-plugin"));
16
21
  const source_map_upload_plugin_1 = __importDefault(require("./rspack-plugins/source-map-upload-plugin"));
17
22
  const dev_server_snapdom_proxy_1 = require("./utils/dev-server-snapdom-proxy");
18
23
  function sendBackendUnavailable502(_err, _req, res) {
@@ -26,14 +31,51 @@ function sendBackendUnavailable502(_err, _req, res) {
26
31
  }
27
32
  // 检查是否启用宽松 build 模式
28
33
  const isLooseMode = process.env.FORCE_FRAMEWORK_BUILD_LOOSE_MODE === 'true';
34
+ function readAppFlags(rootDir) {
35
+ try {
36
+ return JSON.parse(fs_1.default.readFileSync(path_1.default.resolve(rootDir, 'package.json'), 'utf-8')).flags || {};
37
+ }
38
+ catch {
39
+ return {};
40
+ }
41
+ }
29
42
  function createRecommendRspackConfig(options) {
30
43
  const { isDev = true, enableReactRefresh = isDev, needRoutes = true, clientBasePath = '', publicPath = '', // 静态资源路径
31
44
  } = options;
32
45
  const assetsCDNPath = publicPath + '/';
33
46
  const rootDir = process.cwd();
34
47
  const serverPort = process.env.SERVER_PORT || '3000';
48
+ const appFlags = readAppFlags(rootDir);
49
+ // 从 clientBasePath(如 /app/app123)中提取 appId,用于构造旧路径代理规则
50
+ // 仅在新路径格式(/app/:appId)下启用旧路径兼容
51
+ const isNewPathFormat = /^\/app\/[^/]/.test(clientBasePath);
52
+ const appId = isNewPathFormat
53
+ ? clientBasePath.replace(/\/+$/, '').split('/').pop()
54
+ : '';
55
+ // 提前创建实例,以便在 plugins 和 setupMiddlewares 中复用
56
+ const staticAssetsPlugin = new static_assets_plugin_1.default({ clientBasePath, rootDir });
35
57
  // 优先从环境变量获取 RELEASE_ID,否则使用时间戳作为兜底
36
58
  const releaseId = process.env.RELEASE_ID || new Date().toISOString();
59
+ // 读取构建前生成的路由定义 JSON,供 DefinePlugin 注入到 bundle
60
+ // build.sh 在 client 构建之前已将 page-routes.json / api-routes.json 写入 dist/
61
+ let pageRouteDefinitions = [];
62
+ let apiRouteDefinitions = [];
63
+ if (needRoutes) {
64
+ try {
65
+ const pageRoutesPath = path_1.default.resolve(rootDir, 'dist/page-routes.json');
66
+ if (fs_1.default.existsSync(pageRoutesPath)) {
67
+ pageRouteDefinitions = JSON.parse(fs_1.default.readFileSync(pageRoutesPath, 'utf-8'));
68
+ }
69
+ }
70
+ catch { /* ignore */ }
71
+ try {
72
+ const apiRoutesPath = path_1.default.resolve(rootDir, 'dist/api-routes.json');
73
+ if (fs_1.default.existsSync(apiRoutesPath)) {
74
+ apiRouteDefinitions = JSON.parse(fs_1.default.readFileSync(apiRoutesPath, 'utf-8'));
75
+ }
76
+ }
77
+ catch { /* ignore */ }
78
+ }
37
79
  return {
38
80
  mode: isDev ? 'development' : 'production',
39
81
  cache: false,
@@ -43,9 +85,20 @@ function createRecommendRspackConfig(options) {
43
85
  resolve: {
44
86
  mainFields: ['module', 'browser', 'main'],
45
87
  extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
88
+ alias: {
89
+ // 确保所有 echarts 子路径导入(如 echarts/core, echarts/lib/...)
90
+ // 都解析到 preset 自带的 echarts,避免第三方扩展(如 echarts-wordcloud)
91
+ // 安装了独立的 echarts 副本导致注册不到同一个实例上
92
+ echarts: path_1.default.dirname(require.resolve('echarts/package.json')),
93
+ },
46
94
  },
47
95
  module: {
48
96
  rules: [
97
+ // Support ?raw query to import file content as string (like Vite's ?raw)
98
+ {
99
+ resourceQuery: /\?raw$/,
100
+ type: 'asset/source',
101
+ },
49
102
  {
50
103
  test: /\.svg$/,
51
104
  type: 'asset',
@@ -120,11 +173,6 @@ function createRecommendRspackConfig(options) {
120
173
  transform: {
121
174
  react: {
122
175
  runtime: 'automatic',
123
- ...(isDev
124
- ? {
125
- importSource: path_1.default.dirname(require.resolve('@lark-apaas/miaoda-inspector-jsx-runtime')),
126
- }
127
- : {}),
128
176
  development: isDev,
129
177
  refresh: enableReactRefresh,
130
178
  },
@@ -146,14 +194,19 @@ function createRecommendRspackConfig(options) {
146
194
  ],
147
195
  },
148
196
  plugins: [
197
+ // 运行时注入插件 - 自动将 @lark-apaas/client-toolkit/runtime 注入到所有入口之前
198
+ new runtime_injection_plugin_1.default(),
149
199
  new core_1.default.BannerPlugin({
150
200
  banner: `window.__RELEASE_COMMIT_ID__ = '${releaseId}';`,
151
201
  raw: true,
152
202
  }),
153
- // 针对 clsx/echarts 等包,让其默认好用
154
- new core_1.default.NormalModuleReplacementPlugin(/^(clsx|echarts)$/, function (resource) {
203
+ // 针对 clsx/echarts/echarts-for-react 等包,让其默认好用
204
+ new core_1.default.NormalModuleReplacementPlugin(/^(clsx|echarts|echarts-for-react)$/, function (resource) {
155
205
  if (!resource.context.endsWith('module-alias')) {
156
- if (resource.request.endsWith('echarts')) {
206
+ if (resource.request === 'echarts-for-react') {
207
+ resource.request = require.resolve('./module-alias/echarts-for-react');
208
+ }
209
+ else if (resource.request === 'echarts') {
157
210
  resource.request = require.resolve('./module-alias/echarts');
158
211
  }
159
212
  else {
@@ -167,6 +220,15 @@ function createRecommendRspackConfig(options) {
167
220
  'process.env.CLIENT_BASE_PATH': JSON.stringify(clientBasePath),
168
221
  'process.env.FORCE_FRAMEWORK_DOMAIN_MAIN': JSON.stringify(process.env.FORCE_FRAMEWORK_DOMAIN_MAIN ?? ''),
169
222
  'process.env.CWD': JSON.stringify(''),
223
+ // runtime 注入标志位,用于 client-toolkit 兼容性判断
224
+ 'process.env.__RUNTIME_INJECTED__': JSON.stringify('true'),
225
+ // 构建工具标识,用于 source map 处理逻辑判断
226
+ 'process.env.BUILD_TOOL': JSON.stringify('rspack'),
227
+ // 模板 flags 配置
228
+ 'process.env.APP_FLAGS': JSON.stringify(appFlags),
229
+ // 路由定义,供客户端匹配 referer_path 和 api
230
+ 'process.env.__PAGE_ROUTE_DEFINITIONS__': JSON.stringify(JSON.stringify(pageRouteDefinitions)),
231
+ 'process.env.__API_ROUTE_DEFINITIONS__': JSON.stringify(JSON.stringify(apiRouteDefinitions)),
170
232
  // 解决 window 未定义问题
171
233
  'typeof window': JSON.stringify('object'),
172
234
  window: 'globalThis',
@@ -189,17 +251,24 @@ function createRecommendRspackConfig(options) {
189
251
  },
190
252
  minify: false, // 关闭 html 压缩,导致数据注入异常
191
253
  }),
254
+ // 视图上下文注入插件(必须在 slardar 之前,确保 HTML 中 viewContext 先于 slardar)
255
+ new view_context_injection_plugin_1.default(),
192
256
  // 性能监控&tea埋点sdk插件
193
257
  new slardar_performance_monitor_plugin_1.default(),
194
- // 视图上下文注入插件
195
- new view_context_injection_plugin_1.default(),
196
258
  // OG Meta 标签注入插件
197
259
  new og_meta_injection_plugin_1.default(),
198
- // 开发环境下,解析路由
199
- isDev && needRoutes &&
260
+ // 生产环境:生成 legacy CSS 并注入检测脚本
261
+ !isDev && new css_legacy_plugin_1.default(),
262
+ // 生产环境:为旧浏览器提供 polyfill(仅在需要时加载)
263
+ !isDev && new polyfill_plugin_1.default(),
264
+ // 静态资源插件 - 支持 @shared/static/* 导入
265
+ staticAssetsPlugin,
266
+ // 解析路由(dev + prod 均生成 routes.json 和 page-routes.json)
267
+ needRoutes &&
200
268
  new route_parser_plugin_1.default({
201
269
  appPath: './client/src/app.tsx',
202
270
  outputPath: path_1.default.resolve(rootDir, 'dist/client/routes.json'),
271
+ ...(isDev ? { basePath: '' } : {}),
203
272
  }),
204
273
  // 热更新
205
274
  enableReactRefresh && new plugin_react_refresh_1.default({
@@ -223,10 +292,14 @@ function createRecommendRspackConfig(options) {
223
292
  minimizerOptions: {
224
293
  // 保持不压缩
225
294
  minify: !isDev,
226
- mangle: !isDev,
295
+ // mangle 配置:启用 Safari 10 兼容性
296
+ mangle: !isDev ? {
297
+ safari10: true, // 避免 Safari 严格模式的重复参数问题
298
+ } : false,
227
299
  format: {
228
300
  beautify: isDev,
229
301
  comments: false,
302
+ safari10: true, // Safari 10/11 兼容性修复
230
303
  },
231
304
  compress: {
232
305
  keep_classnames: true,
@@ -313,6 +386,23 @@ function createRecommendRspackConfig(options) {
313
386
  changeOrigin: true,
314
387
  onError: sendBackendUnavailable502,
315
388
  },
389
+ // 旧路径(/af/p/:appId)兼容代理,转发到 Node Server 由 legacy-path-redirect 中间件处理
390
+ ...(appId
391
+ ? [
392
+ {
393
+ context: [`/af/p/${appId}/api`],
394
+ target: `http://localhost:${serverPort}`,
395
+ changeOrigin: true,
396
+ onError: sendBackendUnavailable502,
397
+ },
398
+ {
399
+ context: [`/af/p/${appId}/__innerapi__`],
400
+ target: `http://localhost:${serverPort}`,
401
+ changeOrigin: true,
402
+ onError: sendBackendUnavailable502,
403
+ },
404
+ ]
405
+ : []),
316
406
  {
317
407
  context: (pathname, req) => {
318
408
  // 代理所有请求 HTML 响应的请求(页面路由)
@@ -325,9 +415,7 @@ function createRecommendRspackConfig(options) {
325
415
  changeOrigin: true,
326
416
  logLevel: 'debug',
327
417
  onError: (err, req, res) => (0, devtool_kits_1.handleDevProxyError)(err, req, res, {
328
- logDir: process.env.LOG_DIR || './logs',
329
- logFileName: 'server.std.log',
330
- maxErrorLogs: 100,
418
+ target: `http://localhost:${serverPort}`,
331
419
  }),
332
420
  },
333
421
  ],
@@ -337,6 +425,8 @@ function createRecommendRspackConfig(options) {
337
425
  },
338
426
  setupMiddlewares: (middlewares, devServer) => {
339
427
  if (devServer.app) {
428
+ // 开发环境下提供 shared/static 文件服务,对齐 Vite preset 的 configureServer 中间件
429
+ devServer.app.use(staticAssetsPlugin.createDevMiddleware());
340
430
  (0, dev_server_snapdom_proxy_1.registerSnapDomProxyMiddleware)(devServer.app, { baseUrl: clientBasePath });
341
431
  (0, devtool_kits_1.registerMiddlewares)(devServer.app, [
342
432
  (0, devtool_kits_1.createDevLogsMiddleware)({ logDir: process.env.LOG_DIR || './logs' }),
@@ -0,0 +1,30 @@
1
+ import type { Compiler } from '@rspack/core';
2
+ /**
3
+ * CSS Legacy Plugin
4
+ *
5
+ * 在构建完成后生成一份兼容旧浏览器的 CSS 文件(.legacy.css)
6
+ * 并在 HTML 中注入检测脚本,根据浏览器版本加载对应的 CSS
7
+ */
8
+ export declare class CssLegacyPlugin {
9
+ static pluginName: string;
10
+ apply(compiler: Compiler): void;
11
+ /**
12
+ * 预处理 CSS:移除/转换 LightningCSS 不支持降级的特性
13
+ */
14
+ private preprocessCss;
15
+ /**
16
+ * 后处理 CSS:清理可能残留的不支持特性
17
+ */
18
+ private postprocessCss;
19
+ /**
20
+ * 展开 @layer 规则,保留内部样式
21
+ */
22
+ private unwrapAtLayer;
23
+ /**
24
+ * 移除 @supports (color: color-mix(...)) 块
25
+ * 这些是渐进增强代码,旧浏览器可以使用 fallback
26
+ */
27
+ private removeColorMixSupports;
28
+ private generateDetectionScript;
29
+ }
30
+ export default CssLegacyPlugin;