Skip to content

Instantly share code, notes, and snippets.

@Excalibaard
Created May 6, 2022 08:33
Show Gist options
  • Save Excalibaard/5ec706565788ed9c118e08794b28149c to your computer and use it in GitHub Desktop.
Save Excalibaard/5ec706565788ed9c118e08794b28149c to your computer and use it in GitHub Desktop.
SVGO + Vue inline SVG loader
const { optimize, loadConfig } = require('svgo');
const { getOptions } = require('loader-utils');
async function loader(svg) {
const { configFile, ...options } = getOptions(this) || {};
let config;
if (typeof configFile === 'string') {
config = await loadConfig(configFile, this.context);
} else if (configFile !== false) {
config = await loadConfig(null, this.context);
}
const result = optimize(svg, {
path: this.resourcePath,
...config,
...options,
});
if (result.error) throw Error(result.error);
return `<template>${result.data}</template>`;
}
module.exports = function(svg) {
const callback = this.async();
loader
.call(this, svg)
.then(result => callback(null, result))
.catch(error => callback(error));
};
@Excalibaard
Copy link
Author

Excalibaard commented Jul 21, 2022

@dschrul Not yet, I used this in enterprise code for a company I don't work at anymore. I'll see when I have the time to make a sample project. but won't make any promises.

I made this gist before I realized it could be a lot simpler though. This gist replaces the entire vue-svg-loader functionality, which is two steps:

  1. Optimizing your SVG with SVGO
  2. Wrapping it in <template> tags (so vue-loader in the next step will interpret the input as a Vue component)

But, Webpack loaders are made to be chained. Instead of this gist, I suggest you use two loaders, one for each process.
The first step can use svgo-loader which is actively maintained by the svgo team. The second step is one line of code (see line 22 of this gist), which is too small to have as a dependency IMO (remember leftpad). Instead, you can add your own loader. I described the process of adding your own loader in this post below.

damianstasik/vue-svg-loader#156 (comment)

Here's an example of what it could look like in your project (modified from the comment above). It avoids resolveLoader, though I only recommend this if you're not expecting to use more custom loaders.

//  /webpack.config.js
const path = require('path');

module.exports = {
  // ...,
  module: {
    rules: [
      {
        test: /\.svg$/,
        resourceQuery: /inline/,  // forces explicitly importing SVG as 'inline', so normal behavior is an image file as expected.
        use: [
          {
            loader: 'vue-loader'
          },
          { 
            loader: path.resolve('utils/vue-template-wrap-loader.js') 
          },
          {
            loader: 'svgo-loader',
            options: {
              // configFile: false     // uncomment if not using a svgo.config.js file in your project root
              .... // add svgo options for inlined SVG here
            }
          }
        ]
      },
      {
        test: /\.svg$/,
        use: [
          {
            loader: 'file-loader'
          },
          {
            loader: 'svgo-loader',
            options: {
               .... // maybe some different svgo options
            }
          },
        ]
      }
    ]
  }
}



//  /utils/vue-template-wrap-loader.js

module.exports = function vueTemplateWrapLoader(svg) {
  return `<template>${svg}</template>`;
};

Then import SVGs like:

import SVGIcon1 from 'Icon.svg?inline'  // SVG as Vue component
import SVGIcon2 from 'Icon.svg'         // SVG as image

@dschrul
Copy link

dschrul commented Aug 3, 2022

thanks a lot @Excalibaard

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment