Compiling SVGs with the Build Plugins API


#1

I’m looking into the new Build Plugins API, and it seems like it would be perfect to take individual SVG files and compile them into a sprite in order to be able to lower the number of HTTP calls and style them with CSS.

I can see two ways to do it:

  • Take all the SVGs and compile them into a single SVG, which gets injected at the top of the document (the process described in that blog post)
  • Take each SVG file and inject its code into a new, individual template. So you’d be able to use logo.svg simply by calling {{> logo}}.

I looked at the documentation and while both seem like they should be possible, I’m not sure how to proceed exactly.

It seems like addHtml is what I need, but it’s not clear what type of arguments you need to pass to it, and so far it doesn’t seem to do anything for me. So anyway, I would love a few pointers on the best way to achieve this! (@sashko maybe?)


#2

Why not wrap this npm package? It has everything you need it seems and wouldn’t take much to get running.


#3

This looks interesting, but my question was more about the parsing/compiling files aspect, which that package doesn’t really deal with. So it’s more a complement I think?


#4

Disclaimer, I haven’t used the build plugins api just yet, but perhaps something along these lines would work? (freehand, so will probably contain errors)

Plugin.registerCompiler({
    extensions: ["svg"],
    filenames: []
}, function () {
    var compiler = new SVGCompiler();
    return compiler;
});

function SVGCompiler() {};

SVGCompiler.prototype.processFilesForTarget = function (files) {
    var outputPath = "/somepath/";

    //Spriter config
    var spriter = new SVGSpriter({
        dest: outputPath,
        mode: {
            css: {
                render: {
                    css: true
                }
            }
        }
    });

    //Iterate over files and add them to spriter
    files.forEach(function (file) {   
        var path = file.getDirname(); //Get path of file
        
        spriter.add(
            path, //Path to file
            file, //Filename
            fs.readFileSync(path+file, {encoding: 'utf-8'}) //file data
        );        
    });

    //Compile sprite to destination
    spriter.compile({
        //Optional config
    }, function(err, res, data){
        //Add the resulting data
        files.addStylesheet({
            data: res.css,
            path: outputpath+"svgSprite.css"
        });

        files.addAsset({
            data: res.sprite,
            path: outputpath+"svgSprite.svg"
        });
    });
};

Resources used to get to this:

We should figure this out and make a package for it, I’m sure a lot of people would find a use for it, I sure would.


#5

Yeah I think this is about right!


#6

Thanks! This looks like it could work, but the addStylesheet method belongs to file, not files. I’m not sure what’s the best way to access it outside of that forEach loop though, since we only want to call it once?

Edit: I guess I’ll just do files[0]?


#7

OK, here’s what I have so far:

Package.describe({
  name: "utilities:compile-svg",
  summary: "Get SVG images and make them available as templates",
  version: "0.1.0"
});

Package.registerBuildPlugin({
  name: "compileSVG",
  use: [
    'caching-html-compiler',
    'ecmascript',
    'templating-tools',
    'underscore'
  ],
  sources: ['lib/compile-svg.js'],
  npmDependencies: {
    "svg-sprite": "1.2.10"
  }
});

Package.onUse(function (api) {

  api.use('isobuild:compiler-plugin@1.0.0');

});
var SVGSpriter = Npm.require("svg-sprite");
var fs = Plugin.fs;

Plugin.registerCompiler({
    extensions: ["svg"],
    archMatching: 'web',
    filenames: []
}, function () {
    var compiler = new SVGCompiler();
    return compiler;
});

function SVGCompiler() {};

SVGCompiler.prototype.processFilesForTarget = function (files) {
  var outputPath = "/somepath/";

  var mode = {
    "css": true,
    "symbol": true
  };

  //Spriter config
  var spriter = new SVGSpriter({
      dest: outputPath,
      mode: mode
  });

  //Iterate over files and add them to spriter
  files.forEach(function (file) { 
    var fullPath = file._resourceSlot.inputResource.path;
    var fileName = fullPath.replace(/^.*[\\\/]/, '');
    var data = fs.readFileSync(fullPath, {encoding: 'utf-8'});

    spriter.add(
        fullPath, //Path to file
        fileName, //Filename
        data //file data
    );        
  });

  //Compile sprite to destination
  spriter.compile(function(err, res, data){

    //Add the resulting data
    files[0].addHtml({section: "body", data: "???"});

    files[0].addStylesheet({
      data: "???",
      path: outputPath+"svgSprite.css"
    });

    files[0].addAsset({
      data: "???",
      path: outputPath+"svgSprite.svg"
    });

  });

};

The issue is that I’m not sure what to pass to addHtml, addStylesheet, or addAsset. If I just pass the output of the spriter it throws errors, and I can’t yet figure out how to convert that output to a string to pass it properly…

Oh and also, while this works for SVGs contained in my main app directories, it doesn’t trigger for SVGs contained in packages.


#8

Here’s the repo btw: https://github.com/meteor-utilities/compile-svg


#9

OK, after a lot of trial and error I almost got it working:

https://github.com/SachaG/compile-svg/blob/master/lib/compile-svg.js

The only remaining hurdle (apart from the fact that it doesn’t parse SVGs contained in packages yet) is that the generated <symbol> tag IDs are blank for some reason…


#10

On line 30-31 is the name being output correctly?


#11

No it’s not, that’s the issue: https://github.com/jkphl/svg-sprite/issues/112


#12

Have to run the code to debug, if you don’t have a solution by tonight I’ll do my best to give it a go as well.


#13

I’d love to try and debug this, but I realized I don’t actually know how to tell a Meteor package (in this case running locally using the PACKAGE_DIRS technique) to use a local NPM package’s code instead of the one downloaded from the online repository…


#14

It works!


#15

Looks great, good job!


#16

sadly I found the thread this late. last week I worked on exactly the same build plugin and I experiensed some difficulties as well. I wrote the same callback version as @sacha, but it didnt work (race condition during the build IMHO). the next one I wrote was a future powered version (since the build process uses fibers to parallelize its tasks) - I didnt notice an inconsistency so fare. But this is not the way its supposed to be done - some thoughts about it

since @sacha s repo is offline (or in a place were I couldnt find it) maybe some of this could be helpful to someone.


Should we have a category for performance?
#17

Oh sorry, I moved the repo to: https://github.com/meteor-utilities/compile-svg