@lark-apaas/fullstack-rspack-preset 1.0.32-alpha.2 → 1.0.32-alpha.21

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
@@ -1,18 +1,56 @@
1
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 __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
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
39
  exports.createRecommendRspackConfig = createRecommendRspackConfig;
40
+ const fs_1 = __importDefault(require("fs"));
7
41
  const path_1 = __importDefault(require("path"));
8
42
  const core_1 = __importDefault(require("@rspack/core"));
9
43
  const devtool_kits_1 = require("@lark-apaas/devtool-kits");
10
44
  const plugin_react_refresh_1 = __importDefault(require("@rspack/plugin-react-refresh"));
11
45
  const dev_server_listener_1 = require("./utils/dev-server-listener");
12
- const route_parser_plugin_1 = __importDefault(require("./rspack-plugins/route-parser-plugin"));
46
+ const route_parser_plugin_1 = __importStar(require("./rspack-plugins/route-parser-plugin"));
13
47
  const slardar_performance_monitor_plugin_1 = __importDefault(require("./rspack-plugins/slardar-performance-monitor-plugin"));
14
48
  const view_context_injection_plugin_1 = __importDefault(require("./rspack-plugins/view-context-injection-plugin"));
15
49
  const og_meta_injection_plugin_1 = __importDefault(require("./rspack-plugins/og-meta-injection-plugin"));
50
+ const runtime_injection_plugin_1 = __importDefault(require("./rspack-plugins/runtime-injection-plugin"));
51
+ const css_legacy_plugin_1 = __importDefault(require("./rspack-plugins/css-legacy-plugin"));
52
+ const polyfill_plugin_1 = __importDefault(require("./rspack-plugins/polyfill-plugin"));
53
+ const static_assets_plugin_1 = __importDefault(require("./rspack-plugins/static-assets-plugin"));
16
54
  const source_map_upload_plugin_1 = __importDefault(require("./rspack-plugins/source-map-upload-plugin"));
17
55
  const dev_server_snapdom_proxy_1 = require("./utils/dev-server-snapdom-proxy");
