Files
webstudio/packages/icons/svg-string.ts
Oleg Isonen dc508445ce feat: Gradients UI (#5449)
## Description

Closes https://github.com/webstudio-is/webstudio/issues/3448

## Todo

- [x] linear gradient
- [x] without stops linear-gradient(red,green)
- [x] delete stops
- [x] support variables
- [x] tweak design
- [x] conic
- [x] one-color
- [x] radial
2025-11-22 14:28:17 +01:00

87 lines
2.2 KiB
TypeScript

import { readdir, readFile, writeFile } from "node:fs/promises";
import { basename, extname } from "node:path";
import { createHash } from "node:crypto"; // Import createHash
import { type Config, optimize } from "svgo";
const pascalcase = (string: string) => {
return string
.replace(/[^A-Z0-9]+([A-Z0-9])?/gi, (_invalid, char) =>
char == null ? "" : char.toUpperCase()
)
.replace(/^[a-z]/, (char) => char.toUpperCase());
};
const transformComponentName = (filename: string) => {
const name = basename(filename, extname(filename));
// digits cannot start variable name
return `${pascalcase(name).replace(/^[0-9]/, (char) => `_${char}`)}Icon`;
};
const prefixCache = new Map<string, string>();
const getHashedPrefix = (path?: string) => {
if (path === undefined) {
return "svg";
}
const cached = prefixCache.get(path);
if (cached !== undefined) {
return cached;
}
const hash = createHash("sha256").update(path).digest("hex").slice(0, 8);
prefixCache.set(path, hash);
return hash;
};
const plugins: Config["plugins"] = [
{
name: "preset-default",
params: {
overrides: {
// preserve viewBox
removeViewBox: false,
convertTransform: false,
inlineStyles: false,
cleanupIds: false,
},
},
},
// convert width/height to viewBox if missing
{ name: "removeDimensions" },
{
name: "prefixIds",
params: {
prefix: (_node: unknown, info: { path?: string }) =>
getHashedPrefix(info.path),
prefixClassNames: false,
},
},
{
name: "addAttributesToSVGElement",
params: {
attributes: [
{
fill: "currentColor",
width: "100%",
height: "100%",
style: "display: block;",
},
],
},
},
];
let moduleContent = "";
for (const name of await readdir("./icons")) {
if (name.endsWith(".svg")) {
const exportName = transformComponentName(name);
const content = await readFile(`./icons/${name}`, "utf-8");
const { data: optimized } = optimize(content, {
path: `./icons/${name}`,
plugins,
});
moduleContent += `export const ${exportName} = \`${optimized.trim()}\`;\n`;
}
}
await writeFile("./src/__generated__/svg.ts", moduleContent);