v1.19.0 — a real yaml mode, and two diagnostics that were compiled away - #27
Merged
Conversation
… away
Four findings from the app that adopted 1.18.0. Three acted on, one declined.
CodeEditor loaded the JSON grammar for anything that was not `markdown`, so a
consuming app's compiled-flow-definition page highlighted YAML through a JSON
parser for the life of the page. It survived because the wrong grammar does not
fail loudly: JSON still paints nine tokens in a typical flow definition — every
`:` as punctuation, every bare integer as a number — which reads as syntax
highlighting from across the room. What it cannot paint is `#`, a comment in
YAML and nothing at all in JSON, so the Yaml story asserts on that line and
fails when the document is served the JSON grammar. A "some token is coloured"
assertion passes on the broken version, which is why there wasn't one.
`yaml` is now a mode on both code surfaces (@codemirror/lang-yaml, a new
optional peer — see the changelog's upgrade note, it is the one thing this
release asks of a consumer), and an unknown language renders unhighlighted and
warns instead of silently reaching for JSON. A component that quietly picks the
wrong grammar is worse than one that refuses the mode: the first is
indistinguishable from working.
DataTable.rowKey has always been required with no default, and nothing said so
at runtime — nine call sites shipped without it and keyed every row `undefined`.
Vue's own "Missing required prop" warning cannot fire for any prop in this
package: the library is compiled with isProd, @vue/compiler-sfc emits
`required`/`type` only for a development build, and dist declares the prop as
the bare `rowKey: {}`. Rendering DataTable with no rowKey against a development
Vue produces no Vue warning at all. Same shape as the import.meta.env.DEV
problem in helpers/dev.ts, so the check is written out by hand like the tone
deprecation, and verified against the built bundle rather than src.
CodeEditorProps.modelValue becomes `string | null`, which is what the runtime
has always accepted (`?? ''` guards every read). Not `string | number | null`
like Input: that is a native input whose value the DOM stringifies anyway,
while this holds a document. Textarea, the closer sibling, is `string | null`.
Declined: narrowing DataTableColumn.key to `keyof T & string`. It would forbid
action and computed columns, it disagrees with SortState.key which is a free
string on the wire, and for the row shapes it was proposed for it is the
identity — DataTable is `T extends Record<string, unknown>`, an interface only
satisfies that by declaring the index signature, and once `[key: string]:
unknown` is present `keyof T & string` IS `string` and `T[K]` IS `unknown`. The
obstacle is the constraint, not the key. Reasoning in full in the changelog.
Verified against a consumer fixture resolving the package through its exports
map: vue-tsc accepts `language="yaml"` and a nullable v-model with no `?? ''`,
rejects an unknown mode, and the built bundle warns for a missing rowKey under
a development Vue while a supplied one stays silent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four findings from
flows.codebaradopting v1.18.0 (which took its own type backlog 169 → 82). Two fixed, one fixed differently than asked, one declined with evidence.1.
CodeEditorhad noyamlmode — and silently painted YAML with the JSON grammarA real rendering bug, not a typing nicety. The consumer's compiled-flow-definition page has been highlighting YAML as JSON since it was written.
@codemirror/lang-yamladded the same way as the other grammars;language="yaml"is now a real mode onCodeEditorandCodePreview— editing YAML but only previewing it as plain text isn't defensible. The unknown-language arm is now explicit: no grammar, plus awarnOncedev warning through the existinghelpers/dev.tsmechanism, rather than silently falling back to JSON.Why the obvious test would not have caught it. Running both grammars over a typical flow definition at the lezer level: the JSON grammar still paints 9 tokens on YAML — every
:as punctuation,3and50as numbers. The existingexpectHighlightedhelper ("some token is coloured") therefore passes on the broken version. What JSON cannot paint is#, a comment in YAML and nothing in JSON. The new story asserts on that line specifically, and was mutation-tested by forcing it ontolanguage="json"to confirm it fails.2.
CodeEditorProps['modelValue']widened tostring | nullNot
string | number | null. The runtime?? ''guards prove null is accepted, which removes three consumer-side?? ''.numberwas declined:Inputis a native<input>whose value the DOM stringifies anyway, whileCodeEditorholds a document — a number would be silently stringified in and returned as a string.Textarea, the closer sibling, isstring | null, and that is the shape matched.3.
DataTableColumn.keynarrowed tokeyof T & string— declinedThree grounds, the third decisive and verified in a consumer fixture:
{ key: 'actions' }+#cell-actions), and the kit offers no other way to place one outside the trailing#row-actionscell.SortState.keyis a free string on purpose — it goes to an API in server mode, where joined and computed sort keys are ordinary.DataTableis<T extends Record<string, unknown>>; an interface satisfies that constraint only by declaring an index signature (this package's own DataTable story does exactly that), and once[key: string]: unknownis present,keyof T & stringisstringandT[K]isunknown.valuewould still beunknown. It would only bite for rows declared as type aliases.The real obstacle is the
Record<string, unknown>constraint, notkey. Relaxing it toT extends objectis strictly more permissive and would break nobody, but it ripples intouseSort's signature and the internal indexing — a separate release. Written up in the changelog.4.
rowKeydev warning — added, and the premise was stronger than reportedThe consumer shipped nine
<DataTable>call sites with norow-key, so every row was keyedundefined. The suspicion was that a Vue dev-mode warning should have caught this years earlier.It cannot. Vue's "Missing required prop" warning cannot fire for any prop in this package: the library compiles with
isProd: true,@vue/compiler-sfcemitsrequired/typeonly for a dev build, anddist/flows.jsdeclares the prop as the barerowKey: {}. Confirmed by SSR-rendering the built bundle against a development Vue with norowKey— Vue says nothing.Same shape as the
import.meta.env.DEVregression already documented inhelpers/dev.ts, so the check is written out viawarnOnce. The changelog notes that every other required prop is unvalidated for the same reason; a general fix means compiling the library in dev mode, which is its own release.Breaking changes
@codemirror/lang-yaml— the only thing this release asks of a consumer. Declared optional, but "optional" describes the manifest, not the bundler:dist/flows.jsis one file that dynamic-imports every grammar by bare specifier, so a consuming build resolves all of them whether or not it renders an editor. An app that upgrades without installing it fails at build time, not runtime. Mitigating: this already applies tolang-json,lang-markdown,commands,language,stateandview, so every existing consumer already installs that set — this adds one to it.CodeEditorProps['modelValue']includesnull. Reading it into astringneeds a fallback; passing in is strictly freer. Same class asBreadcrumbItem.hrefin 1.18.0.languageno longer highlights as JSON. Only reachable from untyped call sites; anything passing"yaml"now gets YAML.rowKey-less tables starts seeing a dev warning immediately.Verification
A consumer fixture resolving through the package's
exportsmap (--traceResolutionconfirms it reachesdist/index.d.ts), in three halves:vue-tscon a real SFC —language="yaml",v-modelon aRef<string | null>with no?? '',#cell-*slots; type-level assertions that'toml'is not a mode and that thekeyof T & stringcollapse holds. Negative-controlled: flipping one assertion errors.yaml()resolves from the consumer, the bundle dynamic-imports@codemirror/lang-yamlwith no inlined copy.rowKey, silent when supplied.npm run build(all four verify steps),vue-tsc, ESLint (0 errors, the same 7 pre-existing warnings asmain), Playwright 246 passed — 243 plus three new stories.Version: 1.19.0
Minor rather than major: no existing API changes shape incompatibly, the widening and the new union member are additive, and the peer addition is an install step against a set consumers already carry rather than an API break. If you read a build-breaking peer addition as major, 2.0.0 is the alternative and the changelog gives the facts to decide.
Release mechanics: the manifest is already bumped in the commit (1.18.0's precedent, and
verify:versionon PRs requires it), so the tag step isnpm version 1.19.0 --allow-same-version— still derived from the manifest, never hand-written.npm version minorat that point would land on 1.20.0.🤖 Generated with Claude Code