@codincod/codemirror-lang-javascript
v0.1.1
Published
JavaScript and TypeScript language support for the CodeMirror code editor
Maintainers
Readme
@codincod/codemirror-lang-javascript
This package implements JavaScript, TypeScript, JSX and TSX language support for the CodeMirror code editor, using the Lezer grammar Marijn Haverbeke wrote for @lezer/javascript, with the parse fixes below on top of it. The editor half comes from @codemirror/lang-javascript; the two are merged here so that one package holds the grammar and the language support that has to move with it.
Written in part for CodinCod, a competitive coding platform, where it colours the editor people solve puzzles in.
This code is released under an MIT license.
Usage
import {EditorView, basicSetup} from "codemirror"
import {javascript} from "@codincod/codemirror-lang-javascript"
const view = new EditorView({
parent: document.body,
doc: `import config from "./config.json" with { type: "json" }
const swap = (arr, i, j) => {
[arr[i], arr[j]] = [arr[j], arr[i]]
}
`,
extensions: [basicSetup, javascript()],
})For TypeScript, javascript({typescript: true}); for JSX, {jsx: true}; both
together for TSX.
API Reference
- javascript(config?: {jsx?: boolean, typescript?: boolean}) → LanguageSupport
- javascriptLanguage, typescriptLanguage, jsxLanguage, tsxLanguage: LRLanguage
- parser: LRParser
- autoCloseTags, snippets, typescriptSnippets, esLint, localCompletionSource, completionPath, scopeCompletionSource: from @codemirror/lang-javascript, unchanged
What changed
Sixteen parse fixes over @lezer/javascript 1.5.4, each with grammar tests. On
this machine's node_modules, 15252 files and 239 MiB of published JavaScript
and TypeScript, they take the share of files parsing with no error node from
78.11% to 91.51%, and the error nodes from 117109 to 37145.
Destructuring assignment onto member expressions. [a[0], a[1]] = [a[1],
a[0]], the ordinary way to swap two elements, and ({x: o.a} = z). The targets
of a destructuring assignment may be member expressions where the targets of a
destructuring binding may not, and no pattern rule can hold one. An array or
object literal is now assignable, and the pattern rules carry a dynamic
precedence so that [a, b] = [b, a] still comes back as an ArrayPattern, as
it always did.
Import attributes, ES2025. import x from "y" with {type: "json"}, the
older assert spelling, the same clause on export … from, and the second
argument to a dynamic import(). A new ImportAttributes node, which indents
like the braced list it is.
Then twelve TypeScript constructs the grammar had no rule for. The last of them is the one a person writing TypeScript hits first:
export {type A, B}, inline type modifiers in an export groupimport fs = require("fs")andexport import atob = globalThis.atobdeclare module "fs" { … }, an ambient module named by a stringglobal { … }inside such a modulelet x!: number, the definite assignment assertion{ <K extends keyof T>(key: K): T[K] }, a call signature taking type parametersnew <A>(a: A) => A, a constructor type taking type parametersinfer U extends K, TypeScript 4.7interface I<in out K>, paired variance annotations[a: number, b?: string, ...rest: X[]], optional and named-rest tuple memberstypeof import("m")(v: unknown): v is T => …, a type guard on an arrow function, and(v: unknown) => v is Tas a function type
export * as default from "m" rounds it out.
What is still not read
About 1300 of the 15252 files still leave error nodes, and @lezer/javascript
leaves them in the same places. The largest single group, 882 files, is Babel's
_interopRequireWildcard helper in CommonJS builds. It reduces to 123
characters of valid JavaScript that every parser but this one accepts, and every
smaller version of it parses: it needs the whole combination of a parenthesised
function assignment, a sequence expression ending in an object literal, and
three if statements. That is the signature of @lezer/lr pruning one of two
live GLR stacks, not of a missing production, and it is not something the
grammar can say differently.
On hand-written source rather than published builds the number is far smaller: 307 files of this editor's own TypeScript and these packages' sources parse 97.72% clean, against 96.74% before.
Testing
npm test runs the grammar specs in test/*.txt and the editor tests beside
them. The specs are @lezer/javascript's own, plus fixes.txt for the changes
above; the editor tests are @codemirror/lang-javascript's indentation and
syntax-query suites converted to node:test, plus highlighting.
npm run corpus -- <dir> parses a directory of real JavaScript and TypeScript
and reports the share of files with no error node, grouped by directory. The
dialect comes from the extension, and minified bundles are skipped. No corpus
ships with the package; point it at your own.
