egoist / tailwindcss-icons

Use any icon (100,000+) from Iconify, for TailwindCSS
MIT License
830 stars 17 forks source link

Load svgs from filesystem #37

Closed XEngine closed 3 weeks ago

XEngine commented 5 months ago

Is it possible to load svg files from filesystem like this one in unocss?

hyoban commented 5 months ago

Maybe you can try the following code

const { iconsPlugin } = require("@egoist/tailwindcss-icons");
const fs = require("fs");
const path = require("path");

function getCollection(dir, name) {
  const files = fs.readdirSync(dir);
  const collection = {
    [name]: {
      icons: {},
    },
  };

  let stat;
  for (const file of files) {
    const filePath = `${dir}/${file}`;
    try {
      stat = fs.lstatSync(filePath);
    } catch (err) {
      continue;
    }
    if (stat.isFile()) {
      const svg = fs.readFileSync(filePath, "utf-8");
      const filename = path.basename(file, ".svg");
      collection[name].icons[filename] = {
        body: svg.replace(/<svg[^>]*>/, "").replace(/<\/svg>/, ""),
        width: 24,
        height: 24,
      };
    }
  }
  return collection;
}

const collections = getCollection("./src/icons", "custom");

module.exports = {
  content: ["./src/**/*.{ts,tsx}"],
  plugins: [iconsPlugin({ collections })],
};
XEngine commented 5 months ago

Maybe you can try the following code

const { iconsPlugin } = require("@egoist/tailwindcss-icons");
const fs = require("fs");
const path = require("path");

function getCollection(dir, name) {
  const files = fs.readdirSync(dir);
  const collection = {
    [name]: {
      icons: {},
    },
  };

  let stat;
  for (const file of files) {
    const filePath = `${dir}/${file}`;
    try {
      stat = fs.lstatSync(filePath);
    } catch (err) {
      continue;
    }
    if (stat.isFile()) {
      const svg = fs.readFileSync(filePath, "utf-8");
      const filename = path.basename(file, ".svg");
      collection[name].icons[filename] = {
        body: svg.replace(/<svg[^>]*>/, "").replace(/<\/svg>/, ""),
        width: 24,
        height: 24,
      };
    }
  }
  return collection;
}

const collections = getCollection("./src/icons", "custom");

module.exports = {
  content: ["./src/**/*.{ts,tsx}"],
  plugins: [iconsPlugin({ collections })],
};

Thank you! It looks pretty much awesome, I also made a something close to that

function FileSystemIconLoader(dir, transform) {
    const files = readdirSync(dir).filter(f => f.endsWith('.svg'));
    const result = new Map()

    files.forEach(file => {
        const filename = join(dir, file);
        const stat = lstatSync(filename);
        if (stat.isFile()) {
            let svg = new SVG(readFileSync(filename, "utf-8"));
            // Clean up and optimise icons
            try {
                cleanupSVG(svg);
                parseColors(svg, {
                    defaultColor: 'currentColor',
                    callback: (attr, colorStr, color) => {
                        return !color || isEmptyColor(color) ? colorStr : 'currentColor';
                    },
                });
                runSVGO(svg);
            } catch (err) {
                // Invalid icon
                console.error(`Error parsing ${name}:`, err);
                return;
            }

            result.set(camelToKebab(file.replace('.svg', '')), {
                body: svg.getBody(),
                width: svg.viewBox.width,
                height: svg.viewBox.height,
                top: svg.viewBox.top,
                left: svg.viewBox.left,
            })
        }
    })

    return {icons: Object.fromEntries(result)}
}

and then use it like :

 iconsPlugin({
            // Select the icon collections you want to use
            // You can also ignore this option to automatically discover all icon collections you have installed
            collections: {
                regular: FileSystemIconLoader(path.resolve(__dirname, './icons/regular')),
                bold: FileSystemIconLoader(path.resolve(__dirname, './icons/bold')),
                misc: FileSystemIconLoader(path.resolve(__dirname, './icons/misc')),
                duotone: FileSystemIconLoader(path.resolve(__dirname, './icons/duotone')),
                typography: FileSystemIconLoader(path.resolve(__dirname, './icons/typography')),
                ...getIconCollections(['solar'])
            },
        }),
SadWood commented 3 months ago

Here's one way I've implemented it, just for your reference.

First, you need to install @iconify/tools and @iconify/utils.

npm install @iconify/tools @iconify/utils --save

import { cleanupSVG, importDirectorySync, isEmptyColor, parseColors, runSVGO } from '@iconify/tools'
import { compareColors, stringToColor } from '@iconify/utils/lib/colors'

function getCollections(dir) {
  // Import icons
  const iconSet = importDirectorySync(dir, {
    includeSubDirs: false,
  })

  // Validate, clean up, fix palette and optimise
  iconSet.forEachSync((name, type) => {
    if (type !== 'icon')
      return

    const svg = iconSet.toSVG(name)
    if (!svg) {
      // Invalid icon
      iconSet.remove(name)
      return
    }

    // Clean up and optimise icons
    try {
      // Clean up icon code
      cleanupSVG(svg)

      // Change color to `currentColor`
      // Skip this step if icon has hardcoded palette
      const blackColor = stringToColor('black')
      const whiteColor = stringToColor('white')
      parseColors(svg, {
        defaultColor: 'currentColor',
        callback: (attr, colorStr, color) => {
          if (!color) {
            // Color cannot be parsed!
            throw new Error(`Invalid color: "${colorStr}" in attribute ${attr}`)
          }

          if (isEmptyColor(color)) {
            // Color is empty: 'none' or 'transparent'. Return as is
            return color
          }

          // Change black to 'currentColor'
          if (compareColors(color, blackColor))
            return 'currentColor'

          // Remove shapes with white color
          if (compareColors(color, whiteColor))
            return 'remove'

          // Icon is not monotone
          return color
        },
      })

      // Optimise
      runSVGO(svg)
    }
    catch (err) {
      // Invalid icon
      console.error(`Error parsing ${name}:`, err)
      iconSet.remove(name)
      return
    }

    // Update icon
    iconSet.fromSVG(name, svg)
  })

  // Export
  return iconSet.export()
}

Reference Source