gulp-stacksvg 3.0.0 → 5.0.0

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/LICENSE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # MIT License
2
2
 
3
- Copyright © 2022-2023 Sergey Artemov <firefoxic.dev@gmail.com>
4
- Copyright © 2014–2021 Andrey Kuzmin <unsoundscapes@gmail.com>
3
+ Copyright © Sergey Artemov <firefoxic.dev@gmail.com>, 2022
4
+ Copyright © Andrey Kuzmin <unsoundscapes@gmail.com>, 2014
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
7
 
package/README.md CHANGED
@@ -1,71 +1,63 @@
1
1
  <!-- markdownlint-disable MD007 -->
2
2
  # gulp-stacksvg
3
3
 
4
- [![Test Status][test-image]][test-url]
5
4
  [![License: MIT][license-image]][license-url]
5
+ [![Changelog][changelog-image]][changelog-url]
6
6
  [![NPM version][npm-image]][npm-url]
7
- [![Vulnerabilities count][vulnerabilities-image]][vulnerabilities-url]
7
+ [![Test Status][test-image]][test-url]
8
8
 
9
- The gulp plugin to combine svg files into one using the stack method.
9
+ The gulp plugin to combine svg files into one using the stack method.
10
10
 
11
11
  ## Installation
12
12
 
13
13
  ```shell
14
- npm install gulp-stacksvg --save-dev
14
+ pnpm add -D gulp gulp-stacksvg
15
15
  ```
16
16
 
17
17
  ## Usage
18
18
 
19
- The following script will combine all SVG sources into a single SVG file with stack method.
19
+ Add the following to your `gulpfile.js`:
20
20
 
21
21
  ```js
22
22
  import { stacksvg } from "gulp-stacksvg"
23
- import gulp from "gulp"
24
-
25
- const { src, dest } = gulp
23
+ import { dest, src } from "gulp"
26
24
 
27
- function makeStack () {
28
- return src(`./src/icons/**/*.svg`)
29
- .pipe(stacksvg({ output: `sprite` }))
30
- .pipe(dest(`./dest/icons`))
25
+ export function createStack () {
26
+ return src(`./src/shared/icons/**/*.svg`)
27
+ .pipe(stacksvg())
28
+ .pipe(dest(`./dist/shared/icons`))
31
29
  }
32
30
  ```
33
31
 
34
- ### Available options
35
-
36
- | Option | Description | Default |
37
- |-------------|--------------------------------------------------------------------------------------|---------|
38
- | `output` | Sets the stack file name. Accepts values ​both with and without the `.svg` extension. | `stack` |
39
- | `separator` | Replaces the directory separator for the `id` attribute. | `_` |
40
- | `spacer` | Joins space-separated words for the `id` attribute. | `-` |
41
-
42
- ### Inlining stacksvg result into markup
32
+ To combine all icons from `./src/shared/icons/` into the `./dist/shared/icons/stack.svg` run:
43
33
 
44
- You just don't have to want it.
34
+ ```shell
35
+ pnpm exec gulp createStack
36
+ ```
45
37
 
46
- ## Why a stack?
38
+ ## Why a stack?
47
39
 
48
- 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.
40
+ 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.
49
41
 
50
- We can use the stack in all four possible ways:
42
+ We can use the stack in all four possible ways:
51
43
 
52
- - in markup:
44
+ - in markup:
53
45
 
54
- - in `src` of `img` tag — static,
55
- - in the `href` of the `use` tag — with the possibility of repainting,
46
+ - in `src` of `img` tag — static,
47
+ - in the `href` of the `use` tag — with the possibility of repainting,
56
48
 
57
- - in styles:
49
+ - in styles:
58
50
 
59
- - in `url()` properties `background` — static,
60
- - in `url()` properties `mask` — with the possibility of repainting.
51
+ - in `url()` properties `background` — static,
52
+ - in `url()` properties `mask` — with the possibility of repainting.
61
53
 
