@lark-apaas/fullstack-rspack-preset 1.0.38-alpha.0 → 1.0.38
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/polyfills/index.d.ts +14 -0
- package/lib/polyfills/index.js +35 -0
- package/lib/preset.js +13 -1
- package/lib/rspack-plugins/css-legacy-plugin.d.ts +30 -0
- package/lib/rspack-plugins/css-legacy-plugin.js +218 -0
- package/lib/rspack-plugins/polyfill-plugin.d.ts +12 -0
- package/lib/rspack-plugins/polyfill-plugin.js +73 -0
- package/package.json +5 -4
|
@@ -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
|
@@ -14,6 +14,8 @@ const slardar_performance_monitor_plugin_1 = __importDefault(require("./rspack-p
|
|
|
14
14
|
const view_context_injection_plugin_1 = __importDefault(require("./rspack-plugins/view-context-injection-plugin"));
|
|
15
15
|
const og_meta_injection_plugin_1 = __importDefault(require("./rspack-plugins/og-meta-injection-plugin"));
|
|
16
16
|
const runtime_injection_plugin_1 = __importDefault(require("./rspack-plugins/runtime-injection-plugin"));
|
|
17
|
+
const css_legacy_plugin_1 = __importDefault(require("./rspack-plugins/css-legacy-plugin"));
|
|
18
|
+
const polyfill_plugin_1 = __importDefault(require("./rspack-plugins/polyfill-plugin"));
|
|
17
19
|
const dev_server_snapdom_proxy_1 = require("./utils/dev-server-snapdom-proxy");
|
|
18
20
|
function sendBackendUnavailable502(_err, _req, res) {
|
|
19
21
|
if (res.headersSent)
|
|
@@ -165,6 +167,8 @@ function createRecommendRspackConfig(options) {
|
|
|
165
167
|
'process.env.CWD': JSON.stringify(''),
|
|
166
168
|
// runtime 注入标志位,用于 client-toolkit 兼容性判断
|
|
167
169
|
'process.env.__RUNTIME_INJECTED__': JSON.stringify('true'),
|
|
170
|
+
// 构建工具标识,用于 source map 处理逻辑判断
|
|
171
|
+
'process.env.BUILD_TOOL': JSON.stringify('rspack'),
|
|
168
172
|
// 解决 window 未定义问题
|
|
169
173
|
'typeof window': JSON.stringify('object'),
|
|
170
174
|
window: 'globalThis',
|
|
@@ -193,6 +197,10 @@ function createRecommendRspackConfig(options) {
|
|
|
193
197
|
new view_context_injection_plugin_1.default(),
|
|
194
198
|
// OG Meta 标签注入插件
|
|
195
199
|
new og_meta_injection_plugin_1.default(),
|
|
200
|
+
// 生产环境:生成 legacy CSS 并注入检测脚本
|
|
201
|
+
!isDev && new css_legacy_plugin_1.default(),
|
|
202
|
+
// 生产环境:为旧浏览器提供 polyfill(仅在需要时加载)
|
|
203
|
+
!isDev && new polyfill_plugin_1.default(),
|
|
196
204
|
// 开发环境下,解析路由
|
|
197
205
|
isDev && needRoutes &&
|
|
198
206
|
new route_parser_plugin_1.default({
|
|
@@ -217,10 +225,14 @@ function createRecommendRspackConfig(options) {
|
|
|
217
225
|
minimizerOptions: {
|
|
218
226
|
// 保持不压缩
|
|
219
227
|
minify: !isDev,
|
|
220
|
-
mangle
|
|
228
|
+
// mangle 配置:启用 Safari 10 兼容性
|
|
229
|
+
mangle: !isDev ? {
|
|
230
|
+
safari10: true, // 避免 Safari 严格模式的重复参数问题
|
|
231
|
+
} : false,
|
|
221
232
|
format: {
|
|
222
233
|
beautify: isDev,
|
|
223
234
|
comments: false,
|
|
235
|
+
safari10: true, // Safari 10/11 兼容性修复
|
|
224
236
|
},
|
|
225
237
|
compress: {
|
|
226
238
|
keep_classnames: true,
|
|
@@ -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;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CssLegacyPlugin = void 0;
|
|
4
|
+
const lightningcss_1 = require("lightningcss");
|
|
5
|
+
/**
|
|
6
|
+
* CSS Legacy Plugin
|
|
7
|
+
*
|
|
8
|
+
* 在构建完成后生成一份兼容旧浏览器的 CSS 文件(.legacy.css)
|
|
9
|
+
* 并在 HTML 中注入检测脚本,根据浏览器版本加载对应的 CSS
|
|
10
|
+
*/
|
|
11
|
+
class CssLegacyPlugin {
|
|
12
|
+
apply(compiler) {
|
|
13
|
+
compiler.hooks.thisCompilation.tap(CssLegacyPlugin.pluginName, (compilation) => {
|
|
14
|
+
compilation.hooks.processAssets.tapPromise({
|
|
15
|
+
name: CssLegacyPlugin.pluginName,
|
|
16
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE + 1,
|
|
17
|
+
}, async (assets) => {
|
|
18
|
+
const cssFiles = Object.keys(assets).filter((name) => name.endsWith('.css') && !name.endsWith('.legacy.css'));
|
|
19
|
+
for (const cssFile of cssFiles) {
|
|
20
|
+
const asset = assets[cssFile];
|
|
21
|
+
const source = asset.source();
|
|
22
|
+
let cssContent = typeof source === 'string' ? source : source.toString('utf-8');
|
|
23
|
+
try {
|
|
24
|
+
// 1. 先用正则移除/转换 LightningCSS 不支持的特性
|
|
25
|
+
cssContent = this.preprocessCss(cssContent);
|
|
26
|
+
// 2. 使用 LightningCSS 转换其他现代 CSS 特性
|
|
27
|
+
const result = (0, lightningcss_1.transform)({
|
|
28
|
+
filename: cssFile,
|
|
29
|
+
code: Buffer.from(cssContent),
|
|
30
|
+
minify: true,
|
|
31
|
+
targets: {
|
|
32
|
+
ios_saf: (12 << 16),
|
|
33
|
+
safari: (12 << 16),
|
|
34
|
+
chrome: (80 << 16),
|
|
35
|
+
},
|
|
36
|
+
include: lightningcss_1.Features.Nesting |
|
|
37
|
+
lightningcss_1.Features.MediaQueries |
|
|
38
|
+
lightningcss_1.Features.Colors |
|
|
39
|
+
lightningcss_1.Features.Selectors |
|
|
40
|
+
lightningcss_1.Features.ColorFunction |
|
|
41
|
+
lightningcss_1.Features.OklabColors |
|
|
42
|
+
lightningcss_1.Features.LabColors |
|
|
43
|
+
lightningcss_1.Features.P3Colors |
|
|
44
|
+
lightningcss_1.Features.HexAlphaColors |
|
|
45
|
+
lightningcss_1.Features.LightDark,
|
|
46
|
+
errorRecovery: true,
|
|
47
|
+
});
|
|
48
|
+
// 3. 后处理:移除可能残留的不支持特性
|
|
49
|
+
let legacyCss = Buffer.from(result.code).toString('utf-8');
|
|
50
|
+
legacyCss = this.postprocessCss(legacyCss);
|
|
51
|
+
const legacyFileName = cssFile.replace('.css', '.legacy.css');
|
|
52
|
+
compilation.emitAsset(legacyFileName, new compiler.webpack.sources.RawSource(Buffer.from(legacyCss)));
|
|
53
|
+
console.log(`[CssLegacyPlugin] Generated ${legacyFileName} (${Math.round(legacyCss.length / 1024)}KB)`);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error(`[CssLegacyPlugin] Failed to transform ${cssFile}:`, error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// 修改 HTML,注入 CSS 版本检测脚本
|
|
61
|
+
compilation.hooks.processAssets.tapPromise({
|
|
62
|
+
name: `${CssLegacyPlugin.pluginName}-html`,
|
|
63
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE + 1,
|
|
64
|
+
}, async (assets) => {
|
|
65
|
+
const htmlFiles = Object.keys(assets).filter((name) => name.endsWith('.html'));
|
|
66
|
+
for (const htmlFile of htmlFiles) {
|
|
67
|
+
const asset = assets[htmlFile];
|
|
68
|
+
const source = asset.source();
|
|
69
|
+
let htmlContent = typeof source === 'string' ? source : source.toString('utf-8');
|
|
70
|
+
const cssLinkRegex = /<link[^>]+href="([^"]+\.css)"[^>]*>/g;
|
|
71
|
+
const cssLinks = [...htmlContent.matchAll(cssLinkRegex)];
|
|
72
|
+
if (cssLinks.length === 0)
|
|
73
|
+
continue;
|
|
74
|
+
const detectionScript = this.generateDetectionScript(cssLinks);
|
|
75
|
+
for (const match of cssLinks) {
|
|
76
|
+
htmlContent = htmlContent.replace(match[0], '');
|
|
77
|
+
}
|
|
78
|
+
htmlContent = htmlContent.replace(/<head([^>]*)>/i, `<head$1>\n${detectionScript}`);
|
|
79
|
+
compilation.updateAsset(htmlFile, new compiler.webpack.sources.RawSource(htmlContent));
|
|
80
|
+
console.log(`[CssLegacyPlugin] Updated ${htmlFile} with CSS detection script`);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 预处理 CSS:移除/转换 LightningCSS 不支持降级的特性
|
|
87
|
+
*/
|
|
88
|
+
preprocessCss(css) {
|
|
89
|
+
// 1. 移除所有 @property 规则(iOS 16.4+ 才支持)
|
|
90
|
+
css = css.replace(/@property\s+--[\w-]+\s*\{[^}]*\}/g, '');
|
|
91
|
+
// 2. 展开 @layer 规则(iOS 15.4+ 才支持)
|
|
92
|
+
// 简单处理:移除 @layer xxx { 和对应的 },保留内部内容
|
|
93
|
+
css = this.unwrapAtLayer(css);
|
|
94
|
+
// 3. 移除 @supports 中的 color-mix 检测块(这些是渐进增强,旧浏览器不需要)
|
|
95
|
+
css = this.removeColorMixSupports(css);
|
|
96
|
+
return css;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 后处理 CSS:清理可能残留的不支持特性
|
|
100
|
+
*/
|
|
101
|
+
postprocessCss(css) {
|
|
102
|
+
// 移除可能残留的 @layer
|
|
103
|
+
css = this.unwrapAtLayer(css);
|
|
104
|
+
// 移除残留的 @property
|
|
105
|
+
css = css.replace(/@property\s+--[\w-]+\s*\{[^}]*\}/g, '');
|
|
106
|
+
// 移除残留的 color-mix @supports 块
|
|
107
|
+
css = this.removeColorMixSupports(css);
|
|
108
|
+
// 移除空的 @supports 块
|
|
109
|
+
css = css.replace(/@supports\s*\([^)]*\)\s*\{\s*\}/g, '');
|
|
110
|
+
return css;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 展开 @layer 规则,保留内部样式
|
|
114
|
+
*/
|
|
115
|
+
unwrapAtLayer(css) {
|
|
116
|
+
// 匹配 @layer name { ... } 结构
|
|
117
|
+
// 需要处理嵌套大括号
|
|
118
|
+
let result = css;
|
|
119
|
+
let changed = true;
|
|
120
|
+
while (changed) {
|
|
121
|
+
changed = false;
|
|
122
|
+
// 简单的 @layer 匹配(非嵌套情况)
|
|
123
|
+
const layerRegex = /@layer\s+[\w-]+\s*\{/g;
|
|
124
|
+
let match;
|
|
125
|
+
while ((match = layerRegex.exec(result)) !== null) {
|
|
126
|
+
const startIndex = match.index;
|
|
127
|
+
const contentStart = startIndex + match[0].length;
|
|
128
|
+
// 找到匹配的闭合大括号
|
|
129
|
+
let depth = 1;
|
|
130
|
+
let i = contentStart;
|
|
131
|
+
while (i < result.length && depth > 0) {
|
|
132
|
+
if (result[i] === '{')
|
|
133
|
+
depth++;
|
|
134
|
+
else if (result[i] === '}')
|
|
135
|
+
depth--;
|
|
136
|
+
i++;
|
|
137
|
+
}
|
|
138
|
+
if (depth === 0) {
|
|
139
|
+
// 提取 @layer 内部的内容
|
|
140
|
+
const innerContent = result.slice(contentStart, i - 1);
|
|
141
|
+
// 用内部内容替换整个 @layer 块
|
|
142
|
+
result = result.slice(0, startIndex) + innerContent + result.slice(i);
|
|
143
|
+
changed = true;
|
|
144
|
+
break; // 重新开始匹配
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* 移除 @supports (color: color-mix(...)) 块
|
|
152
|
+
* 这些是渐进增强代码,旧浏览器可以使用 fallback
|
|
153
|
+
*/
|
|
154
|
+
removeColorMixSupports(css) {
|
|
155
|
+
let result = css;
|
|
156
|
+
let changed = true;
|
|
157
|
+
while (changed) {
|
|
158
|
+
changed = false;
|
|
159
|
+
// 匹配 @supports (color:color-mix(...)) { ... }
|
|
160
|
+
const supportsRegex = /@supports\s*\(\s*color\s*:\s*color-mix\s*\([^)]+\)\s*\)\s*\{/g;
|
|
161
|
+
let match;
|
|
162
|
+
while ((match = supportsRegex.exec(result)) !== null) {
|
|
163
|
+
const startIndex = match.index;
|
|
164
|
+
const contentStart = startIndex + match[0].length;
|
|
165
|
+
// 找到匹配的闭合大括号
|
|
166
|
+
let depth = 1;
|
|
167
|
+
let i = contentStart;
|
|
168
|
+
while (i < result.length && depth > 0) {
|
|
169
|
+
if (result[i] === '{')
|
|
170
|
+
depth++;
|
|
171
|
+
else if (result[i] === '}')
|
|
172
|
+
depth--;
|
|
173
|
+
i++;
|
|
174
|
+
}
|
|
175
|
+
if (depth === 0) {
|
|
176
|
+
// 移除整个 @supports 块
|
|
177
|
+
result = result.slice(0, startIndex) + result.slice(i);
|
|
178
|
+
changed = true;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
generateDetectionScript(cssLinks) {
|
|
186
|
+
const cssUrls = cssLinks.map((match) => match[1]);
|
|
187
|
+
// 检测脚本 + document.write 直接写入正确的 CSS(在加载前完成检测)
|
|
188
|
+
return `<script>
|
|
189
|
+
(function() {
|
|
190
|
+
var isModernBrowser = (function() {
|
|
191
|
+
try {
|
|
192
|
+
if (typeof CSS === 'undefined' || typeof CSS.supports !== 'function') return false;
|
|
193
|
+
if (!CSS.supports('selector(:has(*))')) return false;
|
|
194
|
+
if (!CSS.supports('color', 'color-mix(in srgb, red, blue)')) return false;
|
|
195
|
+
if (!CSS.supports('color', 'oklch(50% 0.1 0)')) return false;
|
|
196
|
+
return true;
|
|
197
|
+
} catch (e) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
})();
|
|
201
|
+
|
|
202
|
+
var cssUrls = ${JSON.stringify(cssUrls)};
|
|
203
|
+
var suffix = isModernBrowser ? '' : '.legacy';
|
|
204
|
+
|
|
205
|
+
cssUrls.forEach(function(url) {
|
|
206
|
+
var finalUrl = url.replace(/\\.css$/, suffix + '.css');
|
|
207
|
+
document.write('<link rel="stylesheet" href="' + finalUrl + '">');
|
|
208
|
+
});
|
|
209
|
+
})();
|
|
210
|
+
</script>
|
|
211
|
+
<noscript>
|
|
212
|
+
${cssUrls.map((url) => ` <link rel="stylesheet" href="${url}">`).join('\n')}
|
|
213
|
+
</noscript>`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
exports.CssLegacyPlugin = CssLegacyPlugin;
|
|
217
|
+
CssLegacyPlugin.pluginName = 'CssLegacyPlugin';
|
|
218
|
+
exports.default = CssLegacyPlugin;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polyfill Plugin
|
|
3
|
+
*
|
|
4
|
+
* 使用 core-js 为旧版浏览器生成独立的 polyfill 文件
|
|
5
|
+
* 通过检测脚本按需加载,现代浏览器不会加载 polyfill
|
|
6
|
+
*/
|
|
7
|
+
import type { Compiler } from '@rspack/core';
|
|
8
|
+
export declare class PolyfillPlugin {
|
|
9
|
+
apply(compiler: Compiler): void;
|
|
10
|
+
private generateDetectionScript;
|
|
11
|
+
}
|
|
12
|
+
export default PolyfillPlugin;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Polyfill Plugin
|
|
4
|
+
*
|
|
5
|
+
* 使用 core-js 为旧版浏览器生成独立的 polyfill 文件
|
|
6
|
+
* 通过检测脚本按需加载,现代浏览器不会加载 polyfill
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.PolyfillPlugin = void 0;
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const PLUGIN_NAME = 'PolyfillPlugin';
|
|
15
|
+
const POLYFILL_ENTRY = path_1.default.resolve(__dirname, '../polyfills/index');
|
|
16
|
+
class PolyfillPlugin {
|
|
17
|
+
apply(compiler) {
|
|
18
|
+
// 添加 polyfills 作为单独的 entry
|
|
19
|
+
compiler.hooks.entryOption.tap(PLUGIN_NAME, (_context, entry) => {
|
|
20
|
+
if (typeof entry === 'function') {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// 添加 polyfills entry,单独打包
|
|
24
|
+
entry['polyfills'] = {
|
|
25
|
+
import: [POLYFILL_ENTRY],
|
|
26
|
+
filename: 'polyfills.js',
|
|
27
|
+
// 不需要 runtime,保持文件独立
|
|
28
|
+
runtime: false,
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
|
|
32
|
+
// 在 HTML 中注入检测脚本
|
|
33
|
+
compilation.hooks.processAssets.tapPromise({
|
|
34
|
+
name: `${PLUGIN_NAME}-html`,
|
|
35
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE,
|
|
36
|
+
}, async (assets) => {
|
|
37
|
+
const htmlFiles = Object.keys(assets).filter((name) => name.endsWith('.html'));
|
|
38
|
+
const publicPath = compilation.outputOptions.publicPath || '/';
|
|
39
|
+
for (const htmlFile of htmlFiles) {
|
|
40
|
+
const asset = assets[htmlFile];
|
|
41
|
+
const source = asset.source();
|
|
42
|
+
let htmlContent = typeof source === 'string' ? source : source.toString('utf-8');
|
|
43
|
+
const polyfillUrl = `${publicPath}polyfills.js`.replace(/\/+/g, '/').replace(':/', '://');
|
|
44
|
+
const detectionScript = this.generateDetectionScript(polyfillUrl);
|
|
45
|
+
// 注入到 <head> 最前面
|
|
46
|
+
htmlContent = htmlContent.replace(/<head([^>]*)>/i, `<head$1>\n${detectionScript}`);
|
|
47
|
+
compilation.updateAsset(htmlFile, new compiler.webpack.sources.RawSource(htmlContent));
|
|
48
|
+
console.log(`[PolyfillPlugin] Updated ${htmlFile} with polyfill detection`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
generateDetectionScript(polyfillUrl) {
|
|
54
|
+
return `<script>
|
|
55
|
+
(function(){
|
|
56
|
+
var needsPolyfill=(function(){
|
|
57
|
+
try{
|
|
58
|
+
if(typeof crypto==="undefined"||typeof crypto.randomUUID!=="function")return true;
|
|
59
|
+
if(typeof Object.hasOwn!=="function")return true;
|
|
60
|
+
if(typeof[].at!=="function")return true;
|
|
61
|
+
if(typeof[].findLast!=="function")return true;
|
|
62
|
+
return false;
|
|
63
|
+
}catch(e){return true}
|
|
64
|
+
})();
|
|
65
|
+
if(needsPolyfill){
|
|
66
|
+
document.write('<script src="${polyfillUrl}"><\\/script>');
|
|
67
|
+
}
|
|
68
|
+
})();
|
|
69
|
+
</script>`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.PolyfillPlugin = PolyfillPlugin;
|
|
73
|
+
exports.default = PolyfillPlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-rspack-preset",
|
|
3
|
-
"version": "1.0.38
|
|
3
|
+
"version": "1.0.38",
|
|
4
4
|
"files": [
|
|
5
5
|
"lib",
|
|
6
6
|
"patches",
|
|
@@ -28,12 +28,13 @@
|
|
|
28
28
|
"prepublishOnly": "npm run build"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
+
"core-js": "^3.40.0",
|
|
31
32
|
"@babel/parser": "^7.28.0",
|
|
32
33
|
"@babel/traverse": "^7.28.0",
|
|
33
34
|
"@babel/types": "^7.28.2",
|
|
34
|
-
"@lark-apaas/devtool-kits": "1.2.17
|
|
35
|
+
"@lark-apaas/devtool-kits": "^1.2.17",
|
|
35
36
|
"@lark-apaas/miaoda-inspector-babel-plugin": "^1.0.0",
|
|
36
|
-
"@lark-apaas/miaoda-inspector-jsx-runtime": "^1.0.
|
|
37
|
+
"@lark-apaas/miaoda-inspector-jsx-runtime": "^1.0.1",
|
|
37
38
|
"@lark-apaas/styled-jsx": "^1.0.1",
|
|
38
39
|
"@rspack/plugin-react-refresh": "^1.5.1",
|
|
39
40
|
"@swc/plugin-styled-jsx": "^11.0.0",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"colorjs.io": "^0.5.2",
|
|
43
44
|
"dotenv": "^16.4.5",
|
|
44
45
|
"echarts": "^6.0.0",
|
|
46
|
+
"lightningcss": "^1.28.0",
|
|
45
47
|
"patch-package": "^8.0.0",
|
|
46
48
|
"postcss-import": "^16.1.1",
|
|
47
49
|
"react-refresh": "^0.17.0",
|
|
@@ -55,7 +57,6 @@
|
|
|
55
57
|
"vitest": "^2.1.8"
|
|
56
58
|
},
|
|
57
59
|
"peerDependencies": {
|
|
58
|
-
"@lark-apaas/client-toolkit": "^1.2.8",
|
|
59
60
|
"@rspack/core": "^1.5.5",
|
|
60
61
|
"react": ">=16.14.0",
|
|
61
62
|
"react-dom": ">=16.14.0"
|