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