Files
upstream-gitea/tools/generate-codemirror-languages.ts
upstream-mirror 52ae0c5802
Some checks failed
cache-seeder / prune (push) Has been skipped
cache-seeder / frontend (push) Failing after 3s
cache-seeder / python (push) Failing after 3s
cache-seeder / gobuild (push) Failing after 4s
cache-seeder / lint (bindata, lint-backend) (push) Failing after 3s
giteabot backport / giteabot (push) Has been skipped
giteabot / giteabot (push) Has been skipped
cache-prune / prune (push) Has been skipped
release-nightly-snapcraft / build-and-publish (ubuntu-24.04) (push) Has been cancelled
release-nightly-snapcraft / build-and-publish (ubuntu-24.04-arm) (push) Has been cancelled
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-container (push) Has been cancelled
cron-renovate / cron-renovate (push) Has been skipped
snapshot of https://github.com/go-gitea/gitea.git (main, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:41:59 +05:00

85 lines
3.3 KiB
JavaScript
Executable File

#!/usr/bin/env node
import {writeFile} from 'node:fs/promises';
import {languages as cmLanguages} from '@codemirror/language-data';
import * as linguist from 'linguist-languages';
import type {Language} from 'linguist-languages';
const renames: Record<string, string> = {
'Protocol Buffer': 'ProtoBuf',
};
// Languages whose entry is constructed manually in the runtime; skip during generation.
const skipNames = new Set(['Dockerfile', 'Markdown']);
// Extensions claimed by several unrelated languages with no good default; strip globally.
const ambiguousExt = new Set(['cgi', 'fcgi', 'inc']);
// Per-language drops for non-text formats (.frm = binary VB6 forms) or where Linguist's
// primary owner conflicts with a more specialised CodeMirror mode (.spec → RPM Spec).
const excludeExt: Record<string, string[]> = {
'INI': ['frm'],
'Python': ['spec'],
'Ruby': ['spec'],
};
// Per-CM-language additions for filenames Linguist classifies as separate languages
// (.editorconfig, .gitconfig, .npmrc) or omits entirely (Snakefile).
const extraFilenames: Record<string, string[]> = {
'Properties files': ['.editorconfig', '.gitconfig', '.npmrc'],
'Python': ['Snakefile'],
};
// Per-CM-language additions widely used in practice but absent from Linguist's list.
const extraExtensions: Record<string, string[]> = {
'Properties files': ['conf'],
};
type CmLanguage = {
name: string;
extensions: string[];
filenames: string[];
};
const cmByAlias = new Map<string, string>();
// Map of extension -> the CM language that originally owns it. Used to prevent Linguist
// from broadening one language's extension claim into another's territory (e.g. Linguist's
// PLSQL lists .sql, but CM's SQL is the canonical owner).
const cmOriginalExtOwner = new Map<string, string>();
for (const lang of cmLanguages) {
cmByAlias.set(lang.name.toLowerCase(), lang.name);
for (const a of lang.alias) cmByAlias.set(a.toLowerCase(), lang.name);
for (const ext of lang.extensions) {
if (!cmOriginalExtOwner.has(ext)) cmOriginalExtOwner.set(ext, lang.name);
}
}
const out: CmLanguage[] = [];
const seen = new Set<string>();
for (const [linguistName, entry] of Object.entries(linguist as Record<string, Language>)) {
const cmName = renames[linguistName] ?? cmByAlias.get(linguistName.toLowerCase());
// Multiple Linguist entries can alias to the same CM language (e.g. JSON5 → JSON).
if (!cmName || skipNames.has(cmName) || seen.has(cmName)) continue;
seen.add(cmName);
const exExt = new Set(excludeExt[linguistName]);
// CodeMirror's matchFilename uses /\.([^.]+)$/, so multi-dot extensions like
// ".cmake.in" can't match as extensions and are dropped here.
const extensions = (entry.extensions ?? [])
.map((e) => e.replace(/^\./, ''))
.filter((e) => {
if (e.includes('.') || ambiguousExt.has(e) || exExt.has(e)) return false;
const owner = cmOriginalExtOwner.get(e);
return !owner || owner === cmName;
});
out.push({
name: cmName,
extensions: [...extensions, ...(extraExtensions[cmName] ?? [])],
filenames: [...(entry.filenames ?? []), ...(extraFilenames[cmName] ?? [])],
});
}
out.sort((a, b) => a.name.localeCompare(b.name));
const outPath = new URL('../assets/codemirror-languages.json', import.meta.url);
await writeFile(outPath, `${JSON.stringify(out, null, 2)}\n`);
console.info(`wrote ${out.length} languages to ${outPath.pathname}`);