svgConvert.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import fs from 'fs';
  2. import { SVG, cleanupSVG, parseColors, /*runSVGO,*/ isEmptyColor } from '@iconify/tools';
  3. import { Plugin } from 'vite';
  4. interface Options {
  5. singleColorIconDir?: string;
  6. multiColorIconDir?: string;
  7. }
  8. export default function svgConvert(options: Options = {}): Plugin {
  9. const {
  10. singleColorIconDir = 'src/assets/iconify/single-color',
  11. multiColorIconDir = 'src/assets/iconify/multi-color',
  12. } = options;
  13. return {
  14. name: 'vite-plugin-svg-convert',
  15. async transform(_, id) {
  16. // 只处理 SVG 文件
  17. if (!id.endsWith('.svg')) return null;
  18. // 检查是否是目标目录下的文件
  19. const isSingleColor = id.includes(singleColorIconDir);
  20. const isMultiColor = id.includes(multiColorIconDir);
  21. if (!isSingleColor && !isMultiColor) return null;
  22. // 读取 SVG 文件内容
  23. const svgContent = await fs.promises.readFile(id, 'utf-8');
  24. // 检查是否包含 image 标签
  25. if (svgContent.includes('<image')) {
  26. // 如果是多色图标且包含 image 标签,直接返回原始内容
  27. if (isMultiColor) {
  28. const viewBox = svgContent.match(/viewBox="([^"]+)"/)?.[1] || '0 0 1 1';
  29. const [, , width, height] = viewBox.split(' ').map(Number);
  30. // 提取 SVG 标签内的内容
  31. const innerContent = svgContent.replace(/<svg[^>]*>([\s\S]*)<\/svg>/i, '$1');
  32. return {
  33. code: `export default {width: ${width}, height: ${height}, body: ${JSON.stringify(innerContent)}}`,
  34. map: null,
  35. };
  36. }
  37. }
  38. // 创建 SVG 实例
  39. const svg = new SVG(svgContent);
  40. // 清理 SVG
  41. await cleanupSVG(svg);
  42. // 根据目录类型选择不同的处理方式
  43. if (isSingleColor) {
  44. // 单色图标处理
  45. parseColors(svg, {
  46. defaultColor: 'currentColor',
  47. callback: (_, colorStr, color) => {
  48. return !color || isEmptyColor(color) ? colorStr : 'currentColor';
  49. },
  50. });
  51. }
  52. // 获取优化后的 SVG 内容
  53. const width = svg.viewBox.width;
  54. const height = svg.viewBox.height;
  55. // const aspectRatio = height / width;
  56. // const newWidth = '1rem';
  57. // const newHeight = `${aspectRatio}rem`;
  58. // // 运行 SVGO 优化
  59. // await runSVGO(svg, {
  60. // plugins: [
  61. // {
  62. // name: 'removeAttrs',
  63. // params: {
  64. // attrs: ['width', 'height'],
  65. // },
  66. // },
  67. // {
  68. // name: 'addAttributesToSVGElement',
  69. // params: {
  70. // attributes: [{ width: newWidth }, { height: newHeight }],
  71. // },
  72. // },
  73. // ],
  74. // });
  75. // 获取优化后的 SVG 内容
  76. const optimizedSvg = svg.toMinifiedString();
  77. // 提取 SVG 标签内的内容
  78. const innerContent = optimizedSvg.replace(/<svg[^>]*>([\s\S]*)<\/svg>/i, '$1');
  79. // 返回转换后的代码(不生成 sourcemap,避免 Vite 警告)
  80. return {
  81. code: `export default {width: ${width}, height: ${height}, body: ${JSON.stringify(innerContent)}}`,
  82. map: null,
  83. };
  84. },
  85. };
  86. }