Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load svgs from filesystem #37

Closed
XEngine opened this issue Jan 24, 2024 · 3 comments
Closed

Load svgs from filesystem #37

XEngine opened this issue Jan 24, 2024 · 3 comments

Comments

@XEngine
Copy link

XEngine commented Jan 24, 2024

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

@hyoban
Copy link
Collaborator

hyoban commented Jan 26, 2024

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
Copy link
Author

XEngine commented Jan 26, 2024

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
Copy link

SadWood commented Mar 29, 2024

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

@XEngine XEngine closed this as completed Jun 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants