diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8c11fc7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 19b6eeb..1e3d4ba 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,39 @@ -**NOTICE [2016-06-26]: I'm not using or developing this lib anymore. Therefore I'm closing issues (which I cannot handle anymore), but you can send PR to improve or fix problems you facing with this lib.** - -# SVG Inline Loader for Webpack - -This Webpack loader inlines SVG as module. If you use Adobe suite or Sketch to export SVGs, you will get auto-generated, unneeded crusts. This loader removes it for you, too. +[![npm][npm]][npm-url] +[![deps][deps]][deps-url] +[![test][test]][test-url] +[![coverage][cover]][cover-url] +[![chat][chat]][chat-url] + +
+ + + + +

SVG Inline Loader for Webpack

+

This Webpack loader inlines SVG as module. If you use Adobe suite or Sketch to export SVGs, you will get auto-generated, unneeded crusts. This loader removes it for you, too.

+

+ +

Install

+ +```bash +npm install svg-inline-loader --save-dev +``` -## Config +

Configuration

Simply add configuration object to `module.loaders` like this. ```javascript { test: /\.svg$/, - loader: 'svg-inline' + loader: 'svg-inline-loader' } ``` -warning: You should configure this loader only once via `module.loaders` or `require('!...')`. See [#15](https://github.com/sairion/svg-inline-loader/issues/15) for detail. +warning: You should configure this loader only once via `module.loaders` or `require('!...')`. See [#15](https://github.com/webpack-contrib/svg-inline-loader/issues/15) for detail. -### query options +

Query Options

#### `removeTags: boolean` @@ -31,6 +47,12 @@ warning: this won't work unless you specify `removeTags: true` default: `removingTags: ['title', 'desc', 'defs', 'style']` +#### `warnTags: [...string]` + +warns about tags, ex: ['desc', 'defs', 'style'] + +default: `warnTags: []` + #### `removeSVGTagAttrs: boolean` Removes `width` and `height` attributes from ``. @@ -43,6 +65,11 @@ Removes attributes from inside the ``. default: `removingTagAttrs: []` +#### `warnTagAttrs: [...string]` + +Warns to console about attributes from inside the ``. + +default: `warnTagAttrs: []` #### `classPrefix: boolean || string` Adds a prefix to class names to avoid collision across svg files. @@ -55,17 +82,24 @@ Adds a prefix to ids to avoid collision across svg files. default: `idPrefix: false` +#### `raw: boolean` + +Returns the content of the cleaned up SVG as a string instead of a module. Useful if you plan on passing +the output on to file-loader, etc. + +default: `raw: false` + +

Example Usage

-##### Example Usage ```js // Using default hashed prefix (__[hash:base64:7]__) -var logoTwo = require('svg-inline?classPrefix!./logo_two.svg'); +var logoTwo = require('svg-inline-loader?classPrefix!./logo_two.svg'); // Using custom string -var logoOne = require('svg-inline?classPrefix=my-prefix-!./logo_one.svg'); +var logoOne = require('svg-inline-loader?classPrefix=my-prefix-!./logo_one.svg'); // Using custom string and hash -var logoThree = require('svg-inline?classPrefix=__prefix-[sha512:hash:hex:5]__!./logo_three.svg'); +var logoThree = require('svg-inline-loader?classPrefix=__prefix-[sha512:hash:hex:5]__!./logo_three.svg'); ``` See [loader-utils](https://github.com/webpack/loader-utils#interpolatename) for hash options. @@ -73,12 +107,54 @@ Preferred usage is via a `module.loaders`: ```js { test: /\.svg$/, - loader: 'svg-inline?classPrefix' + loader: 'svg-inline-loader?classPrefix' } ``` -## Notes - -- `` React Component is **DEPRECATED**, use `svg-inline-react` package instead. -- Known problems: - - currently inlining SVG in css is unable. See #22 +

Maintainers

+ + + + + + + + + + +
+ +
+ Juho Vepsäläinen +
+ +
+ Joshua Wiens +
+ +
+ Kees Kluskens +
+ +
+ Sean Larkin +
+ +[npm]: https://img.shields.io/npm/v/svg-inline-loader.svg +[npm-url]: https://npmjs.com/package/svg-inline-loader + +[deps]: https://david-dm.org/webpack-contrib/svg-inline-loader.svg +[deps-url]: https://david-dm.org/webpack-contrib/svg-inline-loader + +[chat]: https://img.shields.io/badge/gitter-webpack%2Fwebpack-brightgreen.svg +[chat-url]: https://gitter.im/webpack/webpack + +[test]: https://travis-ci.org/webpack-contrib/svg-inline-loader.svg?branch=master +[test-url]: https://travis-ci.org/webpack-contrib/svg-inline-loader + +[cover]: https://codecov.io/gh/webpack-contrib/svg-inline-loader/branch/master/graph/badge.svg +[cover-url]: https://codecov.io/gh/webpack-contrib/svg-inline-loader diff --git a/config.js b/config.js index 9c65dcf..5dde54f 100644 --- a/config.js +++ b/config.js @@ -9,4 +9,7 @@ module.exports = { ], removingTagAttrs: [], classPrefix: false, + idPrefix: false, + warnTags: [], + warnTagAttrs: [] }; diff --git a/index.js b/index.js index 419ff36..1558352 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ var simpleHTMLTokenizer = require('simple-html-tokenizer'); var tokenize = simpleHTMLTokenizer.tokenize; var generate = simpleHTMLTokenizer.generate; var loaderUtils = require('loader-utils'); +var assign = require('object-assign'); var conditions = require('./lib/conditions'); var transformer = require('./lib/transformer'); @@ -16,19 +17,24 @@ var regexSequences = [ // SVG XML -> HTML5 [/\<([A-Za-z]+)([^\>]*)\/\>/g, "<$1$2>"], // convert self-closing XML SVG nodes to explicitly closed HTML5 SVG nodes [/\s+/g, " "], // replace whitespace sequences with a single space - [/\> \<"], // remove whitespace between tags + [/\> \<"] // remove whitespace between tags ]; function getExtractedSVG(svgStr, query) { + var config; // interpolate hashes in classPrefix - if(!!query && !!query.classPrefix) { - const name = query.classPrefix === true ? '__[hash:base64:7]__' : query.classPrefix; - query.classPrefix = loaderUtils.interpolateName({}, name, {content: svgStr}); - } + if(!!query) { + config = assign({}, query); + + if (!!config.classPrefix) { + const name = config.classPrefix === true ? '__[hash:base64:7]__' : config.classPrefix; + config.classPrefix = loaderUtils.interpolateName({}, name, { content: svgStr }); + } - if (!!query && !!query.idPrefix) { - const id_name = query.idPrefix === true ? '__[hash:base64:7]__' : query.idPrefix; - query.idPrefix = loaderUtils.interpolateName({}, id_name, {content: svgStr}); + if (!!config.idPrefix) { + const id_name = config.idPrefix === true ? '__[hash:base64:7]__' : config.idPrefix; + config.idPrefix = loaderUtils.interpolateName({}, id_name, { content: svgStr }); + } } // Clean-up XML crusts like comments and doctype, etc. @@ -47,7 +53,7 @@ function getExtractedSVG(svgStr, query) { } // If the token is start-tag, then remove width and height attributes. - return generate(transformer.runTransform(tokens, query)); + return generate(transformer.runTransform(tokens, config)); } function SVGInlineLoader(content) { @@ -56,7 +62,13 @@ function SVGInlineLoader(content) { // Configuration var query = loaderUtils.parseQuery(this.query); - return "module.exports = " + JSON.stringify(getExtractedSVG(content, query)); + var extractedSVG = getExtractedSVG(content, query); + + if (query.raw) { + return extractedSVG; + } else { + return "module.exports = " + JSON.stringify(extractedSVG); + } } SVGInlineLoader.getExtractedSVG = getExtractedSVG; diff --git a/karma.conf.js b/karma.conf.js index 352f746..e830121 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -6,8 +6,8 @@ var webpackConf = { loaders: [ { test: /\.json$/, - loaders: ['json'], - }, + loaders: ['json'] + } ] }, entry: [ @@ -15,7 +15,7 @@ var webpackConf = { ], resolve: { extensions: ["", ".js", ".jsx"], - }, + } }; module.exports = function(config) { @@ -23,11 +23,11 @@ module.exports = function(config) { basePath: '.', frameworks: ['mocha'], files: [ - './tests/**/*.js', + './tests/**/*.js' ], exclude: [], preprocessors: { - './tests/**/*.js': ['webpack'], + './tests/**/*.js': ['webpack'] }, reporters: ['spec'], port: 9876, @@ -39,7 +39,7 @@ module.exports = function(config) { 'karma-spec-reporter', 'karma-mocha', 'karma-chrome-launcher', - 'karma-webpack', + 'karma-webpack' ], webpack: webpackConf, autoWatch: true, diff --git a/lib/conditions.js b/lib/conditions.js index 8f049fa..9157319 100644 --- a/lib/conditions.js +++ b/lib/conditions.js @@ -26,11 +26,18 @@ function createHasNoAttributes(attributes) { } } +function createHasAttributes(attributes) { + return function hasAttributes(attributeToken) { + return attributes.indexOf(attributeToken[0]) > -1; + } +} + module.exports = { isSVGToken: isSVGToken, isStyleToken: isStyleToken, isFilledObject: isFilledObject, hasNoWidthHeight: hasNoWidthHeight, createHasNoAttributes: createHasNoAttributes, + createHasAttributes: createHasAttributes, isStartTag: isStartTag }; diff --git a/lib/transformer.js b/lib/transformer.js index 989df92..cef721d 100644 --- a/lib/transformer.js +++ b/lib/transformer.js @@ -20,11 +20,30 @@ function createRemoveTagAttrs(removingTagAttrs) { return tag; }; } - +function createWarnTagAttrs(warnTagAttrs) { + warnTagAttrs = warnTagAttrs || []; + var hasNoAttributes = conditions.createHasAttributes(warnTagAttrs); + return function warnTagAttrs(tag) { + if (conditions.isStartTag(tag)) { + var attrs=tag.attributes.filter(hasNoAttributes); + if(attrs.length > 0) { + var attrList=[]; + for(var i=0;i -1; } - +function isWarningTag(warningTags, tag) { + return warningTags.indexOf(tag.tagName) > -1; +} // FIXME: Due to limtation of parser, we need to implement our // very own little state machine to express tree structure @@ -46,6 +65,16 @@ function createRemoveTags(removingTags) { }; } +function createWarnTags(warningTags) { + warningTags = warningTags || []; + + return function warnTags(tag) { + if (conditions.isStartTag(tag) && isWarningTag(warningTags, tag)) { + console.warn('svg-inline-loader: forbidden tag ' + tag.tagName); + } + return tag; + }; +} function getAttributeIndex (tag, attr) { if( tag.attributes !== undefined && tag.attributes.length > 0 ) { for(var i = 0; i < tag.attributes.length; i++) { @@ -58,7 +87,8 @@ function getAttributeIndex (tag, attr) { } function createClassPrefix(classPrefix) { - var re = /\.[\w\-]+/g; + //http://stackoverflow.com/questions/12391760/regex-match-css-class-name-from-single-string-containing-multiple-classes + var re = /\.(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)(?![^\{]*\})/g; var inStyleTag = false; return function prefixClasses(tag) { @@ -83,7 +113,17 @@ function createClassPrefix(classPrefix) { else { var classIdx = getAttributeIndex(tag,'class'); if(classIdx >= 0) { - tag.attributes[classIdx][1] = classPrefix + tag.attributes[classIdx][1]; + //Prefix classes when multiple classes are present + var classes = tag.attributes[classIdx][1]; + var prefixedClassString = ""; + + classes = classes.replace(/[ ]+/,' '); + classes = classes.split(' '); + classes.forEach(function(classI){ + prefixedClassString += classPrefix + classI + ' '; + }); + + tag.attributes[classIdx][1] = prefixedClassString; } } return tag; @@ -131,7 +171,9 @@ function runTransform(tokens, configOverride) { if (config.classPrefix !== false) transformations.push(createClassPrefix(config.classPrefix)); if (config.idPrefix !== false) transformations.push(createIdPrefix(config.idPrefix)); if (config.removeSVGTagAttrs === true) transformations.push(removeSVGTagAttrs); + if (config.warnTags.length > 0) transformations.push(createWarnTags(config.warnTags)); if (config.removeTags === true) transformations.push(createRemoveTags(config.removingTags)); + if (config.warnTagAttrs.length > 0) transformations.push(createWarnTagAttrs(config.warnTagAttrs)); if (config.removingTagAttrs.length > 0) transformations.push(createRemoveTagAttrs(config.removingTagAttrs)); transformations.forEach(function (transformation) { diff --git a/package.json b/package.json index 35e23f0..4adcf7d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svg-inline-loader", - "version": "0.6.1", + "version": "0.7.1", "description": "Cleans up and inlines your SVG files into Webpack module.", "main": "index.js", "scripts": { @@ -29,6 +29,7 @@ }, "devDependencies": { "chai": "^3.0.0", + "chai-spies": "^0.7.1", "json-loader": "^0.5.4", "karma": "^1.0.0", "karma-chrome-launcher": "^1.0.1", diff --git a/tests/svg-inline-loader.test.js b/tests/svg-inline-loader.test.js index 977d17f..676440c 100644 --- a/tests/svg-inline-loader.test.js +++ b/tests/svg-inline-loader.test.js @@ -2,7 +2,12 @@ var simpleHTMLTokenizer = require('simple-html-tokenizer'); var tokenize = simpleHTMLTokenizer.tokenize; var SVGInlineLoader = require('../index'); -var assert = require('chai').assert; +var chai = require('chai'); +var assert = chai.assert; +var expect = chai.expect; +var spies = require('chai-spies'); +chai.use(spies); +var createSpy = chai.spy; var _ = require('lodash'); var svgWithRect = require('raw!./fixtures/xml-rect.svg'); @@ -117,5 +122,34 @@ describe('getExtractedSVG()', function(){ } }); }); + it('should be able to warn about tagsAttrs to be removed listed in `warnTagAttrs` option via console.log', function () { + var svg = require('raw!./fixtures/with-ids.svg'); + var tobeWarned = ['id']; + var oldConsoleWarn = console.warn; + var warnings=[]; + console.warn=createSpy(function (str) { + warnings.push(str); + }); + var processedSVG = SVGInlineLoader.getExtractedSVG(svg, { warnTagAttrs: tobeWarned }); + var reTokenizedSVG = tokenize(processedSVG); + expect(console.warn).to.have.been.called.with('svg-inline-loader: tag path has forbidden attrs: id'); + console.warn = oldConsoleWarn; // reset console back + }); + it('should be able to specify tags to be warned about by `warnTags` option', function () { + var svg = require('raw!./fixtures/removing-tags.svg'); + var tobeWarnedAbout = ['title', 'desc', 'defs', 'style', 'image']; + var oldConsoleWarn = console.warn; + var warnings=[]; + console.warn=createSpy(function (str) { + warnings.push(str); + }); + var processedStyleInsertedSVG = SVGInlineLoader.getExtractedSVG(svg, { warnTags: tobeWarnedAbout }); + var reTokenizedStyleInsertedSVG = tokenize(processedStyleInsertedSVG); + + expect(console.warn).to.have.been.called(); + expect(console.warn).to.have.been.called.min(3); + expect(console.warn).to.have.been.called.with('svg-inline-loader: forbidden tag style'); + console.warn = oldConsoleWarn; // reset console back + }); });