Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

jcrushsvg

braksator2.6kUnlicense1.1.40

Javascript based SVG deduplicator and compressor

SVG, compressor, deduplicator, deduplication, dedupe, compress, crush, jcrush, shrink, minify, minifier

readme

npm

JCrush SVG

Deduplicates and compresses SVG files using Javascript.

It creates Javascript files that provide the SVG code.

  • Works best on groups of SVG files that have already been compressed with a regular SVG compressor. (For the use-case this was created for it resulted in 83% compression)
  • Uses JCrush. See that project for more info (and an in-built plugin for deduplicating Javascript code).
  • You should merge, minify, and reprocess the main output file produced with this module using JCrush for further optimization.
  • If you don't want deduplication and just need something to optimize SVG/HTML DOM code (more than minifiers), see HyperCrush.

Installation

This is a Node.JS module available from the Node Package Manager (NPM).

https://www.npmjs.com/package/jcrushsvg

Here's the command to download and install from NPM:

npm install jcrushsvg -S

or with Yarn:

yarn add jcrushsvg

Setup Instructions

  1. Place all relevant SVG files in one directory:

    • Example: /src/svg
  2. Choose a processing method:

    • Decide whether to use the terminal, a custom script, or Gulp to process the directory with this module.
    • Refer to the Usage section below for further instructions.
  3. Modify your Javascript code:

    • Instead of using the SVG images in an <img> tag, call a function with the file's basename (slug).
    • The usage differs based on the bundle option:
      • If bundle is false (default):
        • The functionality is asynchronous. To load the SVG code into a DOM element, use:
          svg('main-logo', document.getElementById("logo"))
        • Note: This will replace whatever is already inside the #logo element, so plan your code accordingly.
      • If bundle is true:
        • The SVG code is stored in the main JS file. The function will return the SVG code like so:
          var svgCode = svg('main-logo')
  4. Include the main Javascript outFile in your HTML:

    • Or, use some automation to merge it into your scripts.
    • Note: There's no need to include each individual image's corresponding JS file.
  5. Merge/minify/compress the main outFile:

    • You should merge, minify, or compress the main outFile as you would with your own code.
    • Note: This module leaves that file in a human-readable state.

      Usage

From the Terminal

It may be sufficient to just run:

node node_modules/jcrushsvg --inDir="/src/svg" --outDir="/svg" --outFile="svg.js"

...once and you're done.

Write a Custom Script

Create a file svgTask.js with the contents:

