@keyblade/vite-plugin-vue-pro 1.0.12 → 1.0.14

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.
@@ -8,7 +8,11 @@ export interface LogPageRouteOptions {
8
8
  /**
9
9
  * 开发环境下,在控制台输出当前访问页面对应的源码文件路径(可点击跳转)。
10
10
  *
11
- * 由前端注入脚本上报当前路由,服务端根据路由解析对应的 .vue 文件:
12
- * 依次尝试 `<basePath>/<route>/index.vue` 与 `<basePath>/<route>.vue`。
11
+ * 优先方案:前端读取 vue-router 当前匹配组件的 `__file`(@vitejs/plugin-vue 在 dev 注入的真实源码路径)
12
+ * 直接上报,服务端原样打印——可精确处理动态参数、URL 编码、任意嵌套等所有情况。
13
+ *
14
+ * 兜底方案:当拿不到 `__file` 时(如未用 vue-router、挂载结构特殊),前端上报路由字符串,
15
+ * 服务端按约定推断 `.vue` 文件:先试 `<route>/index.vue` 与 `<route>.vue`,
16
+ * 再把末段连字符当作潜在目录分隔符尝试,并逐级回退末尾段以应对动态参数。
13
17
  */
14
18
  export default function logPageRoute(options?: LogPageRouteOptions): Plugin;
@@ -1,71 +1,132 @@
1
- import y from "node:fs";
2
- import n from "node:path";
3
- const v = "POST";
4
- function T(f) {
5
- const { basePath: r = "src/views", outputPrefix: l = "web" } = f || {};
6
- let c = "";
7
- function h(t) {
8
- const o = t ? [n.join(r, t, "index.vue"), n.join(r, `${t}.vue`)] : [n.join(r, "index.vue")];
9
- for (const e of o) {
10
- const i = n.resolve(process.cwd(), e);
11
- if (y.existsSync(i))
12
- return e.replace(/\\/g, "/");
1
+ import T from "node:fs";
2
+ import i from "node:path";
3
+ const w = "POST";
4
+ function P(h) {
5
+ const { basePath: c = "src/views", outputPrefix: p = "web" } = h || {};
6
+ let d = "";
7
+ function l(e) {
8
+ const t = e.replace(/\\/g, "/");
9
+ return p ? i.join(p, t).replace(/\\/g, "/") : t;
10
+ }
11
+ function f(e) {
12
+ for (const t of e) {
13
+ const o = i.resolve(process.cwd(), t);
14
+ if (T.existsSync(o))
15
+ return t.replace(/\\/g, "/");
13
16
  }
14
17
  return "";
15
18
  }
19
+ function g(e) {
20
+ if (!e) return "";
21
+ const t = i.isAbsolute(e) ? i.relative(process.cwd(), e) : e;
22
+ return t.startsWith("..") ? e.replace(/\\/g, "/") : l(t);
23
+ }
24
+ function m(e, t) {
25
+ const o = [
26
+ i.join(c, e, t, "index.vue"),
27
+ i.join(c, e, `${t}.vue`)
28
+ ], r = t.split("-");
29
+ for (let n = r.length - 1; n >= 1; n--) {
30
+ const u = r.slice(0, n).join("-"), s = r.slice(n).join("-");
31
+ o.push(i.join(c, e, u, `${s}.vue`)), o.push(i.join(c, e, u, s, "index.vue"));
32
+ }
33
+ return o;
34
+ }
35
+ function v(e) {
36
+ if (!e)
37
+ return l(f([i.join(c, "index.vue")]));
38
+ let t = e;
39
+ try {
40
+ t = decodeURIComponent(e);
41
+ } catch (r) {
42
+ }
43
+ const o = t.split("/").filter(Boolean);
44
+ for (let r = o.length; r >= 1; r--) {
45
+ const n = o.slice(0, r), u = n[n.length - 1] || "", s = n.slice(0, -1).join("/"), a = f(m(s, u));
46
+ if (a) return l(a);
47
+ }
48
+ return "";
49
+ }
50
+ function _(e) {
51
+ !e || e === d || (d = e, console.info(`\x1B[32m➜ \x1B[37m正在访问: \x1B[36m${e}\x1B[0m`));
52
+ }
16
53
  return {
17
54
  name: "keyblade-pro-log-page-route",
18
55
  enforce: "pre",
19
56
  apply: "serve",
20
- configureServer(t) {
21
- t.middlewares.use("/__log_page_route", (o, e, i) => {
22
- if (o.method !== v)
23
- return i();
24
- const g = 1e3 * 10, d = setTimeout(() => {
25
- e.statusCode = 504, e.setHeader("Content-Type", "application/json"), e.end(JSON.stringify({ success: !1, error: "Gateway Timeout" }));
26
- }, g);
27
- let p = "";
28
- o.on("data", (a) => {
29
- p += a;
30
- }), o.on("end", () => {
57
+ configureServer(e) {
58
+ e.middlewares.use("/__log_page_route", (t, o, r) => {
59
+ if (t.method !== w)
60
+ return r();
61
+ const n = 1e3 * 10, u = setTimeout(() => {
62
+ o.statusCode = 504, o.setHeader("Content-Type", "application/json"), o.end(JSON.stringify({ success: !1, error: "Gateway Timeout" }));
63
+ }, n);
64
+ let s = "";
65
+ t.on("data", (a) => {
66
+ s += a;
67
+ }), t.on("end", () => {
31
68
  try {
32
- const { route: a } = JSON.parse(p), m = a.replace(/^\//, "").replace(/\/$/, "").split("?")[0], s = h(m);
33
- if (s) {
34
- const u = l ? n.join(l, s).replace(/\\/g, "/") : s;
35
- if (u === c) {
36
- clearTimeout(d), e.statusCode = 200, e.setHeader("Content-Type", "application/json"), e.end(JSON.stringify({ success: !0 }));
37
- return;
38
- }
39
- c = u, console.info(`\x1B[32m➜ \x1B[37m正在访问: \x1B[36m${u}\x1B[0m`);
40
- }
69
+ const { file: a, route: y } = JSON.parse(s), S = String(y || "").replace(/^\//, "").replace(/\/$/, "").split("?")[0] || "", j = a ? g(a) : v(S);
70
+ _(j);
41
71
  } catch (a) {
42
72
  }
43
- clearTimeout(d), e.statusCode = 200, e.setHeader("Content-Type", "application/json"), e.end(JSON.stringify({ success: !0 }));
73
+ clearTimeout(u), o.statusCode = 200, o.setHeader("Content-Type", "application/json"), o.end(JSON.stringify({ success: !0 }));
44
74
  });
45
75
  });
46
76
  },
47
- transformIndexHtml(t) {
48
- return t.replace("</head>", `
77
+ transformIndexHtml(e) {
78
+ return e.replace("</head>", `
49
79
  <script>
50
80
  (function() {
51
81
  if (window.__logPageRouteInitialized) return;
52
82
  window.__logPageRouteInitialized = true;
53
83
 
54
- function getRoute() {
55
- var hash = location.hash;
56
- if (hash && hash.length > 1) {
57
- return hash.slice(1).split('?')[0];
84
+ var lastSent = '';
85
+ var sendTimer = null;
86
+
87
+ // 找到 Vue 应用实例(Vue3 会在挂载容器上挂 __vue_app__)
88
+ function getApp() {
89
+ var selectors = ['#app', '#root'];
90
+ for (var i = 0; i < selectors.length; i++) {
91
+ var el = document.querySelector(selectors[i]);
92
+ if (el && el.__vue_app__) return el.__vue_app__;
58
93
  }
59
- var pathname = location.pathname;
60
- return (pathname || '/').split('?')[0];
94
+ if (document.body) {
95
+ var nodes = document.body.querySelectorAll('*');
96
+ for (var j = 0; j < nodes.length; j++) {
97
+ if (nodes[j].__vue_app__) return nodes[j].__vue_app__;
98
+ }
99
+ }
100
+ return null;
61
101
  }
62
102
 
63
- var lastRoute = '';
64
- var lastHref = location.href;
65
- var sendTimer = null;
103
+ // 读取 vue-router 当前匹配的最深层组件的 __file(dev 下由 @vitejs/plugin-vue 注入)
104
+ function getMatchedFile() {
105
+ try {
106
+ var app = getApp();
107
+ if (!app || !app.config || !app.config.globalProperties) return '';
108
+ var router = app.config.globalProperties.$router;
109
+ if (!router || !router.currentRoute || !router.currentRoute.value) return '';
110
+ var matched = router.currentRoute.value.matched || [];
111
+ for (var i = matched.length - 1; i >= 0; i--) {
112
+ var comps = matched[i].components || {};
113
+ for (var key in comps) {
114
+ var c = comps[key];
115
+ if (c && c.__file) return c.__file;
116
+ }
117
+ }
118
+ } catch (e) {}
119
+ return '';
120
+ }
121
+
122
+ function getRoute() {
123
+ var hash = location.hash;
124
+ if (hash && hash.length > 1) return hash.slice(1).split('?')[0];
125
+ return (location.pathname || '/').split('?')[0];
126
+ }
66
127
 
67
- function doSend(route) {
68
- var data = JSON.stringify({ route: route });
128
+ function post(payload) {
129
+ var data = JSON.stringify(payload);
69
130
  if (navigator.sendBeacon) {
70
131
  navigator.sendBeacon('/__log_page_route', new Blob([data], { type: 'application/json' }));
71
132
  } else {
@@ -78,54 +139,53 @@ function T(f) {
78
139
  }
79
140
  }
80
141
 
81
- function sendRoute() {
82
- var route = getRoute();
83
- if (!route) {
84
- route = '/';
142
+ // 异步路由组件可能尚未加载完成,重试若干次后回退到上报路由
143
+ function report(attempt) {
144
+ attempt = attempt || 0;
145
+ var file = getMatchedFile();
146
+ if (file) {
147
+ if (file === lastSent) return;
148
+ lastSent = file;
149
+ post({ file: file });
150
+ return;
151
+ }
152
+ if (attempt < 10) {
153
+ setTimeout(function() { report(attempt + 1); }, 120);
154
+ return;
85
155
  }
86
- route = route.replace(/\\/$/, '') || '/';
87
- if (route === lastRoute) return;
88
- lastRoute = route;
156
+ var route = getRoute().replace(/\\/$/, '') || '/';
157
+ if (route === lastSent) return;
158
+ lastSent = route;
159
+ post({ route: route });
160
+ }
89
161
 
162
+ function schedule() {
90
163
  if (sendTimer) clearTimeout(sendTimer);
91
- sendTimer = setTimeout(function() {
92
- doSend(route);
93
- }, 100);
164
+ sendTimer = setTimeout(function() { report(0); }, 100);
94
165
  }
95
166
 
96
167
  if (document.readyState === 'loading') {
97
- document.addEventListener('DOMContentLoaded', sendRoute);
168
+ document.addEventListener('DOMContentLoaded', schedule);
98
169
  } else {
99
- sendRoute();
170
+ schedule();
100
171
  }
101
- window.addEventListener('hashchange', sendRoute);
172
+ window.addEventListener('hashchange', schedule);
173
+ window.addEventListener('popstate', schedule);
102
174
  var originalPushState = history.pushState;
103
175
  var originalReplaceState = history.replaceState;
104
176
  history.pushState = function() {
105
177
  originalPushState.apply(history, arguments);
106
- var newHref = location.href;
107
- if (newHref !== lastHref) {
108
- lastHref = newHref;
109
- sendRoute();
110
- }
178
+ schedule();
111
179
  };
112
180
  history.replaceState = function() {
113
181
  originalReplaceState.apply(history, arguments);
114
- var newHref = location.href;
115
- if (newHref !== lastHref) {
116
- lastHref = newHref;
117
- sendRoute();
118
- }
182
+ schedule();
119
183
  };
120
- window.addEventListener('popstate', function() {
121
- lastHref = location.href;
122
- sendRoute();
123
- });
124
184
  })();
125
185
  <\/script>` + "</head>");
126
186
  }
127
187
  };
128
188
  }
129
189
  export {
130
- T as default
190
+ P as default
131
191
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@keyblade/vite-plugin-vue-pro",
3
3
  "description": "KeyBlade Vite Plugin Vue Pro",
4
4
  "author": "yangshuai <704807396@qq.com>",
5
- "version": "1.0.12",
5
+ "version": "1.0.14",
6
6
  "private": false,
7
7
  "type": "module",
8
8
  "main": "es/index.js",