index.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { console } from 'node:inspector'
  2. /**
  3. * @typedef {object} AssetPathResolverOptions
  4. * @property {string} [alias='~@assets'] - 要匹配替换的路径前缀,例如 "~@assets"
  5. * @property {string} [local='/src/assets'] - 本地资源根路径,例如 "/src/assets" 或 "/public/assets"
  6. * @property {string} [cdn] - CDN 路径前缀,例如 "https://cdn.assets.com"
  7. * @property {boolean} [remote=false] - 是否启用远程资源路径,默认 false(即使用本地路径)
  8. * @property {RegExp} [fileRegex=/\.(js|ts|jsx|tsx|vue|html|svelte|css)$/] 需要处理的文件类型的正则表达式。
  9. */
  10. /**
  11. * 创建资源路径替换插件
  12. * @param {AssetPathResolverOptions} options - 插件配置选项
  13. * @returns {import('vite').Plugin} Vite插件
  14. */
  15. export default function vitePluginAssetPathResolver(options) {
  16. // 设置默认选项
  17. options = {
  18. alias: options.alias || '~@assets',
  19. local: options.local || '/src/assets',
  20. cdn: options.cdn,
  21. remote: options.remote || false,
  22. fileRegex: options.fileRegex || /\.(js|ts|jsx|tsx|vue|nvue|html|svelte|css)$/,
  23. }
  24. // 验证必要的配置项
  25. if (!options.alias)
  26. throw new Error('Missing required option: alias')
  27. if (!options.local)
  28. throw new Error('Missing required option: local')
  29. if (!options.cdn)
  30. throw new Error('Missing required option: cdn')
  31. // 规范化路径前缀
  32. const aliasPattern = new RegExp(`${escapeRegExp(options.alias)}\\/(.*?)(['"])`, 'g')
  33. const isRemote = options.remote === true
  34. const localPath = options.local.replace(/\\/g, '/').startsWith('/') ? options.local.replace(/\\/g, '/') : `/${options.local.replace(/\\/g, '/')}`
  35. // 确保CDN路径结尾有斜杠
  36. const cdnPath = options.cdn.endsWith('/') ? options.cdn : `${options.cdn}/`
  37. return {
  38. name: 'vite-plugin-asset-path-resolver',
  39. enforce: 'pre',
  40. /**
  41. * 转换代码中的资源路径
  42. * @param {string} code - 源代码
  43. * @param {string} id - 文件ID
  44. * @returns {string|undefined} 转换后的代码或undefined(如果未修改)
  45. */
  46. transform(code, id) {
  47. // 跳过不匹配的文件
  48. if (!options?.fileRegex?.test?.(id)) {
  49. return undefined
  50. }
  51. // 跳过不包含目标前缀的文件
  52. if (options.alias && !code.includes(options.alias)) {
  53. return undefined
  54. }
  55. // --- FIX START ---
  56. // The replacement now correctly uses forward slashes for the path.
  57. const replacement = isRemote
  58. ? (_, assetPath, quote) => `${cdnPath}${assetPath.replace(/\\/g, '/')}${quote}`
  59. : (_, assetPath, quote) => `${localPath}/${assetPath.replace(/\\/g, '/')}${quote}`
  60. // --- FIX END ---
  61. // 执行替换
  62. const result = code.replace(aliasPattern, replacement)
  63. // 如果代码没变,返回undefined
  64. return result !== code ? result : undefined
  65. },
  66. /**
  67. * 转换HTML代码中的资源路径
  68. * @param {string} html - HTML代码
  69. * @returns {string} 转换后的HTML代码
  70. */
  71. transformIndexHtml(html) {
  72. // --- FIX START ---
  73. // The replacement now correctly uses forward slashes for the path.
  74. const replacement = isRemote
  75. ? (_, assetPath, quote) => `${cdnPath}${assetPath.replace(/\\/g, '/')}${quote}`
  76. : (_, assetPath, quote) => `${localPath}/${assetPath.replace(/\\/g, '/')}${quote}`
  77. // --- FIX END ---
  78. // 处理HTML中的资源路径
  79. return html.replace(aliasPattern, replacement)
  80. },
  81. /**
  82. * 配置插件
  83. * @param {import('vite').ResolvedConfig} config - Vite配置
  84. */
  85. configResolved(config) {
  86. console.log(`[asset-path-resolver] Mode: ${isRemote ? 'remote (CDN)' : 'local'}`)
  87. console.log(`[asset-path-resolver] Alias: ${options.alias}`)
  88. console.log(`[asset-path-resolver] Path: ${isRemote ? cdnPath : localPath}`)
  89. },
  90. }
  91. }
  92. /**
  93. * 转义正则表达式中的特殊字符
  94. * @param {string} string - 需要转义的字符串
  95. * @returns {string} 转义后的字符串
  96. */
  97. function escapeRegExp(string) {
  98. return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  99. }