@cyberalien/svg-utils 0.0.7 → 0.0.9

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.
@@ -1,5 +1,16 @@
1
+ interface Options {
2
+ prefix?: string;
3
+ length: number;
4
+ lengths?: Record<string, number>;
5
+ }
1
6
  /**
2
7
  * Hash an object, make sure hash is unique
8
+ *
9
+ * Number of unique hashes per length, with prefix:
10
+ * 6 chars = 2b unique hashes
11
+ * 7 chars = 78b unique hashes <-- got collision here
12
+ * 8 chars = 2.9t unique hashes
13
+ * 9 chars = 113t unique hashes
3
14
  */
4
- declare function getUniqueHash(data: unknown, hasPrefix?: boolean, size?: number): string;
15
+ declare function getUniqueHash(data: unknown, options: Options): string;
5
16
  export { getUniqueHash };
@@ -6,10 +6,20 @@ const uniqueHashes = Object.create(null);
6
6
  const uniqueWithPrefixHashes = Object.create(null);
7
7
  /**
8
8
  * Hash an object, make sure hash is unique
9
+ *
10
+ * Number of unique hashes per length, with prefix:
11
+ * 6 chars = 2b unique hashes
12
+ * 7 chars = 78b unique hashes <-- got collision here
13
+ * 8 chars = 2.9t unique hashes
14
+ * 9 chars = 113t unique hashes
9
15
  */
10
- function getUniqueHash(data, hasPrefix = true, size = 8) {
16
+ function getUniqueHash(data, options) {
17
+ const { prefix, length, lengths } = options;
11
18
  const str = typeof data === "string" ? data : JSON.stringify(sortObject(data));
12
- const hash = hashToString(hashString(str), hasPrefix, size);
19
+ const hasPrefix = !!prefix;
20
+ const values = hashString(str);
21
+ let hash = hashToString(values, hasPrefix, length);
22
+ if (lengths?.[hash]) hash = hashToString(values, hasPrefix, lengths[hash]);
13
23
  const cache = hasPrefix ? uniqueWithPrefixHashes : uniqueHashes;
14
24
  if (!cache[hash]) cache[hash] = str;
15
25
  else if (cache[hash] !== str) {
@@ -17,7 +27,7 @@ function getUniqueHash(data, hasPrefix = true, size = 8) {
17
27
  console.warn("Data 2:", str);
18
28
  throw new Error(`Hash collision detected: ${hash}`);
19
29
  }
20
- return hash;
30
+ return `${prefix}${hash}`;
21
31
  }
22
32
 
23
33
  export { getUniqueHash };
package/lib/svg/ids.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  import { ParsedXMLTagElement } from "../xml/types.js";
2
2
  type HashCallback = (id: string, content: string, tagName: string) => string;
3
+ type Result = Record<string, ParsedXMLTagElement[]>;
3
4
  /**
4
5
  * Change IDs in SVG using a callback function
5
6
  */
6
- declare function changeSVGIDs(root: ParsedXMLTagElement[], callback: HashCallback): void;
7
- export { changeSVGIDs };
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 };
package/lib/svg/ids.js CHANGED
@@ -9,6 +9,7 @@ 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
13
  const usage = [];
13
14
  const parse = (replacement) => {
14
15
  iterateXMLContent(root, (node, stack) => {
@@ -20,7 +21,12 @@ function changeSVGIDs(root, callback) {
20
21
  if (idNodes.has(nodeID)) throw new Error(`Duplicate ID found: ${nodeID}`);
21
22
  idNodes.set(nodeID, node);
22
23
  idMap.set(node, nodeID);
23
- } else if (nodeID === replacement[0]) attribs.id = replacement[1];
24
+ } else if (nodeID === replacement[0]) {
25
+ const newID = replacement[1];
26
+ attribs.id = newID;
27
+ if (!results[newID]) results[newID] = [node];
28
+ else results[newID].push(node);
29
+ }
24
30
  }
25
31
  for (const attrib in attribs) {
26
32
  const value = attribs[attrib];
@@ -78,7 +84,7 @@ function changeSVGIDs(root, callback) {
78
84
  });
79
85
  };
80
86
  parse();
81
- if (!idMap.size) return;
87
+ if (!idMap.size) return results;
82
88
  const allIDs = new Set(idMap.values());
83
89
  const parseIDs = (parseAll = false) => {
84
90
  const oldSize = allIDs.size;
@@ -100,8 +106,23 @@ function changeSVGIDs(root, callback) {
100
106
  };
101
107
  while (allIDs.size) if (!parseIDs()) {
102
108
  parseIDs(true);
103
- return;
109
+ return results;
110
+ }
111
+ return results;
112
+ }
113
+ /**
114
+ * Remove duplicate IDs from SVG
115
+ */
116
+ function removeDuplicateIDs(root, data) {
117
+ const remove = [];
118
+ for (const id in data) {
119
+ const nodes = data[id];
120
+ if (nodes.length > 1) remove.push(...nodes.slice(1));
104
121
  }
122
+ if (remove.length) return iterateXMLContent(root, (node) => {
123
+ if (remove.includes(node)) return "remove";
124
+ });
125
+ return root;
105
126
  }
106
127
 
107
- export { changeSVGIDs };
128
+ export { changeSVGIDs, removeDuplicateIDs };
@@ -8,5 +8,5 @@ import { ParsedXMLNode, ParsedXMLTagElement } from "./types.js";
8
8
  * - 'abort': stop iteration
9
9
  */
10
10
  type CallbackResult = void | 'remove' | 'skip' | 'abort';
11
- declare function iterateXMLContent(root: ParsedXMLTagElement[], callback: (node: ParsedXMLNode, stack: ParsedXMLTagElement[]) => CallbackResult): ParsedXMLNode[];
11
+ declare function iterateXMLContent(root: ParsedXMLTagElement[], callback: (node: ParsedXMLNode, stack: ParsedXMLTagElement[]) => CallbackResult): ParsedXMLTagElement[];
12
12
  export { iterateXMLContent };
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.7",
6
+ "version": "0.0.9",
7
7
  "license": "MIT",
8
8
  "bugs": "https://github.com/cyberalien/svg-utils/issues",
9
9
  "homepage": "https://cyberalien.dev/",