62
- [Demo page](https://firefoxic.github.io/gulp-stacksvg/example/) to prove it.
54
+ [Demo page](https://firefoxic.github.io/gulp-stacksvg/example/) to prove it.
63
55
 
64
- ## Stack under the hood
56
+ ## Stack under the hood
65
57
 
66
- 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.
58
+ 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.
67
59
 
68
- 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.
60
+ 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.
69
61
 
70
62
  ```xml
71
63
  <svg xmlns="http://www.w3.org/2000/svg">
@@ -101,13 +93,13 @@ This can be done much easier. In general, the stack is arranged almost like a sy
101
93
  </svg>
102
94
  ```
103
95
 
104
- The magic is in the stack inner style, which shows only the fragment requested by the link, hiding everything else:
96
+ The magic is in the stack inner style, which shows only the fragment requested by the link, hiding everything else:
105
97
 
106
98
  ```css
107
99
  :root svg:not(:target) { display: none }
108
100
  ```
109
101
 
110
- 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/docs/example/stack.svg#heart-red" alt="heart">
102
+ 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/docs/example/stack.svg#heart-red" alt="heart">
111
103
 
112
104
  ```html
113
105
  <button class="button button--icon_heart" type="button">
@@ -120,46 +112,41 @@ And now the icons from the external sprite are available in the styles <img widt
120
112
  display: inline-flex;
121
113
  align-items: center;
122
114
  gap: 0.5em;
123
- }
124
-
125
- .button--icon_heart {
126
- --icon: url("../icons/stack.svg#heart");
127
- }
128
115
 
129
- .button:hover {
130
- --fill: red;
131
- }
132
-
133
- .button::before {
134
- content: "";
135
- width: 1em;
136
- height: 1em;
137
- /* icon shape */
138
- mask: var(--icon) no-repeat center / contain;
139
- /* icon color */
140
- background: var(--fill, orangered);
116
+ &:hover {
117
+ --fill: red;
118
+ }
119
+
120
+ &::before {
121
+ content: "";
122
+ width: 1em;
123
+ height: 1em;
124
+ /* icon shape */
125
+ mask: var(--icon) no-repeat center / contain;
126
+ /* icon color */
127
+ background: var(--fill, orangered);
128
+ }
129
+
130
+ &:where(.button--icon_heart) {
131
+ --icon: url("../icons/stack.svg#heart");
132
+ }
141
133
  }
142
134
  ```
143
135
 
144
- > ⚠️ Note:
145
- > We still need the [autoprefixer](https://github.com/postcss/autoprefixer) for the mask property.
146
-
147
- 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.
136
+ 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.
148
137
 
149
- ## Useful links
138
+ ## More info
150
139
 
151
- - [Changelog](CHANGELOG.md)
152
- - [License](LICENSE)
153
140
  - [SVG sprites: old-school, modern, unknown, and forgotten](https://pepelsbey.dev/articles/svg-sprites/#forgotten-stacks) by [Vadim Makeev](https://mastodon.social/@pepelsbey)
154
141
 
155
- [test-url]: https://github.com/firefoxic/gulp-stacksvg/actions
156
- [test-image]: https://github.com/firefoxic/gulp-stacksvg/actions/workflows/test.yml/badge.svg?branch=main
142
+ [license-url]: https://github.com/firefoxic/gulp-stacksvg/blob/main/LICENSE.md
143
+ [license-image]: https://img.shields.io/badge/License-MIT-limegreen.svg
157
144
 
158
- [npm-url]: https://www.npmjs.com/package/gulp-stacksvg
159
- [npm-image]: https://badge.fury.io/js/gulp-stacksvg.svg
145
+ [changelog-url]: https://github.com/firefoxic/gulp-stacksvg/blob/main/CHANGELOG.md
146
+ [changelog-image]: https://img.shields.io/badge/CHANGELOG-md-limegreen
160
147
 
161
- [license-url]: https://github.com/firefoxic/gulp-stacksvg/blob/main/LICENSE
162
- [license-image]: https://img.shields.io/badge/License-MIT-limegreen.svg
148
+ [npm-url]: https://npmjs.com/package/gulp-stacksvg
149
+ [npm-image]: https://badge.fury.io/js/gulp-stacksvg.svg
163
150
 
164
- [vulnerabilities-url]: https://snyk.io/test/github/firefoxic/gulp-stacksvg
165
- [vulnerabilities-image]: https://snyk.io/test/github/firefoxic/gulp-stacksvg/badge.svg
151
+ [test-url]: https://github.com/firefoxic/gulp-stacksvg/actions
152
+ [test-image]: https://github.com/firefoxic/gulp-stacksvg/actions/workflows/test.yaml/badge.svg?branch=main
package/lib/index.js CHANGED
@@ -1,12 +1,12 @@
1
- import { basename, extname, sep } from "node:path"
2
- import { Transform } from "node:stream"
3
1
  import { createHmac } from "node:crypto"
2
+ import { Transform } from "node:stream"
3
+ import { basename, extname, sep } from "node:path"
4
4
 
5
5
  import { parse } from "node-html-parser"
6
6
  import PluginError from "plugin-error"
7
7
  import Vinyl from "vinyl"
8
8
 
9
- const excessAttrs = [
9
+ let excessAttrs = [
10
10
  `enable-background`,
11
11
  `height`,
12
12
  `version`,
@@ -16,29 +16,29 @@ const excessAttrs = [
16
16
  `y`,
17
17
  ]
18
18
 
19
- const xlink = `http://www.w3.org/1999/xlink`
19
+ const XLINK = `http://www.w3.org/1999/xlink`
20
20
 
21
21
  /**
22
- * Combine svg files into one using the stack method.
23
- *
24
- * @param {Object} [options] - The option object.
25
- * @param {string} [options.output=stack] - The name of the output file.
26
- * @param {string} [options.separator=_] - The symbol that will replace the directory separator for the fragment id.
27
- * @param {string} [options.spacer=-] - The character that will replace the whitespace characters for the fragment id.
22
+ * Gulp plugin for combining SVG icons into a single file.
28
23
  *
29
- * @returns {Object} A stream containing a stack of svg icons.
24
+ * @exports {function} stacksvg - Gulp plugin.
30
25
  */
31
- export function stacksvg ( { output = `stack`, separator = `_`, spacer = `-` } = {}) {
32
-
26
+ export function stacksvg () {
33
27
  let isEmpty = true
34
- const ids = {}
35
- const namespaces = new Map([[`http://www.w3.org/2000/svg`, `xmlns`]])
36
- const stack = parse(`<svg><style>:root svg:not(:target){display:none}</style></svg>`)
37
- const rootSvg = stack.querySelector(`svg`)
38
- const stream = new Transform({ objectMode: true })
39
-
28
+ let ids = {}
29
+ let namespaces = new Map([[`http://www.w3.org/2000/svg`, `xmlns`]])
30
+ let stack = parse(`<svg><style>:root svg:not(:target){display:none}</style></svg>`)
31
+ let rootSvg = stack.querySelector(`svg`)
32
+ let stream = new Transform({ objectMode: true })
33
+
34
+ /**
35
+ * Transform function for the plugin.
36
+ *
37
+ * @param {Vinyl} file - Gulp file object.
38
+ * @param {string} [enc=null] - Encoding to use when writing the file.
39
+ * @param {function} cb - Callback function.
40
+ */
40
41
  function transform (file, _, cb) {
41
-
42
42
  if (file.isStream()) {
43
43
  return cb(new PluginError(`gulp-stacksvg`, `Streams are not supported!`))
44
44
  }
@@ -47,13 +47,13 @@ export function stacksvg ( { output = `stack`, separator = `_`, spacer = `-` } =
47
47
  return cb()
48
48
  }
49
49
 
50
- const iconDom = parse(file.contents.toString()).removeWhitespace()
51
- const iconSvg = iconDom.querySelector(`svg`)
50
+ let iconDom = parse(file.contents.toString()).removeWhitespace()
51
+ let iconSvg = iconDom.querySelector(`svg`)
52
52
 
53
53
  isEmpty = false
54
54
 
55
- const iconId = basename(
56
- file.relative.split(sep).join(separator).replace(/\s/g, spacer),
55
+ let iconId = basename(
56
+ file.relative.split(sep).join(`_`).replace(/\s/g, `-`),
57
57
  extname(file.relative),
58
58
  )
59
59
 
@@ -64,9 +64,9 @@ export function stacksvg ( { output = `stack`, separator = `_`, spacer = `-` } =
64
64
  ids[iconId] = true
65
65
  iconSvg.setAttribute(`id`, iconId)
66
66
 
67
- const viewBoxAttr = iconSvg.getAttribute(`viewBox`)
68
- const widthAttr = iconSvg.getAttribute(`width`)?.replace(/[^0-9]/g, ``)
69
- const heightAttr = iconSvg.getAttribute(`height`)?.replace(/[^0-9]/g, ``)
67
+ let viewBoxAttr = iconSvg.getAttribute(`viewBox`)
68
+ let widthAttr = iconSvg.getAttribute(`width`)?.replace(/[^0-9]/g, ``)
69
+ let heightAttr = iconSvg.getAttribute(`height`)?.replace(/[^0-9]/g, ``)
70
70
 
71
71
  if (!viewBoxAttr && widthAttr && heightAttr) {
72
72
  iconSvg.setAttribute(`viewBox`, `0 0 ${widthAttr} ${heightAttr}`)
@@ -75,35 +75,50 @@ export function stacksvg ( { output = `stack`, separator = `_`, spacer = `-` } =
75
75
  excessAttrs.forEach((attr) => iconSvg.removeAttribute(attr))
76
76
  iconSvg.querySelectorAll(`[id]`).forEach(changeInnerId)
77
77
 
78
+ /**
79
+ * Update the id of a child element if it has the same id.
80
+ *
81
+ * @param {Element} targetElem - Child element to update.
82
+ * @param {string} suffix - Suffix to append to the id.
83
+ */
78
84
  function changeInnerId (targetElem, suffix) {
79
85
  let oldId = targetElem.id
80
86
  let newId = `${iconId}_${suffix}`
87
+
81
88
  targetElem.setAttribute(`id`, newId)
82
89
  iconSvg.querySelectorAll(`*`).forEach(updateUsingId)
83
90
 
91
+ /**
92
+ * Update the id of a child element if it has the same id.
93
+ *
94
+ * @param {Element} elem - Child element to update.
95
+ */
84
96
  function updateUsingId (elem) {
85
97
  if (~elem.rawAttrs.search(`#${oldId}`)) {
98
+ // eslint-disable-next-line guard-for-in
86
99
  for (let attr in elem._attrs) {
87
100
  let attrValue = elem._attrs[attr].replace(`#${oldId}`, `#${newId}`)
101
+
88
102
  elem.setAttribute(attr, attrValue)
89
103
  }
90
104
  }
91
105
  }
92
106
  }
93
107
 
94
- const attrs = iconSvg._attrs
108
+ let attrs = iconSvg._attrs
95
109
 
96
110
  for (let attrName in attrs) {
97
111
  if (attrName.startsWith(`xmlns`)) {
98
112
  let nsId = attrs[attrName]
99
113
  let oldNsAlias = attrName.slice(6)
100
114
  let newNsAlias = oldNsAlias
115
+
101
116
  if (namespaces.has(nsId)) {
102
117
  if (namespaces.get(nsId) !== attrName) {
103
118
  newNsAlias = namespaces.get(nsId).slice(6)
104
119
  changeNsAlias(iconDom, oldNsAlias, newNsAlias)
105
120
  }
106
- } else if (nsId === xlink) {
121
+ } else if (nsId === XLINK) {
107
122
  newNsAlias = ``
108
123
  changeNsAlias(iconDom, oldNsAlias, newNsAlias)
109
124
  } else {
@@ -114,13 +129,14 @@ export function stacksvg ( { output = `stack`, separator = `_`, spacer = `-` } =
114
129
  break
115
130
  }
116
131
  }
132
+
117
133
  iconDom.querySelectorAll(`*`).some((elem) => {
118
134
  if (
119
135
  elem.rawTagName.startsWith(`${newNsAlias}:`)
120
- ||
121
- Object.keys(elem._attrs).some((attr) => attr.startsWith(`${newNsAlias}:`))
136
+ || Object.keys(elem._attrs).some((attr) => attr.startsWith(`${newNsAlias}:`))
122
137
  ) {
123
138
  namespaces.set(nsId, `xmlns:${newNsAlias}`)
139
+
124
140
  return true
125
141
  }
126
142
  })
@@ -131,9 +147,15 @@ export function stacksvg ( { output = `stack`, separator = `_`, spacer = `-` } =
131
147
  }
132
148
 
133
149
  rootSvg.appendChild(iconSvg)
150
+
134
151
  cb()
135
152
  }
136
153
 
154
+ /**
155
+ * Flush function for the plugin.
156
+ *
157
+ * @param {function} cb - Callback function.
158
+ */
137
159
  function flush (cb) {
138
160
  if (isEmpty) {
139
161
  return cb()
@@ -143,11 +165,10 @@ export function stacksvg ( { output = `stack`, separator = `_`, spacer = `-` } =
143
165
  rootSvg.setAttribute(nsAttr, nsId)
144
166
  }
145
167
 
146
- output = output.endsWith(`.svg`) ? output : `${output}.svg`
147
-
148
- const file = new Vinyl({ path: output, contents: Buffer.from(stack.toString()) })
168
+ let file = new Vinyl({ path: `stack.svg`, contents: Buffer.from(stack.toString()) })
149
169
 
150
170
  this.push(file)
171
+
151
172
  cb()
152
173
  }
153
174
 
@@ -158,18 +179,20 @@ export function stacksvg ( { output = `stack`, separator = `_`, spacer = `-` } =
158
179
  }
159
180
 
160
181
  /**
161
- * Change the old namespace alias to the new one for the elements and attributes of the icon.
182
+ * Changes the namespace alias of an element and all its children.
162
183
  *
163
- * @param {object} iconDom - The DOM of the icon.
164
- * @param {string} oldAlias - The old namespace alias.
165
- * @param {string} newAlias - The new namespace alias.
184
+ * @param {Element} iconDom - Element to modify.
185
+ * @param {string} oldAlias - Old namespace alias to replace.
186
+ * @param {string} newAlias - New namespace alias.
166
187
  */
167
188
  function changeNsAlias (iconDom, oldAlias, newAlias) {
168
189
  iconDom.querySelectorAll(`*`).forEach((elem) => {
169
190
  let prefix = newAlias === `` ? `` : `${newAlias}:`
191
+
170
192
  if (elem.rawTagName.startsWith(`${oldAlias}:`)) {
171
193
  elem.rawTagName = `${prefix}${elem.rawTagName.slice((oldAlias.length + 1))}`
172
194
  }
195
+
173
196
  for (let name of Object.keys(elem._attrs)) {
174
197
  if (name.startsWith(`${oldAlias}:`)) {
175
198
  elem.setAttribute(`${prefix}${name.slice((oldAlias.length + 1))}`, elem._attrs[name])
@@ -180,11 +203,10 @@ function changeNsAlias (iconDom, oldAlias, newAlias) {
180
203
  }
181
204
 
182
205
  /**
183
- * Get the hash sum of the passed string.
184
- *
185
- * @param {string} str - An arbitrary line of code.
206
+ * Get a hash for a given string.
186
207
  *
187
- * @returns {string} The first 7 characters of the hash sum.
208
+ * @param {string} str - String to hash.
209
+ * @returns {string} Hash of the string.
188
210
  */
189
211
  function getHash (str) {
190
212
  return createHmac(`sha1`, `xmlns`)
package/package.json CHANGED
@@ -1,53 +1,54 @@
1
1
  {
2
2
  "name": "gulp-stacksvg",
3
- "description": "The gulp plugin to combine svg files into one using the stack method.",
4
- "version": "3.0.0",
5
- "type": "module",
6
- "exports": "./lib/index.js",
7
- "files": [
8
- "./lib/index.js"
9
- ],
10
- "dependencies": {
11
- "node-html-parser": "^6.1.11",
12
- "plugin-error": "^2.0.1",
13
- "vinyl": "^3.0.0"
14
- },
15
- "engines": {
16
- "node": "^18.18.2 || ^20.9.0"
17
- },
3
+ "description": "The gulp plugin to combine svg files into one using the stack method.",
4
+ "version": "5.0.0",
18
5
  "license": "MIT",
19
6
  "author": {
20
7
  "name": "Sergey Artemov",
21
8
  "email": "firefoxic.dev@gmail.com"
22
9
  },
23
- "homepage": "https://github.com/firefoxic/gulp-stacksvg",
10
+ "homepage": "https://github.com/firefoxic/gulp-stacksvg#readme",
24
11
  "bugs": {
25
12
  "url": "https://github.com/firefoxic/gulp-stacksvg/issues"
26
13
  },
27
14
  "repository": {
28
15
  "type": "git",
29
- "url": "git://github.com/firefoxic/gulp-stacksvg"
16
+ "url": "git://github.com/firefoxic/gulp-stacksvg.git"
17
+ },
18
+ "type": "module",
19
+ "exports": "./lib/index.js",
20
+ "files": [
21
+ "./lib/"
22
+ ],
23
+ "engines": {
24
+ "node": "^20.12 || >=22.11"
25
+ },
26
+ "dependencies": {
27
+ "node-html-parser": "^6.1.13",
28
+ "plugin-error": "^2.0.1",
29
+ "vinyl": "^3.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "@firefoxic/eslint-config": "^4.0.0",
33
+ "@firefoxic/update-changelog": "^1.0.0",
34
+ "eslint": "^9.13.0",
35
+ "gulp": "^5.0.0"
30
36
  },
31
37
  "keywords": [
38
+ "gulp",
32
39
  "gulpplugin",
33
40
  "svg",
34
41
  "icon",
42
+ "sprite",
35
43
  "stack",
36
- "sprite"
44
+ "vector"
37
45
  ],
38
- "devDependencies": {
39
- "editorconfig-checker": "^5.1.1",
40
- "eslint": "^8.52.0",
41
- "gulp": "^4.0.2",
42
- "husky": "^8.0.3"
43
- },
44
46
  "scripts": {
45
- "lint:ec": "ec",
46
- "lint:es": "eslint ./",
47
- "lint": "pnpm /^lint:/",
47
+ "lint": "eslint",
48
48
  "test": "node --test",
49
49
  "pretest": "pnpm lint",
50
50
  "preversion": "pnpm test",
51
- "postversion": "pnpm publish"
51
+ "version": "update-changelog",
52
+ "postversion": "pnpm publish --provenance --access public --no-git-checks"
52
53
  }
53
54
  }