diff --git a/.gitignore b/.gitignore index c4419b2..2a6158b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ node_modules/ +atlassian-ide-plugin.xml +.idea tets_folder/ coverage/ platform/dist/ diff --git a/package.json b/package.json index cc1ee9a..c4463b8 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "src/svgstore.js", "scripts": { "test": "NODE_ENV=platform ./node_modules/.bin/_mocha ./src/__tests__/index.js", + "run": "webpack", "code:coverage": "NODE_ENV=platform ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha ./src/__tests__/index.js && npm run code:report", "code:report": "CODECLIMATE_REPO_TOKEN=29b2c943849c33562af12b70563d86e95c073e04c7510e9da5d9711cf3233b17 ./node_modules/.bin/codeclimate-test-reporter < coverage/lcov.info", "build": "rm -rf platform/dist/* && NODE_ENV=platform webpack --progress --colors --bail" @@ -36,7 +37,7 @@ "istanbul": "0.4.5", "mocha": "3.2.0", "path": "0.12.7", - "webpack": "1.13.3" + "webpack": "2.1.0-beta.27" }, "repository": { "type": "git", diff --git a/platform/global.js b/platform/global.js index e15568b..a07c401 100644 --- a/platform/global.js +++ b/platform/global.js @@ -19,9 +19,6 @@ module.exports = function(_path) { chunkFilename: '[chunkhash].[id].js', publicPath: '/platform/' }, - resolve: { - extensions: ['', '.js'], - }, plugins: [ // create svgStore instance object new SvgStore.Options({ diff --git a/src/helpers/utils.js b/src/helpers/utils.js index 4a9c20d..2a97d5f 100644 --- a/src/helpers/utils.js +++ b/src/helpers/utils.js @@ -1,14 +1,15 @@ 'use strict'; // Depends -var _ = require('lodash'); -var fs = require('fs'); -var path = require('path'); -var util = require('util'); -var pug = require('pug'); -var Svgo = require('svgo'); -var globby = require('globby'); -var parse = require('htmlparser2'); +const _ = require('lodash'); +const fs = require('fs'); +const path = require('path'); +const util = require('util'); +const crypto = require('crypto'); +const pug = require('pug'); +const Svgo = require('svgo'); +const globby = require('globby'); +const parse = require('htmlparser2'); /** * Create sprite @@ -16,7 +17,7 @@ var parse = require('htmlparser2'); * @param {string} template * @return {string} */ -var _createSprite = function(data, template) { +const _createSprite = function(data, template) { return pug.renderFile(template, data); }; @@ -26,7 +27,7 @@ var _createSprite = function(data, template) { * @param {integer} depth Depth level * @return {void} */ -var _log = function(subject, depth) { +const _log = function(subject, depth) { console.log(util.inspect(subject, { showHidden: true, depth: depth || 2 })); @@ -38,7 +39,7 @@ var _log = function(subject, depth) { * @param {string} id * @return {void} */ -var _fixIds = function(obj, id) { +const _fixIds = function(obj, id) { // add id if (obj.attribs && obj.attribs.id) { obj.attribs.id = [id, obj.attribs.id].join('-'); @@ -55,10 +56,10 @@ var _fixIds = function(obj, id) { * @param {string} id * @return {void} */ -var _fixUrls = function(obj, id) { - var key; - var match; - var json = obj.attribs; +const _fixUrls = function(obj, id) { + let key; + let match; + const json = obj.attribs; if (json) { for (key in json) { if (json.hasOwnProperty(key)) { @@ -77,8 +78,8 @@ var _fixUrls = function(obj, id) { * @param {[type]} id [description] * @return {[type]} [description] */ -var _parseSVG = function(arr, id) { - var data = []; +const _parseSVG = function(arr, id) { + const data = []; arr.forEach(function(obj) { if (obj) { // add unic ids to urls @@ -102,10 +103,10 @@ var _parseSVG = function(arr, id) { * @param {[type]} data [description] * @return {[type]} [description] */ -var _defs = function(id, dom, data) { +const _defs = function(id, dom, data) { // lets find defs into dom - var defs = _.filter(dom.children, { name: 'defs' }); - var parseChilds = function(item, data) { + const defs = _.filter(dom.children, { name: 'defs' }); + const parseChilds = function(item, data) { item.forEach(function(child) { switch (child.name) { case 'use': { @@ -143,9 +144,9 @@ var _defs = function(id, dom, data) { * @param {[type]} data [description] * @return {[type]} [description] */ -var _symbols = function(id, dom, data, prefix) { +const _symbols = function(id, dom, data, prefix) { // create symbol object - var symbol = { + const symbol = { type: 'tag', name: 'symbol', attribs: { @@ -176,7 +177,7 @@ var _symbols = function(id, dom, data, prefix) { * @param {string} filename [description] * @return {string} [description] */ -var _convertFilenameToId = function(filename) { +const _convertFilenameToId = function(filename) { return filename.split('.').join('-').toLowerCase(); }; @@ -185,8 +186,8 @@ var _convertFilenameToId = function(filename) { * @param {string} input Destination path * @return {array} Array of paths */ -var _filesMap = function(input, cb) { - var data = input; +const _filesMap = function(input, cb) { + const data = input; globby(data).then(function(fileList) { cb(fileList); @@ -198,8 +199,8 @@ var _filesMap = function(input, cb) { * @param {[type]} dom [description] * @return {[type]} [description] */ -var _parseDomObject = function(data, filename, dom, prefix) { - var id = _convertFilenameToId(filename); +const _parseDomObject = function(data, filename, dom, prefix) { + const id = _convertFilenameToId(filename); if (dom && dom[0]) { _defs(id, dom[0], data.defs); _symbols(id, dom[0], data.symbols, prefix); @@ -214,9 +215,9 @@ var _parseDomObject = function(data, filename, dom, prefix) { * @param {integer} loop loop count * @return {[type]} minified source */ -var _minify = function(file, svgoOptions) { - var min = new Svgo(svgoOptions); - var source = file; +const _minify = function(file, svgoOptions) { + const min = new Svgo(svgoOptions); + let source = file; function svgoCallback(result) { source = result.data; @@ -231,9 +232,9 @@ var _minify = function(file, svgoOptions) { * [parseFiles description] * @return {[type]} [description] */ -var _parseFiles = function(files, options) { - var self = this; - var data = { +const _parseFiles = function(files, options) { + const self = this; + let data = { svg: options.svg, defs: [], symbols: [] @@ -242,17 +243,17 @@ var _parseFiles = function(files, options) { // each over files files.forEach(function(file) { // load and minify - var buffer = _minify(fs.readFileSync(file, 'utf8'), options.svgoOptions); + const buffer = _minify(fs.readFileSync(file, 'utf8'), options.svgoOptions); // get filename for id generation - var filename = path.basename(file, '.svg'); + const filename = path.basename(file, '.svg'); - var handler = new parse.DomHandler(function(error, dom) { + const handler = new parse.DomHandler(function(error, dom) { if (error) self.log(error); else data = _parseDomObject(data, filename, dom, options.prefix); }); // lets create parser instance - var Parser = new parse.Parser(handler, { + const Parser = new parse.Parser(handler, { xmlMode: true }); Parser.write(buffer); @@ -268,7 +269,7 @@ var _parseFiles = function(files, options) { * @param {[type]} name [description] * @return {[type]} [description] */ -var _hash = function(str, hash) { +const _hash = function(str, hash) { return str.indexOf('[hash]') >= 0 ? str.replace('[hash]', hash) : str; diff --git a/src/svgstore.js b/src/svgstore.js index 9353aee..f6a49f5 100644 --- a/src/svgstore.js +++ b/src/svgstore.js @@ -1,105 +1,123 @@ 'use strict'; // Defaults -var _options = { - svg: { - xmlns: 'http://www.w3.org/2000/svg', - style: 'position:absolute; width: 0; height: 0' - }, - svgoOptions: {}, - name: 'sprite.[hash].svg', - prefix: 'icon-', - template: __dirname + '/templates/layout.pug' +const defaults = { + svg: { + xmlns: 'http://www.w3.org/2000/svg', + style: 'position:absolute; width: 0; height: 0' + }, + svgoOptions: {}, + name: 'sprite.[hash].svg', + prefix: 'icon-', + template: __dirname + '/templates/layout.pug' }; // Depends -var _ = require('lodash'); -var path = require('path'); -var utils = require('./helpers/utils'); -var ConstDependency = require('webpack/lib/dependencies/ConstDependency'); -var async = require('async'); +const _ = require('lodash'); +const path = require('path'); +const utils = require('./helpers/utils'); +const ConstDependency = require('webpack/lib/dependencies/ConstDependency'); +const async = require('async'); -/** - * Constructor - * @param {string} input [description] - * @param {string} output [description] - * @param {object} options [description] - * @return {object} - */ -var WebpackSvgStore = function(options) { - this.options = _.merge({}, _options, options); - return this; -}; +class WebpackSvgStore { -WebpackSvgStore.prototype.apply = function(compiler) { - var tasks = {}; - var options = this.options; - var parseRepl = function(file, value) { - tasks[file] - ? tasks[file].push(value) - : function() { tasks[file] = []; tasks[file].push(value); }(); - }; + /** + * Constructor + * @param {string} input [description] + * @param {string} output [description] + * @param {object} options [description] + * @return {object} + */ + constructor (options) { + this.tasks = {}; + this.options = _.merge({}, defaults, options); + }; - var analyzeAst = function(expr) { - var dep = false; - var data = { - path: '/**/*.svg', - fileName: '[hash].sprite.svg', - context: this.state.current.context - }; - var replacement = false; - expr.init.properties.forEach(function(prop) { - switch (prop.key.name) { - case 'name': data.fileName = prop.value.value; break; - case 'path': data.path = prop.value.value; break; - default: break; - } - }); + parseRepl (file, value) { + this.tasks[file] ? this.tasks[file].push(value) : (() => { + this.tasks[file] = []; + this.tasks[file].push(value); + })(); + } - data.fileName = utils.hash(data.fileName, this.state.current.buildTimestamp); + analyzeAst () { + let self = this; + return function (expr) { + const data = { + path: '/**/*.svg', + fileName: '[hash].sprite.svg', + context: this.state.current.context + }; - replacement = expr.id.name + ' = { filename: ' + "__webpack_require__.p +" + '"' + data.fileName + '" }'; - dep = new ConstDependency(replacement, expr.range); - dep.loc = expr.loc; - this.state.current.addDependency(dep); - // parse repl - parseRepl(this.state.current.request, data); - }; + expr.init.properties.forEach(function (prop) { + switch (prop.key.name) { + case 'name': + data.fileName = prop.value.value; + break; + case 'path': + data.path = prop.value.value; + break; + default: + break; + } + }); - // AST parser - compiler.parser.plugin('var __svg__', analyzeAst); - compiler.parser.plugin('var __sprite__', analyzeAst); - compiler.parser.plugin('var __svgstore__', analyzeAst); - compiler.parser.plugin('var __svgsprite__', analyzeAst); - compiler.parser.plugin('var __webpack_svgstore__', analyzeAst); + data.fileName = utils.hash(data.fileName, this.state.current.buildTimestamp); + replacement = expr.id.name + ' = { filename: ' + "__webpack_require__.p +" + '"' + data.fileName + '" }'; + let dep = new ConstDependency(replacement, expr.range); + dep.loc = expr.loc; + this.state.current.addDependency(dep); + // parse repl + self.parseRepl(this.state.current.request, data); + }; + } - // save file to fs - compiler.plugin('emit', function(compilation, callback) { - async.forEach(Object.keys(tasks), function(key, callback) { - async.forEach(tasks[key], function(task, callback) { - utils.filesMap(path.join(task.context, task.path || ''), function(files) { - // fileContent - var fileContent = utils.createSprite( - utils.parseFiles(files, options), - options.template - ); + apply (compiler) { + // AST parser + compiler.plugin('compilation', (compilation, data) => { + let analzyerFunc = this.analyzeAst(); + data.normalModuleFactory.plugin('parser', (parser, options) => { + parser.plugin('var __svg__', analzyerFunc); + parser.plugin('var __sprite__', analzyerFunc); + parser.plugin('var __svgstore__', analzyerFunc); + parser.plugin('var __svgsprite__', analzyerFunc); + parser.plugin('var __webpack_svgstore__', analzyerFunc); + }) + }); - // add sprite to assets - compilation.assets[task.fileName] = { - size: function() { return Buffer.byteLength(fileContent, 'utf8'); }, - source: function() { return new Buffer(fileContent); } - }; - // done - callback(); - }); - }.bind(this), callback); - }.bind(this), callback); - }.bind(this)); - compiler.plugin('done', function() { - tasks = {}; - }); -}; + // save file to fs + compiler.plugin('emit', (compilation, callback) => { + async.forEach(Object.keys(this.tasks), + (key, outerCallback) => { + async.forEach(this.tasks[key], + (task, callback) => { + utils.filesMap(path.join(task.context, task.path || ''), (files) => { + // fileContent + const fileContent = utils.createSprite( + utils.parseFiles(files, this.options), this.options.template); + + // add sprite to assets + compilation.assets[task.fileName] = { + size: function () { + return Buffer.byteLength(fileContent, 'utf8'); + }, + source: function () { + return new Buffer(fileContent); + } + }; + // done + callback(); + }); + }, outerCallback); + }, callback); + }); + + compiler.plugin('done', () => { + this.tasks = {}; + }); + } +} /**