// 用法:node scripts/generate-language-icons.mjs /path/to/@iconify-json/vscode-icons // 来源:@iconify-json/vscode-icons 1.2.76(MIT);运行时不会发起网络请求。 import { readFileSync, writeFileSync } from 'node:fs' import { resolve } from 'node:path' import { bundledLanguagesInfo } from 'shiki/langs' const source = process.argv[2] if (!source) throw new Error('Provide the extracted vscode-icons package directory') const data = JSON.parse(readFileSync(resolve(source, 'icons.json'), 'utf8')) const overrides = { ahk: 'autohotkey', ahk2: 'autohotkey', asm: 'assembly', bat: 'bat', 'angular-html': 'angular', 'angular-ts': 'angular', 'common-lisp': 'lisp', 'emacs-lisp': 'lisp', 'fortran-fixed-form': 'fortran', 'fortran-free-form': 'fortran', 'git-commit': 'git', 'git-rebase': 'git', jsonc: 'json', jsonl: 'json', shellscript: 'shell', shellsession: 'shell', jsx: 'reactjs', tsx: 'reactts', latex: 'tex', bibtex: 'bibtex', 'objective-c': 'objectivec', 'objective-cpp': 'objectivecpp', dart: 'dartlang', d: 'dlang', v: 'vlang', gdshader: 'godot', fish: 'shell', 'ssh-config': 'shell', 'vue-html': 'vue', 'vue-vine': 'vue', 'html-derivative': 'html', qss: 'qt', rbs: 'ruby', } const groups = new Map() const unmatched = [] for (const info of [...bundledLanguagesInfo, { id: 'text', name: 'Text' }]) { const candidates = [overrides[info.id], info.id, ...(info.aliases ?? []), info.name.toLowerCase().replace(/\s+/g, '')].filter(Boolean) const icon = candidates.map(name => `file-type-${name}`).find(name => data.icons[name]) if (!icon) { unmatched.push(info.id); continue } const ids = groups.get(icon) ?? [] ids.push(info.id) groups.set(icon, ids) } const base = '.milkdown-host .language-list-item[data-language]' const svgUrl = icon => { const item = data.icons[icon] const svg = `${item.body}` return `url("data:image/svg+xml,${encodeURIComponent(svg)}")` } let css = `/* 由 scripts/generate-language-icons.mjs 自动生成。VSCode Icons 采用 MIT 许可证;详情见 language-icons-LICENSE.txt。 */\n${base} { display: flex; align-items: center; gap: 8px; }\n${base}::before { content: ''; flex: 0 0 20px; width: 20px; height: 20px; background: center / contain no-repeat ${svgUrl('default-file')}; }\n` for (const [icon, ids] of groups) { css += ids.map(id => `${base}[data-language="${id}"]::before`).join(',\n') + ` { background-image: ${svgUrl(icon)}; }\n` } writeFileSync(new URL('../src/features/editor/language-icons.css', import.meta.url), css) writeFileSync(new URL('../src/features/editor/language-icons-LICENSE.txt', import.meta.url), readFileSync(resolve(source, 'license.txt'))) console.log(`${bundledLanguagesInfo.length + 1 - unmatched.length} languages mapped; generic file icon for: ${unmatched.join(', ')}`)