@zzzzzzhaopu/vite-plugin-upload-sourcemap 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 [Your Name]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,392 @@
1
+ # @zzzzzzhaopu/vite-plugin-upload-sourcemap
2
+
3
+ > 🚀 A Vite plugin that automatically uploads SourceMap files to monitoring platforms after production build.
4
+
5
+ [English](#english) | [中文](#中文)
6
+
7
+ ---
8
+
9
+ ## English
10
+
11
+ ### Features
12
+
13
+ - ✨ **Automatic Upload**: Uploads SourceMap files after build completion
14
+ - 🗑️ **Auto Cleanup**: Removes .map files after upload (configurable)
15
+ - 🔌 **Easy Integration**: Simple Vite plugin configuration
16
+ - 🛠️ **Flexible**: Supports custom upload functions for different platforms
17
+ - 🎯 **Type Safe**: Full TypeScript support
18
+ - 📦 **Zero Config**: Works out of the box with sensible defaults
19
+
20
+ ### Why Do You Need This?
21
+
22
+ **The Problem:**
23
+ 1. Production code is minified and obfuscated for better performance
24
+ 2. Error stack traces show minified code positions, making debugging difficult
25
+ 3. SourceMaps can't be deployed to production (security risk - exposes source code)
26
+
27
+ **The Solution:**
28
+ 1. Generate SourceMaps during build
29
+ 2. Upload them to your monitoring platform (Sentry, Datadog, etc.)
30
+ 3. Remove them from deployment bundle
31
+ 4. Monitor platform uses them to restore readable stack traces
32
+
33
+ ### Installation
34
+
35
+ ```bash
36
+ npm install @zzzzzzhaopu/vite-plugin-upload-sourcemap -D
37
+ # 或
38
+ pnpm add @zzzzzzhaopu/vite-plugin-upload-sourcemap -D
39
+ # 或
40
+ yarn add @zzzzzzhaopu/vite-plugin-upload-sourcemap -D
41
+ ```
42
+
43
+ ### Quick Start
44
+
45
+ ```typescript
46
+ // vite.config.ts
47
+ import { defineConfig } from 'vite'
48
+ import { uploadSourceMapPlugin } from 'vite-plugin-upload-sourcemap'
49
+
50
+ export default defineConfig(({ mode }) => ({
51
+ plugins: [
52
+ uploadSourceMapPlugin({
53
+ enabled: mode === 'production',
54
+ uploadUrl: 'https://your-platform.com/api/sourcemap',
55
+ apiKey: process.env.SOURCEMAP_API_KEY,
56
+ projectName: 'my-project',
57
+ version: '1.0.0'
58
+ })
59
+ ],
60
+ build: {
61
+ sourcemap: true // Enable sourcemap generation
62
+ }
63
+ }))
64
+ ```
65
+
66
+ ### Configuration
67
+
68
+ ```typescript
69
+ interface SourceMapUploadOptions {
70
+ /** Enable the plugin (default: production only) */
71
+ enabled?: boolean
72
+
73
+ /** Monitoring platform API URL */
74
+ uploadUrl?: string
75
+
76
+ /** API key for authentication */
77
+ apiKey?: string
78
+
79
+ /** Project name */
80
+ projectName?: string
81
+
82
+ /** Project version */
83
+ version?: string
84
+
85
+ /** Remove SourceMap files after upload (default: true) */
86
+ removeSourceMap?: boolean
87
+
88
+ /** Custom upload function (optional) */
89
+ uploadFn?: (
90
+ filePath: string,
91
+ options: {
92
+ uploadUrl: string
93
+ apiKey: string
94
+ projectName: string
95
+ version: string
96
+ }
97
+ ) => Promise<boolean>
98
+ }
99
+ ```
100
+
101
+ ### Platform Integration Examples
102
+
103
+ #### Sentry
104
+
105
+ ```typescript
106
+ import { uploadSourceMapPlugin } from 'vite-plugin-upload-sourcemap'
107
+
108
+ uploadSourceMapPlugin({
109
+ enabled: mode === 'production',
110
+ uploadFn: async (filePath, config) => {
111
+ const fs = await import('fs')
112
+ const path = await import('path')
113
+
114
+ const formData = new FormData()
115
+ const content = fs.readFileSync(filePath, 'utf-8')
116
+
117
+ formData.append('file', new Blob([content]), path.basename(filePath))
118
+ formData.append('name', path.basename(filePath))
119
+
120
+ const response = await fetch(
121
+ `https://sentry.io/api/0/projects/${config.projectName}/releases/${config.version}/files/`,
122
+ {
123
+ method: 'POST',
124
+ headers: {
125
+ 'Authorization': `Bearer ${config.apiKey}`
126
+ },
127
+ body: formData
128
+ }
129
+ )
130
+
131
+ return response.ok
132
+ }
133
+ })
134
+ ```
135
+
136
+ #### Custom Platform
137
+
138
+ ```typescript
139
+ uploadSourceMapPlugin({
140
+ uploadFn: async (filePath, config) => {
141
+ // Your custom upload logic here
142
+ const content = fs.readFileSync(filePath, 'utf-8')
143
+
144
+ const response = await fetch(config.uploadUrl, {
145
+ method: 'POST',
146
+ headers: {
147
+ 'Content-Type': 'application/json',
148
+ 'X-API-KEY': config.apiKey
149
+ },
150
+ body: JSON.stringify({
151
+ project: config.projectName,
152
+ version: config.version,
153
+ filename: path.basename(filePath),
154
+ content
155
+ })
156
+ })
157
+
158
+ return response.ok
159
+ }
160
+ })
161
+ ```
162
+
163
+ ### Environment Variables
164
+
165
+ You can use environment variables for configuration:
166
+
167
+ ```bash
168
+ # .env.production
169
+ VITE_SOURCEMAP_UPLOAD_URL=https://your-platform.com/api/sourcemap
170
+ VITE_SOURCEMAP_API_KEY=your-secret-api-key
171
+ ```
172
+
173
+ ```typescript
174
+ // vite.config.ts
175
+ uploadSourceMapPlugin({
176
+ uploadUrl: process.env.VITE_SOURCEMAP_UPLOAD_URL,
177
+ apiKey: process.env.VITE_SOURCEMAP_API_KEY
178
+ })
179
+ ```
180
+
181
+ ### How It Works
182
+
183
+ ```
184
+ Build Process
185
+
186
+ ├─ Vite bundles your code
187
+ ├─ Generate minified JS files
188
+ ├─ Generate .map files
189
+
190
+ └─ Plugin executes (closeBundle hook)
191
+ ├─ Find all .map files
192
+ ├─ Upload to monitoring platform
193
+ └─ Remove .map files (optional)
194
+ ```
195
+
196
+ ### License
197
+
198
+ MIT
199
+
200
+ ---
201
+
202
+ ## 中文
203
+
204
+ ### 特性
205
+
206
+ - ✨ **自动上传**:构建完成后自动上传 SourceMap 文件
207
+ - 🗑️ **自动清理**:上传后自动删除 .map 文件(可配置)
208
+ - 🔌 **轻松集成**:简单的 Vite 插件配置
209
+ - 🛠️ **灵活可扩展**:支持自定义上传函数以对接不同平台
210
+ - 🎯 **类型安全**:完整的 TypeScript 支持
211
+ - 📦 **零配置**:开箱即用的合理默认配置
212
+
213
+ ### 为什么需要这个插件?
214
+
215
+ **问题背景:**
216
+ 1. 生产环境代码会被压缩和混淆以提升性能
217
+ 2. 错误堆栈显示的是压缩后的代码位置,难以调试
218
+ 3. SourceMap 不能部署到生产环境(会泄露源码)
219
+
220
+ **解决方案:**
221
+ 1. 构建时生成 SourceMap
222
+ 2. 上传到监控平台(Sentry、阿里云 ARMS 等)
223
+ 3. 从部署包中删除
224
+ 4. 监控平台使用 SourceMap 还原可读的错误堆栈
225
+
226
+ ### 安装
227
+
228
+ ```bash
229
+ npm install @zzzzzzhaopu/vite-plugin-upload-sourcemap -D
230
+ # 或
231
+ pnpm add @zzzzzzhaopu/vite-plugin-upload-sourcemap -D
232
+ # 或
233
+ yarn add @zzzzzzhaopu/vite-plugin-upload-sourcemap -D
234
+ ```
235
+
236
+ ### 快速开始
237
+
238
+ ```typescript
239
+ // vite.config.ts
240
+ import { defineConfig } from 'vite'
241
+ import { uploadSourceMapPlugin } from 'vite-plugin-upload-sourcemap'
242
+
243
+ export default defineConfig(({ mode }) => ({
244
+ plugins: [
245
+ uploadSourceMapPlugin({
246
+ enabled: mode === 'production',
247
+ uploadUrl: 'https://your-platform.com/api/sourcemap',
248
+ apiKey: process.env.SOURCEMAP_API_KEY,
249
+ projectName: 'my-project',
250
+ version: '1.0.0'
251
+ })
252
+ ],
253
+ build: {
254
+ sourcemap: true // 启用 sourcemap 生成
255
+ }
256
+ }))
257
+ ```
258
+
259
+ ### 配置选项
260
+
261
+ ```typescript
262
+ interface SourceMapUploadOptions {
263
+ /** 是否启用插件(默认:仅生产环境) */
264
+ enabled?: boolean
265
+
266
+ /** 监控平台 API 地址 */
267
+ uploadUrl?: string
268
+
269
+ /** API 密钥 */
270
+ apiKey?: string
271
+
272
+ /** 项目名称 */
273
+ projectName?: string
274
+
275
+ /** 项目版本 */
276
+ version?: string
277
+
278
+ /** 上传后删除 SourceMap 文件(默认:true) */
279
+ removeSourceMap?: boolean
280
+
281
+ /** 自定义上传函数(可选) */
282
+ uploadFn?: (
283
+ filePath: string,
284
+ options: {
285
+ uploadUrl: string
286
+ apiKey: string
287
+ projectName: string
288
+ version: string
289
+ }
290
+ ) => Promise<boolean>
291
+ }
292
+ ```
293
+
294
+ ### 平台集成示例
295
+
296
+ #### Sentry
297
+
298
+ ```typescript
299
+ import { uploadSourceMapPlugin } from 'vite-plugin-upload-sourcemap'
300
+
301
+ uploadSourceMapPlugin({
302
+ enabled: mode === 'production',
303
+ uploadFn: async (filePath, config) => {
304
+ const fs = await import('fs')
305
+ const path = await import('path')
306
+
307
+ const formData = new FormData()
308
+ const content = fs.readFileSync(filePath, 'utf-8')
309
+
310
+ formData.append('file', new Blob([content]), path.basename(filePath))
311
+ formData.append('name', path.basename(filePath))
312
+
313
+ const response = await fetch(
314
+ `https://sentry.io/api/0/projects/${config.projectName}/releases/${config.version}/files/`,
315
+ {
316
+ method: 'POST',
317
+ headers: {
318
+ 'Authorization': `Bearer ${config.apiKey}`
319
+ },
320
+ body: formData
321
+ }
322
+ )
323
+
324
+ return response.ok
325
+ }
326
+ })
327
+ ```
328
+
329
+ #### 阿里云 ARMS
330
+
331
+ ```typescript
332
+ uploadSourceMapPlugin({
333
+ uploadFn: async (filePath, config) => {
334
+ const fs = await import('fs')
335
+ const path = await import('path')
336
+ const content = fs.readFileSync(filePath, 'utf-8')
337
+
338
+ const response = await fetch(config.uploadUrl, {
339
+ method: 'POST',
340
+ headers: {
341
+ 'Content-Type': 'application/json',
342
+ 'X-ARMS-API-KEY': config.apiKey
343
+ },
344
+ body: JSON.stringify({
345
+ project: config.projectName,
346
+ version: config.version,
347
+ filename: path.basename(filePath),
348
+ content
349
+ })
350
+ })
351
+
352
+ return response.ok
353
+ }
354
+ })
355
+ ```
356
+
357
+ ### 环境变量配置
358
+
359
+ 可以使用环境变量来配置:
360
+
361
+ ```bash
362
+ # .env.production
363
+ VITE_SOURCEMAP_UPLOAD_URL=https://your-platform.com/api/sourcemap
364
+ VITE_SOURCEMAP_API_KEY=your-secret-api-key
365
+ ```
366
+
367
+ ```typescript
368
+ // vite.config.ts
369
+ uploadSourceMapPlugin({
370
+ uploadUrl: process.env.VITE_SOURCEMAP_UPLOAD_URL,
371
+ apiKey: process.env.VITE_SOURCEMAP_API_KEY
372
+ })
373
+ ```
374
+
375
+ ### 工作原理
376
+
377
+ ```
378
+ 构建流程
379
+
380
+ ├─ Vite 打包代码
381
+ ├─ 生成压缩后的 JS 文件
382
+ ├─ 生成 .map 文件
383
+
384
+ └─ 插件执行 (closeBundle 钩子)
385
+ ├─ 查找所有 .map 文件
386
+ ├─ 上传到监控平台
387
+ └─ 删除 .map 文件(可选)
388
+ ```
389
+
390
+ ### 许可证
391
+
392
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var fs = require('fs');
6
+ var path = require('path');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
11
+ var path__default = /*#__PURE__*/_interopDefault(path);
12
+
13
+ // src/index.ts
14
+ function uploadSourceMapPlugin(options = {}) {
15
+ const config = {
16
+ enabled: options.enabled ?? process.env.NODE_ENV === "production",
17
+ uploadUrl: options.uploadUrl || process.env.VITE_SOURCEMAP_UPLOAD_URL || "",
18
+ apiKey: options.apiKey || process.env.VITE_SOURCEMAP_API_KEY || "",
19
+ projectName: options.projectName || process.env.npm_package_name || "unknown-project",
20
+ version: options.version || process.env.npm_package_version || "1.0.0",
21
+ removeSourceMap: options.removeSourceMap ?? true,
22
+ uploadFn: options.uploadFn
23
+ };
24
+ let outDir = "dist";
25
+ const sourceMapFiles = [];
26
+ return {
27
+ // 插件名称
28
+ name: "vite-plugin-upload-sourcemap",
29
+ // 仅在构建时应用
30
+ apply: "build",
31
+ /**
32
+ * 在 Vite 配置解析完成后调用
33
+ * 用于获取构建配置信息(如输出目录)
34
+ */
35
+ configResolved(resolvedConfig) {
36
+ outDir = resolvedConfig.build.outDir;
37
+ },
38
+ /**
39
+ * 在生成产物时调用(产物还在内存中,未写入磁盘)
40
+ * 这个钩子可以获取到所有生成的文件信息,包括 SourceMap 文件
41
+ *
42
+ * @description
43
+ * 在这个阶段我们可以知道哪些文件是 .map 文件,提前记录下来
44
+ * 避免后续在 closeBundle 中遍历文件系统查找
45
+ */
46
+ generateBundle(_options, bundle) {
47
+ Object.keys(bundle).forEach((fileName) => {
48
+ if (fileName.endsWith(".map")) {
49
+ const fullPath = path__default.default.resolve(outDir, fileName);
50
+ sourceMapFiles.push(fullPath);
51
+ }
52
+ });
53
+ },
54
+ /**
55
+ * 在打包完成后调用(所有文件都已写入磁盘)
56
+ * 这是执行 SourceMap 上传的最佳时机
57
+ */
58
+ async closeBundle() {
59
+ if (!config.enabled) {
60
+ console.log("\u23ED\uFE0F SourceMap \u4E0A\u4F20\u63D2\u4EF6\u5DF2\u7981\u7528");
61
+ return;
62
+ }
63
+ if (!config.uploadUrl || !config.apiKey) {
64
+ console.warn("\u26A0\uFE0F SourceMap \u4E0A\u4F20\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF0C\u8DF3\u8FC7\u4E0A\u4F20");
65
+ console.warn(" \u8BF7\u914D\u7F6E uploadUrl \u548C apiKey");
66
+ return;
67
+ }
68
+ console.log("\n\u{1F680} \u5F00\u59CB\u5904\u7406 SourceMap \u6587\u4EF6...\n");
69
+ try {
70
+ if (sourceMapFiles.length === 0) {
71
+ console.log("\u26A0\uFE0F \u672A\u627E\u5230 SourceMap \u6587\u4EF6");
72
+ return;
73
+ }
74
+ console.log(`\u2705 \u627E\u5230 ${sourceMapFiles.length} \u4E2A SourceMap \u6587\u4EF6
75
+ `);
76
+ console.log("\u{1F4E4} \u5F00\u59CB\u4E0A\u4F20 SourceMap...");
77
+ const uploadResults = await Promise.all(
78
+ sourceMapFiles.map((file) => {
79
+ if (config.uploadFn) {
80
+ return config.uploadFn(file, {
81
+ uploadUrl: config.uploadUrl,
82
+ apiKey: config.apiKey,
83
+ projectName: config.projectName,
84
+ version: config.version
85
+ });
86
+ }
87
+ return uploadSourceMap(file, {
88
+ uploadUrl: config.uploadUrl,
89
+ apiKey: config.apiKey,
90
+ projectName: config.projectName,
91
+ version: config.version
92
+ });
93
+ })
94
+ );
95
+ const successCount = uploadResults.filter(Boolean).length;
96
+ console.log(`
97
+ \u2705 \u4E0A\u4F20\u5B8C\u6210: ${successCount}/${sourceMapFiles.length} \u6210\u529F`);
98
+ if (config.removeSourceMap) {
99
+ console.log("\n\u{1F5D1}\uFE0F \u6B63\u5728\u5220\u9664 SourceMap \u6587\u4EF6...");
100
+ sourceMapFiles.forEach((file) => {
101
+ try {
102
+ fs__default.default.unlinkSync(file);
103
+ console.log(` \u2705 \u5DF2\u5220\u9664: ${path__default.default.basename(file)}`);
104
+ } catch (error) {
105
+ console.error(` \u274C \u5220\u9664\u5931\u8D25: ${path__default.default.basename(file)}`, error);
106
+ }
107
+ });
108
+ }
109
+ console.log("\n\u{1F389} SourceMap \u5904\u7406\u5B8C\u6210!");
110
+ } catch (error) {
111
+ console.error("\n\u274C SourceMap \u5904\u7406\u5931\u8D25:", error);
112
+ }
113
+ }
114
+ };
115
+ }
116
+ async function uploadSourceMap(filePath, config) {
117
+ console.log(`\u{1F4E4} \u6B63\u5728\u4E0A\u4F20: ${path__default.default.basename(filePath)}`);
118
+ try {
119
+ const content = fs__default.default.readFileSync(filePath, "utf-8");
120
+ const response = await fetch(config.uploadUrl, {
121
+ method: "POST",
122
+ headers: {
123
+ "Content-Type": "application/json",
124
+ "Authorization": `Bearer ${config.apiKey}`
125
+ },
126
+ body: JSON.stringify({
127
+ project: config.projectName,
128
+ version: config.version,
129
+ filename: path__default.default.basename(filePath),
130
+ content
131
+ })
132
+ });
133
+ if (!response.ok) {
134
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
135
+ }
136
+ console.log(`\u2705 \u4E0A\u4F20\u6210\u529F: ${path__default.default.basename(filePath)}`);
137
+ return true;
138
+ } catch (error) {
139
+ console.error(`\u274C \u4E0A\u4F20\u5931\u8D25: ${path__default.default.basename(filePath)}`, error instanceof Error ? error.message : error);
140
+ return false;
141
+ }
142
+ }
143
+ var index_default = uploadSourceMapPlugin;
144
+
145
+ exports.default = index_default;
146
+ exports.uploadSourceMapPlugin = uploadSourceMapPlugin;
147
+ //# sourceMappingURL=index.cjs.map
148
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["path","fs"],"mappings":";;;;;;;;;;;;;AAoDO,SAAS,qBAAA,CAAsB,OAAA,GAAkC,EAAC,EAAW;AAElF,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,OAAA,CAAQ,IAAI,QAAA,KAAa,YAAA;AAAA,IACrD,SAAA,EAAW,OAAA,CAAQ,SAAA,IAAa,OAAA,CAAQ,IAAI,yBAAA,IAA6B,EAAA;AAAA,IACzE,MAAA,EAAQ,OAAA,CAAQ,MAAA,IAAU,OAAA,CAAQ,IAAI,sBAAA,IAA0B,EAAA;AAAA,IAChE,WAAA,EAAa,OAAA,CAAQ,WAAA,IAAe,OAAA,CAAQ,IAAI,gBAAA,IAAoB,iBAAA;AAAA,IACpE,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,OAAA,CAAQ,IAAI,mBAAA,IAAuB,OAAA;AAAA,IAC/D,eAAA,EAAiB,QAAQ,eAAA,IAAmB,IAAA;AAAA,IAC5C,UAAU,OAAA,CAAQ;AAAA,GACpB;AAGA,EAAA,IAAI,MAAA,GAAS,MAAA;AAIb,EAAA,MAAM,iBAA2B,EAAC;AAElC,EAAA,OAAO;AAAA;AAAA,IAEL,IAAA,EAAM,8BAAA;AAAA;AAAA,IAGN,KAAA,EAAO,OAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMP,eAAe,cAAA,EAAgB;AAE7B,MAAA,MAAA,GAAS,eAAe,KAAA,CAAM,MAAA;AAAA,IAChC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,cAAA,CAAe,UAAU,MAAA,EAAQ;AAE/B,MAAA,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,OAAA,CAAQ,CAAA,QAAA,KAAY;AAEtC,QAAA,IAAI,QAAA,CAAS,QAAA,CAAS,MAAM,CAAA,EAAG;AAC7B,UAAA,MAAM,QAAA,GAAWA,qBAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,QAAQ,CAAA;AAC9C,UAAA,cAAA,CAAe,KAAK,QAAQ,CAAA;AAAA,QAC9B;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM,WAAA,GAAc;AAElB,MAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,QAAA,OAAA,CAAQ,IAAI,oEAAuB,CAAA;AACnC,QAAA;AAAA,MACF;AAGA,MAAA,IAAI,CAAC,MAAA,CAAO,SAAA,IAAa,CAAC,OAAO,MAAA,EAAQ;AACvC,QAAA,OAAA,CAAQ,KAAK,kGAA4B,CAAA;AACzC,QAAA,OAAA,CAAQ,KAAK,+CAA2B,CAAA;AACxC,QAAA;AAAA,MACF;AAEA,MAAA,OAAA,CAAQ,IAAI,kEAA6B,CAAA;AAEzC,MAAA,IAAI;AAEF,QAAA,IAAI,cAAA,CAAe,WAAW,CAAA,EAAG;AAC/B,UAAA,OAAA,CAAQ,IAAI,yDAAsB,CAAA;AAClC,UAAA;AAAA,QACF;AAEA,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,oBAAA,EAAQ,cAAA,CAAe,MAAM,CAAA;AAAA,CAAmB,CAAA;AAG5D,QAAA,OAAA,CAAQ,IAAI,iDAAsB,CAAA;AAClC,QAAA,MAAM,aAAA,GAAgB,MAAM,OAAA,CAAQ,GAAA;AAAA,UAClC,cAAA,CAAe,IAAI,CAAA,IAAA,KAAQ;AAEzB,YAAA,IAAI,OAAO,QAAA,EAAU;AACnB,cAAA,OAAO,MAAA,CAAO,SAAS,IAAA,EAAM;AAAA,gBAC3B,WAAW,MAAA,CAAO,SAAA;AAAA,gBAClB,QAAQ,MAAA,CAAO,MAAA;AAAA,gBACf,aAAa,MAAA,CAAO,WAAA;AAAA,gBACpB,SAAS,MAAA,CAAO;AAAA,eACjB,CAAA;AAAA,YACH;AAEA,YAAA,OAAO,gBAAgB,IAAA,EAAM;AAAA,cAC3B,WAAW,MAAA,CAAO,SAAA;AAAA,cAClB,QAAQ,MAAA,CAAO,MAAA;AAAA,cACf,aAAa,MAAA,CAAO,WAAA;AAAA,cACpB,SAAS,MAAA,CAAO;AAAA,aACjB,CAAA;AAAA,UACH,CAAC;AAAA,SACH;AAEA,QAAA,MAAM,YAAA,GAAe,aAAA,CAAc,MAAA,CAAO,OAAO,CAAA,CAAE,MAAA;AACnD,QAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,iCAAA,EAAa,YAAY,CAAA,CAAA,EAAI,cAAA,CAAe,MAAM,CAAA,aAAA,CAAK,CAAA;AAGnE,QAAA,IAAI,OAAO,eAAA,EAAiB;AAC1B,UAAA,OAAA,CAAQ,IAAI,uEAA6B,CAAA;AACzC,UAAA,cAAA,CAAe,QAAQ,CAAA,IAAA,KAAQ;AAC7B,YAAA,IAAI;AACF,cAAAC,mBAAA,CAAG,WAAW,IAAI,CAAA;AAClB,cAAA,OAAA,CAAQ,IAAI,CAAA,6BAAA,EAAYD,qBAAA,CAAK,QAAA,CAAS,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,YAC/C,SAAS,KAAA,EAAO;AACd,cAAA,OAAA,CAAQ,MAAM,CAAA,mCAAA,EAAaA,qBAAA,CAAK,SAAS,IAAI,CAAC,IAAI,KAAK,CAAA;AAAA,YACzD;AAAA,UACF,CAAC,CAAA;AAAA,QACH;AAEA,QAAA,OAAA,CAAQ,IAAI,iDAAsB,CAAA;AAAA,MACpC,SAAS,KAAA,EAAO;AACd,QAAA,OAAA,CAAQ,KAAA,CAAM,gDAAuB,KAAK,CAAA;AAAA,MAE5C;AAAA,IACF;AAAA,GACF;AACF;AAaA,eAAe,eAAA,CACb,UACA,MAAA,EACkB;AAClB,EAAA,OAAA,CAAQ,IAAI,CAAA,oCAAA,EAAYA,qBAAA,CAAK,QAAA,CAAS,QAAQ,CAAC,CAAA,CAAE,CAAA;AAEjD,EAAA,IAAI;AAEF,IAAA,MAAM,OAAA,GAAUC,mBAAA,CAAG,YAAA,CAAa,QAAA,EAAU,OAAO,CAAA;AAajD,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,MAAA,CAAO,SAAA,EAAW;AAAA,MAC7C,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,kBAAA;AAAA,QAChB,eAAA,EAAiB,CAAA,OAAA,EAAU,MAAA,CAAO,MAAM,CAAA;AAAA,OAC1C;AAAA,MACA,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,QACnB,SAAS,MAAA,CAAO,WAAA;AAAA,QAChB,SAAS,MAAA,CAAO,OAAA;AAAA,QAChB,QAAA,EAAUD,qBAAA,CAAK,QAAA,CAAS,QAAQ,CAAA;AAAA,QAChC;AAAA,OACD;AAAA,KACF,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,MAAM,CAAA,KAAA,EAAQ,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AAAA,IACnE;AAEA,IAAA,OAAA,CAAQ,IAAI,CAAA,iCAAA,EAAWA,qBAAA,CAAK,QAAA,CAAS,QAAQ,CAAC,CAAA,CAAE,CAAA;AAChD,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,iCAAA,EAAWA,qBAAA,CAAK,QAAA,CAAS,QAAQ,CAAC,CAAA,CAAA,EAAI,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,KAAK,CAAA;AAClG,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAGA,IAAO,aAAA,GAAQ","file":"index.cjs","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport type { Plugin } from 'vite'\n\n/**\n * SourceMap 上传插件配置项\n */\nexport interface SourceMapUploadOptions {\n /** 是否启用插件(默认仅在生产环境启用) */\n enabled?: boolean\n /** 监控平台 API 地址 */\n uploadUrl?: string\n /** API 密钥 */\n apiKey?: string\n /** 项目名称 */\n projectName?: string\n /** 项目版本 */\n version?: string\n /** 上传完成后是否删除 SourceMap 文件(默认 true) */\n removeSourceMap?: boolean\n /** 自定义上传函数(可选,用于对接特定监控平台) */\n uploadFn?: (filePath: string, options: Required<Pick<SourceMapUploadOptions, 'uploadUrl' | 'apiKey' | 'projectName' | 'version'>>) => Promise<boolean>\n}\n\n/**\n * Vite 插件:上传 SourceMap 到监控平台\n * \n * @description\n * 这个插件会在打包完成后自动执行以下操作:\n * 1. 查找 dist 目录下所有的 .map 文件\n * 2. 上传到指定的监控平台(如 Sentry、阿里云 ARMS 等)\n * 3. 上传完成后自动删除 .map 文件(可配置)\n * \n * @example\n * ```ts\n * // vite.config.ts\n * import { uploadSourceMapPlugin } from 'vite-plugin-upload-sourcemap'\n * \n * export default defineConfig({\n * plugins: [\n * uploadSourceMapPlugin({\n * enabled: mode === 'production',\n * uploadUrl: 'https://your-platform.com/api/sourcemap',\n * apiKey: process.env.SOURCEMAP_API_KEY,\n * projectName: 'my-project',\n * version: '1.0.0',\n * removeSourceMap: true\n * })\n * ]\n * })\n * ```\n */\nexport function uploadSourceMapPlugin(options: SourceMapUploadOptions = {}): Plugin {\n // 默认配置\n const config = {\n enabled: options.enabled ?? process.env.NODE_ENV === 'production',\n uploadUrl: options.uploadUrl || process.env.VITE_SOURCEMAP_UPLOAD_URL || '',\n apiKey: options.apiKey || process.env.VITE_SOURCEMAP_API_KEY || '',\n projectName: options.projectName || process.env.npm_package_name || 'unknown-project',\n version: options.version || process.env.npm_package_version || '1.0.0',\n removeSourceMap: options.removeSourceMap ?? true,\n uploadFn: options.uploadFn\n }\n\n // 用于存储输出目录路径\n let outDir = 'dist'\n \n // 用于存储在构建过程中生成的 SourceMap 文件路径\n // 在 generateBundle 钩子中收集,在 closeBundle 钩子中使用\n const sourceMapFiles: string[] = []\n\n return {\n // 插件名称\n name: 'vite-plugin-upload-sourcemap',\n \n // 仅在构建时应用\n apply: 'build',\n \n /**\n * 在 Vite 配置解析完成后调用\n * 用于获取构建配置信息(如输出目录)\n */\n configResolved(resolvedConfig) {\n // 获取实际的输出目录\n outDir = resolvedConfig.build.outDir\n },\n \n /**\n * 在生成产物时调用(产物还在内存中,未写入磁盘)\n * 这个钩子可以获取到所有生成的文件信息,包括 SourceMap 文件\n * \n * @description\n * 在这个阶段我们可以知道哪些文件是 .map 文件,提前记录下来\n * 避免后续在 closeBundle 中遍历文件系统查找\n */\n generateBundle(_options, bundle) {\n // 遍历所有生成的文件\n Object.keys(bundle).forEach(fileName => {\n // 如果是 .map 文件,记录其完整路径\n if (fileName.endsWith('.map')) {\n const fullPath = path.resolve(outDir, fileName)\n sourceMapFiles.push(fullPath)\n }\n })\n },\n \n /**\n * 在打包完成后调用(所有文件都已写入磁盘)\n * 这是执行 SourceMap 上传的最佳时机\n */\n async closeBundle() {\n // 如果插件未启用,直接返回\n if (!config.enabled) {\n console.log('⏭️ SourceMap 上传插件已禁用')\n return\n }\n\n // 验证必要的配置\n if (!config.uploadUrl || !config.apiKey) {\n console.warn('⚠️ SourceMap 上传配置不完整,跳过上传')\n console.warn(' 请配置 uploadUrl 和 apiKey')\n return\n }\n\n console.log('\\n🚀 开始处理 SourceMap 文件...\\n')\n\n try {\n // 1. 检查是否有 SourceMap 文件(已在 generateBundle 中收集)\n if (sourceMapFiles.length === 0) {\n console.log('⚠️ 未找到 SourceMap 文件')\n return\n }\n\n console.log(`✅ 找到 ${sourceMapFiles.length} 个 SourceMap 文件\\n`)\n\n // 2. 上传所有 SourceMap 文件\n console.log('📤 开始上传 SourceMap...')\n const uploadResults = await Promise.all(\n sourceMapFiles.map(file => {\n // 如果提供了自定义上传函数,使用自定义函数\n if (config.uploadFn) {\n return config.uploadFn(file, {\n uploadUrl: config.uploadUrl,\n apiKey: config.apiKey,\n projectName: config.projectName,\n version: config.version\n })\n }\n // 否则使用默认上传函数\n return uploadSourceMap(file, {\n uploadUrl: config.uploadUrl,\n apiKey: config.apiKey,\n projectName: config.projectName,\n version: config.version\n })\n })\n )\n\n const successCount = uploadResults.filter(Boolean).length\n console.log(`\\n✅ 上传完成: ${successCount}/${sourceMapFiles.length} 成功`)\n\n // 3. 删除 SourceMap 文件(如果配置了)\n if (config.removeSourceMap) {\n console.log('\\n🗑️ 正在删除 SourceMap 文件...')\n sourceMapFiles.forEach(file => {\n try {\n fs.unlinkSync(file)\n console.log(` ✅ 已删除: ${path.basename(file)}`)\n } catch (error) {\n console.error(` ❌ 删除失败: ${path.basename(file)}`, error)\n }\n })\n }\n\n console.log('\\n🎉 SourceMap 处理完成!')\n } catch (error) {\n console.error('\\n❌ SourceMap 处理失败:', error)\n // 不中断构建流程\n }\n }\n }\n}\n\n/**\n * 上传单个 SourceMap 文件到监控平台(默认实现)\n * \n * @param filePath - SourceMap 文件的绝对路径\n * @param config - 上传配置\n * @returns 上传是否成功\n * \n * @description\n * 这是一个示例实现,实际使用时建议通过 uploadFn 参数传入自定义上传函数\n * 以对接具体的监控平台(如 Sentry、阿里云 ARMS 等)\n */\nasync function uploadSourceMap(\n filePath: string, \n config: Required<Pick<SourceMapUploadOptions, 'uploadUrl' | 'apiKey' | 'projectName' | 'version'>>\n): Promise<boolean> {\n console.log(`📤 正在上传: ${path.basename(filePath)}`)\n \n try {\n // 读取文件内容\n const content = fs.readFileSync(filePath, 'utf-8')\n \n /* \n * ==============================================\n * 🔧 默认实现:基础的 HTTP POST 请求\n * ==============================================\n * \n * 建议通过 uploadFn 参数传入自定义上传函数以对接具体平台\n * \n * 不同监控平台的 API 接口差异较大,这里提供一个通用的实现\n * 实际使用时请根据平台文档修改\n */\n \n const response = await fetch(config.uploadUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify({\n project: config.projectName,\n version: config.version,\n filename: path.basename(filePath),\n content: content\n })\n })\n \n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n \n console.log(`✅ 上传成功: ${path.basename(filePath)}`)\n return true\n } catch (error) {\n console.error(`❌ 上传失败: ${path.basename(filePath)}`, error instanceof Error ? error.message : error)\n return false\n }\n}\n\n// 默认导出(支持两种导入方式)\nexport default uploadSourceMapPlugin\n\n"]}
@@ -0,0 +1,52 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ /**
4
+ * SourceMap 上传插件配置项
5
+ */
6
+ interface SourceMapUploadOptions {
7
+ /** 是否启用插件(默认仅在生产环境启用) */
8
+ enabled?: boolean;
9
+ /** 监控平台 API 地址 */
10
+ uploadUrl?: string;
11
+ /** API 密钥 */
12
+ apiKey?: string;
13
+ /** 项目名称 */
14
+ projectName?: string;
15
+ /** 项目版本 */
16
+ version?: string;
17
+ /** 上传完成后是否删除 SourceMap 文件(默认 true) */
18
+ removeSourceMap?: boolean;
19
+ /** 自定义上传函数(可选,用于对接特定监控平台) */
20
+ uploadFn?: (filePath: string, options: Required<Pick<SourceMapUploadOptions, 'uploadUrl' | 'apiKey' | 'projectName' | 'version'>>) => Promise<boolean>;
21
+ }
22
+ /**
23
+ * Vite 插件:上传 SourceMap 到监控平台
24
+ *
25
+ * @description
26
+ * 这个插件会在打包完成后自动执行以下操作:
27
+ * 1. 查找 dist 目录下所有的 .map 文件
28
+ * 2. 上传到指定的监控平台(如 Sentry、阿里云 ARMS 等)
29
+ * 3. 上传完成后自动删除 .map 文件(可配置)
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * // vite.config.ts
34
+ * import { uploadSourceMapPlugin } from 'vite-plugin-upload-sourcemap'
35
+ *
36
+ * export default defineConfig({
37
+ * plugins: [
38
+ * uploadSourceMapPlugin({
39
+ * enabled: mode === 'production',
40
+ * uploadUrl: 'https://your-platform.com/api/sourcemap',
41
+ * apiKey: process.env.SOURCEMAP_API_KEY,
42
+ * projectName: 'my-project',
43
+ * version: '1.0.0',
44
+ * removeSourceMap: true
45
+ * })
46
+ * ]
47
+ * })
48
+ * ```
49
+ */
50
+ declare function uploadSourceMapPlugin(options?: SourceMapUploadOptions): Plugin;
51
+
52
+ export { type SourceMapUploadOptions, uploadSourceMapPlugin as default, uploadSourceMapPlugin };
@@ -0,0 +1,52 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ /**
4
+ * SourceMap 上传插件配置项
5
+ */
6
+ interface SourceMapUploadOptions {
7
+ /** 是否启用插件(默认仅在生产环境启用) */
8
+ enabled?: boolean;
9
+ /** 监控平台 API 地址 */
10
+ uploadUrl?: string;
11
+ /** API 密钥 */
12
+ apiKey?: string;
13
+ /** 项目名称 */
14
+ projectName?: string;
15
+ /** 项目版本 */
16
+ version?: string;
17
+ /** 上传完成后是否删除 SourceMap 文件(默认 true) */
18
+ removeSourceMap?: boolean;
19
+ /** 自定义上传函数(可选,用于对接特定监控平台) */
20
+ uploadFn?: (filePath: string, options: Required<Pick<SourceMapUploadOptions, 'uploadUrl' | 'apiKey' | 'projectName' | 'version'>>) => Promise<boolean>;
21
+ }
22
+ /**
23
+ * Vite 插件:上传 SourceMap 到监控平台
24
+ *
25
+ * @description
26
+ * 这个插件会在打包完成后自动执行以下操作:
27
+ * 1. 查找 dist 目录下所有的 .map 文件
28
+ * 2. 上传到指定的监控平台(如 Sentry、阿里云 ARMS 等)
29
+ * 3. 上传完成后自动删除 .map 文件(可配置)
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * // vite.config.ts
34
+ * import { uploadSourceMapPlugin } from 'vite-plugin-upload-sourcemap'
35
+ *
36
+ * export default defineConfig({
37
+ * plugins: [
38
+ * uploadSourceMapPlugin({
39
+ * enabled: mode === 'production',
40
+ * uploadUrl: 'https://your-platform.com/api/sourcemap',
41
+ * apiKey: process.env.SOURCEMAP_API_KEY,
42
+ * projectName: 'my-project',
43
+ * version: '1.0.0',
44
+ * removeSourceMap: true
45
+ * })
46
+ * ]
47
+ * })
48
+ * ```
49
+ */
50
+ declare function uploadSourceMapPlugin(options?: SourceMapUploadOptions): Plugin;
51
+
52
+ export { type SourceMapUploadOptions, uploadSourceMapPlugin as default, uploadSourceMapPlugin };
package/dist/index.js ADDED
@@ -0,0 +1,138 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ // src/index.ts
5
+ function uploadSourceMapPlugin(options = {}) {
6
+ const config = {
7
+ enabled: options.enabled ?? process.env.NODE_ENV === "production",
8
+ uploadUrl: options.uploadUrl || process.env.VITE_SOURCEMAP_UPLOAD_URL || "",
9
+ apiKey: options.apiKey || process.env.VITE_SOURCEMAP_API_KEY || "",
10
+ projectName: options.projectName || process.env.npm_package_name || "unknown-project",
11
+ version: options.version || process.env.npm_package_version || "1.0.0",
12
+ removeSourceMap: options.removeSourceMap ?? true,
13
+ uploadFn: options.uploadFn
14
+ };
15
+ let outDir = "dist";
16
+ const sourceMapFiles = [];
17
+ return {
18
+ // 插件名称
19
+ name: "vite-plugin-upload-sourcemap",
20
+ // 仅在构建时应用
21
+ apply: "build",
22
+ /**
23
+ * 在 Vite 配置解析完成后调用
24
+ * 用于获取构建配置信息(如输出目录)
25
+ */
26
+ configResolved(resolvedConfig) {
27
+ outDir = resolvedConfig.build.outDir;
28
+ },
29
+ /**
30
+ * 在生成产物时调用(产物还在内存中,未写入磁盘)
31
+ * 这个钩子可以获取到所有生成的文件信息,包括 SourceMap 文件
32
+ *
33
+ * @description
34
+ * 在这个阶段我们可以知道哪些文件是 .map 文件,提前记录下来
35
+ * 避免后续在 closeBundle 中遍历文件系统查找
36
+ */
37
+ generateBundle(_options, bundle) {
38
+ Object.keys(bundle).forEach((fileName) => {
39
+ if (fileName.endsWith(".map")) {
40
+ const fullPath = path.resolve(outDir, fileName);
41
+ sourceMapFiles.push(fullPath);
42
+ }
43
+ });
44
+ },
45
+ /**
46
+ * 在打包完成后调用(所有文件都已写入磁盘)
47
+ * 这是执行 SourceMap 上传的最佳时机
48
+ */
49
+ async closeBundle() {
50
+ if (!config.enabled) {
51
+ console.log("\u23ED\uFE0F SourceMap \u4E0A\u4F20\u63D2\u4EF6\u5DF2\u7981\u7528");
52
+ return;
53
+ }
54
+ if (!config.uploadUrl || !config.apiKey) {
55
+ console.warn("\u26A0\uFE0F SourceMap \u4E0A\u4F20\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF0C\u8DF3\u8FC7\u4E0A\u4F20");
56
+ console.warn(" \u8BF7\u914D\u7F6E uploadUrl \u548C apiKey");
57
+ return;
58
+ }
59
+ console.log("\n\u{1F680} \u5F00\u59CB\u5904\u7406 SourceMap \u6587\u4EF6...\n");
60
+ try {
61
+ if (sourceMapFiles.length === 0) {
62
+ console.log("\u26A0\uFE0F \u672A\u627E\u5230 SourceMap \u6587\u4EF6");
63
+ return;
64
+ }
65
+ console.log(`\u2705 \u627E\u5230 ${sourceMapFiles.length} \u4E2A SourceMap \u6587\u4EF6
66
+ `);
67
+ console.log("\u{1F4E4} \u5F00\u59CB\u4E0A\u4F20 SourceMap...");
68
+ const uploadResults = await Promise.all(
69
+ sourceMapFiles.map((file) => {
70
+ if (config.uploadFn) {
71
+ return config.uploadFn(file, {
72
+ uploadUrl: config.uploadUrl,
73
+ apiKey: config.apiKey,
74
+ projectName: config.projectName,
75
+ version: config.version
76
+ });
77
+ }
78
+ return uploadSourceMap(file, {
79
+ uploadUrl: config.uploadUrl,
80
+ apiKey: config.apiKey,
81
+ projectName: config.projectName,
82
+ version: config.version
83
+ });
84
+ })
85
+ );
86
+ const successCount = uploadResults.filter(Boolean).length;
87
+ console.log(`
88
+ \u2705 \u4E0A\u4F20\u5B8C\u6210: ${successCount}/${sourceMapFiles.length} \u6210\u529F`);
89
+ if (config.removeSourceMap) {
90
+ console.log("\n\u{1F5D1}\uFE0F \u6B63\u5728\u5220\u9664 SourceMap \u6587\u4EF6...");
91
+ sourceMapFiles.forEach((file) => {
92
+ try {
93
+ fs.unlinkSync(file);
94
+ console.log(` \u2705 \u5DF2\u5220\u9664: ${path.basename(file)}`);
95
+ } catch (error) {
96
+ console.error(` \u274C \u5220\u9664\u5931\u8D25: ${path.basename(file)}`, error);
97
+ }
98
+ });
99
+ }
100
+ console.log("\n\u{1F389} SourceMap \u5904\u7406\u5B8C\u6210!");
101
+ } catch (error) {
102
+ console.error("\n\u274C SourceMap \u5904\u7406\u5931\u8D25:", error);
103
+ }
104
+ }
105
+ };
106
+ }
107
+ async function uploadSourceMap(filePath, config) {
108
+ console.log(`\u{1F4E4} \u6B63\u5728\u4E0A\u4F20: ${path.basename(filePath)}`);
109
+ try {
110
+ const content = fs.readFileSync(filePath, "utf-8");
111
+ const response = await fetch(config.uploadUrl, {
112
+ method: "POST",
113
+ headers: {
114
+ "Content-Type": "application/json",
115
+ "Authorization": `Bearer ${config.apiKey}`
116
+ },
117
+ body: JSON.stringify({
118
+ project: config.projectName,
119
+ version: config.version,
120
+ filename: path.basename(filePath),
121
+ content
122
+ })
123
+ });
124
+ if (!response.ok) {
125
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
126
+ }
127
+ console.log(`\u2705 \u4E0A\u4F20\u6210\u529F: ${path.basename(filePath)}`);
128
+ return true;
129
+ } catch (error) {
130
+ console.error(`\u274C \u4E0A\u4F20\u5931\u8D25: ${path.basename(filePath)}`, error instanceof Error ? error.message : error);
131
+ return false;
132
+ }
133
+ }
134
+ var index_default = uploadSourceMapPlugin;
135
+
136
+ export { index_default as default, uploadSourceMapPlugin };
137
+ //# sourceMappingURL=index.js.map
138
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport type { Plugin } from 'vite'\n\n/**\n * SourceMap 上传插件配置项\n */\nexport interface SourceMapUploadOptions {\n /** 是否启用插件(默认仅在生产环境启用) */\n enabled?: boolean\n /** 监控平台 API 地址 */\n uploadUrl?: string\n /** API 密钥 */\n apiKey?: string\n /** 项目名称 */\n projectName?: string\n /** 项目版本 */\n version?: string\n /** 上传完成后是否删除 SourceMap 文件(默认 true) */\n removeSourceMap?: boolean\n /** 自定义上传函数(可选,用于对接特定监控平台) */\n uploadFn?: (filePath: string, options: Required<Pick<SourceMapUploadOptions, 'uploadUrl' | 'apiKey' | 'projectName' | 'version'>>) => Promise<boolean>\n}\n\n/**\n * Vite 插件:上传 SourceMap 到监控平台\n * \n * @description\n * 这个插件会在打包完成后自动执行以下操作:\n * 1. 查找 dist 目录下所有的 .map 文件\n * 2. 上传到指定的监控平台(如 Sentry、阿里云 ARMS 等)\n * 3. 上传完成后自动删除 .map 文件(可配置)\n * \n * @example\n * ```ts\n * // vite.config.ts\n * import { uploadSourceMapPlugin } from 'vite-plugin-upload-sourcemap'\n * \n * export default defineConfig({\n * plugins: [\n * uploadSourceMapPlugin({\n * enabled: mode === 'production',\n * uploadUrl: 'https://your-platform.com/api/sourcemap',\n * apiKey: process.env.SOURCEMAP_API_KEY,\n * projectName: 'my-project',\n * version: '1.0.0',\n * removeSourceMap: true\n * })\n * ]\n * })\n * ```\n */\nexport function uploadSourceMapPlugin(options: SourceMapUploadOptions = {}): Plugin {\n // 默认配置\n const config = {\n enabled: options.enabled ?? process.env.NODE_ENV === 'production',\n uploadUrl: options.uploadUrl || process.env.VITE_SOURCEMAP_UPLOAD_URL || '',\n apiKey: options.apiKey || process.env.VITE_SOURCEMAP_API_KEY || '',\n projectName: options.projectName || process.env.npm_package_name || 'unknown-project',\n version: options.version || process.env.npm_package_version || '1.0.0',\n removeSourceMap: options.removeSourceMap ?? true,\n uploadFn: options.uploadFn\n }\n\n // 用于存储输出目录路径\n let outDir = 'dist'\n \n // 用于存储在构建过程中生成的 SourceMap 文件路径\n // 在 generateBundle 钩子中收集,在 closeBundle 钩子中使用\n const sourceMapFiles: string[] = []\n\n return {\n // 插件名称\n name: 'vite-plugin-upload-sourcemap',\n \n // 仅在构建时应用\n apply: 'build',\n \n /**\n * 在 Vite 配置解析完成后调用\n * 用于获取构建配置信息(如输出目录)\n */\n configResolved(resolvedConfig) {\n // 获取实际的输出目录\n outDir = resolvedConfig.build.outDir\n },\n \n /**\n * 在生成产物时调用(产物还在内存中,未写入磁盘)\n * 这个钩子可以获取到所有生成的文件信息,包括 SourceMap 文件\n * \n * @description\n * 在这个阶段我们可以知道哪些文件是 .map 文件,提前记录下来\n * 避免后续在 closeBundle 中遍历文件系统查找\n */\n generateBundle(_options, bundle) {\n // 遍历所有生成的文件\n Object.keys(bundle).forEach(fileName => {\n // 如果是 .map 文件,记录其完整路径\n if (fileName.endsWith('.map')) {\n const fullPath = path.resolve(outDir, fileName)\n sourceMapFiles.push(fullPath)\n }\n })\n },\n \n /**\n * 在打包完成后调用(所有文件都已写入磁盘)\n * 这是执行 SourceMap 上传的最佳时机\n */\n async closeBundle() {\n // 如果插件未启用,直接返回\n if (!config.enabled) {\n console.log('⏭️ SourceMap 上传插件已禁用')\n return\n }\n\n // 验证必要的配置\n if (!config.uploadUrl || !config.apiKey) {\n console.warn('⚠️ SourceMap 上传配置不完整,跳过上传')\n console.warn(' 请配置 uploadUrl 和 apiKey')\n return\n }\n\n console.log('\\n🚀 开始处理 SourceMap 文件...\\n')\n\n try {\n // 1. 检查是否有 SourceMap 文件(已在 generateBundle 中收集)\n if (sourceMapFiles.length === 0) {\n console.log('⚠️ 未找到 SourceMap 文件')\n return\n }\n\n console.log(`✅ 找到 ${sourceMapFiles.length} 个 SourceMap 文件\\n`)\n\n // 2. 上传所有 SourceMap 文件\n console.log('📤 开始上传 SourceMap...')\n const uploadResults = await Promise.all(\n sourceMapFiles.map(file => {\n // 如果提供了自定义上传函数,使用自定义函数\n if (config.uploadFn) {\n return config.uploadFn(file, {\n uploadUrl: config.uploadUrl,\n apiKey: config.apiKey,\n projectName: config.projectName,\n version: config.version\n })\n }\n // 否则使用默认上传函数\n return uploadSourceMap(file, {\n uploadUrl: config.uploadUrl,\n apiKey: config.apiKey,\n projectName: config.projectName,\n version: config.version\n })\n })\n )\n\n const successCount = uploadResults.filter(Boolean).length\n console.log(`\\n✅ 上传完成: ${successCount}/${sourceMapFiles.length} 成功`)\n\n // 3. 删除 SourceMap 文件(如果配置了)\n if (config.removeSourceMap) {\n console.log('\\n🗑️ 正在删除 SourceMap 文件...')\n sourceMapFiles.forEach(file => {\n try {\n fs.unlinkSync(file)\n console.log(` ✅ 已删除: ${path.basename(file)}`)\n } catch (error) {\n console.error(` ❌ 删除失败: ${path.basename(file)}`, error)\n }\n })\n }\n\n console.log('\\n🎉 SourceMap 处理完成!')\n } catch (error) {\n console.error('\\n❌ SourceMap 处理失败:', error)\n // 不中断构建流程\n }\n }\n }\n}\n\n/**\n * 上传单个 SourceMap 文件到监控平台(默认实现)\n * \n * @param filePath - SourceMap 文件的绝对路径\n * @param config - 上传配置\n * @returns 上传是否成功\n * \n * @description\n * 这是一个示例实现,实际使用时建议通过 uploadFn 参数传入自定义上传函数\n * 以对接具体的监控平台(如 Sentry、阿里云 ARMS 等)\n */\nasync function uploadSourceMap(\n filePath: string, \n config: Required<Pick<SourceMapUploadOptions, 'uploadUrl' | 'apiKey' | 'projectName' | 'version'>>\n): Promise<boolean> {\n console.log(`📤 正在上传: ${path.basename(filePath)}`)\n \n try {\n // 读取文件内容\n const content = fs.readFileSync(filePath, 'utf-8')\n \n /* \n * ==============================================\n * 🔧 默认实现:基础的 HTTP POST 请求\n * ==============================================\n * \n * 建议通过 uploadFn 参数传入自定义上传函数以对接具体平台\n * \n * 不同监控平台的 API 接口差异较大,这里提供一个通用的实现\n * 实际使用时请根据平台文档修改\n */\n \n const response = await fetch(config.uploadUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify({\n project: config.projectName,\n version: config.version,\n filename: path.basename(filePath),\n content: content\n })\n })\n \n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n \n console.log(`✅ 上传成功: ${path.basename(filePath)}`)\n return true\n } catch (error) {\n console.error(`❌ 上传失败: ${path.basename(filePath)}`, error instanceof Error ? error.message : error)\n return false\n }\n}\n\n// 默认导出(支持两种导入方式)\nexport default uploadSourceMapPlugin\n\n"]}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@zzzzzzhaopu/vite-plugin-upload-sourcemap",
3
+ "version": "1.0.0",
4
+ "description": "A Vite plugin to automatically upload SourceMap files to monitoring platforms after production build",
5
+ "keywords": [
6
+ "vite",
7
+ "vite-plugin",
8
+ "sourcemap",
9
+ "source-map",
10
+ "upload",
11
+ "sentry",
12
+ "monitoring",
13
+ "error-tracking"
14
+ ],
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "module": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js",
23
+ "require": "./dist/index.cjs"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "scripts": {
32
+ "build": "tsup",
33
+ "dev": "tsup --watch",
34
+ "prepublishOnly": "pnpm build"
35
+ },
36
+ "peerDependencies": {
37
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.0.0",
41
+ "tsup": "^8.0.0",
42
+ "typescript": "^5.0.0",
43
+ "vite": "^7.0.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ },
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/your-username/vite-plugin-upload-sourcemap.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/your-username/vite-plugin-upload-sourcemap/issues"
54
+ },
55
+ "homepage": "https://github.com/your-username/vite-plugin-upload-sourcemap#readme",
56
+ "author": "Your Name <your.email@example.com>",
57
+ "license": "MIT",
58
+ "publishConfig": {
59
+ "access": "public",
60
+ "registry": "https://registry.npmjs.org/"
61
+ }
62
+ }