vite-plugin-preloader 1.0.0 → 1.1.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/README.md +162 -87
- package/dist/index.d.mts +1 -17
- package/dist/index.d.ts +1 -17
- package/dist/index.js +210 -153
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +210 -153
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -27,153 +27,181 @@ module.exports = __toCommonJS(index_exports);
|
|
|
27
27
|
|
|
28
28
|
// src/runtime.ts
|
|
29
29
|
var runtimeTemplate = `// \u{1F680} Auto-generated by vite-plugin-preloader
|
|
30
|
-
|
|
30
|
+
(function() {
|
|
31
|
+
'use strict';
|
|
32
|
+
|
|
33
|
+
// \u{1F3AF} \u9884\u52A0\u8F7D\u914D\u7F6E\uFF08\u6784\u5EFA\u65F6\u6CE8\u5165\uFF09
|
|
34
|
+
const PRELOAD_ROUTES = __PRELOAD_ROUTES__
|
|
35
|
+
const PRELOAD_OPTIONS = __PRELOAD_OPTIONS__
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
class PreloaderManager {
|
|
38
|
+
constructor() {
|
|
39
|
+
this.preloadedRoutes = new Set()
|
|
40
|
+
this.isPreloading = false
|
|
41
|
+
this.stats = {
|
|
42
|
+
total: 0, completed: 0, failed: 0, startTime: 0, endTime: 0
|
|
43
|
+
}
|
|
44
|
+
this.statusElement = null
|
|
45
|
+
}
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
completed: number
|
|
42
|
-
failed: number
|
|
43
|
-
startTime: number
|
|
44
|
-
endTime: number
|
|
45
|
-
}
|
|
47
|
+
async start() {
|
|
48
|
+
if (this.isPreloading) return
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
this.isPreloading = true
|
|
51
|
+
this.stats = {
|
|
52
|
+
total: PRELOAD_ROUTES.length,
|
|
53
|
+
completed: 0, failed: 0,
|
|
54
|
+
startTime: Date.now(), endTime: 0
|
|
55
|
+
}
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
private stats = ref({
|
|
55
|
-
total: 0, completed: 0, failed: 0, startTime: 0, endTime: 0
|
|
56
|
-
})
|
|
57
|
+
if (PRELOAD_OPTIONS.debug) {
|
|
58
|
+
console.log(\`\u{1F680} [\u9884\u52A0\u8F7D] \u5F00\u59CB\u9884\u52A0\u8F7D \${PRELOAD_ROUTES.length} \u4E2A\u9875\u9762\`)
|
|
59
|
+
}
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
// \u663E\u793A\u72B6\u6001\u6307\u793A\u5668
|
|
62
|
+
this.showStatus()
|
|
63
|
+
|
|
64
|
+
const sortedRoutes = [...PRELOAD_ROUTES].sort((a, b) => a.priority - b.priority)
|
|
65
|
+
|
|
66
|
+
for (const route of sortedRoutes) {
|
|
67
|
+
await this.preloadSingle(route)
|
|
68
|
+
this.updateStatus()
|
|
69
|
+
await this.sleep(100)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.stats.endTime = Date.now()
|
|
73
|
+
this.isPreloading = false
|
|
74
|
+
|
|
75
|
+
if (PRELOAD_OPTIONS.debug) {
|
|
76
|
+
console.log(\`\u{1F389} [\u9884\u52A0\u8F7D] \u5B8C\u6210! \u8017\u65F6 \${this.stats.endTime - this.stats.startTime}ms\`)
|
|
77
|
+
}
|
|
60
78
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
total: PRELOAD_ROUTES.length,
|
|
64
|
-
completed: 0, failed: 0,
|
|
65
|
-
startTime: Date.now(), endTime: 0
|
|
79
|
+
// \u9690\u85CF\u72B6\u6001\u6307\u793A\u5668
|
|
80
|
+
this.hideStatus()
|
|
66
81
|
}
|
|
67
82
|
|
|
68
|
-
|
|
83
|
+
async preloadSingle(route) {
|
|
84
|
+
if (this.preloadedRoutes.has(route.path)) return
|
|
69
85
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
86
|
+
try {
|
|
87
|
+
const startTime = Date.now()
|
|
88
|
+
// \u4F7F\u7528 eval \u6765\u52A8\u6001\u6267\u884C\u5BFC\u5165\u51FD\u6570\u5B57\u7B26\u4E32
|
|
89
|
+
const importFn = eval('(' + route.component + ')')
|
|
90
|
+
await importFn()
|
|
91
|
+
const loadTime = Date.now() - startTime
|
|
92
|
+
|
|
93
|
+
this.preloadedRoutes.add(route.path)
|
|
94
|
+
this.stats.completed++
|
|
95
|
+
|
|
96
|
+
if (PRELOAD_OPTIONS.debug) {
|
|
97
|
+
console.log(\`\u2705 [\u9884\u52A0\u8F7D] \${route.path} (\${loadTime}ms) - \${route.reason}\`)
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.stats.failed++
|
|
101
|
+
if (PRELOAD_OPTIONS.debug) {
|
|
102
|
+
console.error(\`\u274C [\u9884\u52A0\u8F7D] \${route.path} \u5931\u8D25:\`, error)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
75
105
|
}
|
|
76
106
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
console.log(\`\u{1F389} [\u9884\u52A0\u8F7D] \u5B8C\u6210! \u8017\u65F6 \${this.stats.value.endTime - this.stats.value.startTime}ms\`)
|
|
81
|
-
}
|
|
107
|
+
showStatus() {
|
|
108
|
+
if (!PRELOAD_OPTIONS.showStatus || !document.body) return
|
|
82
109
|
|
|
83
|
-
|
|
84
|
-
|
|
110
|
+
// \u6DFB\u52A0\u6837\u5F0F
|
|
111
|
+
if (!document.getElementById('preloader-styles')) {
|
|
112
|
+
const style = document.createElement('style')
|
|
113
|
+
style.id = 'preloader-styles'
|
|
114
|
+
const position = PRELOAD_OPTIONS.statusPosition.replace('-', ': 20px; ') + ': 20px;'
|
|
115
|
+
style.textContent = \`
|
|
116
|
+
.preloader-status {
|
|
117
|
+
position: fixed; \${position}
|
|
118
|
+
background: rgba(0,0,0,0.8); color: white; padding: 8px 16px;
|
|
119
|
+
border-radius: 6px; font-size: 12px; z-index: 9999;
|
|
120
|
+
pointer-events: none; font-family: system-ui;
|
|
121
|
+
transition: opacity 0.3s ease;
|
|
122
|
+
}
|
|
123
|
+
.preloader-status.fade-out {
|
|
124
|
+
opacity: 0;
|
|
125
|
+
}
|
|
126
|
+
\`
|
|
127
|
+
document.head.appendChild(style)
|
|
128
|
+
}
|
|
85
129
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.preloadedRoutes.add(route.path)
|
|
92
|
-
this.stats.value.completed++
|
|
93
|
-
console.log(\`\u2705 [\u9884\u52A0\u8F7D] \${route.path} (\${loadTime}ms) - \${route.reason}\`)
|
|
94
|
-
} catch (error) {
|
|
95
|
-
this.stats.value.failed++
|
|
96
|
-
console.error(\`\u274C [\u9884\u52A0\u8F7D] \${route.path} \u5931\u8D25:\`, error)
|
|
130
|
+
// \u521B\u5EFA\u72B6\u6001\u5143\u7D20
|
|
131
|
+
this.statusElement = document.createElement('div')
|
|
132
|
+
this.statusElement.className = 'preloader-status'
|
|
133
|
+
this.updateStatus()
|
|
134
|
+
document.body.appendChild(this.statusElement)
|
|
97
135
|
}
|
|
98
|
-
}
|
|
99
136
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
137
|
+
updateStatus() {
|
|
138
|
+
if (!this.statusElement) return
|
|
139
|
+
this.statusElement.textContent = \`\u{1F504} \u6B63\u5728\u4F18\u5316\u9875\u9762... \${this.stats.completed}/\${this.stats.total}\`
|
|
140
|
+
}
|
|
103
141
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
142
|
+
hideStatus() {
|
|
143
|
+
if (!this.statusElement) return
|
|
144
|
+
this.statusElement.classList.add('fade-out')
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
if (this.statusElement && this.statusElement.parentNode) {
|
|
147
|
+
this.statusElement.parentNode.removeChild(this.statusElement)
|
|
148
|
+
}
|
|
149
|
+
}, 300)
|
|
150
|
+
}
|
|
107
151
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
...this.stats.value,
|
|
111
|
-
preloadedPaths: Array.from(this.preloadedRoutes),
|
|
112
|
-
isPreloading: this.isPreloading.value
|
|
152
|
+
sleep(ms) {
|
|
153
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
113
154
|
}
|
|
114
|
-
}
|
|
115
155
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
name: 'PreloadStatus',
|
|
120
|
-
setup() {
|
|
121
|
-
onMounted(() => {
|
|
122
|
-
if (!document.getElementById('preloader-styles')) {
|
|
123
|
-
const style = document.createElement('style')
|
|
124
|
-
style.id = 'preloader-styles'
|
|
125
|
-
const position = PRELOAD_OPTIONS.statusPosition.replace('-', ': 20px; ') + ': 20px;'
|
|
126
|
-
style.textContent = \`
|
|
127
|
-
.preloader-status {
|
|
128
|
-
position: fixed; \${position}
|
|
129
|
-
background: rgba(0,0,0,0.8); color: white; padding: 8px 16px;
|
|
130
|
-
border-radius: 6px; font-size: 12px; z-index: 9999;
|
|
131
|
-
pointer-events: none; font-family: system-ui;
|
|
132
|
-
}
|
|
133
|
-
\`
|
|
134
|
-
document.head.appendChild(style)
|
|
135
|
-
}
|
|
136
|
-
})
|
|
156
|
+
isPreloaded(path) {
|
|
157
|
+
return this.preloadedRoutes.has(path)
|
|
158
|
+
}
|
|
137
159
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
'\u{1F504} \u6B63\u5728\u4F18\u5316\u9875\u9762... ',
|
|
144
|
-
\`\${self.stats.value.completed}/\${self.stats.value.total}\`
|
|
145
|
-
])
|
|
146
|
-
}
|
|
160
|
+
getStats() {
|
|
161
|
+
return {
|
|
162
|
+
...this.stats,
|
|
163
|
+
preloadedPaths: Array.from(this.preloadedRoutes),
|
|
164
|
+
isPreloading: this.isPreloading
|
|
147
165
|
}
|
|
148
|
-
}
|
|
166
|
+
}
|
|
149
167
|
}
|
|
150
|
-
}
|
|
151
168
|
|
|
152
|
-
// \u{1F680} \u5168\u5C40\u5B9E\u4F8B
|
|
153
|
-
const preloader = new PreloaderManager()
|
|
169
|
+
// \u{1F680} \u5168\u5C40\u5B9E\u4F8B
|
|
170
|
+
const preloader = new PreloaderManager()
|
|
154
171
|
|
|
155
|
-
// \u{1F6E0}\uFE0F \u5F00\u53D1\u73AF\u5883\u8C03\u8BD5\u5DE5\u5177
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
172
|
+
// \u{1F6E0}\uFE0F \u5F00\u53D1\u73AF\u5883\u8C03\u8BD5\u5DE5\u5177
|
|
173
|
+
if (PRELOAD_OPTIONS.debug) {
|
|
174
|
+
window.preloaderDebug = {
|
|
175
|
+
stats: () => preloader.getStats(),
|
|
176
|
+
restart: () => preloader.start(),
|
|
177
|
+
check: (path) => preloader.isPreloaded(path),
|
|
178
|
+
help: () => console.log('\u{1F6E0}\uFE0F \u9884\u52A0\u8F7D\u8C03\u8BD5: stats() | restart() | check(path)')
|
|
179
|
+
}
|
|
180
|
+
console.log('\u{1F6E0}\uFE0F \u9884\u52A0\u8F7D\u8C03\u8BD5\u5DE5\u5177: window.preloaderDebug')
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// \u{1F680} \u81EA\u52A8\u542F\u52A8 - \u7B49\u5F85 DOM \u52A0\u8F7D\u5B8C\u6210
|
|
184
|
+
function autoStart() {
|
|
185
|
+
if (document.readyState === 'loading') {
|
|
186
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
187
|
+
setTimeout(() => preloader.start(), PRELOAD_OPTIONS.delay)
|
|
188
|
+
})
|
|
189
|
+
} else {
|
|
190
|
+
setTimeout(() => preloader.start(), PRELOAD_OPTIONS.delay)
|
|
191
|
+
}
|
|
162
192
|
}
|
|
163
|
-
console.log('\u{1F6E0}\uFE0F \u9884\u52A0\u8F7D\u8C03\u8BD5\u5DE5\u5177: window.preloaderDebug')
|
|
164
|
-
}
|
|
165
193
|
|
|
166
|
-
// \
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
194
|
+
// \u7ACB\u5373\u6267\u884C\u81EA\u52A8\u542F\u52A8
|
|
195
|
+
autoStart()
|
|
196
|
+
|
|
197
|
+
// \u5BFC\u51FA\u5230\u5168\u5C40\uFF08\u53EF\u9009\u4F7F\u7528\uFF09
|
|
198
|
+
window.usePreloader = () => ({
|
|
199
|
+
start: () => preloader.start(),
|
|
200
|
+
isPreloaded: (path) => preloader.isPreloaded(path),
|
|
201
|
+
getStats: () => preloader.getStats()
|
|
202
|
+
})
|
|
172
203
|
|
|
173
|
-
|
|
174
|
-
if (typeof window !== 'undefined') {
|
|
175
|
-
setTimeout(() => preloader.start(), PRELOAD_OPTIONS.delay || 2000)
|
|
176
|
-
}`;
|
|
204
|
+
})();`;
|
|
177
205
|
|
|
178
206
|
// src/generator.ts
|
|
179
207
|
var CodeGenerator = class {
|
|
@@ -186,6 +214,9 @@ var CodeGenerator = class {
|
|
|
186
214
|
generateRuntime() {
|
|
187
215
|
const routes = this.processRoutes();
|
|
188
216
|
const options = this.processOptions();
|
|
217
|
+
if (options.debug) {
|
|
218
|
+
console.log("\u{1F527} [\u9884\u52A0\u8F7D] \u751F\u6210\u8FD0\u884C\u65F6\u4EE3\u7801\uFF0C\u8DEF\u7531\u6570\u91CF:", routes.length);
|
|
219
|
+
}
|
|
189
220
|
return runtimeTemplate.replace("__PRELOAD_ROUTES__", JSON.stringify(routes, null, 2)).replace("__PRELOAD_OPTIONS__", JSON.stringify(options, null, 2));
|
|
190
221
|
}
|
|
191
222
|
/**
|
|
@@ -193,6 +224,15 @@ var CodeGenerator = class {
|
|
|
193
224
|
*/
|
|
194
225
|
processRoutes() {
|
|
195
226
|
return this.options.routes.map((route) => {
|
|
227
|
+
if (typeof route === "string") {
|
|
228
|
+
const componentPath2 = this.inferComponentPath(route);
|
|
229
|
+
return {
|
|
230
|
+
path: route,
|
|
231
|
+
component: `() => import('${componentPath2}')`,
|
|
232
|
+
reason: "\u81EA\u52A8\u63A8\u65AD\u7684\u9884\u52A0\u8F7D\u9875\u9762",
|
|
233
|
+
priority: 2
|
|
234
|
+
};
|
|
235
|
+
}
|
|
196
236
|
const componentPath = route.component || this.inferComponentPath(route.path);
|
|
197
237
|
return {
|
|
198
238
|
path: route.path,
|
|
@@ -206,70 +246,87 @@ var CodeGenerator = class {
|
|
|
206
246
|
* 处理选项配置
|
|
207
247
|
*/
|
|
208
248
|
processOptions() {
|
|
249
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
209
250
|
return {
|
|
210
|
-
delay: this.options.delay
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
251
|
+
delay: this.options.delay ?? 2e3,
|
|
252
|
+
// 默认2秒
|
|
253
|
+
showStatus: this.options.showStatus ?? true,
|
|
254
|
+
// 默认显示状态
|
|
255
|
+
statusPosition: this.options.statusPosition ?? "bottom-right",
|
|
256
|
+
// 默认右下角
|
|
257
|
+
debug: this.options.debug ?? isDev
|
|
258
|
+
// 开发环境默认开启调试,生产环境默认关闭
|
|
214
259
|
};
|
|
215
260
|
}
|
|
216
261
|
/**
|
|
217
262
|
* 推断组件路径
|
|
218
263
|
*/
|
|
219
264
|
inferComponentPath(routePath) {
|
|
220
|
-
const cleanPath = routePath.replace(/^\//, "")
|
|
221
|
-
|
|
265
|
+
const cleanPath = routePath.replace(/^\//, "");
|
|
266
|
+
if (cleanPath.startsWith("demo/")) {
|
|
267
|
+
return `@/views/${cleanPath}/index.vue`;
|
|
268
|
+
}
|
|
269
|
+
const pathSegments = cleanPath.split("/");
|
|
270
|
+
return `@/views/${pathSegments.join("/")}/index.vue`;
|
|
222
271
|
}
|
|
223
272
|
/**
|
|
224
|
-
*
|
|
273
|
+
* 生成注入到 HTML 头部的脚本
|
|
225
274
|
*/
|
|
226
275
|
generateHtmlInject() {
|
|
227
|
-
|
|
228
|
-
|
|
276
|
+
return `<script type="module">
|
|
277
|
+
${this.generateRuntime()}
|
|
278
|
+
</script>`;
|
|
229
279
|
}
|
|
230
280
|
};
|
|
231
281
|
|
|
232
282
|
// src/index.ts
|
|
233
|
-
var VIRTUAL_MODULE_ID = "virtual:preloader";
|
|
234
|
-
var RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
|
|
235
283
|
function preloaderPlugin(options) {
|
|
236
284
|
let generator;
|
|
285
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
286
|
+
const finalOptions = {
|
|
287
|
+
debug: isDev,
|
|
288
|
+
// 开发环境默认开启调试
|
|
289
|
+
delay: 2e3,
|
|
290
|
+
// 默认2秒
|
|
291
|
+
showStatus: true,
|
|
292
|
+
// 默认显示状态
|
|
293
|
+
statusPosition: "bottom-right",
|
|
294
|
+
// 默认右下角
|
|
295
|
+
...options
|
|
296
|
+
// 用户配置覆盖默认配置
|
|
297
|
+
};
|
|
237
298
|
return {
|
|
238
299
|
name: "vite-plugin-preloader",
|
|
239
300
|
// 🎯 设置插件执行顺序
|
|
240
301
|
enforce: "post",
|
|
241
302
|
configResolved() {
|
|
242
|
-
generator = new CodeGenerator(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
|
|
253
|
-
return generator.generateRuntime();
|
|
303
|
+
generator = new CodeGenerator(finalOptions);
|
|
304
|
+
if (finalOptions.debug) {
|
|
305
|
+
console.log(`\u{1F680} [\u9884\u52A0\u8F7D\u63D2\u4EF6] \u5DF2\u542F\u7528\uFF0C\u914D\u7F6E\u4E86 ${finalOptions.routes.length} \u4E2A\u8DEF\u7531`);
|
|
306
|
+
console.log("\u{1F4CB} [\u9884\u52A0\u8F7D\u63D2\u4EF6] \u8DEF\u7531\u5217\u8868:", finalOptions.routes);
|
|
307
|
+
console.log("\u2699\uFE0F [\u9884\u52A0\u8F7D\u63D2\u4EF6] \u914D\u7F6E\u9009\u9879:", {
|
|
308
|
+
delay: finalOptions.delay,
|
|
309
|
+
showStatus: finalOptions.showStatus,
|
|
310
|
+
statusPosition: finalOptions.statusPosition,
|
|
311
|
+
debug: finalOptions.debug
|
|
312
|
+
});
|
|
254
313
|
}
|
|
255
|
-
return null;
|
|
256
314
|
},
|
|
257
|
-
// 🎨 HTML
|
|
315
|
+
// 🎨 HTML 转换 - 直接注入脚本到 HTML
|
|
258
316
|
transformIndexHtml(html) {
|
|
259
317
|
const inject = generator.generateHtmlInject();
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
'<div id="app">',
|
|
263
|
-
`<div id="app">
|
|
264
|
-
${inject}`
|
|
265
|
-
);
|
|
318
|
+
if (finalOptions.debug) {
|
|
319
|
+
console.log("\u{1F3A8} [\u9884\u52A0\u8F7D\u63D2\u4EF6] \u6CE8\u5165\u9884\u52A0\u8F7D\u811A\u672C\u5230 HTML");
|
|
266
320
|
}
|
|
267
|
-
return html
|
|
321
|
+
return html.replace("</head>", `${inject}
|
|
322
|
+
</head>`);
|
|
268
323
|
},
|
|
269
324
|
// 🔥 HMR 支持
|
|
270
325
|
handleHotUpdate(ctx) {
|
|
271
326
|
if (ctx.file.includes("vite.config")) {
|
|
272
|
-
|
|
327
|
+
if (finalOptions.debug) {
|
|
328
|
+
console.log("\u{1F504} [\u9884\u52A0\u8F7D\u63D2\u4EF6] \u914D\u7F6E\u5DF2\u66F4\u65B0");
|
|
329
|
+
}
|
|
273
330
|
ctx.server.ws.send({
|
|
274
331
|
type: "full-reload"
|
|
275
332
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/runtime.ts","../src/generator.ts"],"sourcesContent":["// ============================================================================\r\n// 🚀 src/index.ts - 主插件文件\r\n// ============================================================================\r\n\r\nimport type { Plugin } from 'vite'\r\nimport type { PreloaderOptions } from './types'\r\nimport { CodeGenerator } from './generator'\r\n\r\nconst VIRTUAL_MODULE_ID = 'virtual:preloader'\r\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0' + VIRTUAL_MODULE_ID\r\n\r\nexport default function preloaderPlugin(options: PreloaderOptions): Plugin {\r\n let generator: CodeGenerator\r\n\r\n return {\r\n name: 'vite-plugin-preloader',\r\n \r\n // 🎯 设置插件执行顺序\r\n enforce: 'post',\r\n \r\n configResolved() {\r\n generator = new CodeGenerator(options)\r\n console.log(`🚀 预加载插件已启用,配置了 ${options.routes.length} 个路由`)\r\n },\r\n\r\n resolveId(id) {\r\n if (id === VIRTUAL_MODULE_ID) {\r\n return RESOLVED_VIRTUAL_MODULE_ID\r\n }\r\n return null // 🔧 修复:明确返回 null\r\n },\r\n\r\n load(id) {\r\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\r\n return generator.generateRuntime()\r\n }\r\n return null // 🔧 修复:明确返回 null\r\n },\r\n\r\n // 🎨 HTML 转换(修复类型错误)\r\n transformIndexHtml(html) {\r\n const inject = generator.generateHtmlInject()\r\n if (inject) {\r\n return html.replace(\r\n '<div id=\"app\">',\r\n `<div id=\"app\">\\n ${inject}`\r\n )\r\n }\r\n return html // 🔧 修复:确保总是返回 html\r\n },\r\n\r\n // 🔥 HMR 支持\r\n handleHotUpdate(ctx) {\r\n if (ctx.file.includes('vite.config')) {\r\n console.log('🔄 预加载配置已更新')\r\n ctx.server.ws.send({\r\n type: 'full-reload'\r\n })\r\n return []\r\n }\r\n return undefined // 🔧 修复:明确返回 undefined\r\n }\r\n }\r\n}\r\n\r\n// 命名导出,支持多种导入方式\r\nexport { type PreloaderOptions, type PreloadRoute } from './types'","// ============================================================================\r\n// ⚡ src/runtime.ts - 运行时代码模板(字符串)\r\n// ============================================================================\r\n\r\nexport const runtimeTemplate = `// 🚀 Auto-generated by vite-plugin-preloader\r\nimport { ref, defineComponent, h, onMounted } from 'vue'\r\n\r\ninterface PreloadRoute {\r\n path: string\r\n component: () => Promise<any>\r\n reason: string\r\n priority: number\r\n}\r\n\r\ninterface PreloadStats {\r\n total: number\r\n completed: number\r\n failed: number\r\n startTime: number\r\n endTime: number\r\n}\r\n\r\n// 🎯 预加载配置(构建时注入)\r\nconst PRELOAD_ROUTES = __PRELOAD_ROUTES__\r\nconst PRELOAD_OPTIONS = __PRELOAD_OPTIONS__\r\n\r\nclass PreloaderManager {\r\n private preloadedRoutes = new Set()\r\n private isPreloading = ref(false)\r\n private stats = ref({\r\n total: 0, completed: 0, failed: 0, startTime: 0, endTime: 0\r\n })\r\n\r\n async start() {\r\n if (this.isPreloading.value) return\r\n\r\n this.isPreloading.value = true\r\n this.stats.value = {\r\n total: PRELOAD_ROUTES.length,\r\n completed: 0, failed: 0,\r\n startTime: Date.now(), endTime: 0\r\n }\r\n\r\n console.log(\\`🚀 [预加载] 开始预加载 \\${PRELOAD_ROUTES.length} 个页面\\`)\r\n\r\n const sortedRoutes = [...PRELOAD_ROUTES].sort((a, b) => a.priority - b.priority)\r\n \r\n for (const route of sortedRoutes) {\r\n await this.preloadSingle(route)\r\n await this.sleep(100)\r\n }\r\n\r\n this.stats.value.endTime = Date.now()\r\n this.isPreloading.value = false\r\n \r\n console.log(\\`🎉 [预加载] 完成! 耗时 \\${this.stats.value.endTime - this.stats.value.startTime}ms\\`)\r\n }\r\n\r\n async preloadSingle(route) {\r\n if (this.preloadedRoutes.has(route.path)) return\r\n\r\n try {\r\n const startTime = Date.now()\r\n await route.component()\r\n const loadTime = Date.now() - startTime\r\n \r\n this.preloadedRoutes.add(route.path)\r\n this.stats.value.completed++\r\n console.log(\\`✅ [预加载] \\${route.path} (\\${loadTime}ms) - \\${route.reason}\\`)\r\n } catch (error) {\r\n this.stats.value.failed++\r\n console.error(\\`❌ [预加载] \\${route.path} 失败:\\`, error)\r\n }\r\n }\r\n\r\n sleep(ms) {\r\n return new Promise(resolve => setTimeout(resolve, ms))\r\n }\r\n\r\n isPreloaded(path) {\r\n return this.preloadedRoutes.has(path)\r\n }\r\n\r\n getStats() {\r\n return {\r\n ...this.stats.value,\r\n preloadedPaths: Array.from(this.preloadedRoutes),\r\n isPreloading: this.isPreloading.value\r\n }\r\n }\r\n\r\n createStatusComponent() {\r\n const self = this\r\n return defineComponent({\r\n name: 'PreloadStatus',\r\n setup() {\r\n onMounted(() => {\r\n if (!document.getElementById('preloader-styles')) {\r\n const style = document.createElement('style')\r\n style.id = 'preloader-styles'\r\n const position = PRELOAD_OPTIONS.statusPosition.replace('-', ': 20px; ') + ': 20px;'\r\n style.textContent = \\`\r\n .preloader-status {\r\n position: fixed; \\${position}\r\n background: rgba(0,0,0,0.8); color: white; padding: 8px 16px;\r\n border-radius: 6px; font-size: 12px; z-index: 9999;\r\n pointer-events: none; font-family: system-ui;\r\n }\r\n \\`\r\n document.head.appendChild(style)\r\n }\r\n })\r\n\r\n return () => {\r\n if (!self.isPreloading.value || !import.meta.env.DEV || !PRELOAD_OPTIONS.showStatus) {\r\n return null\r\n }\r\n return h('div', { class: 'preloader-status' }, [\r\n '🔄 正在优化页面... ',\r\n \\`\\${self.stats.value.completed}/\\${self.stats.value.total}\\`\r\n ])\r\n }\r\n }\r\n })\r\n }\r\n}\r\n\r\n// 🚀 全局实例\r\nconst preloader = new PreloaderManager()\r\n\r\n// 🛠️ 开发环境调试工具\r\nif (import.meta.env.DEV && PRELOAD_OPTIONS.debug) {\r\n window.preloaderDebug = {\r\n stats: () => preloader.getStats(),\r\n restart: () => preloader.start(),\r\n check: (path) => preloader.isPreloaded(path),\r\n help: () => console.log('🛠️ 预加载调试: stats() | restart() | check(path)')\r\n }\r\n console.log('🛠️ 预加载调试工具: window.preloaderDebug')\r\n}\r\n\r\n// 🎯 导出\r\nexport const usePreloader = () => ({\r\n start: () => preloader.start(),\r\n isPreloaded: (path) => preloader.isPreloaded(path),\r\n StatusComponent: preloader.createStatusComponent()\r\n})\r\n\r\n// 🚀 自动启动\r\nif (typeof window !== 'undefined') {\r\n setTimeout(() => preloader.start(), PRELOAD_OPTIONS.delay || 2000)\r\n}`","// ============================================================================\r\n// 🛠️ src/generator.ts - 代码生成器\r\n// ============================================================================\r\n\r\nimport type { PreloaderOptions, PreloadRoute } from './types'\r\nimport { runtimeTemplate } from './runtime'\r\n\r\nexport class CodeGenerator {\r\n constructor(private options: PreloaderOptions) {}\r\n\r\n /**\r\n * 生成运行时代码\r\n */\r\n generateRuntime(): string {\r\n const routes = this.processRoutes()\r\n const options = this.processOptions()\r\n\r\n return runtimeTemplate\r\n .replace('__PRELOAD_ROUTES__', JSON.stringify(routes, null, 2))\r\n .replace('__PRELOAD_OPTIONS__', JSON.stringify(options, null, 2))\r\n }\r\n\r\n /**\r\n * 处理路由配置\r\n */\r\n private processRoutes(): any[] {\r\n return this.options.routes.map(route => {\r\n const componentPath = route.component || this.inferComponentPath(route.path)\r\n return {\r\n path: route.path,\r\n component: `() => import('${componentPath}')`,\r\n reason: route.reason || '用户配置的预加载页面',\r\n priority: route.priority || 2\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * 处理选项配置\r\n */\r\n private processOptions() {\r\n return {\r\n delay: this.options.delay || 2000,\r\n showStatus: this.options.showStatus !== false,\r\n statusPosition: this.options.statusPosition || 'bottom-right',\r\n debug: this.options.debug || false\r\n }\r\n }\r\n\r\n /**\r\n * 推断组件路径\r\n */\r\n private inferComponentPath(routePath: string): string {\r\n const cleanPath = routePath.replace(/^\\//, '').replace(/\\//g, '-')\r\n return `@/views/${cleanPath}/index.vue`\r\n }\r\n\r\n /**\r\n * 生成HTML注入代码\r\n */\r\n generateHtmlInject(): string {\r\n if (this.options.showStatus === false) return ''\r\n \r\n return '<preloader-status></preloader-status>'\r\n }\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACIO,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGxB,IAAM,gBAAN,MAAoB;AAAA,EACzB,YAAoB,SAA2B;AAA3B;AAAA,EAA4B;AAAA;AAAA;AAAA;AAAA,EAKhD,kBAA0B;AACxB,UAAM,SAAS,KAAK,cAAc;AAClC,UAAM,UAAU,KAAK,eAAe;AAEpC,WAAO,gBACJ,QAAQ,sBAAsB,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC,EAC7D,QAAQ,uBAAuB,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAuB;AAC7B,WAAO,KAAK,QAAQ,OAAO,IAAI,WAAS;AACtC,YAAM,gBAAgB,MAAM,aAAa,KAAK,mBAAmB,MAAM,IAAI;AAC3E,aAAO;AAAA,QACL,MAAM,MAAM;AAAA,QACZ,WAAW,iBAAiB,aAAa;AAAA,QACzC,QAAQ,MAAM,UAAU;AAAA,QACxB,UAAU,MAAM,YAAY;AAAA,MAC9B;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB;AACvB,WAAO;AAAA,MACL,OAAO,KAAK,QAAQ,SAAS;AAAA,MAC7B,YAAY,KAAK,QAAQ,eAAe;AAAA,MACxC,gBAAgB,KAAK,QAAQ,kBAAkB;AAAA,MAC/C,OAAO,KAAK,QAAQ,SAAS;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,WAA2B;AACpD,UAAM,YAAY,UAAU,QAAQ,OAAO,EAAE,EAAE,QAAQ,OAAO,GAAG;AACjE,WAAO,WAAW,SAAS;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,qBAA6B;AAC3B,QAAI,KAAK,QAAQ,eAAe,MAAO,QAAO;AAE9C,WAAO;AAAA,EACT;AACF;;;AFzDA,IAAM,oBAAoB;AAC1B,IAAM,6BAA6B,OAAO;AAE3B,SAAR,gBAAiC,SAAmC;AACzE,MAAI;AAEJ,SAAO;AAAA,IACL,MAAM;AAAA;AAAA,IAGN,SAAS;AAAA,IAET,iBAAiB;AACf,kBAAY,IAAI,cAAc,OAAO;AACrC,cAAQ,IAAI,sFAAmB,QAAQ,OAAO,MAAM,qBAAM;AAAA,IAC5D;AAAA,IAEA,UAAU,IAAI;AACZ,UAAI,OAAO,mBAAmB;AAC5B,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,IAAI;AACP,UAAI,OAAO,4BAA4B;AACrC,eAAO,UAAU,gBAAgB;AAAA,MACnC;AACA,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,mBAAmB,MAAM;AACvB,YAAM,SAAS,UAAU,mBAAmB;AAC5C,UAAI,QAAQ;AACV,eAAO,KAAK;AAAA,UACV;AAAA,UACA;AAAA,MAAuB,MAAM;AAAA,QAC/B;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,gBAAgB,KAAK;AACnB,UAAI,IAAI,KAAK,SAAS,aAAa,GAAG;AACpC,gBAAQ,IAAI,4DAAa;AACzB,YAAI,OAAO,GAAG,KAAK;AAAA,UACjB,MAAM;AAAA,QACR,CAAC;AACD,eAAO,CAAC;AAAA,MACV;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/runtime.ts","../src/generator.ts"],"sourcesContent":["// ============================================================================\r\n// 🚀 src/index.ts - 主插件文件\r\n// ============================================================================\r\nimport type { Plugin } from 'vite'\r\nimport type { PreloaderOptions } from './types'\r\nimport { CodeGenerator } from './generator'\r\n\r\nexport default function preloaderPlugin(options: PreloaderOptions): Plugin {\r\n let generator: CodeGenerator\r\n const isDev = process.env.NODE_ENV !== 'production'\r\n \r\n // 智能默认配置\r\n const finalOptions = {\r\n debug: isDev, // 开发环境默认开启调试\r\n delay: 2000, // 默认2秒\r\n showStatus: true, // 默认显示状态\r\n statusPosition: 'bottom-right' as const, // 默认右下角\r\n ...options // 用户配置覆盖默认配置\r\n }\r\n\r\n return {\r\n name: 'vite-plugin-preloader',\r\n \r\n // 🎯 设置插件执行顺序\r\n enforce: 'post',\r\n \r\n configResolved() {\r\n generator = new CodeGenerator(finalOptions)\r\n \r\n if (finalOptions.debug) {\r\n console.log(`🚀 [预加载插件] 已启用,配置了 ${finalOptions.routes.length} 个路由`)\r\n console.log('📋 [预加载插件] 路由列表:', finalOptions.routes)\r\n console.log('⚙️ [预加载插件] 配置选项:', {\r\n delay: finalOptions.delay,\r\n showStatus: finalOptions.showStatus,\r\n statusPosition: finalOptions.statusPosition,\r\n debug: finalOptions.debug\r\n })\r\n }\r\n },\r\n\r\n // 🎨 HTML 转换 - 直接注入脚本到 HTML\r\n transformIndexHtml(html) {\r\n const inject = generator.generateHtmlInject()\r\n \r\n if (finalOptions.debug) {\r\n console.log('🎨 [预加载插件] 注入预加载脚本到 HTML')\r\n }\r\n \r\n // 注入到 head 标签末尾\r\n return html.replace('</head>', `${inject}\\n</head>`)\r\n },\r\n\r\n // 🔥 HMR 支持\r\n handleHotUpdate(ctx) {\r\n if (ctx.file.includes('vite.config')) {\r\n if (finalOptions.debug) {\r\n console.log('🔄 [预加载插件] 配置已更新')\r\n }\r\n ctx.server.ws.send({\r\n type: 'full-reload'\r\n })\r\n return []\r\n }\r\n return undefined\r\n }\r\n }\r\n}\r\n\r\n// 命名导出,支持多种导入方式\r\nexport { type PreloaderOptions, type PreloadRoute } from './types'","// ============================================================================\r\n// ⚡ src/runtime.ts - 运行时代码模板(字符串)\r\n// ============================================================================\r\nexport const runtimeTemplate = `// 🚀 Auto-generated by vite-plugin-preloader\r\n(function() {\r\n 'use strict';\r\n \r\n // 🎯 预加载配置(构建时注入)\r\n const PRELOAD_ROUTES = __PRELOAD_ROUTES__\r\n const PRELOAD_OPTIONS = __PRELOAD_OPTIONS__\r\n\r\n class PreloaderManager {\r\n constructor() {\r\n this.preloadedRoutes = new Set()\r\n this.isPreloading = false\r\n this.stats = {\r\n total: 0, completed: 0, failed: 0, startTime: 0, endTime: 0\r\n }\r\n this.statusElement = null\r\n }\r\n\r\n async start() {\r\n if (this.isPreloading) return\r\n\r\n this.isPreloading = true\r\n this.stats = {\r\n total: PRELOAD_ROUTES.length,\r\n completed: 0, failed: 0,\r\n startTime: Date.now(), endTime: 0\r\n }\r\n\r\n if (PRELOAD_OPTIONS.debug) {\r\n console.log(\\`🚀 [预加载] 开始预加载 \\${PRELOAD_ROUTES.length} 个页面\\`)\r\n }\r\n\r\n // 显示状态指示器\r\n this.showStatus()\r\n\r\n const sortedRoutes = [...PRELOAD_ROUTES].sort((a, b) => a.priority - b.priority)\r\n \r\n for (const route of sortedRoutes) {\r\n await this.preloadSingle(route)\r\n this.updateStatus()\r\n await this.sleep(100)\r\n }\r\n\r\n this.stats.endTime = Date.now()\r\n this.isPreloading = false\r\n \r\n if (PRELOAD_OPTIONS.debug) {\r\n console.log(\\`🎉 [预加载] 完成! 耗时 \\${this.stats.endTime - this.stats.startTime}ms\\`)\r\n }\r\n\r\n // 隐藏状态指示器\r\n this.hideStatus()\r\n }\r\n\r\n async preloadSingle(route) {\r\n if (this.preloadedRoutes.has(route.path)) return\r\n\r\n try {\r\n const startTime = Date.now()\r\n // 使用 eval 来动态执行导入函数字符串\r\n const importFn = eval('(' + route.component + ')')\r\n await importFn()\r\n const loadTime = Date.now() - startTime\r\n \r\n this.preloadedRoutes.add(route.path)\r\n this.stats.completed++\r\n \r\n if (PRELOAD_OPTIONS.debug) {\r\n console.log(\\`✅ [预加载] \\${route.path} (\\${loadTime}ms) - \\${route.reason}\\`)\r\n }\r\n } catch (error) {\r\n this.stats.failed++\r\n if (PRELOAD_OPTIONS.debug) {\r\n console.error(\\`❌ [预加载] \\${route.path} 失败:\\`, error)\r\n }\r\n }\r\n }\r\n\r\n showStatus() {\r\n if (!PRELOAD_OPTIONS.showStatus || !document.body) return\r\n\r\n // 添加样式\r\n if (!document.getElementById('preloader-styles')) {\r\n const style = document.createElement('style')\r\n style.id = 'preloader-styles'\r\n const position = PRELOAD_OPTIONS.statusPosition.replace('-', ': 20px; ') + ': 20px;'\r\n style.textContent = \\`\r\n .preloader-status {\r\n position: fixed; \\${position}\r\n background: rgba(0,0,0,0.8); color: white; padding: 8px 16px;\r\n border-radius: 6px; font-size: 12px; z-index: 9999;\r\n pointer-events: none; font-family: system-ui;\r\n transition: opacity 0.3s ease;\r\n }\r\n .preloader-status.fade-out {\r\n opacity: 0;\r\n }\r\n \\`\r\n document.head.appendChild(style)\r\n }\r\n\r\n // 创建状态元素\r\n this.statusElement = document.createElement('div')\r\n this.statusElement.className = 'preloader-status'\r\n this.updateStatus()\r\n document.body.appendChild(this.statusElement)\r\n }\r\n\r\n updateStatus() {\r\n if (!this.statusElement) return\r\n this.statusElement.textContent = \\`🔄 正在优化页面... \\${this.stats.completed}/\\${this.stats.total}\\`\r\n }\r\n\r\n hideStatus() {\r\n if (!this.statusElement) return\r\n this.statusElement.classList.add('fade-out')\r\n setTimeout(() => {\r\n if (this.statusElement && this.statusElement.parentNode) {\r\n this.statusElement.parentNode.removeChild(this.statusElement)\r\n }\r\n }, 300)\r\n }\r\n\r\n sleep(ms) {\r\n return new Promise(resolve => setTimeout(resolve, ms))\r\n }\r\n\r\n isPreloaded(path) {\r\n return this.preloadedRoutes.has(path)\r\n }\r\n\r\n getStats() {\r\n return {\r\n ...this.stats,\r\n preloadedPaths: Array.from(this.preloadedRoutes),\r\n isPreloading: this.isPreloading\r\n }\r\n }\r\n }\r\n\r\n // 🚀 全局实例\r\n const preloader = new PreloaderManager()\r\n\r\n // 🛠️ 开发环境调试工具\r\n if (PRELOAD_OPTIONS.debug) {\r\n window.preloaderDebug = {\r\n stats: () => preloader.getStats(),\r\n restart: () => preloader.start(),\r\n check: (path) => preloader.isPreloaded(path),\r\n help: () => console.log('🛠️ 预加载调试: stats() | restart() | check(path)')\r\n }\r\n console.log('🛠️ 预加载调试工具: window.preloaderDebug')\r\n }\r\n\r\n // 🚀 自动启动 - 等待 DOM 加载完成\r\n function autoStart() {\r\n if (document.readyState === 'loading') {\r\n document.addEventListener('DOMContentLoaded', () => {\r\n setTimeout(() => preloader.start(), PRELOAD_OPTIONS.delay)\r\n })\r\n } else {\r\n setTimeout(() => preloader.start(), PRELOAD_OPTIONS.delay)\r\n }\r\n }\r\n\r\n // 立即执行自动启动\r\n autoStart()\r\n\r\n // 导出到全局(可选使用)\r\n window.usePreloader = () => ({\r\n start: () => preloader.start(),\r\n isPreloaded: (path) => preloader.isPreloaded(path),\r\n getStats: () => preloader.getStats()\r\n })\r\n\r\n})();`","// ============================================================================\r\n// 🛠️ src/generator.ts - 代码生成器\r\n// ============================================================================\r\nimport type { PreloaderOptions } from './types'\r\nimport { runtimeTemplate } from './runtime'\r\n\r\nexport class CodeGenerator {\r\n constructor(private options: PreloaderOptions) {}\r\n\r\n /**\r\n * 生成运行时代码\r\n */\r\n generateRuntime(): string {\r\n const routes = this.processRoutes()\r\n const options = this.processOptions()\r\n\r\n if (options.debug) {\r\n console.log('🔧 [预加载] 生成运行时代码,路由数量:', routes.length)\r\n }\r\n\r\n return runtimeTemplate\r\n .replace('__PRELOAD_ROUTES__', JSON.stringify(routes, null, 2))\r\n .replace('__PRELOAD_OPTIONS__', JSON.stringify(options, null, 2))\r\n }\r\n\r\n /**\r\n * 处理路由配置\r\n */\r\n private processRoutes(): any[] {\r\n return this.options.routes.map(route => {\r\n // 处理字符串输入\r\n if (typeof route === 'string') {\r\n const componentPath = this.inferComponentPath(route)\r\n return {\r\n path: route,\r\n component: `() => import('${componentPath}')`,\r\n reason: '自动推断的预加载页面',\r\n priority: 2\r\n }\r\n }\r\n \r\n // 处理对象输入\r\n const componentPath = route.component || this.inferComponentPath(route.path)\r\n return {\r\n path: route.path,\r\n component: `() => import('${componentPath}')`,\r\n reason: route.reason || '用户配置的预加载页面',\r\n priority: route.priority || 2\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * 处理选项配置\r\n */\r\n private processOptions() {\r\n // 智能默认配置\r\n const isDev = process.env.NODE_ENV !== 'production'\r\n \r\n return {\r\n delay: this.options.delay ?? 2000, // 默认2秒\r\n showStatus: this.options.showStatus ?? true, // 默认显示状态\r\n statusPosition: this.options.statusPosition ?? 'bottom-right', // 默认右下角\r\n debug: this.options.debug ?? isDev // 开发环境默认开启调试,生产环境默认关闭\r\n }\r\n }\r\n\r\n /**\r\n * 推断组件路径\r\n */\r\n private inferComponentPath(routePath: string): string {\r\n const cleanPath = routePath.replace(/^\\//, '')\r\n \r\n // 如果是 demo/ 开头的路径,保持原有结构\r\n if (cleanPath.startsWith('demo/')) {\r\n return `@/views/${cleanPath}/index.vue`\r\n }\r\n \r\n // 其他路径的处理\r\n const pathSegments = cleanPath.split('/')\r\n return `@/views/${pathSegments.join('/')}/index.vue`\r\n }\r\n\r\n /**\r\n * 生成注入到 HTML 头部的脚本\r\n */\r\n generateHtmlInject(): string {\r\n return `<script type=\"module\">\r\n${this.generateRuntime()}\r\n</script>`\r\n }\r\n}"],"mappings":"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","names":["componentPath"]}
|