gulp-stacksvg 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -74
- package/index.js +37 -24
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# gulp-stacksvg
|
|
2
2
|
|
|
3
|
-
<img align="right" width="130" height="175" title="SVG Superman" src="https://raw.githubusercontent.com/firefoxic/gulp-stacksvg/master/svg-superman.png">
|
|
4
|
-
|
|
5
3
|
[![Test Status][test-image]][test-url]
|
|
6
4
|
[![License: MIT][license-image]][license-url]
|
|
7
5
|
[![NPM version][npm-image]][npm-url]
|
|
8
6
|
[![Vulnerabilities count][vulnerabilities-image]][vulnerabilities-url]
|
|
9
7
|
|
|
10
|
-
Combine svg files into one with stack method.
|
|
11
|
-
Read more about this in [the Simurai article](https://simurai.com/blog/2012/04/02/svg-stacks).
|
|
8
|
+
Combine svg icon files into one with stack method.
|
|
12
9
|
|
|
13
10
|
## Installation
|
|
14
11
|
|
|
@@ -16,14 +13,6 @@ Read more about this in [the Simurai article](https://simurai.com/blog/2012/04/0
|
|
|
16
13
|
npm install gulp-stacksvg --save-dev
|
|
17
14
|
```
|
|
18
15
|
|
|
19
|
-
### Avalable options
|
|
20
|
-
|
|
21
|
-
| Option | Description | Default |
|
|
22
|
-
|-------------|--------------------------------------------------------------------------------------|-------------|
|
|
23
|
-
| `output` | Sets the stack file name. Accepts values both with and without the `.svg` extension. | `stack.svg` |
|
|
24
|
-
| `separator` | Replaces the directory separator for the `id` attribute. | `_` |
|
|
25
|
-
| `spacer` | Joins space-separated words for the `id` attribute. | `-` |
|
|
26
|
-
|
|
27
16
|
## Usage
|
|
28
17
|
|
|
29
18
|
The following script will combine all svg sources into single svg file with stack method.
|
|
@@ -36,100 +25,125 @@ const { src, dest } = gulp
|
|
|
36
25
|
|
|
37
26
|
function makeStack () {
|
|
38
27
|
return src(`./src/icons/**/*.svg`)
|
|
39
|
-
.pipe(stacksvg())
|
|
28
|
+
.pipe(stacksvg({ output: `sprite` }))
|
|
40
29
|
.pipe(dest(`./dest/icons`))
|
|
41
30
|
}
|
|
42
31
|
```
|
|
43
32
|
|
|
33
|
+
### Avalable options
|
|
34
|
+
|
|
35
|
+
| Option | Description | Default |
|
|
36
|
+
|-------------|--------------------------------------------------------------------------------------|-------------|
|
|
37
|
+
| `output` | Sets the stack file name. Accepts values both with and without the `.svg` extension. | `stack.svg` |
|
|
38
|
+
| `separator` | Replaces the directory separator for the `id` attribute. | `_` |
|
|
39
|
+
| `spacer` | Joins space-separated words for the `id` attribute. | `-` |
|
|
40
|
+
|
|
44
41
|
### Inlining stacksvg result into markup
|
|
45
42
|
|
|
46
43
|
You just don't have to want it.
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
## Why a stack?
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
Unlike all other methods for assembling a sprite, the stack does not limit us in choosing how to insert a vector into a page. Take a look at [the results](https://demos.frontend-design.ru/sprite/src/) of different ways to display fragments of different types of sprites.
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
import { stacksvg } from "gulp-stacksvg"
|
|
54
|
-
import rename from "gulp-rename"
|
|
55
|
-
import gulp from "gulp"
|
|
49
|
+
We can use the stack in all four possible ways:
|
|
56
50
|
|
|
57
|
-
|
|
51
|
+
- in markup:
|
|
52
|
+
- in `src` of `img` tag — static,
|
|
53
|
+
- in the `href` of the `use` tag — with the possibility of repainting,
|
|
54
|
+
- in styles:
|
|
55
|
+
- in `url()` properties `background` — static,
|
|
56
|
+
- in `url()` properties `mask` — with the possibility of repainting.
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
return src(`./src/icons/**/*.svg`, { base: `src/icons` })
|
|
61
|
-
.pipe(rename({ prefix: `icon-` }))
|
|
62
|
-
.pipe(stacksvg())
|
|
63
|
-
.pipe(dest(`./dest/icons`))
|
|
64
|
-
}
|
|
65
|
-
```
|
|
58
|
+
[Demo page](https://firefoxic.github.io/gulp-stacksvg/test/) to prove it.
|
|
66
59
|
|
|
67
|
-
|
|
60
|
+
## Stack under the hood
|
|
68
61
|
|
|
69
|
-
|
|
62
|
+
This method was first mentioned in a Simurai [article](https://simurai.com/blog/2012/04/02/svg-stacks) on April 2, 2012. But even it uses unnecessarily complex code transformations.
|
|
70
63
|
|
|
71
|
-
|
|
72
|
-
Please note that you have to set `xmlMode: true` to parse svgs as xml file.
|
|
64
|
+
This can be done much easier. In general, the stack is arranged almost like a symbol sprite, but without changing the icon tag (it remain the `svg` tag, as in the original icon files) and with the addition of a tiny bit of style.
|
|
73
65
|
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
import gulp from "gulp"
|
|
66
|
+
```svg
|
|
67
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
68
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
78
69
|
|
|
79
|
-
|
|
70
|
+
<style>
|
|
71
|
+
:root { visibility: hidden }
|
|
72
|
+
:target { visibility: visible }
|
|
73
|
+
</style>
|
|
74
|
+
```
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
parserOptions: { xmlMode: true }
|
|
88
|
-
}))
|
|
89
|
-
.pipe(stacksvg())
|
|
90
|
-
.pipe(dest(`./dest/icons`))
|
|
91
|
-
}
|
|
76
|
+
<img align="left" width="90" height="90" title="sun" src="https://raw.githubusercontent.com/firefoxic/gulp-stacksvg/main/test/stack.svg#sun-alpha">
|
|
77
|
+
|
|
78
|
+
```svg
|
|
79
|
+
<svg id="sun" viewBox="0 0 24 24">
|
|
80
|
+
<!-- Inner code of sun icon -->
|
|
81
|
+
</svg>
|
|
92
82
|
```
|
|
93
83
|
|
|
94
|
-
|
|
84
|
+
<img align="left" width="90" height="90" title="heart" src="https://raw.githubusercontent.com/firefoxic/gulp-stacksvg/main/test/stack.svg#heart-red">
|
|
95
85
|
|
|
96
|
-
|
|
86
|
+
```svg
|
|
87
|
+
<svg id="heart" viewBox="0 0 24 24">
|
|
88
|
+
<!-- Inner code of heart icon -->
|
|
89
|
+
</svg>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
<img align="left" width="90" height="90" title="thumbup" src="https://raw.githubusercontent.com/firefoxic/gulp-stacksvg/main/test/stack.svg#thumbup-alpha">
|
|
93
|
+
|
|
94
|
+
```svg
|
|
95
|
+
<svg id="thumbup" viewBox="0 0 24 24">
|
|
96
|
+
<!-- Inner code of thumbup icon -->
|
|
97
|
+
</svg>
|
|
98
|
+
```
|
|
97
99
|
|
|
98
|
-
|
|
100
|
+
```svg
|
|
101
|
+
</svg>
|
|
102
|
+
```
|
|
99
103
|
|
|
100
|
-
|
|
104
|
+
The magic is in the stack inner style:
|
|
101
105
|
|
|
102
|
-
|
|
106
|
+
- `:root { visibility: hidden }` — hides the entire contents of the stack,
|
|
107
|
+
- `:target { visibility: visible }` — shows only the fragment that is requested by its link.
|
|
103
108
|
|
|
104
|
-
|
|
109
|
+
And now the icons from the external sprite are available in the styles <img width="16" height="16" title="heart" src="https://raw.githubusercontent.com/firefoxic/gulp-stacksvg/main/test/stack.svg#heart-red" alt="heart">
|
|
105
110
|
|
|
106
|
-
```
|
|
107
|
-
<
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
+ <use overflow="visible" href="#a"/>
|
|
111
|
-
+ </clipPath>
|
|
112
|
-
</defs>
|
|
113
|
-
-<clipPath id="b">
|
|
114
|
-
- <use overflow="visible" xlink:href="#a"/>
|
|
115
|
-
-</clipPath>
|
|
111
|
+
```html
|
|
112
|
+
<button class="button button--icon_heart" type="button">
|
|
113
|
+
<span class="visually-hidden">Add to favorites</span>
|
|
114
|
+
</button>
|
|
116
115
|
```
|
|
117
116
|
|
|
118
|
-
|
|
117
|
+
```css
|
|
118
|
+
.button {
|
|
119
|
+
display: inline-flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
gap: 0.5em;
|
|
122
|
+
}
|
|
119
123
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
.button--icon_heart {
|
|
125
|
+
--icon: url("../icons/stack.svg#heart");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.button:hover {
|
|
129
|
+
--fill: red;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.button::before {
|
|
133
|
+
content: "";
|
|
134
|
+
width: 1em;
|
|
135
|
+
height: 1em;
|
|
136
|
+
/* icon shape */
|
|
137
|
+
mask: var(--icon) no-repeat center / contain;
|
|
138
|
+
/* icon color */
|
|
139
|
+
background: var(--fill, orangered);
|
|
140
|
+
}
|
|
128
141
|
```
|
|
129
142
|
|
|
130
|
-
|
|
143
|
+
> ⚠️ Note:
|
|
144
|
+
> For the `mask` property, we still need an autoprefixer.
|
|
131
145
|
|
|
132
|
-
|
|
146
|
+
For an icon inserted via `mask`, simply change the `background`. Moreover, unlike `use`, you can draw anything in the background under the mask, for example, a gradient.
|
|
133
147
|
|
|
134
148
|
[test-url]: https://github.com/firefoxic/gulp-stacksvg/actions
|
|
135
149
|
[test-image]: https://github.com/firefoxic/gulp-stacksvg/actions/workflows/test.yml/badge.svg?branch=main
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parse } from "node-html-parser"
|
|
2
2
|
import { basename, extname, sep } from "path"
|
|
3
3
|
import { Transform } from "stream"
|
|
4
4
|
import fancyLog from "fancy-log"
|
|
@@ -20,8 +20,8 @@ export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-`
|
|
|
20
20
|
let isEmpty = true
|
|
21
21
|
const ids = {}
|
|
22
22
|
const namespaces = {}
|
|
23
|
-
const
|
|
24
|
-
const
|
|
23
|
+
const stack = parse(`<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg"><style>:root{visibility:hidden}:target{visibility:visible}</style></svg>`)
|
|
24
|
+
const rootSvg = stack.querySelector(`svg`)
|
|
25
25
|
const stream = new Transform({ objectMode: true })
|
|
26
26
|
|
|
27
27
|
function transform (file, _, cb) {
|
|
@@ -30,43 +30,56 @@ export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-`
|
|
|
30
30
|
return cb(new PluginError(`gulp-stacksvg`, `Streams are not supported!`))
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
if (file.isNull()) {
|
|
33
|
+
if (file.isNull() || !parse(file.contents.toString()).querySelector(`svg`)) {
|
|
34
34
|
return cb()
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
if ($icon.length === 0) {
|
|
40
|
-
return cb()
|
|
41
|
-
}
|
|
37
|
+
const icon = parse(file.contents.toString()).querySelector(`svg`)
|
|
42
38
|
|
|
43
39
|
if (file && isEmpty) {
|
|
44
40
|
isEmpty = false
|
|
45
41
|
}
|
|
46
42
|
|
|
47
|
-
const
|
|
43
|
+
const iconId = basename(
|
|
48
44
|
file.relative.split(sep).join(separator).replace(/\s/g, spacer),
|
|
49
45
|
extname(file.relative)
|
|
50
46
|
)
|
|
51
47
|
|
|
52
|
-
if (
|
|
53
|
-
return cb(new PluginError(`gulp-stacksvg`, `File name should be unique: ${
|
|
48
|
+
if (iconId in ids) {
|
|
49
|
+
return cb(new PluginError(`gulp-stacksvg`, `File name should be unique: ${iconId}`))
|
|
54
50
|
}
|
|
55
51
|
|
|
56
|
-
ids[
|
|
57
|
-
|
|
52
|
+
ids[iconId] = true
|
|
53
|
+
icon.setAttribute(`id`, iconId)
|
|
58
54
|
|
|
59
|
-
const viewBoxAttr =
|
|
60
|
-
const widthAttr =
|
|
61
|
-
const heightAttr =
|
|
55
|
+
const viewBoxAttr = icon.getAttribute(`viewBox`)
|
|
56
|
+
const widthAttr = icon.getAttribute(`width`)?.replace(/[^0-9]/g, ``)
|
|
57
|
+
const heightAttr = icon.getAttribute(`height`)?.replace(/[^0-9]/g, ``)
|
|
62
58
|
|
|
63
59
|
if (!viewBoxAttr && widthAttr && heightAttr) {
|
|
64
|
-
|
|
60
|
+
icon.setAttribute(`viewBox`, `0 0 ${widthAttr} ${heightAttr}`)
|
|
65
61
|
}
|
|
66
62
|
|
|
67
|
-
excessAttrs.forEach((attr) =>
|
|
63
|
+
excessAttrs.forEach((attr) => icon.removeAttribute(attr))
|
|
64
|
+
icon.querySelectorAll(`[id]`).forEach(changeInnerId)
|
|
65
|
+
|
|
66
|
+
function changeInnerId (targetElem, suffix) {
|
|
67
|
+
let oldId = targetElem.id
|
|
68
|
+
let newId = `${iconId}_${suffix}`
|
|
69
|
+
targetElem.setAttribute(`id`, newId)
|
|
70
|
+
icon.querySelectorAll(`*`).forEach(updateUsingId)
|
|
71
|
+
|
|
72
|
+
function updateUsingId (elem) {
|
|
73
|
+
if (~elem.rawAttrs.search(`#${oldId}`)) {
|
|
74
|
+
for (let attr in elem._attrs) {
|
|
75
|
+
let attrValue = elem._attrs[attr].replace(`#${oldId}`, `#${newId}`)
|
|
76
|
+
elem.setAttribute(attr, attrValue)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
68
81
|
|
|
69
|
-
const attrs =
|
|
82
|
+
const attrs = icon._attrs
|
|
70
83
|
|
|
71
84
|
for (let attrName in attrs) {
|
|
72
85
|
if (attrName.startsWith(`xmlns`)) {
|
|
@@ -86,11 +99,11 @@ export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-`
|
|
|
86
99
|
namespaces[attrName] = attrNs
|
|
87
100
|
}
|
|
88
101
|
|
|
89
|
-
|
|
102
|
+
icon.removeAttribute(attrName)
|
|
90
103
|
}
|
|
91
104
|
}
|
|
92
105
|
|
|
93
|
-
|
|
106
|
+
rootSvg.appendChild(icon)
|
|
94
107
|
cb()
|
|
95
108
|
}
|
|
96
109
|
|
|
@@ -100,12 +113,12 @@ export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-`
|
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
for (let nsName in namespaces) {
|
|
103
|
-
|
|
116
|
+
rootSvg.setAttribute(nsName, namespaces[nsName])
|
|
104
117
|
}
|
|
105
118
|
|
|
106
119
|
output = output.endsWith(`.svg`) ? output : `${output}.svg`
|
|
107
120
|
|
|
108
|
-
const file = new Vinyl({ path: output, contents: Buffer.from(
|
|
121
|
+
const file = new Vinyl({ path: output, contents: Buffer.from(stack.toString()) })
|
|
109
122
|
|
|
110
123
|
this.push(file)
|
|
111
124
|
cb()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gulp-stacksvg",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Combine svg files into one with stack method",
|
|
6
6
|
"main": "index.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
11
|
"lint": "eslint ./",
|
|
12
|
-
"test": "gulp
|
|
12
|
+
"test": "gulp test && mocha",
|
|
13
13
|
"pretest": "npm run lint",
|
|
14
14
|
"preversion": "npm test",
|
|
15
15
|
"postversion": "npm publish",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/firefoxic/gulp-stacksvg",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"cheerio": "^1.0.0-rc.12",
|
|
33
32
|
"fancy-log": "^2.0.0",
|
|
33
|
+
"node-html-parser": "^5.4.2",
|
|
34
34
|
"plugin-error": "^2.0.0",
|
|
35
35
|
"vinyl": "^2.2.1"
|
|
36
36
|
},
|