The shared code-language table must not claim a suffix a dedicated viewer declares, and the Host read card must keep the `lang` values it already persisted. - Drop `csv` from the shared table: Code registers before Spreadsheet, so the earlier body would take `.csv` and the browser snapshot gate fails. - Add `readLangHintForPath`, the read card's legacy projection over the shared table: the 43 pre-unification suffixes keep their old short ids byte for byte, new suffixes use the canonical id. - Add an ownership regression test over the full preview registry that turns red when `csv` returns, plus byte-level and alias-normalized tests for the read projection.
description, kind
| description | kind |
|---|---|
| Single file-extension to syntax-highlighting language table shared by the Client code surfaces and the Host read card. | package-library |
dsh-util-code-language
English | 中文
Summary
The repository's one file-extension to syntax-highlighting language table, shared by the Client's document Code preview and diff review and by the Host read tool's persisted lang hint. languageForPath maps a path to a canonical grammar id case-insensitively, recognizing both path separators and treating a leading dot as the separator (.env is dotenv); CODE_HIGHLIGHT_EXTENSIONS lists every suffix for a preview registry. readLangHintForPath projects the read card's older short ids over the table so previously persisted values stay byte-identical. The package is browser-safe, stateless, and leaves tokenization to the Client highlighter.
Table of Contents
Language selection
The table lives in src/index.ts. Each key is a canonical language id — the grammar id the Client highlighter resolves — and each value is the lowercase extensions without the dot that select it. languageForPath(path) takes the text after the last dot of the final path segment, lowercases it, and looks it up in a Map; a Map keeps a filename such as foo.constructor from resolving an Object.prototype member. An unlisted dotfile (.gitignore), an absent extension, a trailing dot, and an unknown suffix all return undefined, which every consumer renders as plain text; a leading dot is still the separator, so .env resolves to dotenv. Both / and \ separate path segments, so a Windows path resolves like a POSIX one.
The set is curated for common source, config, script, data, and markup extensions rather than mirroring a full language registry. Extensions with no matching grammar map to the nearest one (properties to ini, whose registration carries the properties alias); certificate and lock extensions (pem, crt, key, cer, lock) stay unlisted, and csv stays unlisted because the Spreadsheet preview declares the suffix and must keep it, so the earlier Code registration cannot claim it. readLangHintForPath is a projection over this table: a suffix the pre-unification read table recognized returns its old short id, a suffix present only here returns its canonical id, and an unrecognized suffix returns undefined. A consumer that needs the Client highlighter to actually tokenize a language still depends on that grammar being registered there — an id without a loaded grammar renders as plain text rather than failing.
Known Limitations and Deferred Work
- Extension-only matching —
languageForPathreads a suffix, so names recognized without one (Dockerfile,Makefile,.gitignore,.editorconfig) stay unlisted. Filename rules are deferred. - No content sniffing — a file with an absent or unknown extension stays plain text even when its bytes are unambiguous; the table never inspects content.
- Curated, not exhaustive — the table is smaller than Shiki's grammar catalog and GitHub linguist; adding a language means adding both the extension entry and the Client grammar registration.
Dev Note
Working context for maintainers — click to expand
None.
Runtime invariant: No companion is published. This utility owns no mutable runtime relationship.