cascornelissen / svg-spritemap-webpack-plugin

SVG spritemap plugin for webpack
MIT License
207 stars 49 forks source link

Question, how generate json file with all file names #181

Closed borm closed 2 years ago

borm commented 2 years ago

Input

- src
-   icons
    - user.svg
    - log-in.svg
new SVGSpriteMapPlugin(
  [
    path.join(__dirname, 'src/icons/*.svg'),
  ],

Expected result

icons.json
["user", "log-in"]
import icons from 'icons.json';

declare module 'icon' {
  type Icon typeof icons;
}
cascornelissen commented 2 years ago

Not really possible at the moment and it doesn't really sound like something that I think should be supported either but if there's some more context I'm open to discuss on that.

I'd look into a separate plugin (maybe event-hooks-webpack-plugin fits the bill) that allows you to execute some custom logic, parse the SVG and generate the JSON file. But I'm afraid the bigger problem will be that you want all of that in the same compilation step; so it needs to generate the SVG, parse it, write a JSON file while at the same time your TS (which needs this JSON file) is already being compiled in parallel.

Another option would be to hijack the sprite.prefix option, specify a function which will receive the path and generate some JSON structure from there, but I think you'll still run into the same compilation step problem described before.

To work around the compilation step/parallelism issue you can also create the JSON based on the files in your src/icons/ directory (thinking outside of the box here as you don't need to use this plugin to generate the JSON), if you do this before compilation starts the file should always be available and the TS compiler should be happy. Maybe webpack-manifest-plugin can help you in some way as it generates a JSON manifest already.

Looking at the expected result, most projects I've seen use something like this to type their SVG files, does that not solve your problem?

declare module '*.svg' {
    const content: string;
    export default content;
}

I'm closing this ticket for now, as I said I don't think this should be a responsibility of this plugin but feel free to comment/discuss further!

borm commented 2 years ago

@cascornelissen okay, seems i understood the problem what u described, my temporary solution now, dont know how to do that better, but looks it works fine

import { Compiler } from 'webpack';
const path = require('path');
const fs = require('fs');

export default class IconListPlugin {
  apply(compiler: Compiler) {
    const pluginName = IconListPlugin.name;

    compiler.hooks.done.tap(pluginName, () => {
      fs.readdir(
        path.resolve(__dirname, 'src/assets/icons'),
        (err: NodeJS.ErrnoException, icons: Array<string>) => {
          if (err) {
            throw err;
          }
          fs.writeFile(
            path.resolve(__dirname, 'src/assets/icons.json'),
            JSON.stringify(
              icons.reduce(
                (acc, name) => ({
                  ...acc,
                  [name.replace(/\.[^/.]+$/, '')]: name,
                }),
                {}
              ),
              null,
              2
            ),
            () => {
              //
            }
          );
        }
      );
    });
  }
}

Icon.ts

import icons from 'assets/icons.json';

export interface IconProps {
  name: keyof typeof icons;
}
cascornelissen commented 2 years ago

Thanks for reporting back with a solution! That definitely works and is similar to what I was suggesting with event-hooks-webpack-plugin as that could be set up in such a way:

new EventHooksPlugin({
    eventName: () => {
        fs.readdir(...) // Your logic
    }
})