import jcrushSVG from 'jcrushsvg';
let opts = { inDir: '/src/svg', outDir: '/svg', outFile: 'svg.js', processSVG:(filePath, svgContent) => {
  // Check there is no version or it's v1.1... that's the cool one.
  if (svgContent.includes('version=') && !svgContent.includes('version="1.1"')) {
    throw new Error(`SVG in ${filePath} does not have version="1.1". Ensure you set "SVG Profile: SVG 1.1" in save-as dialogue of Adobe Illustrator.`);
  }
  // Check for style tags, not on my watch.
  if (svgContent.match(/<style[^>]*>[\s\S]*?<\/style>/g)) {
    throw new Error(`SVG in ${filePath} contains <style> tags, which are not allowed. Ensure you set "CSS Properties: Presentation Attributes" in save-as dialogue of Adobe Illustrator.`);
  }
  // Check if it uses CSS for colors - get outta here with that!
  if (/style\s*=\s*["'][^"']*color\s*:/i.test(svgContent)) {
    throw new Error(`SVG in ${filePath} uses CSS for colors, which is not allowed. Ensure you set "CSS Properties: Presentation Attributes" in save-as dialogue of Adobe Illustrator.`);
  }
  return svgContent;
}};
jcrushSVG(opts);

Then run this command:

node svgTask

...whenever you've added a new SVG file to the directory.

With Gulp

In your gulpfile.mjs:

Step 1: Import JCrush SVG

import jcrushSVG from 'jcrushsvg';

Step 2: Create a Gulp Task

gulp.task('svg', (done) => {
  let opts = { inDir: '/src/svg', outDir: '/svg', outFile: 'svg.js', checkNew: 1 };
  jcrushSVG(opts);
  done(); // Signal completion
});

Step 3: (Optional) Run JCrush SVG Before Minification

To run JCrush SVG before your minification tasks, add JCrush SVG in series before other tasks, such as in this example:

gulp.task('default', gulp.series(
  'svg' // Run it before your other stuff.  Probably.
  gulp.parallel('minify-css', 'minify-js', 'minify-html'),
));

Alternatively don't include it in your default gulp task. Run the task manually, e.g. gulp svg.


Parameters

opts (Object, optional)

A configuration object with the following properties:

  • inDir (String, default: ''):

    • The folder where the SVG files are. You MUST put this in.
  • outDir (String, default: ''):

    • The folder where the JS files will be outputted. Will assume same as inDir if not supplied, but that isn't ideal.
  • outFile (String, default: 'svg.js'):

    • The name of the main JS file to create that will have the function to provide SVG code.
  • funcName (String, default: svg):

    • The name of the function to create that will provide SVG code.
  • bundle (Boolean, default: false):

    • If true, will load all the SVG code into the main outFile instead of separate files. The SVGs are preloaded and the SVG function will return SVG code as a string.
    • If false, will load SVGs from individual JS files on an as-needed basis, reducing loading overhead. The SVG function accepts a DOM element and will replace its contents with the SVG once it is loaded.
  • param (Boolean, default false):

    • If 'true' will paramaterize the replacement variables for silghtly shorter output.
    • If 'false' will included the replacement variables in the function body.
  • let (Boolean, default true):

    • If 'true' will prepend the main function with a let keyword.
    • If 'false' will omit let keyword.
  • appendExt (Boolean, default: true):

    • If true, will create the individual JS files with ".svg.js" extensions.
    • If false, will create the individual JS files with just ".js" extensions.
  • maxLen (Number, default: 40):

    • The maximum length of substrings to consider. Setting this higher will slow things down.
  • resVars (Array, default: ['el', 'k']): Supply an array of variable names that JCrush must NOT use for some reason. This module will reserve 'el' and 'k', so don't use those for anything else.

  • processSVG (Function, default: null): A function to run custom validation/preprocessing on each SVG tag. 2 params: filePath, svgContent. Throw error to halt processing. Return the changed SVG code.

  • processJS (Function, default: null): A function to run custom processing on JS output. 2 params: filePath, jsContent. Throw error to halt processing. Return the changed JS code.

  • checkNew (Boolean, default: false):

    • If true, will not run if the outFile exists and is newer than all of the files in inDir.
    • If false, will re-run everytime.
  • prog (Boolean, default: true):

    • If true, will output console messages when making progress.
    • If false, will work silently.
  • fin (Boolean, default: true):

    • If true, will output a final console message about bytes saved or failure.
    • If false, will remain silent.

Additionally; JCrush SVG can accept the options of the underlying JCrush package, however if they're not listed above then changing them may break this module's functionality. Tread carefully.


Dynamic Colors

This is an example of a use-case of this module where a number of SVGs were designed to be dynamically recolored with Javascript.

The goal is to be able to control the SVG's colors and opacity via Javascript.

  • The SVGs were all made with 2 colors, the main color being #64bc41 (moderate green)
  • A secondary color was made dark-grey (evidently using black is not compatible with this!)
  • Some shapes were made with 20% opacity, but that would need to be fine tuned based on the dynamic color too.

Approach:

  • Use the processSVG callback to add 3 placeholders to: #64bc41, other colors, and opacity values. The placeholder selected was an appropriate emoji.
  • Use the processJS callback to rectify the placeholders to suitable template literals, and modify the function to accept corresponding parameters.
  • Use the resVars feature to reserve the custom parameters.

The custom SVG processing script:

const jcrushSVG = require('jcrushsvg');

jcrushSVG({ inDir: './src/img/items', outFile: './src/svgItems.js',
  bundle: 1, maxLen: 120, funcName: 'svgItems', resVars: ['bg', 'fg', 'op'],
  processSVG: (filePath, svgContent) => {
    // Replaces color codes with placeholders
    return svgContent.replace(new RegExp('#64bc41', 'gi'), '🟩') // Background
      .replace(/#[a-fA-F0-9]{6}|#[a-fA-F0-9]{3}/gi, '⬛') // Foreground
      .replace(/opacity="([^"]+)"/gi, 'opacity="🔲"'); // Opacity
  },
  processJS:(filePath, jsContent) => {
    return jsContent.replace('k =>', '(k, bg, fg, op) =>')
      .replace(/🟩/g, '${bg}')
      .replace(/⬛/g, '${fg}')
      .replace(/🔲/g, '${op}');
  },
});

Now our app can specify the colors it needs:

// Calls svgItems() to get the SVG code based on item's attributes.
let itemImage = item => svgItems(item.key, item.bgColor, item.fgColor, item.opacity);

Contributing

https://github.com/braksator/jcrushsvg

In lieu of a formal style guide, take care to maintain the existing coding style.