@cyberalien/svg-utils 0.0.13 → 0.0.14

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.
@@ -7,11 +7,16 @@ interface Options {
7
7
  /**
8
8
  * Hash an object, make sure hash is unique
9
9
  *
10
- * Number of unique hashes per length, with prefix:
10
+ * Number of unique hashes per length, with prefix for CSS:
11
11
  * 6 chars = 2b unique hashes
12
12
  * 7 chars = 78b unique hashes <-- got collision here
13
13
  * 8 chars = 2.9t unique hashes
14
14
  * 9 chars = 113t unique hashes
15
+ *
16
+ * Numer of unique hashes per length, with prefix for ID:
17
+ * 6 chars = 47b unique hashes
18
+ * 7 chars = 2.9t unique hashes
19
+ * 8 chars = 183t unique hashes
15
20
  */
16
21
  declare function getUniqueHash(data: unknown, options: Options): string;
17
22
  export { getUniqueHash };
@@ -7,11 +7,16 @@ const uniqueWithPrefixHashes = Object.create(null);
7
7
  /**
8
8
  * Hash an object, make sure hash is unique
9
9
  *
10
- * Number of unique hashes per length, with prefix:
10
+ * Number of unique hashes per length, with prefix for CSS:
11
11
  * 6 chars = 2b unique hashes
12
12
  * 7 chars = 78b unique hashes <-- got collision here
13
13
  * 8 chars = 2.9t unique hashes
14
14
  * 9 chars = 113t unique hashes
15
+ *
16
+ * Numer of unique hashes per length, with prefix for ID:
17
+ * 6 chars = 47b unique hashes
18
+ * 7 chars = 2.9t unique hashes
19
+ * 8 chars = 183t unique hashes
15
20
  */
16
21
  function getUniqueHash(data, options) {
17
22
  const { prefix, length, lengths, css } = options;
package/lib/index.d.ts CHANGED
@@ -6,4 +6,9 @@ import { ParsedXMLNode, ParsedXMLTagElement, ParsedXMLTextElement } from "./xml/
6
6
  import { iterateXMLContent } from "./xml/iterate.js";
7
7
  import { parseXMLContent } from "./xml/parse.js";
8
8
  import { stringifyXMLContent } from "./xml/stringify.js";
9
- export { ParsedXMLNode, ParsedXMLTagElement, ParsedXMLTextElement, cloneObject, compareSets, compareValues, hashString, iterateXMLContent, parseXMLContent, sortObject, stringifyXMLContent };
9
+ import { ChangeIDResult } from "./svg/ids/types.js";
10
+ import { changeIDInString } from "./svg/ids/change.js";
11
+ import { removeDuplicateIDs } from "./svg/ids/duplicate.js";
12
+ import { removeUnusedIDs } from "./svg/ids/unused.js";
13
+ import { changeSVGIDs } from "./svg/ids.js";
14
+ export { ChangeIDResult, ParsedXMLNode, ParsedXMLTagElement, ParsedXMLTextElement, changeIDInString, changeSVGIDs, cloneObject, compareSets, compareValues, hashString, iterateXMLContent, parseXMLContent, removeDuplicateIDs, removeUnusedIDs, sortObject, stringifyXMLContent };
package/lib/index.js CHANGED
@@ -5,5 +5,9 @@ import { hashString } from "./helpers/hash/hash.js";
5
5
  import { iterateXMLContent } from "./xml/iterate.js";
6
6
  import { parseXMLContent } from "./xml/parse.js";
7
7
  import { stringifyXMLContent } from "./xml/stringify.js";
8
+ import { changeIDInString } from "./svg/ids/change.js";
9
+ import { removeDuplicateIDs } from "./svg/ids/duplicate.js";
10
+ import { removeUnusedIDs } from "./svg/ids/unused.js";
11
+ import { changeSVGIDs } from "./svg/ids.js";
8
12
 
9
- export { cloneObject, compareSets, compareValues, hashString, iterateXMLContent, parseXMLContent, sortObject, stringifyXMLContent };
13
+ export { changeIDInString, changeSVGIDs, cloneObject, compareSets, compareValues, hashString, iterateXMLContent, parseXMLContent, removeDuplicateIDs, removeUnusedIDs, sortObject, stringifyXMLContent };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Change ID in a string
3
+ */
4
+ declare function changeIDInString(value: string, oldID: string, newID: string): string;
5
+ export { changeIDInString };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Change ID in a string
3
+ */
4
+ function changeIDInString(value, oldID, newID) {
5
+ const escapedID = oldID.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6
+ return value.replace(new RegExp("([#;\"])(" + escapedID + ")([\")]|\\.[a-z])", "g"), "$1" + newID + "$3");
7
+ }
8
+
9
+ export { changeIDInString };
@@ -0,0 +1,7 @@
1
+ import { ParsedXMLTagElement } from "../../xml/types.js";
2
+ import { ChangeIDResult } from "./types.js";
3
+ /**
4
+ * Remove duplicate IDs from SVG
5
+ */
6
+ declare function removeDuplicateIDs(root: ParsedXMLTagElement[], data: ChangeIDResult): ParsedXMLTagElement[];
7
+ export { removeDuplicateIDs };
@@ -0,0 +1,25 @@
1
+ import { iterateXMLContent } from "../../xml/iterate.js";
2
+
3
+ /**
4
+ * Remove duplicate IDs from SVG
5
+ */
6
+ function removeDuplicateIDs(root, data) {
7
+ const remove = /* @__PURE__ */ new Set();
8
+ for (const id in data.map) {
9
+ const nodes = data.map[id];
10
+ if (nodes.length > 1) remove.add(id);
11
+ }
12
+ if (remove.size) {
13
+ const removing = /* @__PURE__ */ new Set();
14
+ return iterateXMLContent(root, (node) => {
15
+ if (node.type !== "tag") return;
16
+ const id = node.attribs.id;
17
+ if (typeof id !== "string" || !remove.has(id)) return;
18
+ if (removing.has(id)) return "remove";
19
+ removing.add(id);
20
+ });
21
+ }
22
+ return root;
23
+ }
24
+
25
+ export { removeDuplicateIDs };
@@ -0,0 +1,6 @@
1
+ import { ParsedXMLTagElement } from "../../xml/types.js";
2
+ interface ChangeIDResult {
3
+ map: Record<string, ParsedXMLTagElement[]>;
4
+ usage: Record<string, ParsedXMLTagElement[]>;
5
+ }
6
+ export { ChangeIDResult };
File without changes
@@ -0,0 +1,7 @@
1
+ import { ParsedXMLTagElement } from "../../xml/types.js";
2
+ import { ChangeIDResult } from "./types.js";
3
+ /**
4
+ * Remove duplicate IDs from SVG
5
+ */
6
+ declare function removeUnusedIDs(root: ParsedXMLTagElement[], data: ChangeIDResult): ParsedXMLTagElement[];
7
+ export { removeUnusedIDs };
@@ -0,0 +1,25 @@
1
+ import { iterateXMLContent } from "../../xml/iterate.js";
2
+
3
+ /**
4
+ * Remove duplicate IDs from SVG
5
+ */
6
+ function removeUnusedIDs(root, data) {
7
+ const remove = /* @__PURE__ */ new Set();
8
+ for (const id in data.usage) if (!data.usage[id].length) remove.add(id);
9
+ if (remove.size) return iterateXMLContent(root, (node, stack) => {
10
+ if (node.type !== "tag") return;
11
+ const id = node.attribs.id;
12
+ if (typeof id !== "string" || !remove.has(id)) return;
13
+ switch (node.tag) {
14
+ case "mask":
15
+ case "clipPath":
16
+ case "symbol": return "remove";
17
+ }
18
+ const parentNode = stack[stack.length - 1];
19
+ if (parentNode?.tag === "defs") return "remove";
20
+ delete node.attribs.id;
21
+ });
22
+ return root;
23
+ }
24
+
25
+ export { removeUnusedIDs };
package/lib/svg/ids.d.ts CHANGED
@@ -1,12 +1,8 @@
1
1
  import { ParsedXMLTagElement } from "../xml/types.js";
2
+ import { ChangeIDResult } from "./ids/types.js";
2
3
  type HashCallback = (id: string, content: string, tagName: string) => string;
3
- type Result = Record<string, ParsedXMLTagElement[]>;
4
4
  /**
5
5
  * Change IDs in SVG using a callback function
6
6
  */
7
- declare function changeSVGIDs(root: ParsedXMLTagElement[], callback: HashCallback): Result;
8
- /**
9
- * Remove duplicate IDs from SVG
10
- */
11
- declare function removeDuplicateIDs(root: ParsedXMLTagElement[], data: Result): ParsedXMLTagElement[];
12
- export { changeSVGIDs, removeDuplicateIDs };
7
+ declare function changeSVGIDs(root: ParsedXMLTagElement[], callback: HashCallback): ChangeIDResult;
8
+ export { changeSVGIDs };
package/lib/svg/ids.js CHANGED
@@ -1,6 +1,6 @@
1
- import { cloneObject } from "../helpers/misc/clone.js";
2
1
  import { iterateXMLContent } from "../xml/iterate.js";
3
2
  import { stringifyXMLContent } from "../xml/stringify.js";
3
+ import { changeIDInString } from "./ids/change.js";
4
4
 
5
5
  /**
6
6
  * Change IDs in SVG using a callback function
@@ -9,7 +9,10 @@ function changeSVGIDs(root, callback) {
9
9
  const idMap = /* @__PURE__ */ new Map();
10
10
  const idNodes = /* @__PURE__ */ new Map();
11
11
  const nestedIDs = /* @__PURE__ */ new Map();
12
- const results = Object.create(null);
12
+ const results = {
13
+ map: Object.create(null),
14
+ usage: Object.create(null)
15
+ };
13
16
  const usage = [];
14
17
  const parse = (replacement) => {
15
18
  iterateXMLContent(root, (node, stack) => {
@@ -24,8 +27,8 @@ function changeSVGIDs(root, callback) {
24
27
  } else if (nodeID === replacement[0]) {
25
28
  const newID = replacement[1];
26
29
  attribs.id = newID;
27
- if (!results[newID]) results[newID] = [node];
28
- else results[newID].push(node);
30
+ if (!results.map[newID]) results.map[newID] = [node];
31
+ else results.map[newID].push(node);
29
32
  }
30
33
  }
31
34
  for (const attrib in attribs) {
@@ -92,39 +95,15 @@ function changeSVGIDs(root, callback) {
92
95
  const nested = nestedIDs.get(id)?.filter((nestedID) => nestedID !== id && allIDs.has(nestedID)) ?? [];
93
96
  if (parseAll || !nested.length) {
94
97
  const node = idNodes.get(id);
95
- const newNode = cloneObject(node);
96
- delete newNode.attribs.id;
97
- for (const usageItem of usage) if (usageItem.node === node && usageItem.id === id) {
98
- const attr = usageItem.attrib;
99
- const value = newNode.attribs[attr];
100
- if (value === id) newNode.attribs[attr] = "";
101
- else {
102
- const parts = value.split(id);
103
- const newValue = [];
104
- const total = parts.length;
105
- for (let i = 0; i < total; i++) {
106
- if (i > 0) {
107
- newValue.push(parts[i - 1]);
108
- if (parts[i - i].slice(-1) !== ";") {
109
- newValue.push(id);
110
- continue;
111
- }
112
- }
113
- if (i < total - 1) {
114
- if (parts[i].slice(0, 1) !== ".") {
115
- newValue.push(parts[i]);
116
- continue;
117
- }
118
- }
119
- }
120
- newNode.attribs[attr] = newValue.join("");
121
- }
122
- }
123
- const content = stringifyXMLContent([newNode]);
98
+ const content = stringifyXMLContent([node]);
124
99
  if (!content) throw new Error(`Failed to stringify node with ID: ${id}`);
125
- const newID = callback(id, content, node.tag);
100
+ const cleanedContent = changeIDInString(content, id, "{id}");
101
+ const newID = callback(id, cleanedContent, node.tag);
126
102
  if (newID !== id) parse([id, newID]);
127
103
  allIDs.delete(id);
104
+ const idUsage = [];
105
+ for (const item of usage) if (item.id === id) idUsage.push(item.node);
106
+ results.usage[newID] = idUsage;
128
107
  }
129
108
  }
130
109
  return allIDs.size !== oldSize;
@@ -135,26 +114,5 @@ function changeSVGIDs(root, callback) {
135
114
  }
136
115
  return results;
137
116
  }
138
- /**
139
- * Remove duplicate IDs from SVG
140
- */
141
- function removeDuplicateIDs(root, data) {
142
- const remove = /* @__PURE__ */ new Set();
143
- for (const id in data) {
144
- const nodes = data[id];
145
- if (nodes.length > 1) remove.add(id);
146
- }
147
- if (remove.size) {
148
- const removing = /* @__PURE__ */ new Set();
149
- return iterateXMLContent(root, (node) => {
150
- if (node.type !== "tag") return;
151
- const id = node.attribs.id;
152
- if (typeof id !== "string" || !remove.has(id)) return;
153
- if (removing.has(id)) return "remove";
154
- removing.add(id);
155
- });
156
- }
157
- return root;
158
- }
159
117
 
160
- export { changeSVGIDs, removeDuplicateIDs };
118
+ export { changeSVGIDs };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "description": "Common functions for working with SVG used by various packages.",
5
5
  "author": "Vjacheslav Trushkin",
6
- "version": "0.0.13",
6
+ "version": "0.0.14",
7
7
  "license": "MIT",
8
8
  "bugs": "https://github.com/cyberalien/svg-utils/issues",
9
9
  "homepage": "https://cyberalien.dev/",