| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /**
- * 从两套 iconfont CDN CSS(与 setIconfont 历史配置一致)合并类名与 unicode,
- * 生成 iconfont.json + 双字体 iconfont.css,并下载 woff2/ttf 到 src/assets/iconfont。
- * 规则顺序:2298093 为基础,3882322 覆盖同名类(与页面先插 229 再插 388 时一致)。
- *
- * 使用:node scripts/merge-iconfont-from-cdn.mjs
- */
- import fs from 'node:fs';
- import path from 'node:path';
- import https from 'node:https';
- import { fileURLToPath } from 'node:url';
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
- const root = path.resolve(__dirname, '..');
- const outDir = path.join(root, 'src', 'assets', 'iconfont');
- const URL_229_CSS = 'https://at.alicdn.com/t/font_2298093_y6u00apwst.css';
- const URL_388_CSS = 'https://at.alicdn.com/t/c/font_3882322_9ah7y8m9175.css';
- const FONT_FILES = [
- { url: 'https://at.alicdn.com/t/font_2298093_y6u00apwst.woff2?t=1627014681704', name: 'font-2298093.woff2' },
- { url: 'https://at.alicdn.com/t/font_2298093_y6u00apwst.ttf?t=1627014681704', name: 'font-2298093.ttf' },
- { url: 'https://at.alicdn.com/t/c/font_3882322_9ah7y8m9175.woff2?t=1676037377315', name: 'font-3882322.woff2' },
- { url: 'https://at.alicdn.com/t/c/font_3882322_9ah7y8m9175.ttf?t=1676037377315', name: 'font-3882322.ttf' },
- ];
- function fetchText(url) {
- return new Promise((resolve, reject) => {
- https
- .get(url, (res) => {
- if (res.statusCode === 301 || res.statusCode === 302) {
- fetchText(res.headers.location).then(resolve).catch(reject);
- return;
- }
- if (res.statusCode !== 200) {
- reject(new Error(`GET ${url} ${res.statusCode}`));
- return;
- }
- const chunks = [];
- res.on('data', (c) => chunks.push(c));
- res.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
- })
- .on('error', reject);
- });
- }
- function downloadFile(url, dest) {
- return new Promise((resolve, reject) => {
- const file = fs.createWriteStream(dest);
- https
- .get(url, (res) => {
- if (res.statusCode === 301 || res.statusCode === 302) {
- file.close();
- fs.unlink(dest, () => {});
- downloadFile(res.headers.location, dest).then(resolve).catch(reject);
- return;
- }
- if (res.statusCode !== 200) {
- file.close();
- fs.unlink(dest, () => {});
- reject(new Error(`GET ${url} ${res.statusCode}`));
- return;
- }
- res.pipe(file);
- file.on('finish', () => file.close(resolve));
- })
- .on('error', (e) => {
- file.close();
- fs.unlink(dest, () => {});
- reject(e);
- });
- });
- }
- /** @returns {Map<string, string>} className (e.g. icon-foo) -> hex 如 e670(无反斜杠) */
- function parseIconRules(css) {
- const map = new Map();
- const re = /\.(icon-[a-zA-Z0-9_-]+):before\s*\{[^}]*?content:\s*"([^"]+)"/g;
- let m;
- while ((m = re.exec(css))) {
- let hex = m[2].replace(/^\\+/, '');
- map.set(m[1], hex);
- }
- return map;
- }
- /** 合并:388 覆盖 229 */
- function mergeMaps(base229, overlay388) {
- const unicode = new Map(base229);
- const source = new Map();
- for (const k of base229.keys()) source.set(k, '2298093');
- for (const [k, v] of overlay388) {
- unicode.set(k, v);
- source.set(k, '3882322');
- }
- return { unicode, source };
- }
- function buildCss({ unicode, source }) {
- const lines = [
- '/* 由 scripts/merge-iconfont-from-cdn.mjs 生成;双字体与历史双 CDN 叠加顺序一致(388 覆盖同名类) */',
- '@font-face {',
- ' font-family: "iconfont-2298093";',
- " src: url('./font-2298093.woff2') format('woff2'),",
- " url('./font-2298093.ttf') format('truetype');",
- '}',
- '@font-face {',
- ' font-family: "iconfont-3882322";',
- " src: url('./font-3882322.woff2') format('woff2'),",
- " url('./font-3882322.ttf') format('truetype');",
- '}',
- '.iconfont {',
- ' font-size: 16px;',
- ' font-style: normal;',
- ' -webkit-font-smoothing: antialiased;',
- ' -moz-osx-font-smoothing: grayscale;',
- '}',
- '',
- ];
- const sorted = [...unicode.entries()].sort((a, b) => a[0].localeCompare(b[0]));
- for (const [cls, hex] of sorted) {
- const fam = source.get(cls) === '3882322' ? 'iconfont-3882322' : 'iconfont-2298093';
- const short = cls.replace(/^icon-/, '');
- lines.push(`.${cls}:before {`);
- lines.push(` font-family: "${fam}" !important;`);
- lines.push(` content: "\\${hex}";`);
- lines.push('}');
- lines.push('');
- }
- return lines.join('\n');
- }
- function buildJson({ unicode, source }) {
- const glyphs = [...unicode.entries()]
- .sort((a, b) => a[0].localeCompare(b[0]))
- .map(([cls, hex], idx) => {
- const font_class = cls.replace(/^icon-/, '');
- const dec = parseInt(hex, 16);
- return {
- icon_id: String(100000 + idx),
- name: font_class,
- font_class,
- unicode: hex,
- unicode_decimal: dec,
- _source_project: source.get(cls),
- };
- });
- // e-icon-picker / 后端若校验字段,去掉非标准字段
- for (const g of glyphs) delete g._source_project;
- return {
- id: 'merged-2298093-3882322',
- name: 'dvadmin3-merged',
- font_family: 'iconfont',
- css_prefix_text: 'icon-',
- description: '合并自 iconfont 项目 2298093 + 3882322,与菜单 icon 字段一致',
- glyphs,
- };
- }
- async function main() {
- fs.mkdirSync(outDir, { recursive: true });
- console.log('Fetching CSS…');
- const [css229, css388] = await Promise.all([fetchText(URL_229_CSS), fetchText(URL_388_CSS)]);
- const map229 = parseIconRules(css229);
- const map388 = parseIconRules(css388);
- const merged = mergeMaps(map229, map388);
- console.log(`Icons: 2298093=${map229.size}, 3882322=${map388.size}, merged=${merged.unicode.size}`);
- console.log('Downloading font files…');
- for (const f of FONT_FILES) {
- const dest = path.join(outDir, f.name);
- await downloadFile(f.url, dest);
- console.log(' saved', f.name);
- }
- const cssOut = buildCss(merged);
- const jsonOut = buildJson(merged);
- fs.writeFileSync(path.join(outDir, 'iconfont.css'), cssOut, 'utf8');
- fs.writeFileSync(path.join(outDir, 'iconfont.json'), JSON.stringify(jsonOut, null, 2), 'utf8');
- console.log('Wrote iconfont.css, iconfont.json');
- }
- main().catch((e) => {
- console.error(e);
- process.exit(1);
- });
|