Skip to content

Commit

Permalink
[Add] stripdeclarations option
Browse files Browse the repository at this point in the history
Add the option `stripdeclarions` to the loader.
If given will tell the loader to strip out any XML declaration, e.g.

    <?xml version="1.0" encoding="UTF-8"?>

at the beginning of imported SVGs.

Added because Internet Explorer (tested in Edge 14) cannot handle XML
declarations in CSS data URLs (`content: url("data:image/svg...")`).
  • Loading branch information
malthejorgensen committed Jun 21, 2017
1 parent b392b01 commit 5b68171
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 10 deletions.
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

A webpack loader which loads SVG file as utf-8 encoded DataUrl string.

Existing [`url-loader`](https://github.com/webpack-contrib/url-loader) always does Base64 encoding for data-uri. As SVG content is a human-readable xml string, using base64 encoding is not mandatory. Instead, one may only escape [unsafe characters](http://www.ietf.org/rfc/rfc1738.txt) and replace `"` with `'` as described [in this article](http://codepen.io/Tigt/post/optimizing-svgs-in-data-uris).
Existing [`url-loader`](https://github.com/webpack-contrib/url-loader) always does Base64 encoding for data-uri. As SVG content is a human-readable xml string, using base64 encoding is not mandatory. Instead, one may only escape [unsafe characters](http://www.ietf.org/rfc/rfc1738.txt) and replace `"` with `'` as described [in this article](http://codepen.io/Tigt/post/optimizing-svgs-in-data-uris).

There are some benefits for choosing utf-8 encoding over base64.
1. Resulting string is shorter (can be ~2 times shorter for 2K-sized icons);
2. Resulting string will be compressed better when using gzip compression;
There are some benefits for choosing utf-8 encoding over base64.
1. Resulting string is shorter (can be ~2 times shorter for 2K-sized icons);
2. Resulting string will be compressed better when using gzip compression;
3. Browser parses utf-8 encoded string faster than its base64 equivalent.

## Supported parameters
Expand All @@ -23,8 +23,8 @@ Passing this parameter (or setting to `true`) tells to loader *not to include* r

### `limit`

If given will tell the loader not to encode the source file if its content is greater than this limit.
Defaults to no limit.
If given will tell the loader not to encode the source file if its content is greater than this limit.
Defaults to no limit.
If the file is greater than the limit the [`file-loader`](https://github.com/webpack-contrib/file-loader) is used and all query parameters are passed to it.

``` javascript
Expand All @@ -36,6 +36,15 @@ require('svg-url-loader?prefix=img/!./file.svg');
// They are passed to the file-loader if used.
```

### `stripdeclarations`

If given will tell the loader to strip out any XML declaration, e.g. `<?xml version="1.0" encoding="UTF-8"?>` at the beginning of imported SVGs.
Internet Explorer (tested in Edge 14) cannot handle XML declarations in CSS data URLs (`content: url("data:image/svg...")`).

``` javascript
require('svg-url-loader?stripdeclarations!./file.svg');
```

## Usage

[Documentation: Loaders](https://webpack.js.org/concepts/loaders/)
Expand Down
11 changes: 8 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ module.exports = function(content) {
this.cacheable && this.cacheable();

var query = loaderUtils.getOptions(this);

var limit = query.limit ? parseInt(query.limit, 10) : 0;
if(limit <= 0 || content.length < limit) {

if (limit <= 0 || content.length < limit) {
content = content.toString('utf8');

if (query.stripdeclarations) {
content = content.replace(/^\s*<\?xml [^>]*>\s*/i, "");
}

content = content.replace(/"/g, "'");
content = content.replace(/\s+/g, " ");
content = content.replace(/[{}\|\\\^~\[\]`"<>#%]/g, function(match) {
Expand Down
2 changes: 2 additions & 0 deletions test/input/icon-with-declaration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var icon = require('./images/icon-with-declaration.svg');
module.exports = icon;
7 changes: 7 additions & 0 deletions test/input/images/icon-with-declaration.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 60 additions & 1 deletion test/loader.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var webpack = require('webpack');

describe('svg-url-loader', function() {
'use strict';

this.timeout(10000);

var outputDir = path.resolve(__dirname, './output'),
Expand Down Expand Up @@ -85,6 +85,65 @@ describe('svg-url-loader', function() {
});


describe('"stripdeclarations" option', function () {
it('if turned off - should do nothing to an SVG that has an XML declaration', function(done) {
var config = assign({}, globalConfig, {
entry: './test/input/icon-with-declaration.js'
});
config.module.rules[0].use[0].options.stripdeclarations = false;

webpack(config, function(err) {
expect(err).to.be(null);
fs.readFile(getBundleFile(), function(err, data) {
expect(err).to.be(null);
var encoded = (0,eval)(data.toString());
expect(encoded.indexOf("%3C?xml version='1.0' encoding='UTF-8'?%3E")).to.be.greaterThan(-1);
return done();
});
});
});

it('if turned on - should do nothing to an SVG that doesn\'t have an XML declaration', function(done) {
var config = assign({}, globalConfig, {
entry: './test/input/icon.js'
});
config.module.rules[0].use[0].options.stripdeclarations = true;

webpack(config, function(err) {
expect(err).to.be(null);
fs.readFile(getBundleFile(), function(err, data) {
expect(err).to.be(null);
var encoded = (0,eval)(data.toString());
expect(encoded.indexOf('"')).to.be(0);
expect(encoded.lastIndexOf('"')).to.be(encoded.length - 1);
expect(encoded.indexOf('data:image/svg+xml,%3Csvg')).to.be(1);
return done();
});
});
});


it('if turned on - should remove XML declaration from a file that has one', function(done) {
var config = assign({}, globalConfig, {
entry: './test/input/icon-with-declaration.js'
});
config.module.rules[0].use[0].options.stripdeclarations = true;

webpack(config, function(err) {
expect(err).to.be(null);
fs.readFile(getBundleFile(), function(err, data) {
expect(err).to.be(null);
var encoded = (0,eval)(data.toString());
expect(encoded.indexOf('%3C?xml version="1.0" encoding="UTF-8"?%3E')).to.be(-1);
expect(encoded.indexOf('data:image/svg+xml,%3Csvg')).to.be(1);
return done();
});
});
});
});



describe('"limit" option and "url.dataUrlLimit" configuration', function () {
it('should fall back to file-loader if the content of SVG file is longer than "limit" query parameter', function(done) {
var config = assign({}, globalConfig, {
Expand Down

0 comments on commit 5b68171

Please sign in to comment.