diff --git a/.gitignore b/.gitignore index 8787134..313149c 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,4 @@ typings/ #Distribution dist +.tests diff --git a/.npmignore b/.npmignore index 46983be..6830e61 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,8 @@ src/ +tests/ .vscode/ .idea/ +.tests/ tsconfig.json tslint.json diff --git a/README.md b/README.md index 6428b69..ab0b884 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,40 @@ -# svg-iconset-builder -Plugin to create iconsets from SVG files - -> Disclaimer:
-> This plugin was created to prepare iconsets for [Angular Material Icons](https://material.angular.io/components/icon/api#MatIconRegistry)
-> The кesult file will be a set of svg's with unique ids as a filename +# @vkl/svg-iconset-builder +Plugin for creating iconsets from SVG files with customizable optimisations ## Installation Support command line interface: ``` -npm install svg-iconset-builder -g +npm install @vkl/svg-iconset-builder -g ``` Or you can install for yout project ``` -npm install svg-iconset-builder --save +npm install @vkl/svg-iconset-builder --save ``` ## Usage ### Command line ``` -svg-iconset --source= --name= --attrs= --removeViewBox= +svg-iconset --source= --result= --svgoConfig= ``` Examples: ``` -svg-iconset --source=assets/images/icons --name=icons +svg-iconset --source=assets/images/icons --result=icons ``` -Creates file `icons-iconset.svg` in same folder +Creates a file `icons-iconset.svg` in same folder ### In Project ``` - const SvgIconset = require('svg-iconset'); const config = { source: 'assets/images/icons', // Reqired result: 'icons', // Required - // This is optional for optimisations + // This is optional for optimize files using SVGO plugins optimize: { - attrs: 'width, height, fill, stroke', // Will remove attributes removeViewBox: true, // Will remove attribute viewBox (default true) } } new SvgIconset(config).createSet(); - ``` diff --git a/package-lock.json b/package-lock.json index 0d09c0e..0ce5e19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "svg-iconset", + "name": "@vkl/svg-iconset-builder", "version": "0.0.6", "lockfileVersion": 1, "requires": true, diff --git a/package.json b/package.json index 9382710..0d7f3ab 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,9 @@ "main": "./dist/app.js", "scripts": { "build": "tsc", - "prepublish": "npm run build", + "prepare": "npm run build", "postpublish": "rm -rf ./dist/", - "test": "./test" + "test": "tsc -p tests/tsconfig.test.json" }, "bin": { "svg-iconset": "./dist/bin.js" diff --git a/src/app.ts b/src/app.ts index bba9acb..9d186c9 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,13 +3,13 @@ import { join } from 'path'; import { MainConfig, SVGOConfig, OptimizedResponse } from './config'; import { svgClean } from './modules/svgo.clean'; -import { makeConfig } from './modules/svgo.config'; +import { makeConfig, defaultConfig } from './modules/svgo.config'; const SVGO = require('svgo'); export class SvgIconset { - private optimizeConfig: SVGOConfig; + private optimizeConfig: { [prop: string]: boolean }[]; private isValid: boolean = true; private svgoPlugin: typeof SVGO; @@ -18,8 +18,9 @@ export class SvgIconset { this.isValid = false; } this.optimizeConfig = config.optimize !== undefined ? makeConfig(config.optimize) : makeConfig(); - this.svgoPlugin = new SVGO(this.optimizeConfig); - + this.svgoPlugin = new SVGO({ + plugins: this.optimizeConfig + }); } public createSet() { diff --git a/src/bin.ts b/src/bin.ts index 7509296..7eaed85 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -2,16 +2,18 @@ import { exit } from 'process'; import { SvgIconset } from './app'; +import { readFileSync } from 'fs'; +import { join } from 'path'; const argv = require('yargs').argv; -const exampleString = 'Example: svg-iconset --source=assets/images/icons --name=icons'; +const exampleString = 'Example: svg-iconset --source=assets/images/icons --result=icons, check docs https://github.com/DariusNorv/svg-iconset#usage'; +let optimize = undefined; const { source, - name, - attrs, - removeViewBox + result, + svgoConfig } = argv; @@ -21,17 +23,23 @@ if (source === undefined) { exit(); } -if (name === undefined) { +if (result === undefined) { console.error('Result filename is not set'); console.error(exampleString); exit(); } +if (svgoConfig !== undefined) { + try { + optimize = JSON.parse(readFileSync(join(process.cwd(), svgoConfig), 'utf-8')); + } catch (err) { + console.error('SVGO configuration file parse error', err.message); + process.exit(0); + } +} + new SvgIconset({ source, - result: name, - optimize: { - attrs, - removeViewBox - } + result, + optimize }).createSet(); diff --git a/src/config.d.ts b/src/config.d.ts index 7217262..ed21961 100644 --- a/src/config.d.ts +++ b/src/config.d.ts @@ -1,19 +1,3 @@ -declare type SVGOConfig = [ - { - removeAttrs: { - attrs: string - } - }, - { - removeViewBox: boolean - } -] - -export interface SvgOptimizeConfig { - attrs?: string; - removeViewBox?: boolean -} - export interface SVGOResponse { data: string; info: { [propname: string]: string } @@ -27,5 +11,47 @@ export interface OptimizedResponse { export interface MainConfig { source: string; result: string; - optimize?: SvgOptimizeConfig + optimize?: SVGOConfig +} + +interface SVGOConfig extends Iterable<{ [prop: string]: boolean }>{ + removeDoctype?: boolean; + removeXMLProcInst?: boolean; + removeComments?: boolean; + removeMetadata?: boolean; + removeXMLNS?: boolean; + removeEditorsNSData?: boolean; + cleanupAttrs?: boolean; + inlineStyles?: boolean; + minifyStyles?: boolean; + convertStyleToAttrs?: boolean; + cleanupIDs?: boolean; + removeRasterImages?: boolean; + removeUselessDefs?: boolean; + cleanupNumericValues?: boolean; + cleanupListOfValues?: boolean; + convertColors?: boolean; + removeUnknownsAndDefaults?: boolean; + removeNonInheritableGroupAttrs?: boolean; + removeUselessStrokeAndFill?: boolean; + removeViewBox?: boolean; + cleanupEnableBackground?: boolean; + removeHiddenElems?: boolean; + removeEmptyText?: boolean; + convertShapeToPath?: boolean; + moveElemsAttrsToGroup?: boolean; + moveGroupAttrsToElems?: boolean; + collapseGroups?: boolean; + convertPathData?: boolean; + convertTransform?: boolean; + removeEmptyAttrs?: boolean; + removeEmptyContainers?: boolean; + mergePaths?: boolean; + removeUnusedNS?: boolean; + sortAttrs?: boolean; + removeTitle?: boolean; + removeDesc?: boolean; + removeDimensions?: boolean; + removeStyleElement?: boolean; + removeScriptElement?: boolean; } diff --git a/src/modules/svgo.config.ts b/src/modules/svgo.config.ts index 69c11ce..89a05f4 100644 --- a/src/modules/svgo.config.ts +++ b/src/modules/svgo.config.ts @@ -1,27 +1,65 @@ -import { SVGOConfig, SvgOptimizeConfig } from '../config'; +import { SVGOConfig } from '../config'; -const defaultConfig: SVGOConfig = [ - { - removeAttrs: { - attrs: '(width|height)' - }, - }, - { removeViewBox: false } -]; +export const defaultConfig: SVGOConfig = { + removeDoctype: true, + removeXMLProcInst: true, + removeComments: true, + removeMetadata: true, + removeXMLNS: false, + removeEditorsNSData: true, + cleanupAttrs: true, + inlineStyles: true, + minifyStyles: true, + convertStyleToAttrs: true, + cleanupIDs: true, + removeRasterImages: false, + removeUselessDefs: true, + cleanupNumericValues: true, + cleanupListOfValues: false, + convertColors: true, + removeUnknownsAndDefaults: true, + removeNonInheritableGroupAttrs: true, + removeUselessStrokeAndFill: true, + removeViewBox: false, + cleanupEnableBackground: true, + removeHiddenElems: true, + removeEmptyText: true, + convertShapeToPath: true, + moveElemsAttrsToGroup: true, + moveGroupAttrsToElems: true, + collapseGroups: true, + convertPathData: true, + convertTransform: true, + removeEmptyAttrs: true, + removeEmptyContainers: true, + mergePaths: true, + removeUnusedNS: true, + sortAttrs: false, + removeTitle: true, + removeDesc: true, + removeDimensions: true, + removeStyleElement: false, + removeScriptElement: false, -export function makeConfig(config?: SvgOptimizeConfig): SVGOConfig { - - if (config !== undefined) { - const { attrs, removeViewBox } = config; - - if (attrs !== undefined) { - defaultConfig[0].removeAttrs.attrs = `(${attrs.replace(/,\s/g, '|')})`; - } - - if (removeViewBox) { - defaultConfig[1].removeViewBox = removeViewBox; + *[Symbol.iterator]() { + for (const name of Object.keys(this)) { + yield JSON.parse(`{"${name}": ${(this as any)[name]}}`); } } +}; - return defaultConfig; +export function makeConfig(config?: SVGOConfig): { [prop: string]: boolean }[] { + + if (config !== undefined) { + return [...defaultConfig].map(item => { + const key = Object.keys(item)[0]; + if (config.hasOwnProperty(key)) { + item[key] = (config as any)[key]; + } + + return item; + }); + } + + return [...defaultConfig]; } diff --git a/tests/index.ts b/tests/index.ts new file mode 100644 index 0000000..44e2760 --- /dev/null +++ b/tests/index.ts @@ -0,0 +1,8 @@ +import { SvgIconset } from '../src/app'; + +new SvgIconset( + { + source: './resources', + result: 'test', + } +).createSet(); diff --git a/tests/resources/ubuntu.svg b/tests/resources/ubuntu.svg new file mode 100644 index 0000000..e788b27 --- /dev/null +++ b/tests/resources/ubuntu.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/tsconfig.test.json b/tests/tsconfig.test.json new file mode 100644 index 0000000..487939b --- /dev/null +++ b/tests/tsconfig.test.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.json", + "rootDir": "../tests", + "outDir": "./.tests", +} diff --git a/tsconfig.json b/tsconfig.json index 9ebd0a9..80ffb80 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,6 +14,7 @@ "declaration": true, "sourceMap": true, "inlineSources": true, + "downlevelIteration": true, "types": [ "node" ]