theGC/html-webpack-inline-svg-plugin

Does not work with webpack-dev-server

jefvlamings opened this issue · 9 comments

This plugin assumes that images are written to disk. When running webpack-dev-server the assets are not copied to the destination folder and therefor will not be able to be read.

The fs.readFile throws an error because it's not finding the image in the destination folder:

{ Error: ENOENT: no such file or directory, open '.../dist/img/menu-1d840d305cccb544781eccb64422f684.svg'
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: '.../dist/img/menu-1d840d305cccb544781eccb64422f684.svg' }

Please note that the plugin is working fine when just building with webpack.

tr1s commented

+1

I'm having the same issue @jefvlamings. It's looking in dist/ which is the problem.

Any ways to fix this? How do we make it look in src when on webpack-dev-server and dist/ when built?

Setting runPreEmit to true fixes it for me (refer to this)

plugins: [
    new HtmlWebpackInlineSVGPlugin({
        runPreEmit: true,
    })
]

Then set your src path relative to your package.json file. e.g,

my-project
-- package.json
-- webpack-config.js
-- <node_modules>
-- <src>
---- index.html
---- <images>
------ icons.svg
------ foo.svg

so your path will be <img inline src="src/images/icons.svg">

@antesgeist It is not working even with runPreEmit: true.
I have the same project structure and I get this error:

Module not found: Error: Can't resolve './src/svg-drawings/GROUND-FLOOR.svg' in '/mnt/c/Users/JuanRamón/Desktop/svg-interactive-drawings/src':
Error: Can't resolve './src/svg-drawings/GROUND-FLOOR.svg' in '/mnt/c/Users/JuanRamón/Desktop/svg-interactive-drawings/src'

The only thing it is doing is replacing <img inline src="src/svg-drawings/GROUND-FLOOR.svg"> with <img inline="" src="GROUND-FLOOR.svg">

Maybe I have some incompatible webpack.config.js settings. Which were yours?

Does someone manage to fix this?

When I build the project it works fine, but in the Dev Server it's just a regular 'img' tag.

@juanramoncarceles Didn't see the notifications for this man, but yeah, it is still working on my previous project.

It might indeed be a Webpack config issue based on your as I see the plugin isn't picking up the correct project path for your SVG.

See below for further instructions if you're still trying to get this working.

--

@victorcrbt

Here's my relative project repo with this SVG plugin setup. https://github.com/antesgeist/todd. Tested it just now and it's still working.

Dev server: npm start

Check over my comments on my webpack config and see how the rest of the config/directories are setup.

I've run into similar pathing issues before and tested different config types, found this one below that works for my setup.

webpack.config.js [loaders/svg]

    /**
    * SVG Loader
    * 
    * [path] = relative to template url: 
    * e.g, if template is in src/public/index.html then [path] = src/public/
    * [path] is prefixed to "name" to allow html template
    * pre-parsing with inline-svg-plugin
    * 
    * emitFile: false since we don't need to write/emit assets on the
    * final build, we are handling that with inline-svg-plugin instead
    **/

    {
        test: /\.svg$/,
        use: [
            
            {
                loader: 'file-loader',
                options: {
                    name: '[path][name].[ext]',
                    emitFile: false
                }
            },
        ]
    },

webpack.config.js [plugins]

    plugins: [
        new CleanWebpackPlugin('dist', {}),
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'src/public/index.html',
            svgoConfig: {
                removeTitle: false,
                removeViewBox: true,
            }
        }),
        new HtmlWebpackInlineSVGPlugin({
            /** 
             * runPreEmit: true so it'll run "before" the html
             * template is parsed by html-webpack-plugin
             */
            runPreEmit: true,
        }),
        new MiniCssExtractPlugin({
            filename: 'static/styles/[name].[contenthash].css',
            chunkFilename: '[id].css'
        }),
    ]

On my setup, I am pulling SVG assets from node_modules packages but you can also use directories relative to your html template as noted on the snippet above. Here's how you can do so:

--

From node_modules folder.: The tilde "~" prefix signifies that you are looking in the node_modules directory, I think this is default webpack behaviour.

    <img inline src="~jam-icons/svg/airbnb.svg">

--

From template relative directories. e.g,

HTML template: src/public/index.html
Relative SVG folder: src/public/svg/airbnb.svg

    <img inline src="svg/airbnb.svg">

I'm facing the same issue. I've managed to fix it with runPreEmit: true, but I don't like the idea of specifying the path from the project's root directory, such paths look ugly and unintuitive.

@slavafomin

I agree it does look verbose if you have to pass the full path, but as mentioned on my previous reply, you can use this plugin without specifying the project root path by letting Webpack handle the path resolution.

Simply configure your SVG loader like this:

{
        test: /\.svg$/,
        use: [
            
            {
                loader: 'file-loader', 
                options: {
                    /* here, you are telling webpack to load the assets automatically
                       "relative" to your index.html template, not the project root.
                       That is what the [path] alias is doing */

                    name: '[path][name].[ext]',

                    /* this one is not to be confused with the runPreEmit plugin option,
                       this is just so that webpack won't emit (copy over) the svg file
                       into the dist folder when you build your project */

                    emitFile: false 
                }
            },
        ]
    },

Then you use your SVG assets like this:

template: src/public/index.html
SVG folder: src/public/svg/airbnb.svg

<img inline src="svg/airbnb.svg">
theGC commented

hey all, if anyone has a better method than the runPreEmit option then it would be great to integrate it. It was something I never needed but was asked to look into, as such it was rolled up quite quickly and probably isn't the best way of doing this but I don't know of another.

I'll close this for now, but if someone is able to push a PR with a better alternative that would be awesome.

All this confusion with webpack-dev-server and inlined SVGs comes because of the fact in the former documentation it wasn't clear what were the correct paths depending whether loaders were used or not.

Now the documentation has been refactored and it better reflects the three ways SVG files can be referenced:

  • Relative to OUTPUT (no loaders).
  • Relative to ROOT (runPreEmit config option).
  • Relative to SOURCE (with loaders).

Documentation now also has a new webpack-dev-server entry to explain how to use html-webpack-inline-svg-plugin with this dev tool.