18
56
  function sendBackendUnavailable502(_err, _req, res) {
@@ -26,14 +64,37 @@ function sendBackendUnavailable502(_err, _req, res) {
26
64
  }
27
65
  // 检查是否启用宽松 build 模式
28
66
  const isLooseMode = process.env.FORCE_FRAMEWORK_BUILD_LOOSE_MODE === 'true';
67
+ function readAppFlags(rootDir) {
68
+ try {
69
+ return JSON.parse(fs_1.default.readFileSync(path_1.default.resolve(rootDir, 'package.json'), 'utf-8')).flags || {};
70
+ }
71
+ catch {
72
+ return {};
73
+ }
74
+ }
29
75
  function createRecommendRspackConfig(options) {
30
76
  const { isDev = true, enableReactRefresh = isDev, needRoutes = true, clientBasePath = '', publicPath = '', // 静态资源路径
31
77
  } = options;
32
78
  const assetsCDNPath = publicPath + '/';
33
79
  const rootDir = process.cwd();
34
80
  const serverPort = process.env.SERVER_PORT || '3000';
81
+ const appFlags = readAppFlags(rootDir);
82
+ // 从 clientBasePath(如 /app/app123)中提取 appId,用于构造旧路径代理规则
83
+ // 仅在新路径格式(/app/:appId)下启用旧路径兼容
84
+ const isNewPathFormat = /^\/app\/[^/]/.test(clientBasePath);
85
+ const appId = isNewPathFormat
86
+ ? clientBasePath.replace(/\/+$/, '').split('/').pop()
87
+ : '';
88
+ // 提前创建实例,以便在 plugins 和 setupMiddlewares 中复用
89
+ const staticAssetsPlugin = new static_assets_plugin_1.default({ clientBasePath, rootDir });
35
90
  // 优先从环境变量获取 RELEASE_ID,否则使用时间戳作为兜底
36
91
  const releaseId = process.env.RELEASE_ID || new Date().toISOString();
92
+ // 解析路由定义,供 DefinePlugin 注入到 bundle
93
+ let routeDefinitions = [];
94
+ if (needRoutes) {
95
+ const routeBasePath = isDev ? '' : (0, route_parser_plugin_1.normalizeBasePath)(clientBasePath || '');
96
+ routeDefinitions = (0, route_parser_plugin_1.parseRoutesFromFile)('./client/src/app.tsx', routeBasePath);
97
+ }
37
98
  return {
38
99
  mode: isDev ? 'development' : 'production',
39
100
  cache: false,
@@ -43,9 +104,20 @@ function createRecommendRspackConfig(options) {
43
104
  resolve: {
44
105
  mainFields: ['module', 'browser', 'main'],
45
106
  extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
107
+ alias: {
108
+ // 确保所有 echarts 子路径导入(如 echarts/core, echarts/lib/...)
109
+ // 都解析到 preset 自带的 echarts,避免第三方扩展(如 echarts-wordcloud)
110
+ // 安装了独立的 echarts 副本导致注册不到同一个实例上
111
+ echarts: path_1.default.dirname(require.resolve('echarts/package.json')),
112
+ },
46
113
  },
47
114
  module: {
48
115
  rules: [
116
+ // Support ?raw query to import file content as string (like Vite's ?raw)
117
+ {
118
+ resourceQuery: /\?raw$/,
119
+ type: 'asset/source',
120
+ },
49
121
  {
50
122
  test: /\.svg$/,
51
123
  type: 'asset',
@@ -120,11 +192,6 @@ function createRecommendRspackConfig(options) {
120
192
  transform: {
121
193
  react: {
122
194
  runtime: 'automatic',
123
- ...(isDev
124
- ? {
125
- importSource: path_1.default.dirname(require.resolve('@lark-apaas/miaoda-inspector-jsx-runtime')),
126
- }
127
- : {}),
128
195
  development: isDev,
129
196
  refresh: enableReactRefresh,
130
197
  },
@@ -146,10 +213,19 @@ function createRecommendRspackConfig(options) {
146
213
  ],
147
214
  },
148
215
  plugins: [
149
- // 针对 clsx/echarts 等包,让其默认好用
150
- new core_1.default.NormalModuleReplacementPlugin(/^(clsx|echarts)$/, function (resource) {
216
+ // 运行时注入插件 - 自动将 @lark-apaas/client-toolkit/runtime 注入到所有入口之前
217
+ new runtime_injection_plugin_1.default(),
218
+ new core_1.default.BannerPlugin({
219
+ banner: `window.__RELEASE_COMMIT_ID__ = '${releaseId}';`,
220
+ raw: true,
221
+ }),
222
+ // 针对 clsx/echarts/echarts-for-react 等包,让其默认好用
223
+ new core_1.default.NormalModuleReplacementPlugin(/^(clsx|echarts|echarts-for-react)$/, function (resource) {
151
224
  if (!resource.context.endsWith('module-alias')) {
152
- if (resource.request.endsWith('echarts')) {
225
+ if (resource.request === 'echarts-for-react') {
226
+ resource.request = require.resolve('./module-alias/echarts-for-react');
227
+ }
228
+ else if (resource.request === 'echarts') {
153
229
  resource.request = require.resolve('./module-alias/echarts');
154
230
  }
155
231
  else {
@@ -163,7 +239,12 @@ function createRecommendRspackConfig(options) {
163
239
  'process.env.CLIENT_BASE_PATH': JSON.stringify(clientBasePath),
164
240
  'process.env.FORCE_FRAMEWORK_DOMAIN_MAIN': JSON.stringify(process.env.FORCE_FRAMEWORK_DOMAIN_MAIN ?? ''),
165
241
  'process.env.CWD': JSON.stringify(''),
166
- 'window.__APP_VERSION__': JSON.stringify(releaseId),
242
+ // runtime 注入标志位,用于 client-toolkit 兼容性判断
243
+ 'process.env.__RUNTIME_INJECTED__': JSON.stringify('true'),
244
+ // 构建工具标识,用于 source map 处理逻辑判断
245
+ 'process.env.BUILD_TOOL': JSON.stringify('rspack'),
246
+ // 模板 flags 配置
247
+ 'process.env.APP_FLAGS': JSON.stringify(appFlags),
167
248
  // 解决 window 未定义问题
168
249
  'typeof window': JSON.stringify('object'),
169
250
  window: 'globalThis',
@@ -186,17 +267,24 @@ function createRecommendRspackConfig(options) {
186
267
  },
187
268
  minify: false, // 关闭 html 压缩,导致数据注入异常
188
269
  }),
270
+ // 视图上下文注入插件(必须在 slardar 之前,确保 HTML 中 viewContext 先于 slardar)
271
+ new view_context_injection_plugin_1.default(),
189
272
  // 性能监控&tea埋点sdk插件
190
273
  new slardar_performance_monitor_plugin_1.default(),
191
- // 视图上下文注入插件
192
- new view_context_injection_plugin_1.default(),
193
274
  // OG Meta 标签注入插件
194
275
  new og_meta_injection_plugin_1.default(),
276
+ // 生产环境:生成 legacy CSS 并注入检测脚本
277
+ !isDev && new css_legacy_plugin_1.default(),
278
+ // 生产环境:为旧浏览器提供 polyfill(仅在需要时加载)
279
+ !isDev && new polyfill_plugin_1.default(),
280
+ // 静态资源插件 - 支持 @shared/static/* 导入
281
+ staticAssetsPlugin,
195
282
  // 开发环境下,解析路由
196
- isDev && needRoutes &&
283
+ needRoutes &&
197
284
  new route_parser_plugin_1.default({
198
285
  appPath: './client/src/app.tsx',
199
286
  outputPath: path_1.default.resolve(rootDir, 'dist/client/routes.json'),
287
+ ...(isDev ? { basePath: '' } : {}),
200
288
  }),
201
289
  // 热更新
202
290
  enableReactRefresh && new plugin_react_refresh_1.default({
@@ -220,10 +308,14 @@ function createRecommendRspackConfig(options) {
220
308
  minimizerOptions: {
221
309
  // 保持不压缩
222
310
  minify: !isDev,
223
- mangle: !isDev,
311
+ // mangle 配置:启用 Safari 10 兼容性
312
+ mangle: !isDev ? {
313
+ safari10: true, // 避免 Safari 严格模式的重复参数问题
314
+ } : false,
224
315
  format: {
225
316
  beautify: isDev,
226
317
  comments: false,
318
+ safari10: true, // Safari 10/11 兼容性修复
227
319
  },
228
320
  compress: {
229
321
  keep_classnames: true,
@@ -310,6 +402,23 @@ function createRecommendRspackConfig(options) {
310
402
  changeOrigin: true,
311
403
  onError: sendBackendUnavailable502,
312
404
  },
405
+ // 旧路径(/af/p/:appId)兼容代理,转发到 Node Server 由 legacy-path-redirect 中间件处理
406
+ ...(appId
407
+ ? [
408
+ {
409
+ context: [`/af/p/${appId}/api`],
410
+ target: `http://localhost:${serverPort}`,
411
+ changeOrigin: true,
412
+ onError: sendBackendUnavailable502,
413
+ },
414
+ {
415
+ context: [`/af/p/${appId}/__innerapi__`],
416
+ target: `http://localhost:${serverPort}`,
417
+ changeOrigin: true,
418
+ onError: sendBackendUnavailable502,
419
+ },
420
+ ]
421
+ : []),
313
422
  {
314
423
  context: (pathname, req) => {
315
424
  // 代理所有请求 HTML 响应的请求(页面路由)
@@ -322,9 +431,7 @@ function createRecommendRspackConfig(options) {
322
431
  changeOrigin: true,
323
432
  logLevel: 'debug',
324
433
  onError: (err, req, res) => (0, devtool_kits_1.handleDevProxyError)(err, req, res, {
325
- logDir: process.env.LOG_DIR || './logs',
326
- logFileName: 'server.std.log',
327
- maxErrorLogs: 100,
434
+ target: `http://localhost:${serverPort}`,
328
435
  }),
329
436
  },
330
437
  ],
@@ -334,6 +441,8 @@ function createRecommendRspackConfig(options) {
334
441
  },
335
442
  setupMiddlewares: (middlewares, devServer) => {
336
443
  if (devServer.app) {
444
+ // 开发环境下提供 shared/static 文件服务,对齐 Vite preset 的 configureServer 中间件
445
+ devServer.app.use(staticAssetsPlugin.createDevMiddleware());
337
446
  (0, dev_server_snapdom_proxy_1.registerSnapDomProxyMiddleware)(devServer.app, { baseUrl: clientBasePath });
338
447
  (0, devtool_kits_1.registerMiddlewares)(devServer.app, [
339
448
  (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;