@lark-apaas/miaoda-presets 1.0.2 → 1.0.4

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.
@@ -167,6 +167,14 @@ function render() {
167
167
  const origin = targetOrigin || getPreviewParentOrigin();
168
168
  window.parent.postMessage(message, origin);
169
169
  };
170
+ // 通知主应用存在自定义 overlay
171
+ sendPostMessage({
172
+ type: 'app-features',
173
+ payload: [{
174
+ feature: 'error-overlay',
175
+ enable: true,
176
+ }],
177
+ });
170
178
  if (currentCompileErrorMessage) {
171
179
  currentMode = 'compileError';
172
180
  ErrorContainer(rootDocument, root, {
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createRecommendRspackConfig = createRecommendRspackConfig;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const core_1 = __importDefault(require("@rspack/core"));
9
+ const dev_server_listener_1 = require("../utils/dev-server-listener");
9
10
  // eslint-disable-next-line @typescript-eslint/no-var-requires
10
11
  const RouteParserPlugin = require('../rspack-plugins/route-parser-plugin');
11
12
  // eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -123,7 +124,9 @@ function createRecommendRspackConfig(options) {
123
124
  maxChunks: 1,
124
125
  }),
125
126
  // 全栈模式下,增加性能监控上报脚本注入
126
- runtimeMode === 'fullstack' ? new SlardarPerformanceMonitorPlugin() : undefined,
127
+ runtimeMode === 'fullstack'
128
+ ? new SlardarPerformanceMonitorPlugin()
129
+ : undefined,
127
130
  // 开发环境下,解析路由
128
131
  isDev &&
129
132
  needRoutes &&
@@ -177,7 +180,7 @@ function createRecommendRspackConfig(options) {
177
180
  },
178
181
  devtool: isDev ? 'source-map' : false, // 对应vite的sourcemap配置
179
182
  devServer: {
180
- headers: (req) => {
183
+ headers: req => {
181
184
  // 获取请求的Origin头
182
185
  const requestOrigin = req.headers.origin ?? '';
183
186
  // 定义允许的域名白名单
@@ -194,12 +197,17 @@ function createRecommendRspackConfig(options) {
194
197
  'Access-Control-Allow-Origin': allowedOrigin,
195
198
  };
196
199
  },
200
+ onListening(server) {
201
+ if (runtimeMode === 'fullstack') {
202
+ (0, dev_server_listener_1.listenHmrTiming)(server);
203
+ }
204
+ },
197
205
  client: {
198
206
  overlay: {
199
207
  errors: false,
200
208
  warnings: false,
201
- }
202
- }
209
+ },
210
+ },
203
211
  },
204
212
  };
