vite-plugin-preloader 1.1.3 → 2.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/README.md +340 -195
- package/dist/index.d.mts +36 -3
- package/dist/index.d.ts +36 -3
- package/dist/index.js +244 -205
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +234 -205
- package/dist/index.mjs.map +1 -1
- package/package.json +80 -81
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime.ts","../src/generator.ts","../src/index.ts"],"sourcesContent":["// ============================================================================\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 }\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 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.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 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 let componentPath = route.component\r\n \r\n // 在开发环境中,Vite 会处理模块解析\r\n // 我们需要使用正确的模块 ID\r\n if (componentPath.startsWith('@/')) {\r\n // 对于 Vite,@ 别名应该在构建时就被解析\r\n // 但在运行时我们需要使用实际的路径\r\n componentPath = componentPath.replace('@/', '/src/')\r\n }\r\n \r\n // 确保路径以 / 开头(相对于项目根目录)\r\n if (!componentPath.startsWith('/') && !componentPath.startsWith('./')) {\r\n componentPath = '/' + componentPath\r\n }\r\n \r\n if (PRELOAD_OPTIONS.debug) {\r\n console.log(\\`🔍 [预加载] 尝试加载: \\${componentPath}\\`)\r\n }\r\n \r\n const module = await import(/* @vite-ignore */ componentPath)\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 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 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: componentPath,\r\n reason: \"自动推断的预加载页面\",\r\n priority: 2,\r\n };\r\n }\r\n\r\n // 处理对象输入\r\n const componentPath =\r\n route.component || this.inferComponentPath(route.path);\r\n return {\r\n path: route.path,\r\n component: 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 * 推断组件路径 - 使用 Vite 别名格式\r\n */\r\n private inferComponentPath(routePath: string): string {\r\n const cleanPath = routePath.replace(/^\\//, \"\");\r\n\r\n // 直接使用 @ 别名,这在 Vite 构建后会被正确解析\r\n if (cleanPath.startsWith(\"demo/\")) {\r\n // /demo/13-calendar -> @/views/demo/13-calendar/index.vue\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}","import 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 }\r\n },\r\n\r\n // 🎨 HTML 转换 - 直接注入脚本到 HTML\r\n transformIndexHtml(html) {\r\n const inject = generator.generateHtmlInject()\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'"],"mappings":";;;AAGO,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;;;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,CAAC,UAAU;AAExC,UAAI,OAAO,UAAU,UAAU;AAC7B,cAAMA,iBAAgB,KAAK,mBAAmB,KAAK;AACnD,eAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAWA;AAAA,UACX,QAAQ;AAAA,UACR,UAAU;AAAA,QACZ;AAAA,MACF;AAGA,YAAM,gBACJ,MAAM,aAAa,KAAK,mBAAmB,MAAM,IAAI;AACvD,aAAO;AAAA,QACL,MAAM,MAAM;AAAA,QACZ,WAAW;AAAA,QACX,QAAQ,MAAM,UAAU;AAAA,QACxB,UAAU,MAAM,YAAY;AAAA,MAC9B;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB;AAEvB,UAAM,QAAQ,QAAQ,IAAI,aAAa;AAEvC,WAAO;AAAA,MACL,OAAO,KAAK,QAAQ,SAAS;AAAA;AAAA,MAC7B,YAAY,KAAK,QAAQ,cAAc;AAAA;AAAA,MACvC,gBAAgB,KAAK,QAAQ,kBAAkB;AAAA;AAAA,MAC/C,OAAO,KAAK,QAAQ,SAAS;AAAA;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,WAA2B;AACpD,UAAM,YAAY,UAAU,QAAQ,OAAO,EAAE;AAG7C,QAAI,UAAU,WAAW,OAAO,GAAG;AAEjC,aAAO,WAAW,SAAS;AAAA,IAC7B;AAGA,UAAM,eAAe,UAAU,MAAM,GAAG;AACxC,WAAO,WAAW,aAAa,KAAK,GAAG,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,qBAA6B;AAC3B,WAAO;AAAA,EACT,KAAK,gBAAgB,CAAC;AAAA;AAAA,EAEtB;AACF;;;ACrFe,SAAR,gBAAiC,SAAmC;AACzE,MAAI;AACJ,QAAM,QAAQ,QAAQ,IAAI,aAAa;AAGvC,QAAM,eAAe;AAAA,IACnB,OAAO;AAAA;AAAA,IACP,OAAO;AAAA;AAAA,IACP,YAAY;AAAA;AAAA,IACZ,gBAAgB;AAAA;AAAA,IAChB,GAAG;AAAA;AAAA,EACL;AAEA,SAAO;AAAA,IACL,MAAM;AAAA;AAAA,IAGN,SAAS;AAAA,IAET,iBAAiB;AACf,kBAAY,IAAI,cAAc,YAAY;AAE1C,UAAI,aAAa,OAAO;AACtB,gBAAQ,IAAI,yFAAsB,aAAa,OAAO,MAAM,6FAAkB;AAAA,MAChF;AAAA,IACF;AAAA;AAAA,IAGA,mBAAmB,MAAM;AACvB,YAAM,SAAS,UAAU,mBAAmB;AAG5C,aAAO,KAAK,QAAQ,WAAW,GAAG,MAAM;AAAA,QAAW;AAAA,IACrD;AAAA;AAAA,IAGA,gBAAgB,KAAK;AACnB,UAAI,IAAI,KAAK,SAAS,aAAa,GAAG;AACpC,YAAI,aAAa,OAAO;AACtB,kBAAQ,IAAI,2EAAkB;AAAA,QAChC;AACA,YAAI,OAAO,GAAG,KAAK;AAAA,UACjB,MAAM;AAAA,QACR,CAAC;AACD,eAAO,CAAC;AAAA,MACV;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":["componentPath"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime.ts","../src/generator.ts","../src/index.ts"],"sourcesContent":["// ============================================================================\r\n// ⚡ src/runtime.ts - 运行时状态 UI 脚本生成\r\n// 核心预加载由构建时注入的 <link rel=\"prefetch\"> 标签完成(浏览器原生调度)\r\n// 本模块仅负责生成可选的状态显示和调试工具脚本\r\n// ============================================================================\r\n\r\nexport interface RuntimeConfig {\r\n delay: number\r\n debug: boolean\r\n showStatus: boolean\r\n statusPosition: string\r\n routes: Array<{ path: string; chunkPath: string | null; reason: string }>\r\n isBuild: boolean\r\n}\r\n\r\nconst STATUS_STYLES: Record<string, string> = {\r\n 'bottom-right': 'bottom:16px;right:16px',\r\n 'bottom-left': 'bottom:16px;left:16px',\r\n 'top-right': 'top:16px;right:16px',\r\n 'top-left': 'top:16px;left:16px',\r\n}\r\n\r\n/**\r\n * 生成运行时内联脚本字符串\r\n * 负责:网络感知判断、状态 UI 展示、prefetch link 进度监听、debug 工具挂载\r\n */\r\nexport function buildRuntimeScript(config: RuntimeConfig): string {\r\n const posStyle = STATUS_STYLES[config.statusPosition] ?? STATUS_STYLES['bottom-right']\r\n const configJson = JSON.stringify(config)\r\n\r\n // 状态 UI 代码(仅 showStatus=true 时注入)\r\n const statusUiCode = config.showStatus\r\n ? `\r\n function showStatus(){\r\n var links=document.querySelectorAll('link[data-preloader]');\r\n if(!links.length)return;\r\n var el=document.createElement('div');\r\n el.id='__preloader_ui__';\r\n el.style.cssText='position:fixed;${posStyle};z-index:2147483647;'\r\n +'background:rgba(15,15,15,0.82);color:#fff;font-size:12px;line-height:1.5;'\r\n +'padding:7px 14px;border-radius:20px;pointer-events:none;'\r\n +'transition:opacity 0.4s ease;font-family:system-ui,-apple-system,sans-serif;'\r\n +'backdrop-filter:blur(4px);box-shadow:0 2px 8px rgba(0,0,0,0.3);';\r\n el.textContent='⚡ 正在优化 '+links.length+' 个页面...';\r\n document.body&&document.body.appendChild(el);\r\n var done=0,total=links.length;\r\n function tick(){\r\n done++;\r\n el.textContent=done>=total?'✅ 页面优化完成('+total+'个)':'⚡ 优化中 '+done+'/'+total+'...';\r\n if(done>=total){\r\n setTimeout(function(){\r\n el.style.opacity='0';\r\n setTimeout(function(){el&&el.parentNode&&el.parentNode.removeChild(el);},420);\r\n },2200);\r\n }\r\n }\r\n links.forEach(function(l){l.addEventListener('load',tick);l.addEventListener('error',tick);});\r\n setTimeout(function(){el&&el.parentNode&&el.parentNode.removeChild(el);},9000);\r\n }\r\n if(document.readyState==='loading'){\r\n document.addEventListener('DOMContentLoaded',function(){setTimeout(showStatus,C.delay);});\r\n }else{\r\n setTimeout(showStatus,C.delay);\r\n }`\r\n : ''\r\n\r\n // debug 工具代码\r\n const debugCode = config.debug\r\n ? `\r\n console.group('%c🚀 vite-plugin-preloader v2','color:#4ade80;font-weight:700');\r\n console.log('已注入 '+C.routes.length+' 个 <link rel=\\\\\"prefetch\\\\\"> 标签(浏览器原生调度)');\r\n C.routes.forEach(function(r){\r\n var ok=r.chunkPath;\r\n console.log((ok?'✅ ':'⚠️ ')+r.path+(ok?' → '+r.chunkPath:' (chunk 未匹配,仅开发环境有效)')+(r.reason?' | '+r.reason:''));\r\n });\r\n console.groupEnd();\r\n window.__preloaderDebug={\r\n routes:C.routes,\r\n check:function(p){return C.routes.some(function(r){return r.path===p&&!!r.chunkPath;})},\r\n help:function(){console.log('__preloaderDebug: .routes | .check(path)');}\r\n };\r\n console.log('%c🛠 调试: window.__preloaderDebug','color:#94a3b8;font-size:11px');`\r\n : ''\r\n\r\n if (!statusUiCode && !debugCode) return ''\r\n\r\n return `<script>/* vite-plugin-preloader v2 - status/debug */\r\n(function(){\r\n var C=${configJson};\r\n var conn=navigator.connection||navigator.mozConnection||navigator.webkitConnection;\r\n if(conn&&(conn.saveData||['slow-2g','2g'].indexOf(conn.effectiveType)>=0)){\r\n if(C.debug)console.warn('[预加载] 检测到省流/低速网络,跳过状态显示');\r\n return;\r\n }\r\n ${debugCode}\r\n ${statusUiCode}\r\n})();\r\n</script>`\r\n}","// ============================================================================\r\n// 🛠️ src/generator.ts - 代码生成器\r\n// v2 核心改动:\r\n// 1. 读取 Vite ResolvedConfig 获取真实别名和根目录\r\n// 2. 通过 Rollup OutputBundle 的 facadeModuleId 匹配真实 chunk 路径\r\n// 3. 生成 <link rel=\"prefetch\"> / <link rel=\"modulepreload\"> 标签(浏览器原生调度)\r\n// 4. 过滤动态路由参数(:param)和用户 exclude 列表\r\n// 5. 状态脚本由 runtime.ts 独立生成,不再用运行时 import()\r\n// ============================================================================\r\nimport type { ResolvedConfig } from 'vite'\r\nimport type { PreloaderOptions, ResolvedRoute } from './types'\r\n\r\n// 使用鸭子类型兼容 rollup / rolldown(Vite 8 切换到 rolldown)\r\ninterface BundleChunk {\r\n type: string\r\n facadeModuleId?: string | null\r\n}\r\ntype OutputBundleCompat = Record<string, BundleChunk>\r\nimport { buildRuntimeScript } from './runtime'\r\nimport { normalizePath } from 'vite'\r\nimport path from 'node:path'\r\n\r\nexport class CodeGenerator {\r\n /** @ 别名对应的相对路径(相对于 viteRoot),默认 'src' */\r\n private srcAlias = 'src'\r\n private viteRoot = process.cwd()\r\n\r\n constructor(private readonly options: Required<PreloaderOptions>) {}\r\n\r\n /**\r\n * 从 Vite resolvedConfig 中读取项目根目录和 @ 别名\r\n * 必须在 configResolved hook 中调用\r\n */\r\n setViteConfig(config: ResolvedConfig): void {\r\n this.viteRoot = config.root\r\n\r\n // 统一处理数组/对象两种 alias 格式\r\n const alias = config.resolve?.alias\r\n const entries: Array<{ find: string | RegExp; replacement: string }> = Array.isArray(alias)\r\n ? (alias as Array<{ find: string | RegExp; replacement: string }>)\r\n : Object.entries(alias ?? {}).map(([find, replacement]) => ({ find, replacement: replacement as string }))\r\n\r\n const atEntry = entries.find(\r\n (a) => a.find === '@' || (a.find instanceof RegExp && a.find.source === '^@/'),\r\n )\r\n if (atEntry && typeof atEntry.replacement === 'string') {\r\n const rel = normalizePath(path.relative(this.viteRoot, atEntry.replacement))\r\n if (rel) this.srcAlias = rel\r\n }\r\n }\r\n\r\n /**\r\n * 将用户路由配置解析为内部 ResolvedRoute 列表\r\n * 同时过滤:含 :param 的动态路由段、exclude 列表匹配项\r\n */\r\n resolveRoutes(): ResolvedRoute[] {\r\n if (!Array.isArray(this.options.routes) || this.options.routes.length === 0) {\r\n return []\r\n }\r\n\r\n const { exclude } = this.options\r\n\r\n return this.options.routes\r\n .map((route): ResolvedRoute => {\r\n if (typeof route === 'string') {\r\n return {\r\n path: route,\r\n component: this.inferComponentPath(route),\r\n reason: '自动推断的预加载页面',\r\n priority: 2,\r\n }\r\n }\r\n return {\r\n path: route.path,\r\n component: route.component ?? this.inferComponentPath(route.path),\r\n reason: route.reason ?? '用户配置的预加载页面',\r\n priority: route.priority ?? 2,\r\n }\r\n })\r\n .filter((route) => {\r\n // 过滤含动态参数的路由段(如 :id、:slug、:type)\r\n if (/:\\w+/.test(route.path)) return false\r\n // 应用 exclude 过滤(精确匹配或前缀匹配)\r\n if (\r\n exclude.length > 0 &&\r\n exclude.some(\r\n (e) => route.path === e || route.path.startsWith(e.replace(/\\/$/, '') + '/'),\r\n )\r\n ) {\r\n return false\r\n }\r\n return true\r\n })\r\n .sort((a, b) => a.priority - b.priority)\r\n }\r\n\r\n /**\r\n * 根据 Rollup OutputBundle 中的 facadeModuleId 为各路由匹配实际 chunk 文件路径\r\n * 仅在生产构建的 generateBundle hook 中调用\r\n */\r\n fillChunkPaths(routes: ResolvedRoute[], bundle: OutputBundleCompat): ResolvedRoute[] {\r\n // 构建 normalized-abs-path → '/assets/xxx-hash.js' 映射\r\n const chunkMap = new Map<string, string>()\r\n for (const [fileName, chunkOrAsset] of Object.entries(bundle)) {\r\n if (chunkOrAsset.type !== 'chunk') continue\r\n const chunk = chunkOrAsset as BundleChunk\r\n if (!chunk.facadeModuleId) continue\r\n chunkMap.set(normalizePath(chunk.facadeModuleId), '/' + fileName)\r\n }\r\n\r\n return routes.map((route) => {\r\n const absPath = normalizePath(this.resolveComponentAbsPath(route.component))\r\n const chunkPath = chunkMap.get(absPath)\r\n return chunkPath ? { ...route, chunkPath } : route\r\n })\r\n }\r\n\r\n /**\r\n * 生成 <link> 标签字符串(注入到 </body> 前)\r\n * 生产:<link rel=\"prefetch\"> 指向实际 chunk(浏览器空闲时拉取,完全不阻塞主线程)\r\n * 开发:<link rel=\"modulepreload\"> 指向源文件(Vite dev server 处理解析)\r\n */\r\n generateLinkTags(routes: ResolvedRoute[], isBuild: boolean): string {\r\n const tags: string[] = []\r\n\r\n for (const route of routes) {\r\n if (isBuild) {\r\n if (!route.chunkPath) continue // 找不到 chunk 则跳过,后续会 warn\r\n tags.push(\r\n `<link rel=\"prefetch\" href=\"${route.chunkPath}\" as=\"script\" crossorigin data-preloader=\"${encodeURIComponent(route.path)}\">`,\r\n )\r\n } else {\r\n // 开发模式:将 @/ 替换为 /<srcAlias>/\r\n const devPath = route.component.startsWith('@/')\r\n ? route.component.replace('@/', `/${this.srcAlias}/`)\r\n : route.component\r\n tags.push(\r\n `<link rel=\"modulepreload\" href=\"${devPath}\" data-preloader=\"${encodeURIComponent(route.path)}\">`,\r\n )\r\n }\r\n }\r\n\r\n return tags.join('\\n')\r\n }\r\n\r\n /**\r\n * 生成可选的状态 UI 和调试工具脚本\r\n * showStatus=false 且 debug=false 时返回空字符串(0 运行时开销)\r\n */\r\n generateRuntimeScript(routes: ResolvedRoute[], isBuild: boolean): string {\r\n return buildRuntimeScript({\r\n delay: this.options.delay,\r\n debug: this.options.debug,\r\n showStatus: this.options.showStatus,\r\n statusPosition: this.options.statusPosition,\r\n isBuild,\r\n routes: routes.map((r) => ({\r\n path: r.path,\r\n chunkPath: r.chunkPath ?? null,\r\n reason: r.reason,\r\n })),\r\n })\r\n }\r\n\r\n // --------------------------------------------------------------------------\r\n // 私有工具方法\r\n // --------------------------------------------------------------------------\r\n\r\n /** 根据路由路径推断 Vue 组件路径(@/views/{path}/index.vue) */\r\n private inferComponentPath(routePath: string): string {\r\n const cleanPath = routePath.replace(/^\\//, '').replace(/\\/$/, '')\r\n return `@/views/${cleanPath}/index.vue`\r\n }\r\n\r\n /** 将 @/ 别名路径或相对路径解析为绝对路径,用于与 facadeModuleId 比对 */\r\n private resolveComponentAbsPath(componentPath: string): string {\r\n if (componentPath.startsWith('@/')) {\r\n return path.join(this.viteRoot, this.srcAlias, componentPath.slice(2))\r\n }\r\n if (path.isAbsolute(componentPath)) {\r\n return componentPath\r\n }\r\n return path.join(this.viteRoot, componentPath)\r\n }\r\n}\r\n","// ============================================================================\r\n// 🚀 src/index.ts - 插件入口\r\n// v2 核心改动:\r\n// 1. configResolved(config) 正确读取 alias/root/command\r\n// 2. generateBundle hook 扫描 Rollup bundle 匹配真实 chunk 路径\r\n// 3. transformIndexHtml 注入 <link rel=\"prefetch\"> 到 </body> 前\r\n// 4. routes 完整防御校验,防止 undefined/非数组崩溃\r\n// 5. 移除无效的运行时 import() 动态加载逻辑\r\n// 6. 去掉无用的 vue peerDep,apply 区分 SSR 场景\r\n// ============================================================================\r\nimport type { Plugin, ResolvedConfig } from \"vite\";\r\nimport type { PreloaderOptions, ResolvedRoute } from \"./types\";\r\nimport { CodeGenerator } from \"./generator\";\r\n\r\nexport default function preloaderPlugin(\r\n userOptions: PreloaderOptions = {},\r\n): Plugin {\r\n // 合并默认配置,所有字段都有明确默认值,routes 防御校验确保始终为数组\r\n const options: Required<PreloaderOptions> = {\r\n routes: Array.isArray(userOptions.routes) ? userOptions.routes : [],\r\n delay: userOptions.delay ?? 2000,\r\n debug: userOptions.debug ?? process.env.NODE_ENV !== 'production',\r\n showStatus: userOptions.showStatus ?? true,\r\n statusPosition: userOptions.statusPosition ?? 'bottom-right',\r\n exclude: userOptions.exclude ?? [],\r\n };\r\n\r\n const generator = new CodeGenerator(options);\r\n let resolvedRoutes: ResolvedRoute[] = [];\r\n let isBuild = false;\r\n\r\n return {\r\n name: \"vite-plugin-preloader\",\r\n\r\n // post 确保在其他插件(如 @vitejs/plugin-vue)处理完后执行\r\n enforce: \"post\",\r\n\r\n // SSR 构建时不注入客户端预加载脚本\r\n apply(_, { isSsrBuild }) {\r\n return !isSsrBuild;\r\n },\r\n\r\n // ✅ 正确读取 viteConfig:alias、root、command\r\n configResolved(config: ResolvedConfig) {\r\n isBuild = config.command === \"build\";\r\n generator.setViteConfig(config);\r\n resolvedRoutes = generator.resolveRoutes();\r\n\r\n if (options.debug) {\r\n const total = Array.isArray(userOptions.routes)\r\n ? userOptions.routes.length\r\n : 0;\r\n const filtered = total - resolvedRoutes.length;\r\n console.log(\r\n `🚀 [预加载插件 v2] 已启用,${resolvedRoutes.length} 个路由待预加载` +\r\n (filtered > 0 ? `(已过滤 ${filtered} 个动态/排除路由)` : \"\"),\r\n );\r\n if (!isBuild) {\r\n console.log(\r\n ' 开发模式:注入 <link rel=\"modulepreload\">,生产构建后切换为 <link rel=\"prefetch\">',\r\n );\r\n }\r\n }\r\n },\r\n\r\n // ✅ 生产构建阶段:扫描 bundle,为路由匹配真实 chunk hash 路径\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n generateBundle(_opts: unknown, bundle: Record<string, any>) {\r\n if (!isBuild) return;\r\n resolvedRoutes = generator.fillChunkPaths(resolvedRoutes, bundle);\r\n\r\n if (options.debug) {\r\n const matched = resolvedRoutes.filter((r) => r.chunkPath).length;\r\n const unmatched = resolvedRoutes.filter((r) => !r.chunkPath);\r\n console.log(\r\n `📦 [预加载插件 v2] bundle 扫描完成:${matched}/${resolvedRoutes.length} 个路由成功匹配 chunk`,\r\n );\r\n unmatched.forEach((r) => {\r\n console.warn(\r\n ` ⚠️ 未找到 chunk:${r.path} → ${r.component}(组件路径可能不匹配)`,\r\n );\r\n });\r\n }\r\n },\r\n\r\n // ✅ 注入 <link> 标签 + 可选运行时脚本到 </body> 前\r\n transformIndexHtml(html: string) {\r\n const linkTags = generator.generateLinkTags(resolvedRoutes, isBuild);\r\n const runtimeScript = generator.generateRuntimeScript(\r\n resolvedRoutes,\r\n isBuild,\r\n );\r\n\r\n const inject = [linkTags, runtimeScript].filter(Boolean).join(\"\\n\");\r\n if (!inject) return html;\r\n\r\n // 注入到 </body> 前(脚本依赖 DOM 和 Vue 已挂载)\r\n if (html.includes(\"</body>\")) {\r\n return html.replace(\"</body>\", `${inject}\\n</body>`);\r\n }\r\n // 兜底:追加到末尾\r\n return html + \"\\n\" + inject;\r\n },\r\n\r\n // HMR:vite.config 变更时触发完整刷新\r\n handleHotUpdate(ctx) {\r\n if (ctx.file.includes(\"vite.config\")) {\r\n ctx.server.ws.send({ type: \"full-reload\" });\r\n return [];\r\n }\r\n return undefined;\r\n },\r\n };\r\n}\r\n\r\n// 命名导出,支持多种导入方式\r\nexport {\r\n type PreloaderOptions,\r\n type PreloadRoute,\r\n type StatusPosition,\r\n} from \"./types\";\r\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,81 +1,80 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vite-plugin-preloader",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "🚀 Vite plugin for intelligent route preloading - 智能路由预加载插件",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"module": "dist/index.mjs",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.mjs",
|
|
12
|
-
"require": "./dist/index.js"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"README.md",
|
|
18
|
-
"CHANGELOG.md"
|
|
19
|
-
],
|
|
20
|
-
"keywords": [
|
|
21
|
-
"vite",
|
|
22
|
-
"vite-plugin",
|
|
23
|
-
"preload",
|
|
24
|
-
"vue",
|
|
25
|
-
"vue3",
|
|
26
|
-
"performance",
|
|
27
|
-
"route",
|
|
28
|
-
"lazy-loading",
|
|
29
|
-
"optimization",
|
|
30
|
-
"build-tool",
|
|
31
|
-
"intelligent",
|
|
32
|
-
"preloading"
|
|
33
|
-
],
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "tsup",
|
|
36
|
-
"dev": "tsup --watch",
|
|
37
|
-
"prepublishOnly": "npm run build",
|
|
38
|
-
"test": "vitest",
|
|
39
|
-
"test:watch": "vitest --watch",
|
|
40
|
-
"lint": "eslint src --fix",
|
|
41
|
-
"format": "prettier --write src/",
|
|
42
|
-
"type-check": "tsc --noEmit"
|
|
43
|
-
},
|
|
44
|
-
"peerDependencies": {
|
|
45
|
-
"vite": "^7.0.0 || ^6.0.0 || ^5.0.0"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"@
|
|
50
|
-
"@typescript-eslint/
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-plugin-preloader",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "🚀 Vite plugin for intelligent route preloading - 智能路由预加载插件",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"vite",
|
|
22
|
+
"vite-plugin",
|
|
23
|
+
"preload",
|
|
24
|
+
"vue",
|
|
25
|
+
"vue3",
|
|
26
|
+
"performance",
|
|
27
|
+
"route",
|
|
28
|
+
"lazy-loading",
|
|
29
|
+
"optimization",
|
|
30
|
+
"build-tool",
|
|
31
|
+
"intelligent",
|
|
32
|
+
"preloading"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup",
|
|
36
|
+
"dev": "tsup --watch",
|
|
37
|
+
"prepublishOnly": "npm run build",
|
|
38
|
+
"test": "vitest",
|
|
39
|
+
"test:watch": "vitest --watch",
|
|
40
|
+
"lint": "eslint src --fix",
|
|
41
|
+
"format": "prettier --write src/",
|
|
42
|
+
"type-check": "tsc --noEmit"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"vite": "^8.0.0 || ^7.0.0 || ^6.0.0 || ^5.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.13.9",
|
|
49
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
50
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
51
|
+
"eslint": "^9.21.0",
|
|
52
|
+
"prettier": "^3.5.3",
|
|
53
|
+
"tsup": "^8.3.5",
|
|
54
|
+
"typescript": "~5.8.0",
|
|
55
|
+
"vite": "^8.0.1",
|
|
56
|
+
"vitest": "^3.0.8",
|
|
57
|
+
"vue": "^3.5.13"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18.0.0"
|
|
61
|
+
},
|
|
62
|
+
"author": {
|
|
63
|
+
"name": "ChenYu",
|
|
64
|
+
"email": "ycyplus@gmail.com",
|
|
65
|
+
"url": "https://github.com/ChenyCHENYU"
|
|
66
|
+
},
|
|
67
|
+
"license": "MIT",
|
|
68
|
+
"homepage": "https://github.com/ChenyCHENYU/vite-plugin-preloader#readme",
|
|
69
|
+
"repository": {
|
|
70
|
+
"type": "git",
|
|
71
|
+
"url": "git+https://github.com/ChenyCHENYU/vite-plugin-preloader.git"
|
|
72
|
+
},
|
|
73
|
+
"bugs": {
|
|
74
|
+
"url": "https://github.com/ChenyCHENYU/vite-plugin-preloader/issues"
|
|
75
|
+
},
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public",
|
|
78
|
+
"registry": "https://registry.npmjs.org/"
|
|
79
|
+
}
|
|
80
|
+
}
|