@iconify/tools 2.0.9 → 2.0.13
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/lib/colors/parse.d.ts +19 -3
- package/lib/colors/parse.js +109 -40
- package/lib/colors/parse.mjs +86 -39
- package/lib/colors/validate.js +5 -2
- package/lib/colors/validate.mjs +3 -1
- package/lib/download/git/index.js +12 -0
- package/lib/download/git/index.mjs +7 -0
- package/lib/download/git/reset.d.ts +4 -0
- package/lib/download/git/reset.js +16 -0
- package/lib/download/git/reset.mjs +13 -0
- package/lib/export/json-package.js +6 -1
- package/lib/export/json-package.mjs +4 -1
- package/lib/icon-set/index.js +11 -25
- package/lib/icon-set/index.mjs +13 -25
- package/lib/icon-set/merge.js +0 -1
- package/lib/icon-set/merge.mjs +1 -2
- package/lib/icon-set/props.d.ts +1 -1
- package/lib/icon-set/props.js +3 -2
- package/lib/icon-set/props.mjs +2 -2
- package/lib/icon-set/types.d.ts +3 -1
- package/lib/import/figma/nodes.js +3 -5
- package/lib/import/figma/nodes.mjs +3 -5
- package/lib/index.d.ts +3 -0
- package/lib/index.js +7 -1
- package/lib/index.mjs +6 -0
- package/lib/optimise/flags.js +9 -0
- package/lib/optimise/flags.mjs +8 -0
- package/lib/optimise/global-style.d.ts +5 -0
- package/lib/optimise/global-style.js +158 -0
- package/lib/optimise/global-style.mjs +129 -0
- package/lib/svg/analyse/error.d.ts +5 -0
- package/lib/svg/analyse/error.js +22 -0
- package/lib/svg/analyse/error.mjs +16 -0
- package/lib/svg/analyse/types.d.ts +89 -0
- package/lib/svg/analyse/types.js +2 -0
- package/lib/svg/analyse/types.mjs +0 -0
- package/lib/svg/analyse.d.ts +8 -0
- package/lib/svg/analyse.js +352 -0
- package/lib/svg/analyse.mjs +302 -0
- package/lib/svg/cleanup/attribs.d.ts +1 -1
- package/lib/svg/cleanup/attribs.js +8 -0
- package/lib/svg/cleanup/attribs.mjs +8 -1
- package/lib/svg/cleanup/bad-tags.d.ts +1 -1
- package/lib/svg/cleanup/bad-tags.js +0 -2
- package/lib/svg/cleanup/bad-tags.mjs +0 -3
- package/lib/svg/cleanup/inline-style.d.ts +1 -1
- package/lib/svg/cleanup/root-svg.d.ts +1 -1
- package/lib/svg/cleanup/root-svg.js +4 -2
- package/lib/svg/cleanup/root-svg.mjs +3 -3
- package/lib/svg/cleanup/svgo-style.d.ts +1 -1
- package/lib/svg/data/attributes.js +1 -1
- package/lib/svg/data/attributes.mjs +1 -1
- package/lib/svg/data/tags.d.ts +15 -7
- package/lib/svg/data/tags.js +20 -9
- package/lib/svg/data/tags.mjs +11 -6
- package/lib/svg/parse-style.d.ts +7 -7
- package/lib/svg/parse-style.js +27 -7
- package/lib/svg/parse-style.mjs +26 -7
- package/package.json +22 -2
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// src/optimise/global-style.ts
|
|
2
|
+
import "../svg/data/attributes.mjs";
|
|
3
|
+
import { allValidTags, animateTags } from "../svg/data/tags.mjs";
|
|
4
|
+
import { parseSVG } from "../svg/parse.mjs";
|
|
5
|
+
import { parseSVGStyle } from "../svg/parse-style.mjs";
|
|
6
|
+
function getClassList(value) {
|
|
7
|
+
return value == null ? void 0 : value.split(/\s+/);
|
|
8
|
+
}
|
|
9
|
+
var tempDataAttrbiute = "data-gstyle-temp";
|
|
10
|
+
async function cleanupGlobalStyle(svg) {
|
|
11
|
+
const backup = svg.toString();
|
|
12
|
+
let containsTempAttr = false;
|
|
13
|
+
const animatedClasses = new Set();
|
|
14
|
+
await parseSVG(svg, (item) => {
|
|
15
|
+
if (!animateTags.has(item.tagName)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const $element = item.$element;
|
|
19
|
+
if ($element.attr("attributeName") !== "class") {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
["from", "to", "values"].forEach((attr) => {
|
|
23
|
+
const value = $element.attr(attr);
|
|
24
|
+
if (typeof value !== "string") {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
value.split(";").forEach((item2) => {
|
|
28
|
+
getClassList(item2).forEach((className) => {
|
|
29
|
+
animatedClasses.add(className);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
await parseSVGStyle(svg, async (styleItem) => {
|
|
36
|
+
var _a;
|
|
37
|
+
const returnValue = styleItem.value;
|
|
38
|
+
if (styleItem.type !== "global") {
|
|
39
|
+
return returnValue;
|
|
40
|
+
}
|
|
41
|
+
if (styleItem.selectors.length !== 1 || styleItem.selectorTokens.length !== 1) {
|
|
42
|
+
return returnValue;
|
|
43
|
+
}
|
|
44
|
+
const selectorToken = styleItem.selectorTokens[0];
|
|
45
|
+
if (selectorToken.type !== "selector") {
|
|
46
|
+
return returnValue;
|
|
47
|
+
}
|
|
48
|
+
const selector = styleItem.selectors[0];
|
|
49
|
+
const firstChar = selector.charAt(0);
|
|
50
|
+
let matchType;
|
|
51
|
+
if (firstChar === ".") {
|
|
52
|
+
matchType = "class";
|
|
53
|
+
} else if (firstChar === "#") {
|
|
54
|
+
matchType = "id";
|
|
55
|
+
} else if (allValidTags.has(selector)) {
|
|
56
|
+
matchType = "tag";
|
|
57
|
+
} else {
|
|
58
|
+
return returnValue;
|
|
59
|
+
}
|
|
60
|
+
const valueMatch = matchType === "tag" ? selector : selector.slice(1);
|
|
61
|
+
if (matchType === "class" && animatedClasses.has(valueMatch)) {
|
|
62
|
+
return returnValue;
|
|
63
|
+
}
|
|
64
|
+
const isMatch = (tagName, $element) => {
|
|
65
|
+
switch (matchType) {
|
|
66
|
+
case "id":
|
|
67
|
+
return $element.attr("id") === valueMatch;
|
|
68
|
+
case "tag":
|
|
69
|
+
return tagName === valueMatch;
|
|
70
|
+
case "class": {
|
|
71
|
+
const className = $element.attr("class");
|
|
72
|
+
if (!className || getClassList(className).indexOf(valueMatch) === -1) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
};
|
|
79
|
+
await parseSVG(svg, (svgItem) => {
|
|
80
|
+
var _a2;
|
|
81
|
+
const tagName = svgItem.tagName;
|
|
82
|
+
const $element = svgItem.$element;
|
|
83
|
+
if (!isMatch(tagName, $element)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const addedAttributes = new Set((_a2 = $element.attr(tempDataAttrbiute)) == null ? void 0 : _a2.split(/\s+/));
|
|
87
|
+
const prop = styleItem.prop;
|
|
88
|
+
if ($element.attr(prop) !== void 0) {
|
|
89
|
+
if (addedAttributes.has(prop)) {
|
|
90
|
+
throw new Error("Duplicate attribute");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
$element.attr(prop, styleItem.value);
|
|
94
|
+
addedAttributes.add(prop);
|
|
95
|
+
$element.attr(tempDataAttrbiute, Array.from(addedAttributes).join(" "));
|
|
96
|
+
containsTempAttr = true;
|
|
97
|
+
});
|
|
98
|
+
if (matchType === "class" && ((_a = styleItem.nextTokens[0]) == null ? void 0 : _a.type) === "close") {
|
|
99
|
+
await parseSVG(svg, (svgItem) => {
|
|
100
|
+
const $element = svgItem.$element;
|
|
101
|
+
if (!isMatch("", $element)) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const classList = getClassList($element.attr("class"));
|
|
105
|
+
if (!classList) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const filtered = classList.filter((item) => item !== valueMatch);
|
|
109
|
+
if (!filtered.length) {
|
|
110
|
+
$element.removeAttr("class");
|
|
111
|
+
} else {
|
|
112
|
+
$element.attr("class", filtered.join(" "));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
});
|
|
118
|
+
if (containsTempAttr) {
|
|
119
|
+
await parseSVG(svg, (item) => {
|
|
120
|
+
item.$element.removeAttr(tempDataAttrbiute);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
svg.load(backup);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
cleanupGlobalStyle
|
|
129
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyseTagError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Get tag for error message
|
|
6
|
+
*/
|
|
7
|
+
function analyseTagError(element) {
|
|
8
|
+
let result = '<' + element.tagName;
|
|
9
|
+
if (element._id) {
|
|
10
|
+
result += ' id="' + element._id + '"';
|
|
11
|
+
}
|
|
12
|
+
const attribs = element.attribs;
|
|
13
|
+
if (attribs['d']) {
|
|
14
|
+
const value = attribs['d'];
|
|
15
|
+
result +=
|
|
16
|
+
' d="' +
|
|
17
|
+
(value.length > 16 ? value.slice(0, 12) + '...' : value) +
|
|
18
|
+
'"';
|
|
19
|
+
}
|
|
20
|
+
return result + '>';
|
|
21
|
+
}
|
|
22
|
+
exports.analyseTagError = analyseTagError;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/svg/analyse/error.ts
|
|
2
|
+
function analyseTagError(element) {
|
|
3
|
+
let result = "<" + element.tagName;
|
|
4
|
+
if (element._id) {
|
|
5
|
+
result += ' id="' + element._id + '"';
|
|
6
|
+
}
|
|
7
|
+
const attribs = element.attribs;
|
|
8
|
+
if (attribs["d"]) {
|
|
9
|
+
const value = attribs["d"];
|
|
10
|
+
result += ' d="' + (value.length > 16 ? value.slice(0, 12) + "..." : value) + '"';
|
|
11
|
+
}
|
|
12
|
+
return result + ">";
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
analyseTagError
|
|
16
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/// <reference types="cheerio" />
|
|
2
|
+
/**
|
|
3
|
+
* Options
|
|
4
|
+
*/
|
|
5
|
+
export interface AnalyseSVGStructureOptions {
|
|
6
|
+
fixErrors?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Extended properties for element
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Link to element
|
|
13
|
+
*/
|
|
14
|
+
export interface LinkToElementWithID {
|
|
15
|
+
id: string;
|
|
16
|
+
usedAsMask: boolean;
|
|
17
|
+
usedByIndex: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* How element is used by parent elements
|
|
21
|
+
*/
|
|
22
|
+
export interface ExtendedTagElementUses {
|
|
23
|
+
_usedAsMask: boolean;
|
|
24
|
+
_usedAsPaint: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Definition: mask, clip path, symbol, etc...
|
|
28
|
+
*/
|
|
29
|
+
interface ReusableElement {
|
|
30
|
+
id: string;
|
|
31
|
+
isMask: boolean;
|
|
32
|
+
index: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Element with id
|
|
36
|
+
*
|
|
37
|
+
* Similar to ReusableElement, but not necessary a definition - any element with id. Also contains list of child elements
|
|
38
|
+
*/
|
|
39
|
+
export interface ElementWithID {
|
|
40
|
+
id: string;
|
|
41
|
+
isMask: boolean;
|
|
42
|
+
indexes: Set<number>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parent and child elements. Unlike standard tree, this tree is for elements that inherit attributes from parent element
|
|
46
|
+
*/
|
|
47
|
+
interface ExtendedTagElementRelations {
|
|
48
|
+
_parentElement?: number;
|
|
49
|
+
_childElements?: number[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extended tag
|
|
53
|
+
*/
|
|
54
|
+
export interface ExtendedTagElement extends cheerio.TagElement, ExtendedTagElementUses, ExtendedTagElementRelations {
|
|
55
|
+
_index: number;
|
|
56
|
+
_id?: string;
|
|
57
|
+
_belongsTo?: ElementWithID[];
|
|
58
|
+
_reusableElement?: ReusableElement;
|
|
59
|
+
_linksTo?: LinkToElementWithID[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Additional stuff for <svg>
|
|
63
|
+
*/
|
|
64
|
+
export interface ExtendedRootTagElement extends ExtendedTagElement {
|
|
65
|
+
_parsed?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Tree
|
|
69
|
+
*/
|
|
70
|
+
export interface ElementsTreeItem {
|
|
71
|
+
index: number;
|
|
72
|
+
usedAsMask: boolean;
|
|
73
|
+
parent?: ElementsTreeItem;
|
|
74
|
+
children: ElementsTreeItem[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Elements map
|
|
78
|
+
*/
|
|
79
|
+
export declare type ElementsMap = Map<number, ExtendedTagElement>;
|
|
80
|
+
/**
|
|
81
|
+
* Result
|
|
82
|
+
*/
|
|
83
|
+
export interface AnalyseSVGStructureResult {
|
|
84
|
+
elements: ElementsMap;
|
|
85
|
+
ids: Record<string, number>;
|
|
86
|
+
links: LinkToElementWithID[];
|
|
87
|
+
tree: ElementsTreeItem;
|
|
88
|
+
}
|
|
89
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SVG } from './index';
|
|
2
|
+
import type { AnalyseSVGStructureResult, AnalyseSVGStructureOptions } from './analyse/types';
|
|
3
|
+
/**
|
|
4
|
+
* Find all IDs, links, which elements use palette, which items aren't used
|
|
5
|
+
*
|
|
6
|
+
* Before running this function run cleanup functions to change inline style to attributes and fix attributes
|
|
7
|
+
*/
|
|
8
|
+
export declare function analyseSVGStructure(svg: SVG, options?: AnalyseSVGStructureOptions): Promise<AnalyseSVGStructureResult>;
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyseSVGStructure = void 0;
|
|
4
|
+
const parse_1 = require("./parse");
|
|
5
|
+
const attributes_1 = require("./data/attributes");
|
|
6
|
+
const tags_1 = require("./data/tags");
|
|
7
|
+
const error_1 = require("./analyse/error");
|
|
8
|
+
/**
|
|
9
|
+
* Find all IDs, links, which elements use palette, which items aren't used
|
|
10
|
+
*
|
|
11
|
+
* Before running this function run cleanup functions to change inline style to attributes and fix attributes
|
|
12
|
+
*/
|
|
13
|
+
async function analyseSVGStructure(svg, options = {}) {
|
|
14
|
+
// Options
|
|
15
|
+
const fixErrors = options.fixErrors;
|
|
16
|
+
// Root element
|
|
17
|
+
let root = svg.$svg(':root').get(0);
|
|
18
|
+
if (root._parsed) {
|
|
19
|
+
// Reload to reset custom properties
|
|
20
|
+
svg.load(svg.toString());
|
|
21
|
+
root = svg.$svg(':root').get(0);
|
|
22
|
+
}
|
|
23
|
+
root._parsed = true;
|
|
24
|
+
const cheerio = svg.$svg;
|
|
25
|
+
// List of all elements
|
|
26
|
+
const elements = new Map();
|
|
27
|
+
// List of IDs
|
|
28
|
+
const ids = Object.create(null);
|
|
29
|
+
// Links
|
|
30
|
+
let links = [];
|
|
31
|
+
/**
|
|
32
|
+
* Found element with id
|
|
33
|
+
*/
|
|
34
|
+
function addID(element, id) {
|
|
35
|
+
if (ids[id]) {
|
|
36
|
+
throw new Error(`Duplicate id "${id}"`);
|
|
37
|
+
}
|
|
38
|
+
element._id = id;
|
|
39
|
+
ids[id] = element._index;
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Add _belongsTo
|
|
44
|
+
*/
|
|
45
|
+
function gotElementWithID(element, id, isMask) {
|
|
46
|
+
addID(element, id);
|
|
47
|
+
if (!element._belongsTo) {
|
|
48
|
+
element._belongsTo = [];
|
|
49
|
+
}
|
|
50
|
+
element._belongsTo.push({
|
|
51
|
+
id,
|
|
52
|
+
isMask,
|
|
53
|
+
indexes: new Set([element._index]),
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Mark element as reusable, set properties
|
|
59
|
+
*/
|
|
60
|
+
function gotReusableElement(item, isMask) {
|
|
61
|
+
const element = item.element;
|
|
62
|
+
const attribs = element.attribs;
|
|
63
|
+
const index = element._index;
|
|
64
|
+
const id = attribs['id'];
|
|
65
|
+
if (typeof id !== 'string') {
|
|
66
|
+
const message = `Definition element ${(0, error_1.analyseTagError)(element)} does not have id`;
|
|
67
|
+
if (fixErrors) {
|
|
68
|
+
item.removeNode = true;
|
|
69
|
+
item.testChildren = false;
|
|
70
|
+
console.warn(message);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
throw new Error(message);
|
|
74
|
+
}
|
|
75
|
+
if (ids[id] && fixErrors) {
|
|
76
|
+
console.warn(`Duplicate id "${id}"`);
|
|
77
|
+
item.removeNode = true;
|
|
78
|
+
item.testChildren = false;
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
element._reusableElement = {
|
|
82
|
+
id,
|
|
83
|
+
isMask,
|
|
84
|
+
index,
|
|
85
|
+
};
|
|
86
|
+
gotElementWithID(element, id, isMask);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Found element that uses another element
|
|
91
|
+
*/
|
|
92
|
+
function gotElementReference(item, id, usedAsMask) {
|
|
93
|
+
const element = item.element;
|
|
94
|
+
const usedByIndex = element._index;
|
|
95
|
+
const link = {
|
|
96
|
+
id,
|
|
97
|
+
usedByIndex,
|
|
98
|
+
usedAsMask,
|
|
99
|
+
};
|
|
100
|
+
// Add to global list
|
|
101
|
+
links.push(link);
|
|
102
|
+
// Add to element and parent elements
|
|
103
|
+
if (!element._linksTo) {
|
|
104
|
+
element._linksTo = [];
|
|
105
|
+
}
|
|
106
|
+
element._linksTo.push(link);
|
|
107
|
+
}
|
|
108
|
+
// Find all reusable elements and all usages
|
|
109
|
+
let index = 0;
|
|
110
|
+
await (0, parse_1.parseSVG)(svg, (item) => {
|
|
111
|
+
var _a;
|
|
112
|
+
const { tagName, parents } = item;
|
|
113
|
+
if (tags_1.styleTag.has(tagName)) {
|
|
114
|
+
item.testChildren = false;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const element = item.element;
|
|
118
|
+
const attribs = element.attribs;
|
|
119
|
+
// Set index
|
|
120
|
+
index++;
|
|
121
|
+
element._index = index;
|
|
122
|
+
elements.set(index, element);
|
|
123
|
+
if (!parents.length) {
|
|
124
|
+
// root <svg>
|
|
125
|
+
element._usedAsMask = false;
|
|
126
|
+
element._usedAsPaint = true;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
element._usedAsMask = false;
|
|
130
|
+
element._usedAsPaint = false;
|
|
131
|
+
// Get parent element
|
|
132
|
+
const parentItem = parents[0];
|
|
133
|
+
const parentElement = parentItem.element;
|
|
134
|
+
// Check for mask or clip path
|
|
135
|
+
if (tags_1.maskTags.has(tagName)) {
|
|
136
|
+
// Element can only be used as mask or clip path
|
|
137
|
+
if (!gotReusableElement(item, true)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (tags_1.reusableElementsWithPalette.has(tagName)) {
|
|
142
|
+
// Reusable element that uses palette
|
|
143
|
+
if (!gotReusableElement(item, false)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else if (tags_1.defsTag.has(parentItem.tagName)) {
|
|
148
|
+
// Symbol without <symbol> tag
|
|
149
|
+
if (!gotReusableElement(item, false)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else if (!tags_1.defsTag.has(tagName)) {
|
|
154
|
+
// Not reusable element, not <defs>. Copy parent stuff
|
|
155
|
+
element._usedAsMask = parentElement._usedAsMask;
|
|
156
|
+
element._usedAsPaint = parentElement._usedAsPaint;
|
|
157
|
+
element._parentElement = parentElement._index;
|
|
158
|
+
if (!parentElement._childElements) {
|
|
159
|
+
parentElement._childElements = [];
|
|
160
|
+
}
|
|
161
|
+
parentElement._childElements.push(index);
|
|
162
|
+
// Copy id of reusable element from parent
|
|
163
|
+
const parentReusableElement = parentElement._reusableElement;
|
|
164
|
+
if (parentReusableElement) {
|
|
165
|
+
if (element._reusableElement) {
|
|
166
|
+
// Reusable element inside reusable element: should not happen!
|
|
167
|
+
throw new Error(`Reusable element ${(0, error_1.analyseTagError)(element)} is inside another reusable element id="${parentReusableElement.id}"`);
|
|
168
|
+
}
|
|
169
|
+
element._reusableElement = parentReusableElement;
|
|
170
|
+
}
|
|
171
|
+
// Copy all parent ids
|
|
172
|
+
const parentBelongsTo = parentElement._belongsTo;
|
|
173
|
+
if (parentBelongsTo) {
|
|
174
|
+
const list = element._belongsTo || (element._belongsTo = []);
|
|
175
|
+
parentBelongsTo.forEach((item) => {
|
|
176
|
+
item.indexes.add(index);
|
|
177
|
+
list.push(item);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// Check if element has its own id
|
|
181
|
+
if (element._id === void 0) {
|
|
182
|
+
const id = attribs['id'];
|
|
183
|
+
if (typeof id === 'string') {
|
|
184
|
+
if (ids[id] && fixErrors) {
|
|
185
|
+
console.warn(`Duplicate id "${id}"`);
|
|
186
|
+
cheerio(element).removeAttr('id');
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
gotElementWithID(element, id, false);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Check if element uses any ID
|
|
195
|
+
if ((_a = attributes_1.tagSpecificNonPresentationalAttributes[tagName]) === null || _a === void 0 ? void 0 : _a.has('href')) {
|
|
196
|
+
const href = attribs['href'] || attribs['xlink:href'];
|
|
197
|
+
if (typeof href === 'string') {
|
|
198
|
+
if (href.slice(0, 1) !== '#') {
|
|
199
|
+
throw new Error(`Invalid link in ${(0, error_1.analyseTagError)(element)}`);
|
|
200
|
+
}
|
|
201
|
+
const id = href.slice(1);
|
|
202
|
+
gotElementReference(item, id, false);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Check colors
|
|
206
|
+
Object.keys(attribs).forEach((attr) => {
|
|
207
|
+
// Get id
|
|
208
|
+
let value = attribs[attr];
|
|
209
|
+
if (value.slice(0, 5).toLowerCase() !== 'url(#') {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
value = value.slice(5);
|
|
213
|
+
if (value.slice(-1) !== ')') {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const id = value.slice(0, value.length - 1).trim();
|
|
217
|
+
if (attributes_1.urlPresentationalAttributes.has(attr)) {
|
|
218
|
+
// Used as mask, clip path or filter
|
|
219
|
+
gotElementReference(item, id, attr !== 'filter');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (attributes_1.commonColorPresentationalAttributes.has(attr) ||
|
|
223
|
+
attributes_1.markerAttributes.has(attr)) {
|
|
224
|
+
// Used as paint
|
|
225
|
+
gotElementReference(item, id, false);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
// Make sure all required IDs exist
|
|
231
|
+
links = links.filter((item) => {
|
|
232
|
+
const id = item.id;
|
|
233
|
+
if (ids[id]) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
// Attempt to fix error
|
|
237
|
+
function fix() {
|
|
238
|
+
const index = item.usedByIndex;
|
|
239
|
+
const element = elements.get(index);
|
|
240
|
+
const tagName = element.tagName;
|
|
241
|
+
function remove() {
|
|
242
|
+
var _a;
|
|
243
|
+
const $element = cheerio(element);
|
|
244
|
+
const parent = element.parent;
|
|
245
|
+
if (parent) {
|
|
246
|
+
if (parent._childElements) {
|
|
247
|
+
parent._childElements = parent._childElements.filter((num) => num !== index);
|
|
248
|
+
}
|
|
249
|
+
(_a = parent._belongsTo) === null || _a === void 0 ? void 0 : _a.forEach((list) => {
|
|
250
|
+
list.indexes.delete(index);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
$element.remove();
|
|
254
|
+
}
|
|
255
|
+
// Remove links
|
|
256
|
+
if (element._linksTo) {
|
|
257
|
+
element._linksTo = element._linksTo.filter((item) => item.id !== id);
|
|
258
|
+
}
|
|
259
|
+
// Remove <use /> without children
|
|
260
|
+
if (!element.children.length) {
|
|
261
|
+
if (tags_1.useTag.has(tagName)) {
|
|
262
|
+
remove();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Remove attributes that use id
|
|
267
|
+
const matches = new Set(['#' + id, 'url(#' + id + ')']);
|
|
268
|
+
const attribs = element.attribs;
|
|
269
|
+
for (const attr in attribs) {
|
|
270
|
+
if (matches.has(attribs[attr])) {
|
|
271
|
+
cheerio(element).removeAttr(attr);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const message = `Missing element with id="${id}"`;
|
|
276
|
+
if (fixErrors) {
|
|
277
|
+
fix();
|
|
278
|
+
console.warn(message);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
throw new Error(message);
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
});
|
|
285
|
+
// Check if tree item already has child item
|
|
286
|
+
function hasChildItem(tree, child, canThrow) {
|
|
287
|
+
const item = tree.children.find((item) => item.index === child.index &&
|
|
288
|
+
item.usedAsMask === child.usedAsMask);
|
|
289
|
+
if (item && canThrow) {
|
|
290
|
+
throw new Error('Recursion');
|
|
291
|
+
}
|
|
292
|
+
return !!item;
|
|
293
|
+
}
|
|
294
|
+
// Generate tree
|
|
295
|
+
const tree = {
|
|
296
|
+
index: 1,
|
|
297
|
+
usedAsMask: false,
|
|
298
|
+
children: [],
|
|
299
|
+
};
|
|
300
|
+
function parseTreeItem(tree, usedItems, inMask) {
|
|
301
|
+
var _a, _b;
|
|
302
|
+
const element = elements.get(tree.index);
|
|
303
|
+
// Add usage
|
|
304
|
+
if (tree.usedAsMask || inMask) {
|
|
305
|
+
element._usedAsMask = true;
|
|
306
|
+
inMask = true;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
element._usedAsPaint = true;
|
|
310
|
+
}
|
|
311
|
+
usedItems = usedItems.slice(0);
|
|
312
|
+
usedItems.push(element._index);
|
|
313
|
+
// Add all child elements
|
|
314
|
+
(_a = element._childElements) === null || _a === void 0 ? void 0 : _a.forEach((childIndex) => {
|
|
315
|
+
if (usedItems.indexOf(childIndex) !== -1) {
|
|
316
|
+
throw new Error('Recursion');
|
|
317
|
+
}
|
|
318
|
+
const childItem = {
|
|
319
|
+
index: childIndex,
|
|
320
|
+
usedAsMask: false,
|
|
321
|
+
children: [],
|
|
322
|
+
parent: tree,
|
|
323
|
+
};
|
|
324
|
+
tree.children.push(childItem);
|
|
325
|
+
parseTreeItem(childItem, usedItems, inMask);
|
|
326
|
+
});
|
|
327
|
+
// Add all links
|
|
328
|
+
(_b = element._linksTo) === null || _b === void 0 ? void 0 : _b.forEach((link) => {
|
|
329
|
+
const linkIndex = ids[link.id];
|
|
330
|
+
const usedAsMask = link.usedAsMask;
|
|
331
|
+
const childItem = {
|
|
332
|
+
index: linkIndex,
|
|
333
|
+
usedAsMask,
|
|
334
|
+
children: [],
|
|
335
|
+
parent: tree,
|
|
336
|
+
};
|
|
337
|
+
if (hasChildItem(tree, childItem, false)) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
tree.children.push(childItem);
|
|
341
|
+
parseTreeItem(childItem, usedItems, inMask || usedAsMask);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
parseTreeItem(tree, [0], false);
|
|
345
|
+
return {
|
|
346
|
+
elements,
|
|
347
|
+
ids,
|
|
348
|
+
links,
|
|
349
|
+
tree,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
exports.analyseSVGStructure = analyseSVGStructure;
|