205
213
  }
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+ /**
3
+ * Webpack HMR 耗时计算插件 JavaScript 版本
4
+ * 在浏览器 Console 打印热更文件和耗时信息
5
+ */
6
+ class HMRTimingPlugin {
7
+ constructor(options = {}) {
8
+ this.options = {
9
+ threshold: options.threshold || 1000
10
+ };
11
+ this.hmrStartTime = null;
12
+ this.changedFiles = new Set();
13
+ this.devServer = null;
14
+ }
15
+ apply(compiler) {
16
+ const pluginName = 'HMRTimingPlugin';
17
+ // 在 devServer 启动后保存引用
18
+ if (compiler.options.devServer) {
19
+ const originalSetupMiddlewares = compiler.options.devServer.setupMiddlewares;
20
+ const self = this;
21
+ compiler.options.devServer.setupMiddlewares = function (middlewares, devServer) {
22
+ // 保存 devServer 实例的引用
23
+ self.devServer = devServer;
24
+ global.__webpack_dev_server__ = devServer;
25
+ if (originalSetupMiddlewares) {
26
+ return originalSetupMiddlewares(middlewares, devServer);
27
+ }
28
+ return middlewares;
29
+ };
30
+ }
31
+ // 监听文件变化
32
+ compiler.hooks.invalid.tap(pluginName, (fileName) => {
33
+ this.hmrStartTime = Date.now();
34
+ this.changedFiles.clear();
35
+ if (fileName) {
36
+ this.changedFiles.add(fileName);
37
+ }
38
+ });
39
+ // 监听 watch 运行开始
40
+ compiler.hooks.watchRun.tapAsync(pluginName, (compiler, callback) => {
41
+ if (!this.hmrStartTime) {
42
+ this.hmrStartTime = Date.now();
43
+ }
44
+ // 收集变化的文件
45
+ if (compiler.modifiedFiles) {
46
+ compiler.modifiedFiles.forEach(file => this.changedFiles.add(file));
47
+ }
48
+ if (compiler.removedFiles) {
49
+ compiler.removedFiles.forEach(file => this.changedFiles.add(file));
50
+ }
51
+ callback();
52
+ });
53
+ // 监听编译完成
54
+ compiler.hooks.done.tap(pluginName, (stats) => {
55
+ if (this.hmrStartTime === null)
56
+ return;
57
+ const totalTime = Date.now() - this.hmrStartTime;
58
+ // 获取实际更新的模块
59
+ const updatedModules = new Set();
60
+ stats.compilation.modules.forEach(module => {
61
+ const identifier = module.resource || module.identifier?.();
62
+ if (identifier && typeof identifier === 'string') {
63
+ // 过滤掉 node_modules 中的模块
64
+ if (!identifier.includes('node_modules')) {
65
+ updatedModules.add(identifier);
66
+ }
67
+ }
68
+ });
69
+ // 合并文件列表
70
+ const allChangedFiles = new Set([
71
+ ...Array.from(this.changedFiles),
72
+ ...Array.from(updatedModules)
73
+ ]);
74
+ // 简化文件路径
75
+ const simplifiedFiles = Array.from(allChangedFiles)
76
+ .map(file => {
77
+ // 移除项目根路径
78
+ const relativePath = file.replace(compiler.context || '', '');
79
+ // 移除开头的斜杠
80
+ return relativePath.replace(/^[\/\\]/, '');
81
+ })
82
+ .filter(file => file && file.length > 0)
83
+ .slice(0, 10); // 最多显示10个文件
84
+ const timingInfo = {
85
+ totalTime,
86
+ changedFiles: simplifiedFiles,
87
+ modules: stats.compilation.modules.size,
88
+ isOverThreshold: totalTime > this.options.threshold,
89
+ threshold: this.options.threshold
90
+ };
91
+ // 通过 WebSocket 发送到浏览器
92
+ this.sendToBrowser(compiler, timingInfo);
93
+ // 重置
94
+ this.changedFiles.clear();
95
+ });
96
+ // 注入客户端代码
97
+ this.injectClientScript(compiler);
98
+ }
99
+ sendToBrowser(compiler, timingInfo) {
100
+ // 使用 done hook 确保在编译完成后发送
101
+ const server = this.getDevServer(compiler);
102
+ if (server) {
103
+ if (server.sockWrite) {
104
+ // webpack-dev-server 3.x
105
+ server.sockWrite(server.sockets, 'hmr-timing', timingInfo);
106
+ }
107
+ else if (server.sendMessage) {
108
+ // webpack-dev-server 4.x/5.x
109
+ const clients = server.webSocketServer?.clients || server.clients;
110
+ server.sendMessage(clients, 'hmr-timing', timingInfo);
111
+ }
112
+ }
113
+ }
114
+ getDevServer(compiler) {
115
+ // 尝试多种方式获取 devServer 实例
116
+ // 方式1: 从 compiler.options.devServer 存储的引用
117
+ if (compiler.options.devServer && compiler.options.devServer.__instance) {
118
+ return compiler.options.devServer.__instance;
119
+ }
120
+ // 方式2: 从全局变量
121
+ if (global.__webpack_dev_server__) {
122
+ return global.__webpack_dev_server__;
123
+ }
124
+ // 方式3: 从 compiler 上挂载的引用
125
+ if (compiler.__webpack_dev_server__) {
126
+ return compiler.__webpack_dev_server__;
127
+ }
128
+ // 方式4: 在 infrastructure 日志中查找
129
+ if (compiler.infrastructureLogger) {
130
+ const logger = compiler.infrastructureLogger;
131
+ if (logger.__devServer) {
132
+ return logger.__devServer;
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+ injectClientScript(compiler) {
138
+ const clientScript = `
139
+ ;(function() {
140
+ if (typeof window === 'undefined' || !module.hot) return;
141
+
142
+ const threshold = ${this.options.threshold};
143
+
144
+ // 显示 HMR 耗时和变化文件
145
+ function logHMRTiming(data) {
146
+ const color = data.isOverThreshold ? '#ff9800' : '#4caf50';
147
+
148
+ // 主要信息
149
+ console.log(
150
+ '%c🔥 HMR 更新完成 - 耗时: %c' + data.totalTime + 'ms',
151
+ 'color: ' + color + '; font-weight: bold; font-size: 14px;',
152
+ 'color: ' + color + '; font-weight: bold; font-size: 16px;'
153
+ );
154
+
155
+ // 热更文件列表
156
+ if (data.changedFiles && data.changedFiles.length > 0) {
157
+ console.group('📝 热更文件 (' + data.changedFiles.length + ')');
158
+ data.changedFiles.forEach(function(file) {
159
+ console.log(' •', file);
160
+ });
161
+ console.groupEnd();
162
+ }
163
+
164
+ // 超过阈值警告
165
+ if (data.isOverThreshold) {
166
+ console.warn('⚠️ HMR 耗时超过阈值 ' + threshold + 'ms');
167
+ }
168
+
169
+ console.log(''); // 空行分隔
170
+ }
171
+
172
+ // 监听 webpack-dev-server 的 WebSocket 消息
173
+ if (typeof __webpack_dev_server_client__ !== 'undefined') {
174
+ // webpack-dev-server 4.x/5.x
175
+ const originalOnMessage = __webpack_dev_server_client__.onMessage;
176
+ __webpack_dev_server_client__.onMessage = function(message) {
177
+ if (message.type === 'hmr-timing') {
178
+ logHMRTiming(message.data);
179
+ }
180
+ if (originalOnMessage) {
181
+ return originalOnMessage.apply(this, arguments);
182
+ }
183
+ };
184
+ } else {
185
+ // 备用方案:监听 WebSocket
186
+ setTimeout(function() {
187
+ const ws = window.__webpack_dev_server_ws__;
188
+ if (ws && ws.addEventListener) {
189
+ ws.addEventListener('message', function(event) {
190
+ try {
191
+ const message = JSON.parse(event.data);
192
+ if (message.type === 'hmr-timing') {
193
+ logHMRTiming(message.data);
194
+ }
195
+ } catch (e) {}
196
+ });
197
+ }
198
+ }, 1000);
199
+ }
200
+ })();
201
+ `;
202
+ compiler.hooks.compilation.tap('HMRTimingPlugin', (compilation) => {
203
+ compilation.hooks.processAssets.tap({
204
+ name: 'HMRTimingPlugin',
205
+ stage: compilation.constructor.PROCESS_ASSETS_STAGE_ADDITIONAL,
206
+ }, () => {
207
+ // 注入到主入口文件
208
+ const mainAssets = Object.keys(compilation.assets).filter(name => name.match(/^(main|index|app|bundle).*\.js$/) && !name.includes('hot-update'));
209
+ if (mainAssets.length > 0) {
210
+ const assetName = mainAssets[0];
211
+ const asset = compilation.assets[assetName];
212
+ const source = asset.source();
213
+ const newSource = clientScript + '\n' + source;
214
+ compilation.assets[assetName] = {
215
+ source: () => newSource,
216
+ size: () => newSource.length,
217
+ map: () => asset.map ? asset.map() : null
218
+ };
219
+ }
220
+ });
221
+ });
222
+ }
223
+ }
224
+ module.exports = HMRTimingPlugin;
225
+ /**
226
+ * 使用示例:
227
+ *
228
+ * // webpack.config.js
229
+ * const HMRTimingPlugin = require('./HMRTimingPlugin');
230
+ *
231
+ * module.exports = {
232
+ * mode: 'development',
233
+ * devServer: {
234
+ * hot: true,
235
+ * },
236
+ * plugins: [
237
+ * new HMRTimingPlugin({
238
+ * threshold: 1000 // 超过1秒会显示警告
239
+ * })
240
+ * ]
241
+ * };
242
+ *
243
+ * 浏览器控制台输出示例:
244
+ *
245
+ * 🔥 HMR 更新完成 - 耗时: 523ms
246
+ * 📝 热更文件 (2)
247
+ * • src/components/App.jsx
248
+ * • src/styles/main.css
249
+ *
250
+ * 🔥 HMR 更新完成 - 耗时: 1250ms
251
+ * 📝 热更文件 (1)
252
+ * • src/utils/helper.js
253
+ * ⚠️ HMR 耗时超过阈值 1000ms
254
+ *
255
+ * 功能特性:
256
+ * - 显示 HMR 耗时(带颜色提示)
257
+ * - 显示热更新的文件列表(可折叠)
258
+ * - 超过阈值时显示警告
259
+ * - 通过 DevServer WebSocket 通信
260
+ * - 自动过滤 node_modules
261
+ * - 简化文件路径显示
262
+ */
@@ -0,0 +1,113 @@
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
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.listenHmrTiming = listenHmrTiming;
37
+ /* eslint-disable @typescript-eslint/no-explicit-any */
38
+ const fs = __importStar(require("fs"));
39
+ /**
40
+ * 监听 HMR 编译时间
41
+ * @param server
42
+ */
43
+ function listenHmrTiming(server) {
44
+ const compiler = server.compiler;
45
+ const ws = server.webSocketServer;
46
+ let start = 0;
47
+ let changedFiles = new Set();
48
+ // 监听文件变更
49
+ compiler.hooks.watchRun.tap('HmrTiming', (comp) => {
50
+ const modifiedFiles = comp.modifiedFiles;
51
+ if (modifiedFiles && modifiedFiles.size > 0) {
52
+ changedFiles = new Set(modifiedFiles);
53
+ }
54
+ else {
55
+ changedFiles.clear();
56
+ }
57
+ start = Date.now();
58
+ });
59
+ // 编译完成
60
+ compiler.hooks.done.tap('HmrTiming', () => {
61
+ const duration = Date.now() - start;
62
+ // 过滤有效文件 - 只保留实际的项目文件
63
+ const validFiles = [...changedFiles]
64
+ .map(f => f.replace(process.cwd(), '').replace(/\\/g, '/'))
65
+ .filter(filePath => {
66
+ // 过滤无效文件
67
+ if (!filePath || filePath.length === 0)
68
+ return false;
69
+ // 过滤 node_modules 中的文件
70
+ if (filePath.includes('/node_modules/'))
71
+ return false;
72
+ // 只保留支持的文件类型
73
+ const validExtensions = ['.js', '.jsx', '.ts', '.tsx', '.svelte', '.css', '.json', '.html'];
74
+ const hasValidExtension = validExtensions.some(ext => filePath.toLowerCase().endsWith(ext));
75
+ if (!hasValidExtension)
76
+ return false;
77
+ return true;
78
+ });
79
+ // 获取文件统计信息
80
+ const fileStats = validFiles.map(filePath => {
81
+ const fullPath = process.cwd() + filePath;
82
+ try {
83
+ const stats = fs.statSync(fullPath);
84
+ return {
85
+ path: filePath,
86
+ size: stats.size,
87
+ };
88
+ }
89
+ catch (error) {
90
+ return {
91
+ path: filePath,
92
+ size: 0,
93
+ };
94
+ }
95
+ });
96
+ // 计算总文件大小(B)
97
+ const totalSize = fileStats.reduce((sum, file) => sum + file.size, 0);
98
+ const payload = {
99
+ duration,
100
+ fileCount: validFiles.length,
101
+ fileTotalSize: totalSize,
102
+ };
103
+ // 推送到浏览器端
104
+ for (const client of ws.clients) {
105
+ if (client.readyState === 1) {
106
+ client.send(JSON.stringify({
107
+ type: 'hmr-timing',
108
+ data: payload,
109
+ }));
110
+ }
111
+ }
112
+ });
113
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/miaoda-presets",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "files": [
5
5
  "lib"
6
6
  ],