├── .gitattributes
├── .gitignore
├── .npmignore
├── .prettierrc.js
├── .travis.yml
├── .vscode
└── settings.json
├── CHANGELOG.md
├── LICENCE
├── MIGRATING.md
├── README.md
├── TEMPLATES.md
├── examples
├── aspect-ratio-test.html
├── gulpfile.js
├── package.json
├── svg-with-masks-sources
│ ├── ad-clip-path-donut.svg
│ ├── ad-clip-path-heart.svg
│ ├── ad-compose-path-variation.svg
│ ├── ad-compose-path.svg
│ ├── affinity-designer-source.afdesign
│ ├── figma-output-1.svg
│ └── figma-output-2.svg
├── vue
│ ├── index.html
│ ├── layout.vue
│ └── main.js
└── yarn.lock
├── index.js
├── lib
├── default-config.js
├── svg.js
├── templates.js
└── utils.js
├── package.json
├── templates
├── svg-symbols-custom-properties.css
├── svg-symbols-demo-page.html
├── svg-symbols.css
├── svg-symbols.scss
├── svg-symbols.styl
├── svg-symbols.svg
└── svg-symbols.vue
├── test
├── get-svg-datas.js
├── output
│ ├── codepen-symbol.css
│ ├── codepen-symbol.svg
│ ├── gear_without_dimensions-symbol.svg
│ ├── github-symbol.css
│ ├── github-symbol.svg
│ ├── mail-symbol-accessible.svg
│ ├── mail-symbol.css
│ ├── mail-symbol.svg
│ ├── no-title.svg
│ ├── skull-symbol.css
│ ├── skull-symbol.svg
│ ├── svg-symbols.css
│ ├── svg-symbols.svg
│ ├── template.html
│ └── template.json
├── plugin.js
├── source
│ ├── affinity-clip-path-outside-defs.svg
│ ├── aspect-ratio.svg
│ ├── chinese letter with styles.svg
│ ├── crâne noir.svg
│ ├── duplicated-defs-ids-1.svg
│ ├── duplicated-defs-ids-2.svg
│ ├── figma-mask-outside-defs.svg
│ ├── gear_without_dimensions.svg
│ ├── github.svg
│ ├── gradient.svg
│ ├── icon-with-percent-size.svg
│ ├── instagram-black.svg
│ ├── ios.svg
│ ├── skull.svg
│ ├── template.html
│ ├── template.json
│ ├── warning with styles and empty group.svg
│ ├── xlink-href.svg
│ └── zoom.svg
├── templates.js
├── transform-raw-data.js
└── warnings.js
└── yarn.lock
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.afdesign filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | tmp
4 | npm-debug.log
5 | examples/ex-*
6 | examples/dist
7 | examples/.cache
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | tmp
4 | npm-debug.log
5 |
6 | test
7 | examples
8 | .jshintrc
9 | .travis.yml
10 | CHANGELOG.md
11 | gulpfile.js
12 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: false,
3 | singleQuote: true,
4 | trailingComma: `es5`,
5 | }
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "8"
4 | - "6"
5 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "gulpfile"
4 | ]
5 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | - **3.2.0**
2 | - rename `default-sass` template to `default-scss`
3 | - add a `default-vue` template
4 | - **3.1.1**
5 | - add build-in templates for scss, stylus, and CSS custom properties
6 | - **3.1.0**
7 | - improve warning messages
8 | - **3.0.0**
9 | - fix demo page not showing gradients
10 | - **3.0.0**
11 | - remove gulp-util. Gulp-svg-symbols now work with the latest gulp4
12 | - add an `option.svgAttrs`
13 | - replace `option.className` by `options.class`
14 | - replace `option.svgClassname` by `options.svgAttrs.class`
15 | - update `defaultSvg` template. It now will add `xmlns:xlink="http://www.w3.org/1999/xlink"` if needed
16 | - use ES6 functionalities such as: Promise, arrow function & template strings
17 | - don't generated files if no SVG has been found
18 | - **2.0.1**
19 | - Update dependencies
20 | - Better _real situation_ example folder
21 | - **2.0.0**
22 | - Change default SVG template
23 | - Change id handling (moving from [slug](https://www.npmjs.com/package/slug) to [speakingurl](https://www.npmjs.com/package/speakingurl))
24 | - **1.0.0** — Remove SVGO
25 | - **0.3.2** — Better demo page
26 | - **0.3.1** — Add .npmignore
27 | - **0.3.0** — Improve default option configuration
28 | - **0.2.1** — Fix SVG files without dimensions
29 | - **0.2.0** — Custom templates & data handling
30 | - **0.1.5** — Fix test
31 | - **0.1.4** — Fix viewbox issue
32 | - **0.1.3** — Custom `
` & `` content
33 | - **0.1.2** — Config for optional CSS output
34 | - **0.1.1** — Add travis build
35 | - **0.1.0** — Publish to NPM and fix [watch issue](https://github.com/Hiswe/gulp-svg-symbols/issues/2)
36 | - **0.0.2** — Css can be generated with _em_ size
37 | - **0.0.1** — First release
38 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Hiswe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MIGRATING.md:
--------------------------------------------------------------------------------
1 | ### from v3.0+ to v3.2
2 |
3 | - if you were using the sass template, rename `default-sass` to `default-scss` in your gulpfile
4 |
5 | ### from v2.0+ to v3.0
6 |
7 | - Configuration deprecated options (still working but will issue a warning):
8 | - `option.className` has been replaced by `options.class`.
9 | - `option.svgClassname` has been replaced by `options.svgAttrs.class`
10 | - Datas in custom templates has been modified. This can break some templates.
11 | - `icon.className` is replaced by `icon.class` in the icons list
12 | - `svgClassname` is replaced by `svgAttrs.class`
13 |
14 | ### from v1.0+ to v2.0
15 |
16 | - Style attribute from the default SVG template has been removed.
17 | You will have to take care of it manually if you intend to inline the SVG symbols in your DOM. (see `svgClassname` option in the README)
18 |
19 | ### from v0.2+ to v1.0
20 |
21 | - SVGO is no more used.
22 | - `css: false` is removed.
23 | - svgSymbols.demoPage() method has been removed. See **Basics > demo page**
24 |
25 | ### from v0.1 to 0.2+
26 |
27 | - `svgId` is replaced by `id`.
28 | - `accessibility` is replaced by `title`.
29 | - `css: false` is still working but is deprecated.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gulp-svg-symbols
2 |
3 | [](https://badge.fury.io/js/gulp-svg-symbols) [](https://travis-ci.org/Hiswe/gulp-svg-symbols)
4 |
5 | _gulp-svg-symbols_ is a minimal plugin for [gulp](http://gulpjs.com).
6 | It converts a bunch of svg files to a single svg file containing each one as a symbol.
7 | See [css-trick](http://css-tricks.com/svg-symbol-good-choice-icons/) for more details.
8 |
9 |
10 |
11 |
12 |
13 | - [Install](#install)
14 | - [Example](#example)
15 | - [Options](#options)
16 | - [Basics](#basics)
17 | - [id and class](#id-and-class)
18 | - [fontSize](#fontsize)
19 | - [title](#title)
20 | - [svgAttrs](#svgattrs)
21 | - [slug](#slug)
22 | - [templates](#templates)
23 | - [CSS generation](#css-generation)
24 | - [warn](#warn)
25 | - [Advanced](#advanced)
26 | - [templates](#templates-1)
27 | - [transformData](#transformdata)
28 | - [Other observations](#other-observations)
29 | - [Other stuff](#other-stuff)
30 | - [Rendering caveats](#rendering-caveats)
31 | - [Migrating](#migrating)
32 | - [More examples](#more-examples)
33 | - [Usefull frontend lib](#usefull-frontend-lib)
34 | - [Thanks](#thanks)
35 | - [Credits](#credits)
36 | - [Alternatives](#alternatives)
37 |
38 |
39 |
40 | ## Install
41 |
42 | ```
43 | npm install --save-dev gulp-svg-symbols
44 | ```
45 |
46 | ## Example
47 |
48 | In your gulpfile.js:
49 |
50 | ```js
51 | const gulp = require('gulp')
52 | const svgSymbols = require('gulp-svg-symbols')
53 |
54 | gulp.task(`sprites`, function() {
55 | return gulp
56 | .src(`assets/svg/*.svg`)
57 | .pipe(svgSymbols())
58 | .pipe(gulp.dest(`assets`))
59 | })
60 | ```
61 |
62 | In your HTML, you first have to [reference the SVG](http://css-tricks.com/svg-sprites-use-better-icon-fonts/)
63 | then:
64 |
65 | ```html
66 |
67 |
68 |
69 | ```
70 |
71 | - **class** is the one generated in the CSS file
72 | - **xlink:href** is the symbol id in the SVG file
73 |
74 | ## Options
75 |
76 | You can override the [default options](https://github.com/Hiswe/gulp-svg-symbols/blob/master/lib/default-config.js) by passing an object as an argument to `svgSymbols()`
77 |
78 | ### Basics
79 |
80 | #### id and class
81 |
82 | **type:** `function` or `string`
83 | **default:** `'%f'` and `'.%f'`
84 |
85 | Text templates for generating symbols id & icon class
86 | `%f` is the [speakingurled](https://www.npmjs.com/package/speakingurl) file name placeholder.
87 | See more about the name in the [slug option](#slug)
88 |
89 | #### fontSize
90 |
91 | **type:** `number`
92 | **default:** `0`
93 |
94 | This option lets you define a base font.
95 | If it's superior to 0, then the sizes in your CSS file will be in **em** else sizes are provided with **px**.
96 |
97 | #### title
98 |
99 | **type:** `boolean` or `function` or `string`
100 | **default:** `false`
101 |
102 | Specify whether or not you want to add a missing `title` tag in your SVG symbols.
103 | It should be better for _accessibility_.
104 | It takes a text template (like for [id/classname](https://github.com/Hiswe/gulp-svg-symbols#id--classname)):
105 |
106 | ```js
107 | title: `%f icon`
108 | ```
109 |
110 | #### svgAttrs
111 |
112 | **type:** `object`
113 | **default:** `{class: null, xmlns: 'http://www.w3.org/2000/svg'}`
114 |
115 | Specify attributes for the `` container tag in the default SVG template.
116 |
117 | ```js
118 | {
119 | class: `svg-icon-lib`,
120 | 'aria-hidden': `true`,
121 | style: `position: absolute;`,
122 | 'data-enabled': true,
123 | }
124 | ```
125 |
126 | output:
127 |
128 | ```html
129 |
130 | ```
131 |
132 | _notes:_
133 |
134 | - this is how you can add a `class` to the generated SVG
135 | - any string or numeric attribute will be rendered
136 | - boolean attributes will just toggle the attribute without any value. If you need to render the boolean as a value just pass it as a string
137 | - the attribute `xmlns:xlink="http://www.w3.org/1999/xlink"` will be added automatically if any `xlink:` is found in the SVG content
138 |
139 | #### slug
140 |
141 | **type:** `object` or `function`
142 | **default:** `{}`
143 |
144 | In order to have nice ids in the template and to keep the gulp task quite simple, gulp-svg-symbols use [speakingurl](https://www.npmjs.com/package/speakingurl).
145 |
146 | You can pass a [speakingurl's config](https://www.npmjs.com/package/speakingurl#getsluginput-options) here:
147 |
148 | ```js
149 | gulp.src(`*.svg`).pipe(
150 | svgSymbols({
151 | slug: {
152 | separator: `_`,
153 | },
154 | })
155 | )
156 | ```
157 |
158 | You can also provide a custom function which should return a `string`:
159 |
160 | ```js
161 | gulp.src(`*.svg`).pipe(
162 | svgSymbols({
163 | slug: function(name) {
164 | return name.replace(/\s/g, `-`)
165 | },
166 | })
167 | )
168 | ```
169 |
170 | Or if you want to use [gulp-rename](https://www.npmjs.com/package/gulp-rename):
171 |
172 | ```js
173 | gulp
174 | .src(`*.svg`)
175 | .pipe(rename(/* gulp rename options*/))
176 | .pipe(
177 | svgSymbols({
178 | slug: name => name,
179 | })
180 | )
181 | ```
182 |
183 | #### templates
184 |
185 | **type:** `array of string`
186 | **default:** `['default-svg', 'default-css']`
187 |
188 | _gulp-svg-symbols_ comes with some default templates.
189 |
190 | You can control which file are generated by specifying only the templates to keep:
191 |
192 | ```js
193 | templates: [`default-svg`]
194 | ```
195 |
196 | will output **only** the SVG file.
197 |
198 | Here is the list of all provided templates:
199 |
200 | - [**default-svg**](https://github.com/Hiswe/gulp-svg-symbols/blob/master/TEMPLATES.md#default-svg): the bundle of SVG
201 | - [**default-css**](https://github.com/Hiswe/gulp-svg-symbols/blob/master/TEMPLATES.md#default-css): a CSS file gathering all sizes and additional styles
202 | - [**default-demo**](https://github.com/Hiswe/gulp-svg-symbols/blob/master/TEMPLATES.md#default-demo): a demo page which provide an overview of every symbols + a way to copy/paste easily the symbol SVG code
203 | - [**default-vue**](https://github.com/Hiswe/gulp-svg-symbols/blob/master/TEMPLATES.md#default-vue): a vue component
204 | - [**default-css-var**](https://github.com/Hiswe/gulp-svg-symbols/blob/master/TEMPLATES.md#default-css-var): same as the CSS, but all sizes will be also declared as CSS Custom Properties
205 | - [**default-scss**](https://github.com/Hiswe/gulp-svg-symbols/blob/master/TEMPLATES.md#default-scss): same as the CSS, but sizes will be declared as SCSS variables
206 | - [**default-stylus**](https://github.com/Hiswe/gulp-svg-symbols/blob/master/TEMPLATES.md#default-stylus): same as the CSS, but sizes will be declared as Stylus variables
207 |
208 | More details about the build-in templates can be found in the [TEMPLATES.md](https://github.com/Hiswe/gulp-svg-symbols/blob/master/TEMPLATES.md) file
209 |
210 | ##### CSS generation
211 |
212 | You can deactivate CSS output by removing the CSS template from the template array.
213 | See [templates option](https://github.com/Hiswe/gulp-svg-symbols#templates) for more details.
214 |
215 | #### warn
216 |
217 | **default:** `true`
218 |
219 | Disable plugin warn messages (like: missing viewBox & depreciation warnings).
220 |
221 | ### Advanced
222 |
223 | #### templates
224 |
225 | Specify your own templates by providing an absolute path:
226 |
227 | ```js
228 | templates: [
229 | path.join(__dirname, `path/to/my/template.less`),
230 | path.join(__dirname, `path/to/another/template.js`),
231 | // You can still access to default templates by providing:
232 | `default-svg`,
233 | `default-css`,
234 | `default-demo`,
235 | ]
236 | ```
237 |
238 | - template engine is [lodash](http://lodash.com/docs#template).
239 | - the output files will have the same name & extension as your files.
240 | - every template will have acces to those datas:
241 |
242 | ```js
243 | {
244 | svgAttrs: {/* the same object you can pass in configuration */ },
245 | defs: `string`,
246 | icons: [{
247 | id: `string`,
248 | class: `.string`,
249 | width: `a number as a string with a unit`,
250 | height: `a number as a string with a unit`,
251 | style: `string if exists`,
252 | svg: {
253 | name: `string (svg filename without extension)`,
254 | id: `string`,
255 | width: `number`,
256 | height: `number`,
257 | content: `the svg markup as a string`,
258 | viewBox: `string`,
259 | originalAttributes: {
260 | /* every attributes before processing them */
261 | },
262 | },
263 | }, {/*…*/}, ],
264 | }
265 | ```
266 |
267 | - and also 2 helpers functions
268 | - `attributesToString( object )` render an object as a string of attributes
269 | - `svgdataToSymbol( iconData )` render an icon data object to a stringed symbol
270 |
271 | #### transformData
272 |
273 | With the ability to provide custom templates, you also have the ability to configure custom data.
274 |
275 | ```js
276 | transformData: function(svg, defaultData, options) {
277 | /******
278 | svg is same object as the one passed to the templates (see above)
279 |
280 | defaultData are the ones needed by default templates
281 | see /lib/get-default-data.js
282 |
283 | options are the one you have set in your gulpfile,
284 | minus templates & transformData
285 | *******/
286 |
287 | return {
288 | // Return every datas you need
289 | id: defaultData.id,
290 | class: defaultData.class,
291 | width: `${svg.width}em`,
292 | height: `${svg.height}em`
293 | };
294 | }
295 | ```
296 |
297 | In your templates, svg original data are accessible in `icon.svg`.
298 | Of course default templates need `defaultData`.
299 |
300 | ### Other observations
301 |
302 | - If you want to manipulate your icons files, use [gulp-cheerio](https://www.npmjs.com/package/gulp-cheerio)
303 | - If you want to optimize your icons files or the SVG output, use [gulp-svgmin](https://www.npmjs.org/package/gulp-svgmin) (using SVGO)
304 | - If you want to change the generated files name, again use [gulp-rename](https://www.npmjs.org/package/gulp-rename)
305 | - If you want different destination for the files, use [gulp-if](https://www.npmjs.org/package/gulp-if)
306 | - Unlike [gulp-svg-sprites](https://www.npmjs.org/package/gulp-svg-sprites) there is no way to add padding to SVG files.
307 |
308 | If you want to include the SVG symbols directly in the DOM (i.e. no external reference) and mask it, a secure way of hiding it could be achieved in this way:
309 |
310 | ```css
311 | .svg-icon-lib {
312 | border: 0 !important;
313 | clip: rect(0 0 0 0) !important;
314 | height: 1px !important;
315 | margin: -1px !important;
316 | overflow: hidden !important;
317 | padding: 0 !important;
318 | position: absolute !important;
319 | width: 1px !important;
320 | }
321 | ```
322 |
323 | A simple `display: none` will mess with defs rendering (gradients and so on…)
324 |
325 | ## Other stuff
326 |
327 | ### Rendering caveats
328 |
329 | SVG can have rendering issues if:
330 |
331 | - multiple `` have the same ids.
332 | Use [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin#per-file-options) to fix that.
333 | - `` and `` aren't staying inside `` tags.
334 | Move those tags **inside** the `` tags. Manually or programmatically (easy to do with [gulp-cheerio](https://www.npmjs.com/package/gulp-cheerio))
335 |
336 | An example has been made to show all those issues resolved inside the [svgContainingIdenticalId](https://github.com/Hiswe/gulp-svg-symbols/blob/master/examples/gulpfile.js#L198-L282).
337 |
338 | `npm run svg-containing-identical-id` to test.
339 |
340 | ### Migrating
341 |
342 | See [MIGRATING.md](https://github.com/Hiswe/gulp-svg-symbols/blob/master/MIGRATING.md)
343 |
344 | ### More examples
345 |
346 | Go in the [examples folder](https://github.com/Hiswe/gulp-svg-symbols/blob/master/examples), then `npm install && npm run list`.
347 | You will have a list of all task examples there
348 |
349 | ### Usefull frontend lib
350 |
351 | - [svg4everybody](https://www.npmjs.com/package/svg4everybody) leverage external SVG for browser which doesn't support it
352 |
353 | ### Thanks
354 |
355 | - [Florens Verschelde](https://github.com/fvsch) for the usefull insights and PR
356 |
357 | ### Credits
358 |
359 | - [Chris Coyier](http://css-tricks.com/) for the [trick](http://css-tricks.com/svg-symbol-good-choice-icons/)
360 | - [Shaky Shane](https://www.npmjs.org/~shakyshane) for the [gulp-svg-sprites](https://www.npmjs.org/package/gulp-svg-sprites) plugin
361 | - [FWeinb](https://github.com/FWeinb) for the [grunt-svgstore](https://github.com/FWeinb/grunt-svgstore) plugin
362 |
363 | ### Alternatives
364 |
365 | - [gulp-svg-sprite](https://www.npmjs.com/package/gulp-svg-sprite)
366 | - [gulp-svg-store](https://www.npmjs.com/package/gulp-svgstore)
367 | - [gulp-svg-sprites](https://www.npmjs.org/package/gulp-svg-sprites)
368 |
--------------------------------------------------------------------------------
/TEMPLATES.md:
--------------------------------------------------------------------------------
1 | # gulp-svg-symbols build-in templates
2 |
3 | Here are all the templates provided by the plugin and the result you can expect from them:
4 |
5 |
6 |
7 |
8 |
9 | - [default-svg](#default-svg)
10 | - [default-css](#default-css)
11 | - [default-demo](#default-demo)
12 | - [default-vue](#default-vue)
13 | - [usage](#usage)
14 | - [props](#props)
15 | - [default-css-var](#default-css-var)
16 | - [default-scss](#default-scss)
17 | - [default-stylus](#default-stylus)
18 |
19 |
20 |
21 | ## default-svg
22 |
23 | > responsible of generating the bundled SVG file
24 |
25 | file name:
26 |
27 | ```
28 | svg-symbols.svg
29 | ```
30 |
31 | ```xml
32 |
33 |
34 |
35 |
36 |
37 | ```
38 |
39 | ## default-css
40 |
41 | > responsible of generating the CSS file containing the symbols sizes and the CSS rules coming from your SVG files
42 |
43 | file name:
44 |
45 | ```
46 | svg-symbols.css
47 | ```
48 |
49 | ```css
50 | .github {
51 | width: 22px;
52 | height: 24px;
53 | }
54 | ```
55 |
56 | ## default-demo
57 |
58 | > the demo page with the snippets you can copy & paste in your HTML
59 |
60 | file name:
61 |
62 | ```
63 | svg-symbols-demo-page.html
64 | ```
65 |
66 | A single HTML file with all the right styles and a nice presentations 😀
67 |
68 | ## default-vue
69 |
70 | > generate a vue component
71 |
72 | file name:
73 |
74 | ```
75 | svg-symbols.vue
76 | ```
77 |
78 | ### usage
79 |
80 | gulpfile configuration
81 |
82 | ```js
83 | var gulp = require('gulp')
84 | var svgSymbols = require('gulp-svg-symbols')
85 |
86 | function sprites() {
87 | return gulp
88 | .src(`svg/*.svg`)
89 | .pipe(
90 | svgSymbols({
91 | // optional: define a global class for every SVG
92 | svgAttrs: { class: `svg-symbol` },
93 | // optional: customize another class for each SVG
94 | class: `.svg-symbol--%f`,
95 | // choose the vue template
96 | templates: [`default-vue`],
97 | })
98 | )
99 | .pipe(gulp.dest(`components`))
100 | }
101 | exports.sprites = sprites
102 | ```
103 |
104 | Register the component in your Vue application (or Vue component)
105 |
106 | ```js
107 | import Vue from 'vue'
108 | import SvgSymbol from '.svg-symbols'
109 | Vue.component(`svg-symbol`, SvgSymbol)
110 | ```
111 |
112 | Use it in your own components
113 |
114 | ```html
115 |
116 |
117 |
118 |
119 |
120 |
121 |
126 | ```
127 |
128 | ### props
129 |
130 | | Name | | Default | Type | |
131 | | ----- | -------- | ------- | ------ | -------------------------------------------------------------- |
132 | | name | Required | none | String | icon name |
133 | | scale | | 1 | Number | Scale icon size by recomputing SVG's width & height attributes |
134 |
135 | ## default-css-var
136 |
137 | > generate a CSS file containing the symbols sizes as custom properties and the CSS rules coming from your SVG files
138 |
139 | file name:
140 |
141 | ```
142 | svg-symbols-custom-properties.css
143 | ```
144 |
145 | ```css
146 | :root {
147 | --icon-github-width: 22px;
148 | --icon-github-height: 24px;
149 | }
150 | .github {
151 | width: var(--icon-github-width);
152 | height: var(--icon-github-height);
153 | }
154 | ```
155 |
156 | ## default-scss
157 |
158 | > generate a SCSS file containing the symbols sizes as variables and the CSS rules coming from your SVG files
159 |
160 | file name:
161 |
162 | ```
163 | svg-symbols.scss
164 | ```
165 |
166 | ```scss
167 | $icon-github-width: 22px;
168 | $icon-github-height: 24px;
169 |
170 | .github {
171 | width: $icon-github-width;
172 | height: $icon-github-height;
173 | }
174 | ```
175 |
176 | ## default-stylus
177 |
178 | > generate a stylus file containing the symbols sizes as variables and the CSS rules coming from your SVG files
179 |
180 | file name:
181 |
182 | ```
183 | svg-symbols.styl
184 | ```
185 |
186 | ```styl
187 | $icon-github-width = 22px;
188 | $icon-github-height = 24px;
189 |
190 | .github {
191 | width: $icon-github-width;
192 | height: $icon-github-height;
193 | }
194 | ```
195 |
--------------------------------------------------------------------------------
/examples/aspect-ratio-test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Gulp SVG Symbols test page
7 |
8 |
9 |
25 |
33 |
34 |
35 |
36 |
37 | <%= defs %>
38 |
39 | <% _.forEach( icons, function( icon ){ %>
40 | preserveAspectRatio="<%= icon.svg.originalAttributes.preserveAspectRatio %>" <% }%>><% if (icon.title) {%>
41 | <%= icon.title %> <% }%>
42 | <%= icon.svg.content %>
43 | <%
44 | }); %>
45 | line whould take 100% octocat should stay same sized
46 |
47 | untouched SVG
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | SVG with width at 100%
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/examples/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require(`path`)
4 | const gulp = require(`gulp`)
5 | const gulpif = require(`gulp-if`)
6 | const rename = require(`gulp-rename`)
7 | const svgmin = require(`gulp-svgmin`)
8 | const cheerio = require(`gulp-cheerio`)
9 | const gulpSass = require(`gulp-sass`)
10 | const gulpStylus = require(`gulp-stylus`)
11 | const beautify = require('gulp-beautify')
12 | // need to reference the real module
13 | // https://github.com/Hiswe/gulp-svg-symbols/issues/35#issuecomment-254494474
14 | const svgSymbols = require(`gulp-svg-symbols`)
15 | // // for test purpose
16 | // const svgSymbols = require(`../index.js`)
17 |
18 | const svgGlob = `../test/source/*.svg`
19 |
20 | ////////
21 | // DEFAULT OPTIONS OUTPUT
22 | ////////
23 |
24 | function svg() {
25 | return gulp
26 | .src(svgGlob)
27 | .pipe(svgSymbols())
28 | .pipe(gulp.dest(`ex-default`))
29 | }
30 | svg.description = `default use of the plugin`
31 |
32 | ////////
33 | // ADDING THE DEMO PAGE
34 | ////////
35 |
36 | function demoPage() {
37 | return gulp
38 | .src(svgGlob)
39 | .pipe(
40 | svgSymbols({
41 | templates: [`default-svg`, `default-css`, `default-demo`],
42 | })
43 | )
44 | .pipe(gulp.dest(`ex-demo-page`))
45 | }
46 | demoPage.description = `Generating the demo page along with the default templates`
47 |
48 | ////////
49 | // ALL TEMPLATES
50 | ////////
51 |
52 | function allBuildInTemplates() {
53 | return gulp
54 | .src(`../test/source/github.svg`)
55 | .pipe(
56 | svgSymbols({
57 | templates: [
58 | `default-svg`,
59 | `default-css`,
60 | `default-css-var`,
61 | `default-scss`,
62 | `default-stylus`,
63 | `default-demo`,
64 | `default-vue`,
65 | ],
66 | })
67 | )
68 | .pipe(gulp.dest(`ex-build-in-templates`))
69 | }
70 | demoPage.description = `Generating all default templates`
71 |
72 | ////////
73 | // SCSS
74 | ////////
75 |
76 | function scss() {
77 | return gulp
78 | .src(svgGlob)
79 | .pipe(
80 | svgSymbols({
81 | templates: [`default-scss`],
82 | })
83 | )
84 | .pipe(gulp.dest(`ex-scss`))
85 | .pipe(gulpSass())
86 | .pipe(gulp.dest(`ex-scss`))
87 | }
88 | scss.description = `Generating scss file`
89 |
90 | ////////
91 | // STYLUS
92 | ////////
93 |
94 | function stylus() {
95 | return gulp
96 | .src(svgGlob)
97 | .pipe(
98 | svgSymbols({
99 | templates: [`default-stylus`],
100 | })
101 | )
102 | .pipe(gulp.dest(`ex-stylus`))
103 | .pipe(gulpStylus())
104 | .pipe(gulp.dest(`ex-stylus`))
105 | }
106 | stylus.description = `Generating styl file`
107 |
108 | ////////
109 | // CSS VAR
110 | ////////
111 |
112 | function cssVar() {
113 | return gulp
114 | .src(svgGlob)
115 | .pipe(
116 | svgSymbols({
117 | templates: [`default-css-var`],
118 | })
119 | )
120 | .pipe(gulp.dest(`ex-css-var`))
121 | }
122 | cssVar.description = `Generating css file with custom properties`
123 |
124 | ////////
125 | // SVG GLOBAL CLASS NAME
126 | ////////
127 |
128 | // add a class name to the outputted SVG in case of SVG being included in the DOM
129 | function svgClassname() {
130 | return gulp
131 | .src(svgGlob)
132 | .pipe(
133 | svgSymbols({
134 | svgAttrs: { class: `custom-name` },
135 | })
136 | )
137 | .pipe(gulp.dest(`ex-svg-classname`))
138 | }
139 | svgClassname.description = `Adding add a class to the generated SVG file`
140 |
141 | ////////
142 | // CUSTOM TEMPLATES
143 | ////////
144 |
145 | const customCSSTemplate = path.join(__dirname, `../test/source/template.json`)
146 | const customHTMLTemplate = path.join(__dirname, `../test/source/template.html`)
147 |
148 | function customTemplate() {
149 | return gulp
150 | .src(svgGlob)
151 | .pipe(
152 | svgSymbols({
153 | id: `icon-%f`,
154 | className: `.icon-%f`,
155 | title: false,
156 | fontSize: 16,
157 | templates: [
158 | `default-svg`,
159 | `default-demo`,
160 | customCSSTemplate,
161 | customHTMLTemplate,
162 | ],
163 | })
164 | )
165 | .pipe(
166 | rename(function(path) {
167 | path.basename = `icon-files`
168 | })
169 | )
170 | .pipe(gulpif(/[.]svg$/, gulp.dest(`ex-custom-template/views`)))
171 | .pipe(gulpif(/[.]json$/, gulp.dest(`ex-custom-template/front`)))
172 | .pipe(gulpif(/[.]html$/, gulp.dest(`ex-custom-template/tmp`)))
173 | }
174 | customTemplate.description = `Using custom templates`
175 |
176 | ////////
177 | // CUSTOM TEMPLATES with ASPECT RATIO
178 | ////////
179 |
180 | function aspectRatio() {
181 | return gulp
182 | .src([
183 | `../test/source/aspect-ratio.svg`,
184 | `../test/source/chinese letter with styles.svg`,
185 | `../test/source/github.svg`,
186 | ])
187 | .pipe(
188 | svgSymbols({
189 | templates: [
190 | path.join(__dirname, `/aspect-ratio-test.html`),
191 | `default-svg`,
192 | ],
193 | })
194 | )
195 | .pipe(gulp.dest(`ex-aspect-ratio`))
196 | }
197 | aspectRatio.description = `A custom template with aspect ratios`
198 |
199 | ////////
200 | // OUTPUT WITH SVG INCLUDING SAME MASK IDS
201 | ////////
202 |
203 | function svgContainingIdenticalId() {
204 | return (
205 | gulp
206 | .src(`./svg-with-masks-sources/*.svg`)
207 | .pipe(
208 | svgmin(file => {
209 | const { relative } = file
210 | const prefix = path.basename(relative, path.extname(relative))
211 | return {
212 | js2svg: {
213 | pretty: true,
214 | },
215 | plugins: [
216 | {
217 | // this prevent duplicated IDs when bundled in the same file
218 | cleanupIDs: { prefix: `${prefix}-` },
219 | },
220 | {
221 | // some cleaning
222 | removeDoctype: true,
223 | },
224 | {
225 | removeXMLProcInst: true,
226 | },
227 | {
228 | removeTitle: true,
229 | },
230 | {
231 | removeDesc: { removeAny: true },
232 | },
233 | {
234 | convertTransform: {},
235 | },
236 | ],
237 | }
238 | })
239 | )
240 | // We need to move and to the defs…
241 | // …in order for Firefox to render the SVG correctly
242 | .pipe(
243 | cheerio({
244 | run: ($, file) => {
245 | const $clipPath = $(`clipPath`)
246 | const $mask = $(`mask`)
247 | let $defs = $(`defs`)
248 | const hasClipPath = $clipPath.length > 0
249 | const hasMask = $mask.length > 0
250 | const hasDefs = $defs.length > 0
251 | if (!hasClipPath && !hasMask) return
252 | if (!hasDefs) {
253 | $defs = $(` `)
254 | $defs.prependTo(`svg`)
255 | }
256 | function copyToDefs(i, el) {
257 | const $el = $(el)
258 | const $clone = $el.clone()
259 | $clone.appendTo($defs)
260 | $el.remove()
261 | }
262 | if (hasClipPath) $clipPath.each(copyToDefs)
263 | if (hasMask) $mask.each(copyToDefs)
264 | },
265 | parserOptions: {
266 | xmlMode: true,
267 | },
268 | })
269 | )
270 | // reformat
271 | .pipe(beautify.html({ indent_char: ` `, indent_size: 2 }))
272 | .pipe(gulp.dest(`ex-svg-with-masks`))
273 | // everything is ready for gulp-svg-symbols!
274 | .pipe(
275 | svgSymbols({
276 | templates: [`default-demo`],
277 | })
278 | )
279 | .pipe(gulp.dest(`ex-svg-with-masks`))
280 | )
281 | }
282 | svgContainingIdenticalId.description = `How to handle SVGs with masks IDs`
283 |
284 | ////////
285 | // VUE
286 | ////////
287 |
288 | function vue() {
289 | return gulp
290 | .src([
291 | `../test/source/chinese letter with styles.svg`,
292 | `../test/source/gradient.svg`,
293 | `../test/source/xlink-href.svg`,
294 | `../test/source/aspect-ratio.svg`,
295 | `../test/source/crâne noir.svg`,
296 | `../test/source/github.svg`,
297 | ])
298 | .pipe(
299 | svgSymbols({
300 | svgAttrs: { class: `svg-symbol` },
301 | class: `.svg-symbol--%f`,
302 | templates: [`default-vue`, `default-demo`],
303 | })
304 | )
305 | .pipe(gulp.dest(`ex-vue`))
306 | }
307 | vue.description = `vue template`
308 |
309 | ////////
310 | // EXPORTING TASKS
311 | ////////
312 |
313 | gulp.task(`svg`, svg)
314 | gulp.task(`scss`, scss)
315 | gulp.task(`all-build-in-templates`, allBuildInTemplates)
316 | gulp.task(`stylus`, stylus)
317 | gulp.task(`css-var`, cssVar)
318 | gulp.task(`demo-page`, demoPage)
319 | gulp.task(`svg-classname`, svgClassname)
320 | gulp.task(`custom-template`, customTemplate)
321 | gulp.task(`aspect-ratio`, aspectRatio)
322 | gulp.task(`svg-containing-identical-id`, svgContainingIdenticalId)
323 | gulp.task(`vue`, vue)
324 | gulp.task(
325 | `all`,
326 | gulp.parallel(svg, demoPage, svgClassname, customTemplate, aspectRatio, vue)
327 | )
328 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gulp-svg-symbols-examples",
3 | "version": "1.0.0",
4 | "description": "A bunch of gulpfile examples using gulp-svg-symbols",
5 | "main": "gulpfile.js",
6 | "scripts": {
7 | "list": "gulp -T",
8 | "all": "gulp all",
9 | "all-build-in": "gulp all-build-in-templates",
10 | "svg": "gulp svg",
11 | "scss": "gulp scss",
12 | "stylus": "gulp stylus",
13 | "css-var": "gulp css-var",
14 | "demo-page": "gulp demo-page",
15 | "svg-classname": "gulp svg-classname",
16 | "custom-template": "gulp custom-template",
17 | "aspect-ratio": "gulp aspect-ratio",
18 | "svg-containing-identical-id": "gulp svg-containing-identical-id",
19 | "vue": "gulp vue && parcel vue/index.html"
20 | },
21 | "author": "Hiswe",
22 | "license": "MIT",
23 | "devDependencies": {
24 | "@vue/component-compiler-utils": "^3.0.0",
25 | "gulp": "^4.0.0",
26 | "gulp-beautify": "^3.0.0",
27 | "gulp-cheerio": "^0.6.3",
28 | "gulp-if": "^2.0.1",
29 | "gulp-rename": "^1.2.2",
30 | "gulp-sass": "^4.0.1",
31 | "gulp-stylus": "^2.7.0",
32 | "gulp-svg-symbols": "^3.0.2",
33 | "gulp-svgmin": "^2.2.0",
34 | "parcel-bundler": "^1.9.7",
35 | "vue": "^2.5.16",
36 | "vue-template-compiler": "^2.5.16"
37 | },
38 | "dependencies": {
39 | "vue-hot-reload-api": "^2.3.0"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/svg-with-masks-sources/ad-clip-path-donut.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/svg-with-masks-sources/ad-clip-path-heart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/svg-with-masks-sources/ad-compose-path-variation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/svg-with-masks-sources/ad-compose-path.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/svg-with-masks-sources/affinity-designer-source.afdesign:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:abde83c7f7318425a825684ee8f88744cb436c1d398aa699cb94c59c933206ed
3 | size 25239
4 |
--------------------------------------------------------------------------------
/examples/svg-with-masks-sources/figma-output-1.svg:
--------------------------------------------------------------------------------
1 |
2 | 2
3 | Created using Figma
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/examples/svg-with-masks-sources/figma-output-2.svg:
--------------------------------------------------------------------------------
1 |
2 | 3
3 | Created using Figma
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/examples/vue/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Vue icons
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/vue/layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Vue SVG symbols
4 | Fill & scale test
5 |
9 | All icons
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
84 |
85 |
90 |
91 |
--------------------------------------------------------------------------------
/examples/vue/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | import Layout from './layout'
4 | import SvgSymbol from '../ex-vue/svg-symbols'
5 |
6 | Vue.component(`svg-symbol`, SvgSymbol)
7 |
8 | new Vue({
9 | el: `#vue-root`,
10 | render: h => h(Layout),
11 | })
12 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const _ = require(`lodash`)
4 | const path = require(`path`)
5 | const PluginError = require(`plugin-error`)
6 | const through = require(`through2`)
7 |
8 | const defaults = require(`./lib/default-config`)
9 | const svg = require(`./lib/svg`)
10 | const templates = require(`./lib/templates.js`)
11 | const utils = require(`./lib/utils.js`)
12 |
13 | const PLUGIN_NAME = utils.name
14 |
15 | // prettier-ignore
16 | const templatesPath = {
17 | 'default-svg': path.join(__dirname, `./templates/svg-symbols.svg`),
18 | 'default-css': path.join(__dirname, `./templates/svg-symbols.css`),
19 | 'default-css-var': path.join(__dirname, `./templates/svg-symbols-custom-properties.css`),
20 | 'default-scss': path.join(__dirname, `./templates/svg-symbols.scss`),
21 | 'default-stylus': path.join(__dirname, `./templates/svg-symbols.styl`),
22 | 'default-demo': path.join(__dirname, `./templates/svg-symbols-demo-page.html`),
23 | 'default-vue': path.join(__dirname, `./templates/svg-symbols.vue`),
24 | };
25 |
26 | function gulpSvgSymbols(opts = {}) {
27 | const buffer = []
28 | let defs = []
29 |
30 | // clone everything as we don't want to mutate anything
31 | const options = _.defaultsDeep(_.cloneDeep(opts), _.cloneDeep(defaults))
32 | // restore templates array as it will be messed up by _.defaultsDeep
33 | options.templates = opts.templates || defaults.templates
34 |
35 | // expand path to default templates
36 | options.templates = options.templates.map(pathName => {
37 | if (pathName in templatesPath) return templatesPath[pathName]
38 | return pathName
39 | })
40 |
41 | // Handle deprecation warnings for old options and fix the config
42 | // `className` option is now just `class`
43 | if (typeof options.className !== `undefined`) {
44 | utils.logWarn(
45 | options,
46 | `options.className is deprecated. Please replace it with options.class`
47 | )
48 | options.class = options.className
49 | }
50 | // svgClassname option is now living inside svgAttrs
51 | if (typeof options.svgClassname !== `undefined`) {
52 | utils.logWarn(
53 | options,
54 | `options.svgClassname is deprecated. Please replace it with options.svgAttrs.class`
55 | )
56 | options.svgAttrs.class = options.svgClassname
57 | }
58 |
59 | // buffer and transform every files
60 | return through.obj(
61 | function transform(file, encoding, cb) {
62 | if (file.isNull()) {
63 | return cb(null, file)
64 | }
65 |
66 | // we don't handle streams :,(
67 | // use https://github.com/nfroidure/gulp-streamify if you're reading this
68 | // next versions should use https://www.npmjs.com/package/bufferstreams
69 | if (file.isStream()) {
70 | const errorReason = `Streaming is not supported`
71 | this.emit(`error`, new PluginError(PLUGIN_NAME, errorReason))
72 | return cb()
73 | }
74 |
75 | svg.parseFile(file, options).then(result => {
76 | buffer.push(result)
77 | return cb(null)
78 | })
79 |
80 | // put all generated files back in the stream
81 | },
82 | function flush(cb) {
83 | const that = this
84 |
85 | // don't produce any file if no datas
86 | if (buffer.length === 0) {
87 | return cb()
88 | }
89 |
90 | const defsIdList = {}
91 | const svgData = buffer.map(svgRawData => {
92 | // defs are not at an SVG level
93 | // they should be handled globally to the new SVG file
94 | if (svgRawData.defs) defs.push(svgRawData.defs)
95 | // control IDs laying in defs
96 | if (svgRawData.__gatheredIds__) {
97 | _.toPairs(svgRawData.__gatheredIds__).forEach(([key, value]) => {
98 | if (!defsIdList[key]) return (defsIdList[key] = [value])
99 | defsIdList[key].push(value)
100 | })
101 | }
102 | return svg.formatDataForTemplate(svgRawData, options)
103 | })
104 |
105 | // make a warn about duplicated IDs inside defs
106 | const defsIdWarn = []
107 | _.toPairs(defsIdList).forEach(([key, value]) => {
108 | if (value.length < 2) return
109 | const warn = `id “${key}” found in different files (${value.join(
110 | `, `
111 | )})`
112 | defsIdWarn.push(` • ${warn}`)
113 | })
114 | if (defsIdWarn.length) {
115 | utils.logWarn(
116 | options,
117 | ` has some duplicated ids:\n${defsIdWarn.join(
118 | `\n`
119 | )}\nsee https://github.com/Hiswe/gulp-svg-symbols#rendering-caveats`
120 | )
121 | }
122 |
123 | // force defs to have a value.
124 | // better for templates to check if `false` rather than length…
125 | defs = defs.length > 0 ? defs.join(`\n`) : false
126 |
127 | // automatically insert xlink if needed
128 | // even if it's deprecated in SVG2 most software will still produce SVG 1.1
129 | // and I can't find find any good website talking about SVG2 support in browsers…
130 | const haystack =
131 | svgData.map(templates.svgdataToSymbol).join(``) + (defs || ``)
132 | if (/\sxlink:[a-z]+=/.test(haystack)) {
133 | options.svgAttrs[`xmlns:xlink`] = `http://www.w3.org/1999/xlink`
134 | }
135 |
136 | const files = templates.renderAll(options.templates, {
137 | svgAttrs: options.svgAttrs,
138 | icons: svgData,
139 | defs: defs,
140 | })
141 |
142 | function outputFiles(files) {
143 | files.forEach(file => that.push(file))
144 | cb()
145 | }
146 |
147 | Promise.all(files)
148 | .then(outputFiles)
149 | .catch(err => {
150 | this.emit(
151 | `error`,
152 | new PluginError(PLUGIN_NAME, err, { showStack: true })
153 | )
154 | cb()
155 | })
156 | }
157 | )
158 | }
159 |
160 | module.exports = gulpSvgSymbols
161 |
--------------------------------------------------------------------------------
/lib/default-config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = {
4 | // attributes that will be be pushed in the SVG root node
5 | svgAttrs: {
6 | // class that will be added in default template root SVG (deprecated)
7 | class: null,
8 | xmlns: `http://www.w3.org/2000/svg`,
9 | },
10 | // this will be used for generating SVG id and associated class names
11 | id: `%f`,
12 | class: `.%f`,
13 | // Determine size in CSS (em/px)
14 | fontSize: 0,
15 | // debug log
16 | warn: true,
17 | // accessibility title
18 | title: false,
19 | // templates to render
20 | templates: [`default-svg`, `default-css`],
21 | // slug params or custom function
22 | slug: {},
23 | // datas passed to templates while rendering
24 | transformData: function(svg, defaultData, options) {
25 | return defaultData
26 | },
27 | }
28 |
--------------------------------------------------------------------------------
/lib/svg.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const _ = require(`lodash`)
4 | const slug = require(`speakingurl`)
5 | const path = require(`path`)
6 | const cheerio = require(`cheerio`)
7 |
8 | const utils = require(`./utils`)
9 |
10 | //
11 | // GATHER ELEMENTARY INFORMATIONS ABOUT THE SVG FILE
12 | //
13 |
14 | function parseFile(file, options = {}) {
15 | // TODO what if cheerio can't load a content?
16 | const $ = cheerio.load(file.contents.toString(), {
17 | normalizeWhitespace: true,
18 | xmlMode: true,
19 | })
20 | const $svg = $(`svg`)
21 | const attr = $svg[0].attribs
22 | const viewBox = utils.viewboxToArray(attr.viewBox, file.path, options)
23 | const name = /(.*)\.svg/.exec(path.basename(file.path))[1]
24 |
25 | const result = {
26 | name: name,
27 | viewBox: viewBox.join(` `),
28 | originalAttributes: attr,
29 | // SVG files might not have size
30 | // https://github.com/Hiswe/gulp-svg-symbols/issues/10
31 | width: utils.sizeOrViewboxFallback(attr.width, viewBox[2]),
32 | height: utils.sizeOrViewboxFallback(attr.height, viewBox[3]),
33 | }
34 |
35 | // ID generation
36 | // spaces in IDs or Classes are never a good idea
37 | if (_.isFunction(options.slug)) {
38 | // let user add his custom parsing function…
39 | result.id = options.slug(name)
40 | } else if (_.isPlainObject(options.slug)) {
41 | // …or pass custom option to speakingurl
42 | result.id = slug(name, options.slug)
43 | } else {
44 | result.id = slug(name)
45 | }
46 |
47 | // STYLE handling
48 | const $style = $svg.find(`style`)
49 | if ($style.length) {
50 | result.style = $style.html().trim()
51 | // don't format more than adding newlines after each rules end
52 | result.style = result.style.replace(/}\s*(?!\n)/g, `}\n`)
53 | }
54 | $style.remove()
55 |
56 | // DEFS handling
57 | const $defs = $svg.find(`defs`)
58 | if ($defs.children().length) {
59 | result.defs = $defs.html()
60 | // gather all ids inside defs
61 | // this will be used in the plugin to check if ids are not shared among files
62 | $defs.find(`[id]`).each((i, el) => {
63 | result.__gatheredIds__ = result.__gatheredIds__ || {}
64 | const id = $(el).attr(`id`)
65 | result.__gatheredIds__[id] = result.name
66 | })
67 | }
68 | $defs.remove()
69 |
70 | // CONTENT
71 | // only optim is to remove empty group
72 | // but shouldn't be done: SVG Symbol should only do concat SVG files
73 | $svg.find(`g`).each(function() {
74 | if (!$(this).children().length) $(this).remove()
75 | })
76 |
77 | // & check
78 | // if they are not inside a tag it can result in wrong display on Firefox
79 | ;[`clipPath`, `mask`].forEach(tagName => {
80 | const $el = $(tagName)
81 | if (!$el.length) return
82 | const warn = `<${tagName}> tag found outside a tag in file ${
83 | file.path
84 | }. This can lead to unexpected results.\nsee https://github.com/Hiswe/gulp-svg-symbols#rendering-caveats`
85 | utils.logWarn(options, warn)
86 | })
87 |
88 | result.content = $svg.html()
89 |
90 | return Promise.resolve(result)
91 | }
92 |
93 | //
94 | // MODIFY DATAS BEFORE GIVING IT TO TEMPLATES
95 | //
96 |
97 | function formatDataForTemplate(svgRawData, options) {
98 | let result = {}
99 | // this can be overrided by user transformData function
100 | const tmplDatas = {
101 | id: utils.dynamicText(options.id, svgRawData.id),
102 | class: utils.dynamicText(options.class, svgRawData.id),
103 | width: utils.cssSize(svgRawData.width, options.fontSize),
104 | height: utils.cssSize(svgRawData.height, options.fontSize),
105 | }
106 | // It should be handled by a custom template or custom transformData
107 | if (options.title !== false && !//.test(svgRawData.content)) {
108 | tmplDatas.title = utils.dynamicText(options.title, svgRawData.name)
109 | }
110 |
111 | // Styles coming from are kept in the SVG file
112 | // we don't take care of duplicated styles or anything else
113 | if (svgRawData.style) tmplDatas.style = svgRawData.style
114 |
115 | // Apply TransformData option
116 | // no need to be able to call transformData inside transformData %)
117 | result = options.transformData(
118 | svgRawData,
119 | tmplDatas,
120 | _.omit(options, [`transformData`, `templates`])
121 | )
122 | // Always keep a reference of the original datas
123 | result.svg = svgRawData
124 |
125 | return result
126 | }
127 |
128 | module.exports = {
129 | parseFile,
130 | formatDataForTemplate,
131 | }
132 |
--------------------------------------------------------------------------------
/lib/templates.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require(`path`)
4 | const consolidate = require(`consolidate`)
5 | consolidate.requires.lodash = require(`lodash`)
6 | const tmpl = consolidate.lodash
7 | const utils = require(`./utils`)
8 |
9 | function attributesToString(obj) {
10 | return Object.getOwnPropertyNames(obj).reduce((str, key) => {
11 | const attr = key.replace(/[^a-zA-Z-:]/g, ``)
12 | const raw = obj[key]
13 | const rawEscaped = String(raw).replace(/"/g, `"`)
14 | const t = typeof raw
15 | if (t === `boolean` && raw === true) {
16 | return `${str} ${attr}`
17 | }
18 | if (t === `string` || (t === `number` && !isNaN(raw))) {
19 | return `${str} ${attr}="${rawEscaped}"`
20 | }
21 | return str
22 | }, ``)
23 | }
24 |
25 | function svgdataToSymbol(icon) {
26 | const symbolAttrs = attributesToString({
27 | id: icon.id,
28 | viewBox: icon.svg.viewBox,
29 | preserveAspectRatio: icon.svg.originalAttributes.preserveAspectRatio,
30 | })
31 | const title = icon.title ? `${icon.title} ` : ``
32 | return `${title}${icon.svg.content} `
33 | }
34 |
35 | function render(template, datas) {
36 | const name = path.basename(template)
37 | // add some utilities function to lodash template engine
38 | // this avoid bloating the template with javascript
39 | // and keep the use of it optionnal
40 | datas.imports = {
41 | attributesToString,
42 | svgdataToSymbol,
43 | }
44 | return tmpl(template, datas).then(result => utils.createFile(name, result))
45 | }
46 |
47 | function renderAll(templates, datas) {
48 | return templates.map(template => render(template, datas))
49 | }
50 |
51 | module.exports = {
52 | attributesToString,
53 | svgdataToSymbol,
54 | renderAll,
55 | render,
56 | }
57 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const _ = require(`lodash`)
4 | const log = require(`fancy-log`)
5 | const yellow = require(`ansi-yellow`)
6 | const grey = require(`ansi-grey`)
7 | const Vinyl = require(`vinyl`)
8 | const PLUGIN_NAME = `gulp-svg-symbols`
9 |
10 | function logWarn(options, ...message) {
11 | if (options.warn) {
12 | log(yellow(PLUGIN_NAME), ...message)
13 | }
14 | }
15 |
16 | // Format a size to px or em
17 | function cssSize(size, fontSize) {
18 | let unit = `px`
19 | if (_.isNumber(fontSize) && fontSize > 0) {
20 | unit = `em`
21 | }
22 | if (unit === `px`) {
23 | return size + `px`
24 | }
25 | return _.round(size / fontSize, 3) + `em`
26 | }
27 |
28 | function dynamicText(template, name) {
29 | return template.replace(`%f`, name)
30 | }
31 |
32 | function viewboxToArray(viewbox, name, options) {
33 | if (_.isUndefined(viewbox)) {
34 | logWarn(options, grey(`viewbox missing in file`), name)
35 | return [0, 0, 100, 100]
36 | }
37 |
38 | return viewbox.split(` `).map(function(value) {
39 | return parseFloat(value, 10)
40 | })
41 | }
42 |
43 | function sizeOrViewboxFallback(size, fallback) {
44 | // no size -> get viewbox fallback
45 | if (_.isUndefined(size)) {
46 | return fallback
47 | }
48 | // handle percent svg size -> get viewbox fallback
49 | // https://github.com/Hiswe/gulp-svg-symbols/issues/24
50 | if (/\d+%/.test(size)) {
51 | return (parseInt(size, 10) * fallback) / 100
52 | }
53 | return parseInt(size, 10)
54 | }
55 |
56 | function createFile(name, contents) {
57 | return new Vinyl({
58 | cwd: `./`,
59 | base: `./`,
60 | path: name,
61 | contents: new Buffer(contents),
62 | })
63 | }
64 |
65 | module.exports = {
66 | logWarn,
67 | cssSize,
68 | dynamicText,
69 | viewboxToArray,
70 | sizeOrViewboxFallback,
71 | createFile,
72 | name: PLUGIN_NAME,
73 | }
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gulp-svg-symbols",
3 | "version": "3.2.3",
4 | "description": "Convert SVG files to symbols with gulp",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "ava test/*.js",
8 | "lint": "prettier --write \"{{lib,test}/*,index,examples/gulpfile}.js\"",
9 | "toc": "doctoc README.md TEMPLATES.md --github",
10 | "release": "np --no-yarn"
11 | },
12 | "repository": "https://github.com/Hiswe/gulp-svg-symbols.git",
13 | "keywords": [
14 | "gulpplugin",
15 | "svg",
16 | "symbols"
17 | ],
18 | "author": "Yannick Aïvayan (http://hiswe.net)",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/Hiswe/gulp-svg-symbols/issues"
22 | },
23 | "homepage": "https://github.com/Hiswe/gulp-svg-symbols",
24 | "dependencies": {
25 | "ansi-grey": "^0.1.1",
26 | "ansi-yellow": "^0.1.1",
27 | "cheerio": "^1.0.0-rc.3",
28 | "consolidate": "^0.15.1",
29 | "fancy-log": "^1.3.3",
30 | "lodash": "^4.17.11",
31 | "plugin-error": "^1.0.1",
32 | "speakingurl": "^14.0.1",
33 | "through2": "^3.0.1",
34 | "vinyl": "^2.2.0"
35 | },
36 | "devDependencies": {
37 | "ava": "^1.4.1",
38 | "doctoc": "^1.4.0",
39 | "event-stream": "^4.0.1",
40 | "gulp": "^4.0.2",
41 | "intercept-stdout": "^0.1.2",
42 | "prettier": "^1.17.1"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/templates/svg-symbols-custom-properties.css:
--------------------------------------------------------------------------------
1 | :root {<% _.forEach( icons, function( icon ){ %>
2 | --icon-<%= icon.id %>-width: <%= icon.width %>;
3 | --icon-<%= icon.id %>-height: <%= icon.height %>;<% }); %>
4 | }
5 |
6 | <% _.forEach( icons, function( icon ){ %><%= icon.class %> {
7 | width: var(--icon-<%= icon.id %>-width);
8 | height: var(--icon-<%= icon.id %>-height);
9 | }
10 | <%= icon.style %>
11 | <% }); %>
12 |
--------------------------------------------------------------------------------
/templates/svg-symbols-demo-page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= icons.length + ' Symbols' %>
7 |
8 |
376 |
385 |
386 |
387 |
388 |
389 |
390 | <%= defs %>
391 |
392 | <% _.forEach( icons, function( icon ){ %>
393 | preserveAspectRatio="<%= icon.svg.originalAttributes.preserveAspectRatio %>"<% }%>><% if (icon.title) {%>
394 | <%= icon.title %> <% }%>
395 | <%= icon.svg.content %>
396 | <%
397 | }); %>
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
417 |
418 |
419 | <%
420 | icons.forEach(function(icon) {
421 | // Approximate size = content + id +
with viewBox markup
422 | var size = icon.svg.content.length + icon.id.length + 50;
423 | var hrSize = Math.max(Math.round(size/100), 1) / 10;
424 | var dimensions = [
425 | Math.round(icon.svg.width),
426 | Math.round(icon.svg.height)
427 | ];
428 | var search = [
429 | icon.svg.name.trim().toLowerCase(),
430 | icon.id.trim().toLowerCase(),
431 | dimensions.join('x')
432 | ].concat([1, 2, 3, 4, 5, 6, 7, 8, 9].map(function(s){
433 | return (hrSize >= s ? '>' : '<') + s;
434 | }));
435 | var example = '\n' +
436 | ' \n ';
437 | %>
438 |
441 |
445 |
446 | <%= icon.svg.name %>.svg
447 | <%= dimensions.join(' × ') %>
448 | <%= size %> B
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 | <%= _.escape(example) %>
458 |
459 |
460 |
461 | Copy
462 | Show
463 |
464 |
<% }); %>
465 |
466 |
467 |
671 |
672 |
673 |
--------------------------------------------------------------------------------
/templates/svg-symbols.css:
--------------------------------------------------------------------------------
1 | <% _.forEach( icons, function( icon ){ %><%= icon.class %> {
2 | width: <%= icon.width %>;
3 | height: <%= icon.height %>;
4 | }
5 | <%= icon.style %>
6 | <% }); %>
7 |
--------------------------------------------------------------------------------
/templates/svg-symbols.scss:
--------------------------------------------------------------------------------
1 | <% _.forEach( icons, function( icon ){ %>
2 | $icon-<%= icon.id %>-width: <%= icon.width %>;
3 | $icon-<%= icon.id %>-height: <%= icon.height %>;<% }); %>
4 |
5 | <% _.forEach( icons, function( icon ){ %><%= icon.class %> {
6 | width: $icon-<%= icon.id %>-width;
7 | height: $icon-<%= icon.id %>-height;
8 | }
9 | <%= icon.style %>
10 | <% }); %>
11 |
--------------------------------------------------------------------------------
/templates/svg-symbols.styl:
--------------------------------------------------------------------------------
1 | <% _.forEach( icons, function( icon ){ %>
2 | $icon-<%= icon.id %>-width = <%= icon.width %>;
3 | $icon-<%= icon.id %>-height = <%= icon.height %>;<% }); %>
4 |
5 | <% _.forEach( icons, function( icon ){ %><%= icon.class %> {
6 | width: $icon-<%= icon.id %>-width;
7 | height: $icon-<%= icon.id %>-height;
8 | }
9 | <%= icon.style %>
10 | <% }); %>
11 |
--------------------------------------------------------------------------------
/templates/svg-symbols.svg:
--------------------------------------------------------------------------------
1 | >
2 | <% if (defs) { %>
3 | <%= defs.trim() %>
4 |
5 | <% } %><%= icons.map(svgdataToSymbol).join('\n') %>
6 |
7 |
--------------------------------------------------------------------------------
/templates/svg-symbols.vue:
--------------------------------------------------------------------------------
1 |
85 |
--------------------------------------------------------------------------------
/test/get-svg-datas.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import fs from 'fs'
3 | import Vinyl from 'vinyl'
4 | import test from 'ava'
5 |
6 | import { parseFile as formatSvgData } from '../lib/svg.js'
7 |
8 | ////////
9 | // BASIC
10 | ////////
11 |
12 | // Use the skull files for that
13 | const svgFile = new Vinyl({
14 | base: `test/source`,
15 | cwd: `test/`,
16 | path: `test/source/skull.svg`,
17 | contents: fs.readFileSync(`test/source/skull.svg`),
18 | })
19 |
20 | const authorizedInfo = [
21 | `name`,
22 | `viewBox`,
23 | `originalAttributes`,
24 | `id`,
25 | `width`,
26 | `height`,
27 | `content`,
28 | ].sort()
29 |
30 | const expectedInfo = {
31 | content: fs.readFileSync(`test/output/skull-symbol.svg`).toString(),
32 | width: 150,
33 | height: 150,
34 | name: `skull`,
35 | viewBox: `-50 0 150 150`,
36 | }
37 |
38 | const basicTitle = `get SVG datas - Gather basic info from SVG`
39 |
40 | test(`${basicTitle} - should be an object`, async t => {
41 | const result = await formatSvgData(svgFile)
42 | t.true(_.isPlainObject(result))
43 | t.deepEqual(Object.keys(result).sort(), authorizedInfo)
44 | })
45 |
46 | test(`${basicTitle} - should have the right width & height`, async t => {
47 | const result = await formatSvgData(svgFile)
48 | t.is(result.width, expectedInfo.width, `width`)
49 | t.is(result.height, expectedInfo.height, `height`)
50 | })
51 |
52 | test(`${basicTitle} - should have the right viewbox`, async t => {
53 | const result = await formatSvgData(svgFile)
54 | t.is(result.viewbox, expectedInfo.viewbox)
55 | })
56 |
57 | test(`${basicTitle} - output the right name`, async t => {
58 | const result = await formatSvgData(svgFile)
59 | t.is(result.name, expectedInfo.name)
60 | })
61 |
62 | ////////
63 | // NO DIMENSIONS
64 | ////////
65 |
66 | const noDimensionSvgFile = new Vinyl({
67 | base: `test/source`,
68 | cwd: `test/`,
69 | path: `test/source/skull.svg`,
70 | contents: fs.readFileSync(`test/source/gear_without_dimensions.svg`),
71 | })
72 |
73 | const noDimensionExpectedInfo = {
74 | content: fs
75 | .readFileSync(`test/output/gear_without_dimensions-symbol.svg`)
76 | .toString(),
77 | width: 229.6,
78 | height: 259.9,
79 | name: `gear_without_dimensions`,
80 | viewBox: `0 0 229.6 259.9`,
81 | }
82 |
83 | const noDimensionTitle = `get SVG datas - Handle SVG without dimensions`
84 |
85 | test(`${noDimensionTitle} - should have the right width & height`, async t => {
86 | const result = await formatSvgData(noDimensionSvgFile)
87 | t.is(result.width, noDimensionExpectedInfo.width, `width`)
88 | t.is(result.height, noDimensionExpectedInfo.height, `height`)
89 | })
90 |
91 | test(`${noDimensionTitle} - should have the right viewbox`, async t => {
92 | const result = await formatSvgData(noDimensionSvgFile)
93 | t.is(result.viewbox, noDimensionExpectedInfo.viewbox)
94 | })
95 |
96 | ////////
97 | // PERCENT DIMENSIONS
98 | ////////
99 |
100 | // https://github.com/Hiswe/gulp-svg-symbols/issues/24
101 |
102 | const percentSvgFile = new Vinyl({
103 | base: `test/source`,
104 | cwd: `test/`,
105 | path: `test/source/skull.svg`,
106 | contents: fs.readFileSync(`test/source/icon-with-percent-size.svg`),
107 | })
108 |
109 | const percentExpectedInfo = {
110 | width: 20,
111 | height: 26,
112 | name: `icon-with-percent-size`,
113 | viewBox: `0 0 20 26`,
114 | }
115 |
116 | const percentTitle = `get SVG datas - Handle SVG with percent dimensions`
117 |
118 | test(`${percentTitle} - should have the right width & height`, async t => {
119 | const result = await formatSvgData(percentSvgFile)
120 | t.is(result.width, percentExpectedInfo.width, `width`)
121 | t.is(result.height, percentExpectedInfo.height, `height`)
122 | })
123 |
124 | test(`${percentTitle} - should have the right viewbox`, async t => {
125 | const result = await formatSvgData(percentSvgFile)
126 | t.is(result.viewbox, percentExpectedInfo.viewbox)
127 | })
128 |
--------------------------------------------------------------------------------
/test/output/codepen-symbol.css:
--------------------------------------------------------------------------------
1 | .icon-codepen {
2 | width: 1.5em;
3 | height: 1.5em;
4 | }
--------------------------------------------------------------------------------
/test/output/codepen-symbol.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/output/gear_without_dimensions-symbol.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/output/github-symbol.css:
--------------------------------------------------------------------------------
1 | .github {
2 | width: 22px;
3 | height: 24px;
4 | }
--------------------------------------------------------------------------------
/test/output/github-symbol.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | github icon
4 |
5 |
6 |
--------------------------------------------------------------------------------
/test/output/mail-symbol-accessible.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | envelope
4 | a stylised envelope
5 |
6 |
7 |
--------------------------------------------------------------------------------
/test/output/mail-symbol.css:
--------------------------------------------------------------------------------
1 | .icon-mail {
2 | width: 1.75em;
3 | height: 1.5em;
4 | }
--------------------------------------------------------------------------------
/test/output/mail-symbol.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test/output/no-title.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/test/output/skull-symbol.css:
--------------------------------------------------------------------------------
1 | .skull {
2 | width: 150px;
3 | height: 150px;
4 | }
--------------------------------------------------------------------------------
/test/output/skull-symbol.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/output/svg-symbols.css:
--------------------------------------------------------------------------------
1 | .chinese-letter-with-styles {
2 | width: 36px;
3 | height: 36px;
4 | }
5 | .cls-1 { fill: #fff45c; stroke: #fc0; stroke-width: 2px; }
6 | .cls-2, .cls-3 { font-size: 20px; }
7 | .cls-3 { fill: #fc0; font-family: "Microsoft YaHei"; }
8 |
9 | .gear_without_dimensions {
10 | width: 229px;
11 | height: 259px;
12 | }
13 |
14 | .github {
15 | width: 22px;
16 | height: 24px;
17 | }
18 |
19 | .gradient {
20 | width: 100px;
21 | height: 100px;
22 | }
23 |
24 | .ios {
25 | width: 32px;
26 | height: 32px;
27 | }
28 |
29 | .skull {
30 | width: 150px;
31 | height: 150px;
32 | }
33 |
34 | .warning-with-styles-and-empty-group {
35 | width: 64px;
36 | height: 64px;
37 | }
38 | .alert{fill:#C40000;}
39 | .white{fill:#FFFFFF;}
40 | .st0{fill:#808285;}
41 | .st1{fill:#AEAFAF;}
42 | .st2{fill:#231F20;}
43 | .st3{opacity:0.5;fill:#231F20;}
44 | .st4{opacity:0.8;fill:#231F20;}
45 | .st5{opacity:0.1;fill:#231F20;}
46 | .st6{opacity:0.55;fill:#231F20;}
47 | .st7{opacity:0.3;fill:#231F20;}
48 | .st8{fill:#FFFFFF;stroke:#AEAFAF;}
49 | .st9{fill-rule:evenodd;clip-rule:evenodd;fill:#82B82D;}
50 |
51 | .zoom {
52 | width: 20px;
53 | height: 20px;
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/test/output/svg-symbols.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | chinese letter with styles icon
7 | 爽
8 |
9 |
10 | gear_without_dimensions icon
11 |
21 |
22 |
23 | github icon
24 |
37 |
38 |
39 | gradient icon
40 |
42 |
43 |
44 | ios icon
45 |
64 |
65 |
66 | skull icon
67 |
102 |
103 |
104 | warning with styles and empty group icon
105 |
107 |
108 |
109 | zoom icon
110 |
111 |
--------------------------------------------------------------------------------
/test/output/template.html:
--------------------------------------------------------------------------------
1 | pouic
2 | clapou
--------------------------------------------------------------------------------
/test/output/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "icons": [
3 | {"id": "pouic"},
4 | {"id": "clapou"}
5 |
6 | ]
7 | }
--------------------------------------------------------------------------------
/test/plugin.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp'
2 | import es from 'event-stream'
3 | import test from 'ava'
4 |
5 | import svgSymbols from '../index.js'
6 |
7 | ////////
8 | // BASIC
9 | ////////
10 |
11 | const basicTitle = `Plugin – basic`
12 |
13 | test.cb(`${basicTitle} - should produce two files`, t => {
14 | gulp
15 | .src([
16 | `test/source/*.svg`,
17 | `!test/source/crâne noir.svg`,
18 | `!test/source/aspect-ratio.svg`,
19 | ])
20 | .pipe(svgSymbols({ warn: false }))
21 | .pipe(
22 | es.writeArray((err, output) => {
23 | t.is(output.length, 2)
24 | t.is(output[0].path, `svg-symbols.svg`)
25 | t.is(output[1].path, `svg-symbols.css`)
26 | t.end()
27 | })
28 | )
29 | })
30 |
31 | // control duplicate attributes in a watch case
32 | // https://github.com/Hiswe/gulp-svg-symbols/issues/2
33 |
34 | test.cb(`${basicTitle} - have the right output if called many times`, t => {
35 | gulp
36 | .src(`test/source/github.svg`)
37 | .pipe(svgSymbols({ warn: false }))
38 | .pipe(
39 | es.wait(function() {
40 | gulp
41 | .src(`test/source/github.svg`)
42 | .pipe(svgSymbols({ warn: false }))
43 | .pipe(
44 | es.writeArray((err, output) => {
45 | const svg = output[0].contents.toString()
46 | const css = output[1].contents.toString()
47 | t.is(output.length, 2)
48 | t.regex(svg, / {
58 | gulp
59 | .src(`test/source/github.svg`)
60 | .pipe(
61 | svgSymbols({
62 | templates: [`default-demo`],
63 | warn: false,
64 | })
65 | )
66 | .pipe(
67 | es.writeArray((err, output) => {
68 | t.is(output.length, 1)
69 | t.is(output[0].path, `svg-symbols-demo-page.html`)
70 | t.end()
71 | })
72 | )
73 | })
74 |
75 | test.cb(
76 | `${basicTitle} - shouldn't generate any file if no svg are passed in`,
77 | t => {
78 | gulp
79 | .src(`this/path/doesnt/match/any/*.svg`)
80 | .pipe(svgSymbols({ warn: false }))
81 | .pipe(
82 | es.writeArray((err, output) => {
83 | t.is(output.length, 0)
84 | t.end()
85 | })
86 | )
87 | }
88 | )
89 |
90 | ////////
91 | // CONCAT DEFS
92 | ////////
93 |
94 | const defsTitle = `Plugin – defs`
95 |
96 | test.cb(`${defsTitle} - should handle svg with defs`, t => {
97 | gulp
98 | .src(`test/source/gradient.svg`)
99 | .pipe(svgSymbols({ warn: false }))
100 | .pipe(
101 | es.writeArray((err, output) => {
102 | const svgContent = output[0].contents.toString()
103 | t.regex(svgContent, //g)
104 | t.end()
105 | })
106 | )
107 | })
108 |
109 | test.cb(`${defsTitle} - should handle svg withouts defs`, t => {
110 | gulp
111 | .src(`test/source/gear_without_dimensions.svg`)
112 | .pipe(svgSymbols({ warn: false }))
113 | .pipe(
114 | es.writeArray((err, output) => {
115 | const svgContent = output[0].contents.toString()
116 | t.notRegex(svgContent, //g)
117 | t.end()
118 | })
119 | )
120 | })
121 |
122 | test.cb(`${defsTitle} - should handle svg with empty defs`, t => {
123 | gulp
124 | .src(`test/source/chinese letter with styles.svg`)
125 | .pipe(svgSymbols({ warn: false }))
126 | .pipe(
127 | es.writeArray((err, output) => {
128 | const svgContent = output[0].contents.toString()
129 | t.notRegex(svgContent, //g)
130 | t.end()
131 | })
132 | )
133 | })
134 |
135 | ////////
136 | // CONCAT STYLES
137 | ////////
138 |
139 | test.cb(
140 | `Plugin - style tags - should remove style attributes and put content in another file`,
141 | t => {
142 | gulp
143 | .src(`test/source/warning with styles and empty group.svg`)
144 | .pipe(svgSymbols({ warn: false }))
145 | .pipe(
146 | es.writeArray((err, output) => {
147 | const svgContent = output[0].contents.toString()
148 | const cssContent = output[1].contents.toString()
149 | t.notRegex(svgContent, /
19 |
20 |
21 |
22 | 爽
23 |
24 |
25 |
--------------------------------------------------------------------------------
/test/source/crâne noir.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
15 |
22 |
25 |
27 |
30 |
33 |
35 |
37 |
40 |
43 |
46 |
49 |
51 |
53 |
55 |
57 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/test/source/duplicated-defs-ids-1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/source/duplicated-defs-ids-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/source/figma-mask-outside-defs.svg:
--------------------------------------------------------------------------------
1 |
2 | 2
3 | Created using Figma
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/test/source/gear_without_dimensions.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
16 |
--------------------------------------------------------------------------------
/test/source/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
19 |
20 |
--------------------------------------------------------------------------------
/test/source/gradient.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/source/icon-with-percent-size.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/source/instagram-black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | instagram-black
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/source/ios.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
16 |
24 |
26 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/source/skull.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
15 |
22 |
25 |
27 |
30 |
33 |
35 |
37 |
40 |
43 |
46 |
49 |
51 |
53 |
55 |
57 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/test/source/template.html:
--------------------------------------------------------------------------------
1 | <% _.each( icons, function( icon ){ %><%= icon.id %>
2 | <% }); %>
--------------------------------------------------------------------------------
/test/source/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "icons": [
3 | <% _.forEach( icons, function( icon, index, collection )
4 | { %>{"id": "<%= icon.id %>"}<% if (index !== collection.length -1) {%>,<% }%>
5 | <% }); %>
6 | ]
7 | }
--------------------------------------------------------------------------------
/test/source/warning with styles and empty group.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
20 |
21 |
22 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/test/source/xlink-href.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | An example link.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/test/source/zoom.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/templates.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp'
2 | import fs from 'fs'
3 | import path from 'path'
4 | import es from 'event-stream'
5 | import test from 'ava'
6 |
7 | import svgSymbols from '../index.js'
8 | import templates from '../lib/templates.js'
9 |
10 | ////////
11 | // DEFAULT SVG TEMPLATE
12 | ////////
13 |
14 | const defaultSvgTitle = `Attributes handling in default-svg`
15 |
16 | test.cb(`${defaultSvgTitle} - should add xmlns attribute`, t => {
17 | gulp
18 | .src(`test/source/*.svg`)
19 | .pipe(
20 | svgSymbols({
21 | warn: false,
22 | templates: [`default-svg`],
23 | })
24 | )
25 | .pipe(
26 | es.writeArray((err, output) => {
27 | const svg = output[0].contents.toString()
28 | t.regex(svg, /xmlns="http:\/\/www.w3.org\/2000\/svg"/g)
29 | t.end()
30 | })
31 | )
32 | })
33 |
34 | test.cb(`${defaultSvgTitle} - add a class to root SVG when wanted`, t => {
35 | gulp
36 | .src(`test/source/*.svg`)
37 | .pipe(
38 | svgSymbols({
39 | warn: false,
40 | svgAttrs: { class: `foobar` },
41 | templates: [`default-svg`],
42 | })
43 | )
44 | .pipe(
45 | es.writeArray((err, output) => {
46 | const svg = output[0].contents.toString()
47 | t.regex(svg, /class="foobar"/g)
48 | t.end()
49 | })
50 | )
51 | })
52 |
53 | test.cb(`${defaultSvgTitle} - handle deprecated svgClassname`, t => {
54 | gulp
55 | .src(`test/source/*.svg`)
56 | .pipe(
57 | svgSymbols({
58 | warn: false,
59 | svgClassname: `foobar`,
60 | templates: [`default-svg`],
61 | })
62 | )
63 | .pipe(
64 | es.writeArray((err, output) => {
65 | const svg = output[0].contents.toString()
66 | t.regex(svg, /class="foobar"/g)
67 | t.end()
68 | })
69 | )
70 | })
71 |
72 | test.cb(`${defaultSvgTitle} - should add any string attributes`, t => {
73 | gulp
74 | .src(`test/source/*.svg`)
75 | .pipe(
76 | svgSymbols({
77 | warn: false,
78 | svgAttrs: { foo: `bar`, pouic: `clapou` },
79 | templates: [`default-svg`],
80 | })
81 | )
82 | .pipe(
83 | es.writeArray((err, output) => {
84 | const svg = output[0].contents.toString()
85 | t.regex(svg, /foo="bar"/g)
86 | t.regex(svg, /pouic="clapou"/g)
87 | t.end()
88 | })
89 | )
90 | })
91 |
92 | test.cb(
93 | `${defaultSvgTitle} - add any string attributes with double quotes`,
94 | t => {
95 | gulp
96 | .src(`test/source/*.svg`)
97 | .pipe(
98 | svgSymbols({
99 | warn: false,
100 | svgAttrs: { foo: `"bar"` },
101 | templates: [`default-svg`],
102 | })
103 | )
104 | .pipe(
105 | es.writeArray((err, output) => {
106 | const svg = output[0].contents.toString()
107 | t.regex(svg, /foo=""bar""/g)
108 | t.end()
109 | })
110 | )
111 | }
112 | )
113 |
114 | test.cb(`${defaultSvgTitle} - handle any boolean attributes`, t => {
115 | gulp
116 | .src(`test/source/*.svg`)
117 | .pipe(
118 | svgSymbols({
119 | warn: false,
120 | svgAttrs: { foo: true, bar: false },
121 | templates: [`default-svg`],
122 | })
123 | )
124 | .pipe(
125 | es.writeArray((err, output) => {
126 | const svg = output[0].contents.toString()
127 | t.regex(svg, /\sfoo/g)
128 | t.notRegex(svg, /\sbar/g)
129 | t.end()
130 | })
131 | )
132 | })
133 |
134 | test.cb(`${defaultSvgTitle} - remove xmlns attribute if setted to false`, t => {
135 | gulp
136 | .src(`test/source/*.svg`)
137 | .pipe(
138 | svgSymbols({
139 | warn: false,
140 | svgAttrs: { xmlns: false },
141 | templates: [`default-svg`],
142 | })
143 | )
144 | .pipe(
145 | es.writeArray((err, output) => {
146 | const svg = output[0].contents.toString()
147 | t.notRegex(svg, /xmlns="http:\/\/www.w3.org\/2000\/svg"/g)
148 | t.end()
149 | })
150 | )
151 | })
152 |
153 | test.cb(`${defaultSvgTitle} - should handle any number attributes`, t => {
154 | gulp
155 | .src(`test/source/*.svg`)
156 | .pipe(
157 | svgSymbols({
158 | warn: false,
159 | svgAttrs: { foo: 300 },
160 | templates: [`default-svg`],
161 | })
162 | )
163 | .pipe(
164 | es.writeArray((err, output) => {
165 | const svg = output[0].contents.toString()
166 | t.regex(svg, /\sfoo="300"/g)
167 | t.end()
168 | })
169 | )
170 | })
171 |
172 | test.cb(
173 | `${defaultSvgTitle} - add the xlink namespace if found inside the file`,
174 | t => {
175 | gulp
176 | .src(`test/source/xlink-href.svg`)
177 | .pipe(
178 | svgSymbols({
179 | warn: false,
180 | svgAttrs: { foo: 300 },
181 | templates: [`default-svg`],
182 | })
183 | )
184 | .pipe(
185 | es.writeArray((err, output) => {
186 | const svg = output[0].contents.toString()
187 | t.regex(svg, /\sxmlns:xlink="http:\/\/www.w3.org\/1999\/xlink"/g)
188 | t.end()
189 | })
190 | )
191 | }
192 | )
193 |
194 | test.cb(`${defaultSvgTitle} - keep the options`, t => {
195 | gulp
196 | .src(`test/source/xlink-href.svg`)
197 | .pipe(
198 | svgSymbols({
199 | warn: false,
200 | svgAttrs: { foo: 300 },
201 | templates: [`default-svg`],
202 | })
203 | )
204 | .pipe(
205 | es.writeArray((err, output) => {
206 | const svg = output[0].contents.toString()
207 | t.regex(svg, /\sxmlns:xlink="http:\/\/www.w3.org\/1999\/xlink"/g)
208 | t.end()
209 | })
210 | )
211 | })
212 |
213 | ////////
214 | // CUSTOM TEMPLATE
215 | ////////
216 |
217 | const htmlOutput = fs.readFileSync(`test/output/template.html`).toString()
218 | const jsonOutput = fs.readFileSync(`test/output/template.json`).toString()
219 |
220 | const datas = {
221 | icons: [{ id: `pouic` }, { id: `clapou` }],
222 | }
223 | const tmpl = [
224 | path.join(__dirname, `./source/template.html`),
225 | path.join(__dirname, `./source/template.json`),
226 | ]
227 |
228 | const customTmplTitle = `Render custom templates`
229 |
230 | test(`${customTmplTitle} - render a custom template`, async t => {
231 | const file = await templates.render(tmpl[0], datas)
232 | t.is(file.contents.toString(), htmlOutput)
233 | })
234 |
235 | test(`${customTmplTitle} - render an array of templates`, async t => {
236 | const files = await Promise.all(templates.renderAll(tmpl, datas))
237 | t.is(files.length, 2)
238 | t.is(files[0].contents.toString(), htmlOutput)
239 | t.is(files[1].contents.toString(), jsonOutput)
240 | })
241 |
--------------------------------------------------------------------------------
/test/transform-raw-data.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import fs from 'fs'
3 | import test from 'ava'
4 |
5 | import { formatDataForTemplate as transformSvgData } from '../lib/svg.js'
6 | import defaultOptions from '../lib/default-config.js'
7 |
8 | const svgRawData = {
9 | content: fs.readFileSync(`test/output/codepen-symbol.svg`).toString(),
10 | width: 24,
11 | height: 24,
12 | name: `codepen square`,
13 | id: `codepen-square`,
14 | viewBox: `0 0 24 24`,
15 | }
16 |
17 | ////////
18 | // DEFAULT
19 | ////////
20 |
21 | const resultDefault = {
22 | svg: svgRawData,
23 | id: `codepen-square`,
24 | class: `.codepen-square`,
25 | title: `codepen square icon`,
26 | width: `24px`,
27 | height: `24px`,
28 | }
29 |
30 | const defaultTitle = `Transform data - default`
31 |
32 | test(`${defaultTitle} - should be an object`, t => {
33 | const result = transformSvgData(svgRawData, defaultOptions)
34 | t.true(_.isPlainObject(result))
35 | })
36 |
37 | test(`${defaultTitle} - should have the raw datas`, t => {
38 | const result = transformSvgData(svgRawData, defaultOptions)
39 | t.is(result.svg, resultDefault.svg)
40 | })
41 |
42 | test(`${defaultTitle} - should have the right id`, t => {
43 | const result = transformSvgData(svgRawData, defaultOptions)
44 | t.is(result.id, resultDefault.id)
45 | })
46 |
47 | test(`${defaultTitle} - should have the right title`, t => {
48 | const result = transformSvgData(svgRawData, defaultOptions)
49 | t.true(_.isUndefined(result.title))
50 | })
51 |
52 | test(`${defaultTitle} - should have the right width & height`, t => {
53 | const result = transformSvgData(svgRawData, defaultOptions)
54 | t.is(result.width, resultDefault.width, `width`)
55 | t.is(result.height, resultDefault.height, `height`)
56 | })
57 |
58 | ////////
59 | // USER OPTIONS
60 | ////////
61 |
62 | const userOptions = _.defaults(
63 | {
64 | id: `icon-%f`,
65 | class: `.icon-%f`,
66 | fontSize: 16,
67 | title: `logo of %f`,
68 | },
69 | defaultOptions
70 | )
71 |
72 | const resultDefaultOptions = {
73 | svg: svgRawData,
74 | id: `icon-codepen-square`,
75 | class: `.icon-codepen-square`,
76 | title: `logo of codepen square`,
77 | width: `1.5em`,
78 | height: `1.5em`,
79 | }
80 |
81 | const optionsTitle = `Transform data - default & options`
82 |
83 | test(`${optionsTitle} - should be an object`, t => {
84 | const result = transformSvgData(svgRawData, userOptions)
85 | t.true(_.isPlainObject(result))
86 | })
87 |
88 | test(`${optionsTitle} - should have the raw datas`, t => {
89 | const result = transformSvgData(svgRawData, userOptions)
90 | t.is(result.svg, resultDefaultOptions.svg)
91 | })
92 |
93 | test(`${optionsTitle} - should have the right id`, t => {
94 | const result = transformSvgData(svgRawData, userOptions)
95 | t.is(result.id, resultDefaultOptions.id)
96 | })
97 |
98 | test(`${optionsTitle} - should have the right class`, t => {
99 | const result = transformSvgData(svgRawData, userOptions)
100 | t.is(result.class, resultDefaultOptions.class)
101 | })
102 |
103 | test(`${optionsTitle} - should have the right title`, t => {
104 | const result = transformSvgData(svgRawData, userOptions)
105 | t.is(result.title, resultDefaultOptions.title)
106 | })
107 |
108 | test(`${optionsTitle} - should have the right width & height`, t => {
109 | const result = transformSvgData(svgRawData, userOptions)
110 | t.is(result.width, resultDefaultOptions.width, `width`)
111 | t.is(result.height, resultDefaultOptions.height, `height`)
112 | })
113 |
114 | ////////
115 | // CUSTOM OPTIONS
116 | ////////
117 |
118 | const customOptions = _.defaults(
119 | {
120 | id: `svg-icon-%f`,
121 | transformData: (svg, options) => {
122 | return {
123 | svg: false,
124 | id: options.id.replace(`%f`, svg.id),
125 | }
126 | },
127 | },
128 | defaultOptions
129 | )
130 |
131 | const resultCustomOptions = {
132 | svg: svgRawData,
133 | id: `svg-icon-codepen-square`,
134 | }
135 |
136 | const customOptionsTitle = `Transform - custom & options`
137 |
138 | test(`${customOptionsTitle} - should be an object`, t => {
139 | const result = transformSvgData(svgRawData, customOptions)
140 | t.true(_.isPlainObject(result))
141 | })
142 |
143 | test(`${customOptionsTitle} - should have only user keys`, t => {
144 | const result = transformSvgData(svgRawData, customOptions)
145 | const keys = Object.keys(result).sort()
146 | t.deepEqual(keys, [`svg`, `id`].sort())
147 | })
148 |
149 | test(`${customOptionsTitle} - raw datas aren't overwritten`, t => {
150 | const result = transformSvgData(svgRawData, customOptions)
151 | t.is(result.svg, resultCustomOptions.svg)
152 | })
153 |
154 | test(`${customOptionsTitle} - should have the right id`, t => {
155 | const result = transformSvgData(svgRawData, customOptions)
156 | t.is(result.id, resultCustomOptions.id)
157 | })
158 |
159 | test(`${customOptionsTitle} - title should be removable`, t => {
160 | const result = transformSvgData(svgRawData, customOptions)
161 | t.true(_.isUndefined(result.title))
162 | })
163 |
--------------------------------------------------------------------------------
/test/warnings.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp'
2 | import es from 'event-stream'
3 | import test from 'ava'
4 | import intercept from 'intercept-stdout'
5 |
6 | import svgSymbols from '../index.js'
7 | // import templates from '../lib/templates.js';
8 |
9 | test.beforeEach(t => {
10 | t.context.stdout = []
11 | t.context.unhookIntercept = intercept(txt => {
12 | t.context.stdout.push(txt)
13 | // mute stdout
14 | // https://github.com/sfarthin/intercept-stdout/issues/8#issuecomment-250521176
15 | return ``
16 | })
17 | })
18 |
19 | test.afterEach(t => {
20 | t.context.unhookIntercept()
21 | })
22 |
23 | const title = `Warning`
24 |
25 | test.serial.cb(`${title} - className deprecation notice`, t => {
26 | gulp
27 | .src(`test/source/ios.svg`)
28 | .pipe(svgSymbols({ className: `foobar` }))
29 | .pipe(
30 | es.writeArray((err, output) => {
31 | const messageRegex = /options\.className\sis\sdeprecated/
32 | const warnMessage = t.context.stdout.find(e => messageRegex.test(e))
33 | t.truthy(warnMessage, `has the className deprecation warning`)
34 | t.end()
35 | })
36 | )
37 | })
38 |
39 | test.serial.cb(`${title} - svgClassname deprecation notice`, t => {
40 | gulp
41 | .src(`test/source/ios.svg`)
42 | .pipe(svgSymbols({ svgClassname: `foobar` }))
43 | .pipe(
44 | es.writeArray((err, output) => {
45 | const messageRegex = /options\.svgClassname\sis\sdeprecated/
46 | const warnMessage = t.context.stdout.find(e => messageRegex.test(e))
47 | t.truthy(warnMessage, `has the svgClassname deprecation warning`)
48 | t.end()
49 | })
50 | )
51 | })
52 |
53 | test.serial.cb(`${title} - Missing viewbox warning`, t => {
54 | gulp
55 | .src(`test/source/gradient.svg`)
56 | .pipe(svgSymbols())
57 | .pipe(
58 | es.writeArray((err, output) => {
59 | const { stdout } = t.context
60 | const reasonRegex = /viewbox\smissing\sin\sfile/
61 | const filenameRegex = /test\/source\/gradient\.svg/
62 | const reasonMessage = stdout.find(e => reasonRegex.test(e))
63 | const filenameMessage = stdout.find(e => filenameRegex.test(e))
64 | t.truthy(reasonMessage, `has the missing viewbox warning`)
65 | t.truthy(filenameMessage, `has the right filename`)
66 | t.end()
67 | })
68 | )
69 | })
70 |
71 | test.serial.cb(`${title} - outside defs`, t => {
72 | gulp
73 | .src(`test/source/figma-mask-outside-defs.svg`)
74 | .pipe(svgSymbols())
75 | .pipe(
76 | es.writeArray((err, output) => {
77 | const { stdout } = t.context
78 | const reasonRegex = /\stag\sfound\soutside\sa\s\stag/
79 | const filenameRegex = /test\/source\/figma-mask-outside-defs\.svg/
80 | const reasonMessage = stdout.find(e => reasonRegex.test(e))
81 | const filenameMessage = stdout.find(e => filenameRegex.test(e))
82 | t.truthy(reasonMessage, `has the mask warning`)
83 | t.truthy(filenameMessage, `has the right filename`)
84 | t.end()
85 | })
86 | )
87 | })
88 |
89 | test.serial.cb(`${title} - outside defs`, t => {
90 | gulp
91 | .src(`test/source/affinity-clip-path-outside-defs.svg`)
92 | .pipe(svgSymbols())
93 | .pipe(
94 | es.writeArray((err, output) => {
95 | const { stdout } = t.context
96 | /* eslint-disable max-len */
97 | const reasonRegex = /\stag\sfound\soutside\sa\s\stag/
98 | const filenameRegex = /test\/source\/affinity-clip-path-outside-defs\.svg/
99 | const reasonMessage = stdout.find(e => reasonRegex.test(e))
100 | const filenameMessage = stdout.find(e => filenameRegex.test(e))
101 | /* eslint-enable max-len */
102 | t.truthy(reasonMessage, `has the mask warning`)
103 | t.truthy(filenameMessage, `has the right filename`)
104 | t.end()
105 | })
106 | )
107 | })
108 |
109 | test.serial.cb(`${title} - duplicated ids`, t => {
110 | gulp
111 | .src(`test/source/duplicated-defs-ids-*.svg`)
112 | .pipe(svgSymbols())
113 | .pipe(
114 | es.writeArray((err, output) => {
115 | const { stdout } = t.context
116 | /* eslint-disable max-len */
117 | const reasonRegex = /\shas\ssome\sduplicated\sids:/
118 | const firstDuplicatedRegex = /id\s“gradient-background”\sfound\sin\sdifferent\sfiles\s\(duplicated-defs-ids-1,\sduplicated-defs-ids-2\)/
119 | const secondDuplicatedRegex = /id\s“shape-clip”\sfound\sin\sdifferent\sfiles\s\(duplicated-defs-ids-1,\sduplicated-defs-ids-2\)/
120 | const reasonMessage = stdout.find(e => reasonRegex.test(e))
121 | const firstDuplicatedMessage = stdout.find(e =>
122 | firstDuplicatedRegex.test(e)
123 | )
124 | const secondDuplicatedMessage = stdout.find(e =>
125 | secondDuplicatedRegex.test(e)
126 | )
127 | /* eslint-enable max-len */
128 | t.truthy(reasonMessage, `has the mask warning`)
129 | t.truthy(firstDuplicatedMessage, `has the right filename`)
130 | t.truthy(secondDuplicatedMessage, `has the right filename`)
131 | t.end()
132 | })
133 | )
134 | })
135 |
--------------------------------------------------------------------------------