gulp-stacksvg 1.1.0 → 2.0.1
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 +5 -5
- package/index.js +67 -27
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[![NPM version][npm-image]][npm-url]
|
|
6
6
|
[![Vulnerabilities count][vulnerabilities-image]][vulnerabilities-url]
|
|
7
7
|
|
|
8
|
-
Combine
|
|
8
|
+
Combine SVG icon files into one with the stack method.
|
|
9
9
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@ npm install gulp-stacksvg --save-dev
|
|
|
15
15
|
|
|
16
16
|
## Usage
|
|
17
17
|
|
|
18
|
-
The following script will combine all
|
|
18
|
+
The following script will combine all SVG sources into a single SVG file with stack method.
|
|
19
19
|
|
|
20
20
|
```js
|
|
21
21
|
import { stacksvg } from "gulp-stacksvg"
|
|
@@ -30,7 +30,7 @@ function makeStack () {
|
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
###
|
|
33
|
+
### Available options
|
|
34
34
|
|
|
35
35
|
| Option | Description | Default |
|
|
36
36
|
|-------------|--------------------------------------------------------------------------------------|-------------|
|
|
@@ -61,10 +61,10 @@ We can use the stack in all four possible ways:
|
|
|
61
61
|
|
|
62
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.
|
|
63
63
|
|
|
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
|
|
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 remains the `svg` tag, as in the original icon files) and with the addition of a tiny bit of style.
|
|
65
65
|
|
|
66
66
|
```xml
|
|
67
|
-
<svg xmlns="http://www.w3.org/2000/svg"
|
|
67
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
68
68
|
|
|
69
69
|
<style>:root svg:not(:target) { display: none }</style>
|
|
70
70
|
```
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parse } from "node-html-parser"
|
|
2
2
|
import { basename, extname, sep } from "path"
|
|
3
3
|
import { Transform } from "stream"
|
|
4
|
-
import
|
|
4
|
+
import { createHmac } from "crypto"
|
|
5
5
|
import PluginError from "plugin-error"
|
|
6
6
|
import Vinyl from "vinyl"
|
|
7
7
|
|
|
@@ -15,12 +15,14 @@ const excessAttrs = [
|
|
|
15
15
|
`y`
|
|
16
16
|
]
|
|
17
17
|
|
|
18
|
+
const xlink = `http://www.w3.org/1999/xlink`
|
|
19
|
+
|
|
18
20
|
export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-` } = {}) {
|
|
19
21
|
|
|
20
22
|
let isEmpty = true
|
|
21
23
|
const ids = {}
|
|
22
|
-
const namespaces =
|
|
23
|
-
const stack = parse(`<svg
|
|
24
|
+
const namespaces = new Map([[`http://www.w3.org/2000/svg`, `xmlns`]])
|
|
25
|
+
const stack = parse(`<svg><style>:root svg:not(:target){display:none}</style></svg>`)
|
|
24
26
|
const rootSvg = stack.querySelector(`svg`)
|
|
25
27
|
const stream = new Transform({ objectMode: true })
|
|
26
28
|
|
|
@@ -34,7 +36,8 @@ export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-`
|
|
|
34
36
|
return cb()
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
const
|
|
39
|
+
const iconDom = parse(file.contents.toString()).removeWhitespace()
|
|
40
|
+
const iconSvg = iconDom.querySelector(`svg`)
|
|
38
41
|
|
|
39
42
|
isEmpty = false
|
|
40
43
|
|
|
@@ -48,24 +51,24 @@ export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-`
|
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
ids[iconId] = true
|
|
51
|
-
|
|
54
|
+
iconSvg.setAttribute(`id`, iconId)
|
|
52
55
|
|
|
53
|
-
const viewBoxAttr =
|
|
54
|
-
const widthAttr =
|
|
55
|
-
const heightAttr =
|
|
56
|
+
const viewBoxAttr = iconSvg.getAttribute(`viewBox`)
|
|
57
|
+
const widthAttr = iconSvg.getAttribute(`width`)?.replace(/[^0-9]/g, ``)
|
|
58
|
+
const heightAttr = iconSvg.getAttribute(`height`)?.replace(/[^0-9]/g, ``)
|
|
56
59
|
|
|
57
60
|
if (!viewBoxAttr && widthAttr && heightAttr) {
|
|
58
|
-
|
|
61
|
+
iconSvg.setAttribute(`viewBox`, `0 0 ${widthAttr} ${heightAttr}`)
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
excessAttrs.forEach((attr) =>
|
|
62
|
-
|
|
64
|
+
excessAttrs.forEach((attr) => iconSvg.removeAttribute(attr))
|
|
65
|
+
iconSvg.querySelectorAll(`[id]`).forEach(changeInnerId)
|
|
63
66
|
|
|
64
67
|
function changeInnerId (targetElem, suffix) {
|
|
65
68
|
let oldId = targetElem.id
|
|
66
69
|
let newId = `${iconId}_${suffix}`
|
|
67
70
|
targetElem.setAttribute(`id`, newId)
|
|
68
|
-
|
|
71
|
+
iconSvg.querySelectorAll(`*`).forEach(updateUsingId)
|
|
69
72
|
|
|
70
73
|
function updateUsingId (elem) {
|
|
71
74
|
if (~elem.rawAttrs.search(`#${oldId}`)) {
|
|
@@ -77,31 +80,46 @@ export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-`
|
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
const attrs =
|
|
83
|
+
const attrs = iconSvg._attrs
|
|
81
84
|
|
|
82
85
|
for (let attrName in attrs) {
|
|
83
86
|
if (attrName.startsWith(`xmlns`)) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
if (
|
|
89
|
-
|
|
87
|
+
let nsId = attrs[attrName]
|
|
88
|
+
let oldNsAlias = attrName.slice(6)
|
|
89
|
+
let newNsAlias = oldNsAlias
|
|
90
|
+
if (namespaces.has(nsId)) {
|
|
91
|
+
if (namespaces.get(nsId) !== attrName) {
|
|
92
|
+
newNsAlias = namespaces.get(nsId).slice(6)
|
|
93
|
+
changeNsAlias(iconDom, oldNsAlias, newNsAlias)
|
|
90
94
|
}
|
|
95
|
+
} else if (nsId === xlink) {
|
|
96
|
+
newNsAlias = ``
|
|
97
|
+
changeNsAlias(iconDom, oldNsAlias, newNsAlias)
|
|
91
98
|
} else {
|
|
92
|
-
for (let
|
|
93
|
-
if (
|
|
94
|
-
|
|
99
|
+
for (let ns of namespaces.values()) {
|
|
100
|
+
if (ns === attrName) {
|
|
101
|
+
newNsAlias = `${oldNsAlias}${getHash(nsId)}`
|
|
102
|
+
changeNsAlias(iconDom, oldNsAlias, newNsAlias)
|
|
103
|
+
break
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
|
-
|
|
106
|
+
iconDom.querySelectorAll(`*`).some((elem) => {
|
|
107
|
+
if (
|
|
108
|
+
elem.rawTagName.startsWith(`${newNsAlias}:`)
|
|
109
|
+
||
|
|
110
|
+
Object.keys(elem._attrs).some((attr) => attr.startsWith(`${newNsAlias}:`))
|
|
111
|
+
) {
|
|
112
|
+
namespaces.set(nsId, `xmlns:${newNsAlias}`)
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
})
|
|
98
116
|
}
|
|
99
117
|
|
|
100
|
-
|
|
118
|
+
iconSvg.removeAttribute(attrName)
|
|
101
119
|
}
|
|
102
120
|
}
|
|
103
121
|
|
|
104
|
-
rootSvg.appendChild(
|
|
122
|
+
rootSvg.appendChild(iconSvg)
|
|
105
123
|
cb()
|
|
106
124
|
}
|
|
107
125
|
|
|
@@ -110,8 +128,8 @@ export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-`
|
|
|
110
128
|
return cb()
|
|
111
129
|
}
|
|
112
130
|
|
|
113
|
-
for (let
|
|
114
|
-
rootSvg.setAttribute(
|
|
131
|
+
for (let [nsId, nsAttr] of namespaces) {
|
|
132
|
+
rootSvg.setAttribute(nsAttr, nsId)
|
|
115
133
|
}
|
|
116
134
|
|
|
117
135
|
output = output.endsWith(`.svg`) ? output : `${output}.svg`
|
|
@@ -127,3 +145,25 @@ export function stacksvg ({ output = `stack.svg`, separator = `_`, spacer = `-`
|
|
|
127
145
|
|
|
128
146
|
return stream
|
|
129
147
|
}
|
|
148
|
+
|
|
149
|
+
function changeNsAlias (elems, oldAlias, newAlias) {
|
|
150
|
+
elems.querySelectorAll(`*`).forEach((elem) => {
|
|
151
|
+
let prefix = newAlias === `` ? `` : `${newAlias}:`
|
|
152
|
+
if (elem.rawTagName.startsWith(`${oldAlias}:`)) {
|
|
153
|
+
elem.rawTagName = `${prefix}${elem.rawTagName.slice((oldAlias.length + 1))}`
|
|
154
|
+
}
|
|
155
|
+
for (let name of Object.keys(elem._attrs)) {
|
|
156
|
+
if (name.startsWith(`${oldAlias}:`)) {
|
|
157
|
+
elem.setAttribute(`${prefix}${name.slice((oldAlias.length + 1))}`, elem._attrs[name])
|
|
158
|
+
elem.removeAttribute(name)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getHash (str) {
|
|
165
|
+
return createHmac(`sha1`, `xmlns`)
|
|
166
|
+
.update(str)
|
|
167
|
+
.digest(`hex`)
|
|
168
|
+
.slice(0, 7)
|
|
169
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gulp-stacksvg",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Combine svg files into one with stack method",
|
|
6
6
|
"main": "index.js",
|
|
@@ -29,19 +29,19 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/firefoxic/gulp-stacksvg",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"
|
|
33
|
-
"node-html-parser": "^6.1.4",
|
|
32
|
+
"node-html-parser": "^6.1.5",
|
|
34
33
|
"plugin-error": "^2.0.1",
|
|
35
34
|
"vinyl": "^3.0.0"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
38
|
-
"eslint": "^8.
|
|
37
|
+
"eslint": "^8.36.0",
|
|
38
|
+
"fancy-log": "^2.0.0",
|
|
39
39
|
"finalhandler": "^1.2.0",
|
|
40
40
|
"gulp": "^4.0.2",
|
|
41
41
|
"mocha": "^10.2.0",
|
|
42
|
-
"puppeteer": "^19.
|
|
42
|
+
"puppeteer": "^19.7.5",
|
|
43
43
|
"serve-static": "^1.15.0",
|
|
44
|
-
"sinon": "^15.0.
|
|
44
|
+
"sinon": "^15.0.2"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": ">=16"
|