@tourmind-frontend/monitor-plugin-nuxt 1.0.1
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/dist/index.cjs +128 -0
- package/dist/index.d.ts +59 -0
- package/package.json +40 -0
- package/templates/plugin.client.js +22 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// extensions/plugin-nuxt/src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
default: () => MonitorModule
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(src_exports);
|
|
36
|
+
var import_node_path = require("path");
|
|
37
|
+
var import_axios = __toESM(require("axios"));
|
|
38
|
+
var import_form_data = __toESM(require("form-data"));
|
|
39
|
+
var LOG_PREFIX = "[frontend-monitor]";
|
|
40
|
+
var UploadSourceMapWebpackPlugin = class {
|
|
41
|
+
constructor(options) {
|
|
42
|
+
if (!(options == null ? void 0 : options.url)) throw new Error(`${LOG_PREFIX} "uploadUrl" is required`);
|
|
43
|
+
if (!options.project) throw new Error(`${LOG_PREFIX} "project" is required`);
|
|
44
|
+
if (!options.branch) throw new Error(`${LOG_PREFIX} "branch" is required`);
|
|
45
|
+
this.options = options;
|
|
46
|
+
}
|
|
47
|
+
// 兼容 webpack 4 / 5;类型留作 any 以避免把 webpack 拉入类型声明。
|
|
48
|
+
apply(compiler) {
|
|
49
|
+
const opts = this.options;
|
|
50
|
+
compiler.options.devtool = "hidden-source-map";
|
|
51
|
+
compiler.hooks.emit.tapPromise("MonitorUploadSourceMapPlugin", async (compilation) => {
|
|
52
|
+
const files = [];
|
|
53
|
+
const assets = compilation.assets || {};
|
|
54
|
+
for (const name of Object.keys(assets)) {
|
|
55
|
+
if (!name.endsWith(".map")) continue;
|
|
56
|
+
const asset = assets[name];
|
|
57
|
+
const raw = typeof (asset == null ? void 0 : asset.source) === "function" ? asset.source() : "";
|
|
58
|
+
const content = typeof raw === "string" ? raw : raw ? Buffer.from(raw).toString("utf-8") : "";
|
|
59
|
+
if (typeof compilation.deleteAsset === "function") compilation.deleteAsset(name);
|
|
60
|
+
else delete assets[name];
|
|
61
|
+
if (!content) continue;
|
|
62
|
+
const base = name.includes("/") ? name.split("/").pop() : name;
|
|
63
|
+
if (!base) continue;
|
|
64
|
+
files.push([base, content]);
|
|
65
|
+
}
|
|
66
|
+
if (files.length === 0) {
|
|
67
|
+
console.warn(`${LOG_PREFIX} no .map files found in the bundle, skipping upload`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const query = {
|
|
71
|
+
project: opts.project,
|
|
72
|
+
branch: opts.branch,
|
|
73
|
+
timestamp: String(Date.now())
|
|
74
|
+
};
|
|
75
|
+
if (opts.commit) query.commit = opts.commit;
|
|
76
|
+
const qs = new URLSearchParams(query).toString();
|
|
77
|
+
const fullUrl = `${opts.url}${opts.url.includes("?") ? "&" : "?"}${qs}`;
|
|
78
|
+
const form = new import_form_data.default();
|
|
79
|
+
for (const [filename, content] of files) {
|
|
80
|
+
form.append("file", Buffer.from(content, "utf-8"), { filename });
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
await (0, import_axios.default)({
|
|
84
|
+
method: "POST",
|
|
85
|
+
url: fullUrl,
|
|
86
|
+
data: form,
|
|
87
|
+
headers: form.getHeaders(),
|
|
88
|
+
maxBodyLength: Infinity,
|
|
89
|
+
maxContentLength: Infinity
|
|
90
|
+
});
|
|
91
|
+
console.log(
|
|
92
|
+
`${LOG_PREFIX} uploaded ${files.length} sourcemap file(s) (branch=${opts.branch}, commit=${opts.commit || "-"})`
|
|
93
|
+
);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error(`${LOG_PREFIX} upload failed`, err instanceof Error ? err.message : err);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
function MonitorModule(options) {
|
|
101
|
+
if (!(options == null ? void 0 : options.project)) throw new Error(`${LOG_PREFIX} "project" is required`);
|
|
102
|
+
if (!options.reportUrl) throw new Error(`${LOG_PREFIX} "reportUrl" is required`);
|
|
103
|
+
if (!options.uploadUrl) throw new Error(`${LOG_PREFIX} "uploadUrl" is required`);
|
|
104
|
+
if (!options.branch) throw new Error(`${LOG_PREFIX} "branch" is required`);
|
|
105
|
+
this.extendBuild((config, { isClient, isDev }) => {
|
|
106
|
+
var _a;
|
|
107
|
+
const enabled = (_a = options.uploadEnabled) != null ? _a : !isDev;
|
|
108
|
+
if (!enabled || !isClient) return;
|
|
109
|
+
config.plugins = config.plugins || [];
|
|
110
|
+
config.plugins.push(
|
|
111
|
+
new UploadSourceMapWebpackPlugin({
|
|
112
|
+
url: options.uploadUrl,
|
|
113
|
+
project: options.project,
|
|
114
|
+
branch: options.branch,
|
|
115
|
+
commit: options.commit
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
this.addPlugin({
|
|
120
|
+
src: (0, import_node_path.resolve)(__dirname, "../templates/plugin.client.js"),
|
|
121
|
+
fileName: "monitor-tracking.client.js",
|
|
122
|
+
mode: "client",
|
|
123
|
+
options: {
|
|
124
|
+
url: options.reportUrl,
|
|
125
|
+
project: options.project
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface MonitorModuleOptions {
|
|
2
|
+
/** 项目名,必须为 `owner/name` 格式以匹配服务端。 */
|
|
3
|
+
project: string;
|
|
4
|
+
/** 上报接口地址,例如 `https://monitor.example.com/api/report`。 */
|
|
5
|
+
reportUrl: string;
|
|
6
|
+
/** sourcemap 上传接口地址,例如 `https://monitor.example.com/api/upload`。 */
|
|
7
|
+
uploadUrl: string;
|
|
8
|
+
/** 分支名,由调用方自行解析(如 `git rev-parse --abbrev-ref HEAD`)。 */
|
|
9
|
+
branch: string;
|
|
10
|
+
/** Commit hash,由调用方自行解析(如 `git rev-parse HEAD`)。 */
|
|
11
|
+
commit?: string;
|
|
12
|
+
/** 强制启用 / 禁用 sourcemap 上传。默认仅在生产构建(`!isDev`)时为 `true`。 */
|
|
13
|
+
uploadEnabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface NuxtBuildContext {
|
|
16
|
+
isClient: boolean;
|
|
17
|
+
isServer: boolean;
|
|
18
|
+
isDev: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface NuxtWebpackConfig {
|
|
21
|
+
plugins?: unknown[];
|
|
22
|
+
devtool?: string | false;
|
|
23
|
+
}
|
|
24
|
+
interface NuxtModuleThis {
|
|
25
|
+
options: unknown;
|
|
26
|
+
extendBuild(fn: (config: NuxtWebpackConfig, ctx: NuxtBuildContext) => void): void;
|
|
27
|
+
addPlugin(opts: {
|
|
28
|
+
src: string;
|
|
29
|
+
fileName: string;
|
|
30
|
+
mode?: "client" | "server" | "all";
|
|
31
|
+
ssr?: boolean;
|
|
32
|
+
options?: unknown;
|
|
33
|
+
}): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Nuxt 2 模块入口(兼容 `nuxt@>=2.4.0` + `nuxt-property-decorator@>=2.3.0`)。
|
|
37
|
+
*
|
|
38
|
+
* - 客户端生产构建时注入 webpack 插件,把 sourcemap 上传到 `uploadUrl`
|
|
39
|
+
* - 始终注入客户端运行时插件,监听 `window.error` / `unhandledrejection` /
|
|
40
|
+
* `Vue.config.errorHandler`,把错误堆栈上报到 `reportUrl`
|
|
41
|
+
*
|
|
42
|
+
* 在 `nuxt.config.js` 中使用:
|
|
43
|
+
*
|
|
44
|
+
* ```js
|
|
45
|
+
* export default {
|
|
46
|
+
* modules: [
|
|
47
|
+
* ['@tourmind-frontend/monitor-plugin-nuxt', {
|
|
48
|
+
* project: 'your-org/your-repo',
|
|
49
|
+
* reportUrl: 'https://monitor.example.com/api/report',
|
|
50
|
+
* uploadUrl: 'https://monitor.example.com/api/upload',
|
|
51
|
+
* branch: process.env.GIT_BRANCH || 'main',
|
|
52
|
+
* commit: process.env.GIT_COMMIT,
|
|
53
|
+
* }],
|
|
54
|
+
* ],
|
|
55
|
+
* };
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export default function MonitorModule(this: NuxtModuleThis, options: MonitorModuleOptions): void;
|
|
59
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tourmind-frontend/monitor-plugin-nuxt",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Nuxt 2 module that captures runtime errors and uploads sourcemaps to a frontend-monitor server.",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"templates",
|
|
10
|
+
"!dist/tsconfig.tsbuildinfo"
|
|
11
|
+
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"registry": "https://registry.npmjs.org/"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=13"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@tourmind-frontend/monitor-tracking": "workspace:^",
|
|
21
|
+
"axios": "^0.27.2",
|
|
22
|
+
"form-data": "^3.0.0"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"nuxt": ">=2.4.0",
|
|
26
|
+
"vue": ">=2.5.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.0.0",
|
|
30
|
+
"typescript": "^5.6.0"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"nuxt",
|
|
34
|
+
"nuxt-module",
|
|
35
|
+
"nuxt2",
|
|
36
|
+
"sourcemap",
|
|
37
|
+
"frontend-monitor",
|
|
38
|
+
"error-tracking"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Vue from "vue";
|
|
2
|
+
import { register } from "@tourmind-frontend/monitor-tracking";
|
|
3
|
+
|
|
4
|
+
// Nuxt 2 模板:构建阶段会替换 options 占位符
|
|
5
|
+
export default function () {
|
|
6
|
+
register({
|
|
7
|
+
url: "<%= options.url %>",
|
|
8
|
+
project: "<%= options.project %>",
|
|
9
|
+
// 桥接 Vue 2 的 errorHandler,链式调用既有 handler,不覆盖。
|
|
10
|
+
callback: function (handler) {
|
|
11
|
+
var existing = Vue.config.errorHandler;
|
|
12
|
+
Vue.config.errorHandler = function (err, vm, info) {
|
|
13
|
+
try {
|
|
14
|
+
handler(err);
|
|
15
|
+
} catch (_) {
|
|
16
|
+
/* 上报失败不影响业务错误处理 */
|
|
17
|
+
}
|
|
18
|
+
if (typeof existing === "function") return existing(err, vm, info);
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|