{{ message }}
Repository navigation
Drop the legacy muya ambient bridge and its aliases (#4257) - #5526
Merged
Merged
Conversation
Contributor
Contributor
|
Build artifacts for PR #5526: Run: https://github.com/marktext/marktext/actions/runs/35856093520 |
Jocs
force-pushed
the
refactor/muya-core-typings-4257
branch
from
September 23, 2026 11:28
6b9863a to
745cab7
Compare
Jocs
changed the base branch from
refactor/muya-core-typings-4257
to
develop
September 23, 2026 11:30
Item 1 of #4257. Nothing under `packages/desktop` has imported `muya/...` since the renderer moved to `@muyajs/core`, so the ambient declaration that stood in for the legacy JavaScript engine - 26 `any` shims under a file-level `no-explicit-any` disable - has no consumers left. With it go the `muya/*` path in `tsconfig.base.json`, the matching alias in the three electron.vite.config.ts sections and in vitest.config.ts, the `../muyajs/**/*` exclude, and the `@marktext/muyajs` workspace dependency. The lockfile entry was removed by hand rather than by re-resolving: a plain `pnpm install --lockfile-only` also bumps the `file-icons` git tarball, lodash, semver, tinyglobby and vitest's peer resolution, none of which belong in this change. `pnpm install --lockfile-only --frozen-lockfile` accepts the result. `packages/muyajs` itself stays until after 0.20.0 - it still lints and carries its own dependencies; this only cuts the desktop's last tie to it. The renderer bundle is byte-identical to the one built before this commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`ISerializedHistory` calls itself "the public, JSON-serializable shape
returned by `getHistory` and accepted by `setHistory`" but was not exported,
so the desktop had to write `ReturnType<Muya['getHistory']>`. Same for
`IHistorySelection`, `IPublicCursorInput` and `IIndexCursor`. A positional
reference couples a consumer to argument order rather than to a contract:
insert a parameter and `Parameters<...>[0]` silently starts asserting a
different type.
`search`/`replace` get the same treatment from the other side. `search(value,
opts = {})` inferred its option type as `{}` - it accepted anything - while
`replace`'s inferred `{ isSingle, isRegexp }` omitted `isCaseSensitive` and
`isWholeWord`, which `Search.replace` does honour (it spreads `...rest` over
`DEFAULT_SEARCH_OPTIONS`). Annotate both with the `ISearchOption` that already
existed and matched those defaults exactly, plus an `IReplaceOption` for the
one option only `replace` takes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tsconfig.build.json` inherited `include: ["src"]`, and 258 of muya's 474 sources are `__tests__` specs - so 258 of the 472 emitted `.d.ts` were test declarations no consumer can reach, and `files: ["lib"]` would have published them. Excluding them drops the emit to 214 files; `lint:types` still type-checks the specs through the base config. Also make the build incremental. It runs on every `pnpm typecheck` and on `postinstall`, and had no cache at all: 1.6s cold, 0.7s warm. The buildinfo lives inside `lib/`, which `vite build` empties - a build that wipes the declarations wipes the cache that claims they exist. `outDir` goes: it is already set in the config this one extends. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The `paths` entry covered only the bare specifier, so a deep import still resolved to nothing and carried a `@ts-expect-error` saying the subpath "exposes no types". It does now - `lib/types/ui/inlineFormatToolbar/config.d.ts` is emitted like any other - so map the subpaths and drop the directive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…natures
`ReturnType<Muya['getHistory']>` and three `Parameters<Muya[...]>[n]` were
standing in for types muya now exports by name. A positional reference asserts
"whatever argument N happens to be", which silently follows a signature change
instead of failing on it.
The search/replace payload casts were also asserting the wrong shape:
`Parameters<Muya['search']>[1]` resolved to `{}` (it checked nothing) and
`Parameters<Muya['replace']>[1]` omitted `isCaseSensitive`/`isWholeWord`, which
the engine honours and the search bar sends.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`setOptions` was type-checked against the engine while the construction bag next to it stayed `Record<string, unknown>` - and that bag is the larger surface: ~40 options plus four callback hooks, all crossing into the engine unchecked, where a misspelled key or a wrong value passed silently. Typing it `Partial<IMuyaOptions>` turned up exactly one mismatch, the same `sequenceTheme` the watcher already cast. The store keeps that field a free-form string on purpose (it is read back from disk; ~10 fields are widened the same way), so the cast belongs at the crossing rather than in the store - now written once, named, and used by both paths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The null guard added for `setOptions` was written onto the watcher condition, so toggling the preference also skipped `activateSpellchecker` / `deactivateSpellchecker` whenever the engine handle was absent. Those are two independent lifetimes; the guard belongs on the call that needs it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`CMInstance`/`CMCursor` existed to hold an `any` and to carry the comment explaining why. With real types behind them they are two local names that say less than `CodeMirror.Editor` / `CodeMirror.Position` do. The guards that `any` allowed go with them: `cm?.getSelection?.() ?? ''` and `cm?.listSelections?.() ?? []` are unreachable on a non-optional `Editor` whose `DocOrEditor` base declares both as required, and the inline parameter annotation on that `.map` is now inferred from `listSelections(): Range[]`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Same reasoning the Muya handle already carries: the instance is `markRaw`d, so a deep ref only costs an `UnwrapRef` mapping over the class. The unit spec evaluates the SFC's setup body against a stubbed scope, so it needs the import too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The search bar emitted `'prev'`; `Muya.find` takes `'previous' | 'next'`. It worked because the engine reads anything that is not `'next'` as backwards, and the cast at the bus boundary hid the mismatch. Emit the value the engine declares. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The hand-rolled structural copy had already drifted - its `ch?: number | null` came from the call site passing `null`, not from CodeMirror - and this branch had to hand-correct it. `Pick<CodeMirror.Editor, 'setCursor' | 'heightAtLine'>` is the same move `util/cursor.ts` makes against the Muya type, and the unit spec's fake editor stays structurally checked through it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The spec asserted an absence after a fixed 1.5s sleep, twice - which cannot tell "the handler ran and did not throw" from "the IPC never arrived", and cost 3s a run against the 100-150ms this repo settles the same check in. Register a second listener on the same channel instead. Node's EventEmitter runs listeners in registration order and an exception in one aborts the rest, so the flag flipping proves the store's handler ran to completion; the assertions then report which of the two things went wrong. 3.0s -> 47ms, and re-verified against a build with the removed handler put back: the source-mode case still fails there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The comment promised `.vue` would join section 2 "in Commit 8", and said the SFCs were treated as JS until then. Both are long false, and now that section 5 registers the plugin itself the note reads like a leftover to delete. State the reason that survives: `.vue` needs `vue-eslint-parser` as its top-level parser, so it cannot share section 2's config object. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The intro says the bridge is gone; thirty lines below, the `allowJs` bullet still sent readers to `src/types/muya.d.ts` and the `src/types/` listing still named it. The `## muya boundary` section had shrunk to a pointer at the note above it, so it goes too. Also restore the IPC sample: Prettier dropped the trailing comma it needed for the `// ...` continuation to make sense, so lead with the comment instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`lint.yml` carries `paths-ignore: packages/muya/**`, so a muya-only PR never runs the desktop typecheck. That was harmless while the desktop consumed a hand-written stub - muya could not break it by construction. Now that it compiles against the real declarations, the PR that can break it is exactly the one that skips the check: rename a method, narrow a union, change a return shape, and the declarations still emit cleanly while `develop` breaks on the next desktop PR. The existing `build:types` step only proves the emit works. Run the consumer's typecheck here too, against the declarations it just emitted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Caught while rebasing this branch: with `incremental`, `build:types` exited 0 without re-emitting after the checkout, leaving `lib/types/index.d.ts` from the other branch's sources. The desktop then type-checked against declarations that did not match the source it was compiled from - four "has no exported member" errors for exports that were right there in `src/index.ts`. Deleting the build-info file fixed it. Silently type-checking against the wrong declarations is a worse failure than a slow build, and the cache only bought ~0.9s on a command that spends ~3.2s in vue-tsc afterwards. The spec exclusion, which is where most of the win was, stays. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Jocs
force-pushed
the
chore/drop-legacy-muya-bridge-4257
branch
from
September 23, 2026 11:42
535060f to
515c3e5
Compare
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.

Item 1 of #4257, the last one. Stacked on #5523 — closes #4257 when all three
land.
Nothing under
packages/desktophas importedmuya/...since the renderermoved to
@muyajs/core, sosrc/types/muya.d.ts— 26anyshims under afile-level
no-explicit-anydisable, coveringmuya/lib,muya/lib/utils,muya/lib/parser/*and the dozen-plusmuya/lib/ui/*overlays — has noconsumers left. Removed, along with everything that existed to make it
resolve:
muya/*path intsconfig.base.jsonand the../muyajs/**/*excludein
tsconfig.jsonmuyaalias in all threeelectron.vite.config.tssections and invitest.config.ts@marktext/muyajsworkspace dependencypackages/muyajsitself stays until after 0.20.0 — it still lints and carriesits own dependencies. This only cuts the desktop's last tie to it.
The lockfile
Removed by hand, not by re-resolving. A plain
pnpm install --lockfile-onlyalso bumps the
file-iconsgit tarball to a newer commit, lodash 4.17.21 →4.18.1, semver 7.7.4 → 7.8.5, tinyglobby 0.2.16 → 0.2.17 and vitest's peer
resolution (
@types/node@26/vite@8→@types/node@22/vite@7) — none ofwhich belong in this change.
pnpm install --lockfile-only --frozen-lockfileaccepts the hand-edited result, so the lockfile is consistent with the
manifests.
One wrinkle worth knowing: pnpm rewrites the lockfile in its own formatting,
while the committed one is Prettier-formatted (lint-staged runs
prettier --writeon*.yaml). Run Prettier over it after any pnpm write orthe diff is ~13k lines of pure formatting.
Docs
CLAUDE.mdanddocs/dev/TYPESCRIPT.mdboth described the bridge and themuyaalias as current; both are updated, including the stale claim that theonly remaining
anywas insrc/types/muya.d.tspluswatcher.ts(thedirectives that actually remain are mostly in the CodeMirror 5 mode plumbing).
Verification
pnpm typecheckandpnpm lintclean;pnpm test:unit770 passed,unchanged from the branch point.
build:unpack: 275 passed, 4 skipped,and the same 3 pre-existing failures my machine's
zh-CNlocale causes(asserted English UI strings) — identical to the run on Type the editor handle as Muya, from the engine's own declarations (#4257) #5523.
built before it.
🤖 Generated with Claude Code