Skip to content

Instantly share code, notes, and snippets.

@syrxw
Last active December 7, 2020 09:31
Show Gist options
  • Save syrxw/5ef7c2fdcdd03694b7cbfe3451676a92 to your computer and use it in GitHub Desktop.
Save syrxw/5ef7c2fdcdd03694b7cbfe3451676a92 to your computer and use it in GitHub Desktop.
svg symbol合成脚本
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
#!/usr/bin/env node
const program = require("commander");
const fsp = require("fs").promises;
const path = require("path");
const chalk = require("chalk");
const globby = require("globby");
const cheerio = require("cheerio");
const css = require("css");
const chokidar = require("chokidar");
program
.version("0.0.2")
.description("svg图标雪碧图生成器 by syrxw")
.option("-g, --generate", "生成");
const svgFilePath = path.resolve("src/icons/icons.svg"); // svg最终生成文件
const dir = path.resolve("src/assets/image/svg_icons");// svg图标文件路径
const filePaths = globby.sync(["**/*.svg"], {
cwd: dir,
});
let svgFile = null;
function splitFileName(text) {
var pattern = /\.{1}[a-z]{1,}$/;
if (pattern.exec(text) !== null) {
return text.slice(0, pattern.exec(text).index);
} else {
return text;
}
}
// 生成css AST语法树
function parseCSS(content) {
const obj = css.parse(content);
const rules = obj.stylesheet.rules;
return rules.map((item) => {
return {
selector: item.selectors[0],
declarations: item.declarations.map((kitem) => {
return {
property: kitem.property,
value: kitem.value,
};
}),
};
});
}
// 读取svg文件
async function readSvgFile() {
try {
const data = await fsp.readFile(svgFilePath);
svgFile = cheerio.load(data, { xmlMode: true });
} catch (err) {
console.log(chalk.red("icons.svg文件不存在或文件损坏"));
return false;
}
}
async function readSvgIcons() {
try {
svgFile("defs").html(" ");
for (const file of filePaths) {
const data = await fsp.readFile(path.join(dir, file));
const $ = cheerio.load(data, { xmlMode: true });
const fileName = splitFileName(file);
if ($("path").length < 1) {
return false;
} else {
if ($("style").length > 0) {
const parsrCssRst = parseCSS($("style").html());
parsrCssRst.forEach((item) => {
item.declarations.forEach((declar) => {
$(item.selector).attr(declar.property, declar.value);
});
});
}
const svgWidth = $("svg").attr("width");
const svgHeight = $("svg").attr("width");
const element = `<symbol id="${fileName}" viewBox="0 0 ${
svgWidth ? svgWidth.replace("px", "") : 100
} ${svgHeight ? svgHeight.replace("px", "") : 100}">${$(
"path"
)}</symbol>`;
svgFile("defs").append(element);
}
}
} catch (err) {
console.log(chalk.red(err));
return false;
}
}
async function writeIconFile() {
try {
await fsp.writeFile(svgFilePath, svgFile.root().html());
} catch (err) {
console.log(chalk.red(err));
return false;
}
}
async function main() {
try {
await readSvgFile();
} catch (e) {
console.log(chalk.red(err));
}
try {
await readSvgIcons();
} catch (e) {
console.log(chalk.red(err));
}
try {
await writeIconFile();
} catch (e) {
console.log(chalk.red(err));
}
}
main()
<template>
<div class="hello">
<svg class="svg-icon" aria-hidden="true" v-for="n in 38" :key="n">
<use :xlink:href="`#icon_0${n}`"></use>
</svg>
</div>
</template>
<script>
import '@/icons/icons.svg';
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
// vue.config.js
const path = require('path')
module.exports = {
chainWebpack: config => {
const svgRule = config.module.rule('svg')
// 清除已有的所有 loader。
// 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。
svgRule.uses.clear()
svgRule
.test(/\.svg$/)
.include.add(path.resolve(__dirname, './src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon'
})
const fileRule = config.module.rule('file')
fileRule.uses.clear()
fileRule
.test(/\.svg$/)
.exclude.add(path.resolve(__dirname, './src/icons'))
.end()
.use('file-loader')
.loader('file-loader')
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment