Created
August 27, 2023 12:24
-
-
Save adeleke5140/1385f601f9e54cb799e3685cd04be0be to your computer and use it in GitHub Desktop.
Generate svg sprites
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { promises as fs } from "node:fs"; | |
import * as path from "node:path"; | |
import { glob } from "glob"; | |
import { parse } from "node-html-parser"; | |
async function buildIcons() { | |
const cwd = process.cwd(); | |
const inputDir = path.join(cwd, "svgs"); | |
const inputDirRelative = path.relative(cwd, inputDir); | |
const outputDir = path.join(cwd, "src", "component", "icons"); | |
const spriteFilePath = path.join(outputDir, "sprite.svg"); | |
const outpurDirRelative = path.relative(cwd, outputDir); | |
try { | |
const files = glob | |
.sync("**/*.svg", { | |
cwd: inputDir, | |
}) | |
.sort((a, b) => a.localeCompare(b)); | |
if (files.length === 0) { | |
console.log(`No SVG files found in ${inputDirRelative}`); | |
process.exit(0); | |
} | |
console.log( | |
`Generating sprite for ${files.length} SVG files in ${inputDirRelative}` | |
); | |
const spriteSheetContent = await generateSvgSprite({ | |
files, | |
inputDir, | |
}); | |
await fs.mkdir(outputDir, { recursive: true }); | |
await fs.writeFile(spriteFilePath, spriteSheetContent, "utf-8"); | |
await writeIfChanged( | |
path.join(outputDir, "sprite.svg"), | |
spriteSheetContent | |
); | |
} catch (error) { | |
console.error("An error occured:", error); | |
process.exit(1); | |
} | |
/** | |
* Outputs an SVG string with all the icons as symbols | |
*/ | |
async function generateSvgSprite({ | |
files, | |
inputDir, | |
}: { | |
files: string[]; | |
inputDir: string; | |
}) { | |
//Eavh SVG becomes a symbol and we wrap them all in a single SVG | |
const symbols = await Promise.all( | |
files.map(async (file) => { | |
const input = await fs.readFile(path.join(inputDir, file), "utf-8"); | |
const root = parse(input); | |
const svg = root.querySelector("svg"); | |
if (!svg) throw new Error("No SVG element found in file: " + file); | |
svg.tagName = "symbol"; | |
svg.setAttribute("id", file.replace(/\.svg$/, "")); | |
svg.removeAttribute("xmlns"); | |
svg.removeAttribute("xmlns:xlink"); | |
svg.removeAttribute("width"); | |
svg.removeAttribute("height"); | |
svg.removeAttribute("version"); | |
return svg.toString().trim(); | |
}) | |
); | |
return [ | |
`<?xml version="1.0" encoding="UTF-8"?>`, | |
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0">`, | |
`<defs>`, // for semantics: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs | |
...symbols, | |
`</defs>`, | |
`</svg>`, | |
].join("\n"); | |
} | |
async function writeIfChanged(filepath: string, newContent: string) { | |
const currentContent = await fs.readFile(filepath, "utf-8"); | |
if (currentContent !== newContent) { | |
await fs.writeFile(filepath, newContent, "utf-8"); | |
} | |
} | |
} | |
buildIcons(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment