IOweb/node_modules/@iconify/tools/lib/svg/parse-style.mjs
2026-07-03 15:07:38 -05:00

241 lines
7.2 KiB
JavaScript

import { parseInlineStyle } from '../css/parse.mjs';
import { tokensToString } from '../css/parser/export.mjs';
import { getTokens } from '../css/parser/tokens.mjs';
import { tokensTree } from '../css/parser/tree.mjs';
import { parseSVG } from './parse.mjs';
import '../css/parser/error.mjs';
import '../css/parser/strings.mjs';
import '../css/parser/text.mjs';
function assertNotOldCode(value) {
if (value instanceof Promise) {
throw new Error("parseSVGStyle does not support async callbacks");
}
}
function parseSVGStyle(svg, callback) {
parseSVG(svg, (item) => {
const tagName = item.tagName;
const $element = item.$element;
function parseStyleItem() {
const content = $element.text();
if (typeof content !== "string") {
$element.remove();
return;
}
const tokens = getTokens(content);
if (!(tokens instanceof Array)) {
throw new Error("Error parsing style");
}
let changed2 = false;
const selectorStart = [];
let newTokens = [];
while (tokens.length) {
const token = tokens.shift();
if (!token) {
break;
}
switch (token.type) {
case "selector":
selectorStart.push(newTokens.length);
newTokens.push(token);
break;
case "close":
selectorStart.pop();
newTokens.push(token);
break;
case "at-rule": {
selectorStart.push(newTokens.length);
const prop = token.rule;
const value = token.value;
const isAnimation = prop === "keyframes" || prop.slice(0, 1) === "-" && prop.split("-").pop() === "keyframes";
const childTokens = [];
const animationRules = /* @__PURE__ */ Object.create(null);
let depth = 1;
let index = 0;
let isFrom = false;
while (depth > 0) {
const childToken = tokens[index];
index++;
if (!childToken) {
throw new Error(
"Something went wrong parsing CSS"
);
}
childTokens.push(childToken);
switch (childToken.type) {
case "close": {
depth--;
isFrom = false;
break;
}
case "selector": {
depth++;
if (isAnimation) {
const rule = childToken.code;
if (rule === "from" || rule === "0%") {
isFrom = true;
}
}
break;
}
case "at-rule": {
depth++;
if (isAnimation) {
throw new Error(
"Nested at-rule in keyframes ???"
);
}
break;
}
case "rule": {
if (isAnimation && isFrom) {
animationRules[childToken.prop] = childToken.value;
}
break;
}
}
}
const skipCount = childTokens.length;
const result = callback(
isAnimation ? {
type: "keyframes",
prop,
value,
token,
childTokens,
from: animationRules,
prevTokens: newTokens,
nextTokens: tokens.slice(0)
} : {
type: "at-rule",
prop,
value,
token,
childTokens,
prevTokens: newTokens,
nextTokens: tokens.slice(0)
}
);
if (result !== void 0) {
assertNotOldCode(result);
if (isAnimation) {
if (result !== value) {
changed2 = true;
token.value = result;
}
newTokens.push(token);
for (let i = 0; i < skipCount; i++) {
tokens.shift();
}
newTokens = newTokens.concat(childTokens);
} else {
if (result !== value) {
throw new Error(
"Changing value for at-rule is not supported"
);
}
newTokens.push(token);
}
} else {
changed2 = true;
for (let i = 0; i < skipCount; i++) {
tokens.shift();
}
}
break;
}
case "rule": {
const value = token.value;
const selectorTokens = selectorStart.map((index) => newTokens[index]).filter((item2) => item2 !== null);
const result = callback({
type: "global",
prop: token.prop,
value,
token,
selectorTokens,
selectors: selectorTokens.reduce(
(prev, current) => {
switch (current.type) {
case "selector": {
return prev.concat(
current.selectors
);
}
}
return prev;
},
[]
),
prevTokens: newTokens,
nextTokens: tokens.slice(0)
});
if (result !== void 0) {
assertNotOldCode(result);
if (result !== value) {
changed2 = true;
token.value = result;
}
newTokens.push(token);
} else {
changed2 = true;
}
break;
}
}
}
if (changed2) {
const tree = tokensTree(
newTokens.filter((token) => token !== null)
);
if (!tree.length) {
$element.remove();
} else {
const newContent = tokensToString(tree);
item.$element.text("\n" + newContent);
}
}
}
if (tagName === "style") {
parseStyleItem();
return;
}
const attribs = item.element.attribs;
if (attribs.style === void 0) {
return;
}
const parsedStyle = parseInlineStyle(attribs.style);
if (parsedStyle === null) {
$element.removeAttr("style");
return;
}
let changed = false;
for (const prop in parsedStyle) {
const value = parsedStyle[prop];
const result = callback({
type: "inline",
prop,
value,
item
});
assertNotOldCode(result);
if (result !== value) {
changed = true;
if (result === void 0) {
delete parsedStyle[prop];
} else {
parsedStyle[prop] = result;
}
}
}
if (changed) {
const newStyle = Object.keys(parsedStyle).map((key) => key + ":" + parsedStyle[key] + ";").join("");
if (!newStyle.length) {
$element.removeAttr("style");
} else {
$element.attr("style", newStyle);
}
}
});
}
export { parseSVGStyle };