feat(docs): docs snippet typecheck for editor/superdoc/** + fix stale examples (SD-673)#3531
Conversation
β¦examples (SD-673) Adds a CI-deterministic gate that extracts 'Full Example' code blocks from apps/docs/editor/superdoc/**, writes each snippet to a temp file with a small shared ambient prelude, and runs tsc --noEmit --strict against packages/superdoc/dist. JS fences are checked with allowJs + checkJs + // @ts-check; TS fences are checked with full strict. Why: the existing runtime doctest (apps/docs/__tests__/doctest.test.ts) extracts the onReady body and executes it against a mocked superdoc host, so it never catches destructure bugs in the outer config example. The bug class fixed by #3503 (typed callback payloads that silently drifted) was teaching the wrong shape in docs for months β nothing in CI caught it. This gate catches it at write time. Mechanism: - Extends lib/extract.ts to keep the fence language on each example. - Adds yjs / y-websocket to SKIP_IMPORTS (consumer BYO; not part of SuperDoc's typed surface). - New doctest-types.ts script: iterates pattern='superdoc' examples in scope, writes one temp .ts/.js per snippet plus a shared placeholders.d.ts (yourFile, doc1, doc2, cleanup, autoSave, etc.), runs tsc against the packed dist via tsconfig paths, parses errors and maps them back to source file:line. - New apps/docs script entry: pnpm --filter @superdoc/docs run check:types - Wired as the last stage of scripts/check-public-contract.mjs. Fixes (split per the user's request between placeholder-only stubs and real public-shape drift): PLACEHOLDER STUBS (11 ambient declarations in placeholders.d.ts): - File/string values: yourFile, file, doc1, doc2, content - Helper functions: cleanup, autoSave, adjustLayout, showOnlineUsers, updateUserCursors, showLockBanner REAL DOCS API DRIFT FIXES (in scope: editor/superdoc/** + the collaboration configuration cell flagged in audit): apps/docs/editor/superdoc/methods.mdx (29 examples touched): - 22 onReady: (superdoc) => fixed to onReady: ({ superdoc }) => (the typed callback payload is { superdoc }, not the instance) - setActiveEditor example rewritten to capture editors via onEditorCreate (the previous example reached into runtime-only Document.editor which isn't on the public Document type) - search / goToSearchResult: added null guards on the optional SearchMatch[] | undefined return - addCommentsList: signature corrected to HTMLElement (was wrongly documented as string | HTMLElement; the actual type is HTMLElement) - scrollToComment / scrollToElement: superdoc.editor -> superdoc.activeEditor with null guards (activeEditor is Editor | null on the typed surface; superdoc.editor doesn't exist as a public member) - off example: handler annotated with /** @param SuperDocReadyPayload */ so the standalone-handler shape pins to the typed payload - scrollToElement: narrowed BlockNodeAddress before reading nodeId; switched .entityId to .id on DiscoveryItem (the canonical field name) apps/docs/editor/superdoc/events.mdx (8 examples touched): - Subscribing handler now uses JSDoc to type the standalone handler - editor-update: added null guard on the optional editor field (EditorUpdateEvent.editor?: Editor) - content-error: removed documentId from destructure (SuperDocContentErrorPayload is { error, editor }; no documentId) - comments-update: { type, data } -> { type, comment, changes } (matches SuperDocCommentsUpdatePayload, the same drift class fixed in the typed surface by #3503) - awareness-update: { context, states } -> { states, added, removed } (same drift class; runtime emits 'superdoc' not 'context') - locked: added && lockedBy guard before reading lockedBy.name (SuperDocLockedPayload.lockedBy is User | null) - pagination-update: removed 'pagination: true' from Config example (no such field on the typed Config) - exception: rewrote to take payload and read payload.error directly, with a comment on how to narrow the discriminated union (the union has three variants and 'document'/'editor' only exist on specific ones) apps/docs/editor/superdoc/import-export.mdx (5 examples touched): - 5 onReady: (superdoc) => fixed to onReady: ({ superdoc }) => - Added null guards on superdoc.activeEditor before .getHTML / .getJSON / .getMarkdown / .commands access - Switched Usage tabs that show 'superdoc.activeEditor.getX()' to 'superdoc.activeEditor?.getX()' for the same reason apps/docs/editor/collaboration/configuration.mdx (1 cell): - lockedBy type cell: 'Object' -> 'User | null' with a note that the example needs a null check before reading .name Result: - 47 in-scope examples (was 0 type-checked before this PR) - 0 unmet β strict-zero from day 1 for this scope - 1 yjs/y-websocket example skipped (out of scope: consumer BYO, not part of SuperDoc's typed surface) Synthetic regression verified: re-introducing one onReady: (superdoc) => locally produced the expected failure 'Property getHTML does not exist on type SuperDocReadyPayload' with the correct source file:line. Verified: - pnpm --filter @superdoc/docs run check:types -> OK, 47/47 - pnpm check:public:superdoc --skip-build -> PASS (12 ran, 1 skipped, 130.8s)
|
π Docs preview: https://superdoc-caio-pizzol-sd-docs-snippet-typecheck-2.mintlify.app |
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aae5c63f3e
βΉοΈ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with π.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const PLACEHOLDERS_DTS = ` | ||
| // Document/file placeholders used in docs examples. | ||
| declare const yourFile: File; | ||
| declare const file: File; | ||
| declare const doc1: File; | ||
| declare const doc2: File; | ||
| declare const content: string; |
There was a problem hiding this comment.
Add a CSS module stub to the doctest project
Every in-scope Full Example imports superdoc/style.css, but the temp tsconfig only maps the bare superdoc specifier and the shared ambient file only declares placeholders. With the repo's TypeScript 5.9 compiler, unresolved side-effect CSS imports produce TS2882 (Cannot find module or type declarations for side-effect import of 'superdoc/style.css'), so this new check:public:superdoc stage will fail on the docs snippets before it can validate the API examples. Add an ambient declare module 'superdoc/style.css'; (or equivalent resolution) to the generated project.
Useful? React with πΒ / π.
Codecov Reportβ All modified and coverable lines are covered by tests. π’ Thoughts on this report? Let us know! |
### Bug Fixes - copy-pasted text in suggestion mode (#3576) - center virtualized matches after mount in find nav (SD-3315) - stop find navigation jumping to the reverted caret (SD-3315) - honor the focus() contract + fix dangling docs reference (SD-3312) - don't re-center visible matches on find navigation (SD-3315) - report 'zoom' not 'mixed' for a zoom repaint (SD-3311) - wire pointer-source tracking on all init paths; update export snapshot - reset state on unload, dedupe + export payload types, add core tests - skip empty block SDT content selection - type modules.contentControls exactly (no pass-through index) - allow block SDT wrapper deletion to follow lock rules - promote image-bearing inline SDT wrappers to inline-block - keep block sdt fill behind content - hide block sdt fills in output modes - paint block SDT background on chrome layer - use logical inset for inline SDT label position - anchor inline SDT label to start of chrome - route smartTag through export and preserve smartTagPr in SDT flatten (SD-2647) - include inline SDT chrome width in block SDT bounds - keep block SDT chrome at full fragment width - render and round-trip w:smartTag content (SD-2647) - decouple base64 image helper imports - preserve block ids during metadata updates - ignore covered sdt label clicks - sync block sdt label selection updates - show empty SDT placeholder text in viewing and print modes - skip empty sdt scan on arrow right - preserve permission-only sdt placeholders - preserve comment-only sdt placeholders - preserve empty sdt bookmark placeholders - collapse hidden sdt placeholder text - keep sdt placeholder pm range atomic - trust empty sdt paragraph conversion - keep vanished sdt paragraph side effects - preserve vanished block sdt paragraphs - suppress hidden block sdt chrome - keep remeasured sdt placeholder atomic - transform sdt placeholder measure - remeasure sdt placeholders - hide sdt placeholders in print - hide sdt placeholders in viewing - ignore collapsed inline sdt cut - expose block sdt appearance - hide empty block sdt placeholder - align empty block sdt caret - use measured width for empty SDT placeholders - size SDT block labels to content width - collapse selection on sdtContentLocked delete - allow history transactions through sdt lock - drop unreachable move fallback - target marker-only textblock end - ignore empty block sdt key targets - cap block sdt label width - handle sdt marker gaps and block atoms - skip hidden field annotations in sdt navigation - skip hidden metadata sdt markers - skip hidden block sdt markers - handle marker-only sdt paragraphs - keep visible atoms in sdt navigation - skip hidden sdt navigation markers - target nearest sdt cursor position - respect inline atoms in sdt navigation - handle empty block sdt navigation - avoid restoring dragged block to its source position - exclude sdt chrome labels from caret position lookup - keep text-align enabled in locked SDT paragraphs - block disabled toolbar execution - guard unlisted locked toolbar commands - block locked sdt toolbar execution - reject malformed base64 image data URIs - reuse colliding data uri media targets - enforce upload byte cap for data uris - avoid non-image data uri extensions - reject raw raster data uri dimensions - validate oversized async svg images - register preset raster data uris in place - narrow sdt metadata overrides - normalize image data uri extensions - centralize image data url policy - reuse target image relationships - validate in-place svg payloads - validate field annotation data uri exports - reject malformed data uri files - avoid duplicate image rids - block raw raster data uri exports - reject malformed svg data uri payloads - warn on skipped image exports - reject separatorless data uri files - block non-image data uri exports - read svg data uri dimensions - share data uri media parsing - skip invalid data uri image targets - normalize svg data uri filenames - validate in-place svg image data - guard non-base64 data uri exports - export field annotation svgs as svg - allow non-base64 SVG data URLs in image rendering - mirror in-place image media to parent - decode non-base64 data URI exports - extract shared hash helpers - reuse data URI image exports - support non-base64 data URI images in registration - scope inline SDT placeholder to structuredContent metadata - register sized SVG data URI images without canvas processing - persist data URI images set via setPresetContent - share SDT lock predicates - version inline image metadata - align RTL SDT chrome to text - size SDT chrome for justified lines - honor ancestor image SDT locks - dirty inline image SDT changes - align SDT chrome within paragraph width - preserve SDT chrome continuation offsets - offset block SDT chrome for indents - suppress SDT pseudo hover in viewing mode - allow top-aligned inline images - fit block SDT chrome to actual content width - bottom-align text on lines with inline images - keep block SDT chrome and inline images out of paragraph geometry - disable image resize inside content-locked SDTs - detect inline image run changes in paragraph diff - mark block SDT selected when contained image is selected - select inline SDT content as text on Delete - intercept beforeinput insertText at inline SDT boundaries - delete contentLocked SDT wrapper in one step - bump sdBlockRev on ancestors of inline edits - select inline SDT content as text on Backspace - select inline SDT on Backspace at start of following run (SD-3165) - share structured content chrome label set - resolve block labels at node boundary - use contract label selectors - clear label gesture state on cancel - scope label clicks to owning editor - defer label selection to mouseup so native drag still fires - share structured content label classes - avoid deferred block label retry - select labels with active editor - focus editor after label selection - correct cursor placement and label interactions for structured content - handle cell-level SDT in vMerge column lookup (SD-3289) - preserve cell-level SDT wrapping table cells (SD-3289) - preserve recipient identity attrs on replay (SD-3279) - accept legacy fingerprints in compare/apply (SD-3279) - drop misleading pnpm run type-check hint from audit - structural-fail on missing dist + sync check-jsdoc header - strip session-local sdBlockId from diff fingerprint (SD-3279) - autoFit table width overflow from cell preferences (#3522) - flip public-method-coverage to strict-zero gate (SD-673) - flip jsdoc-hygiene-ts to strict-zero gate (SD-673) - rename jsdoc-hygiene-ts self-tests + wire into CI (fixes vitest discovery on #3511) - jsdoc-hygiene-ts handles private-identifier symbols + README - more bugs - add ui for overlapping delete, other fixes - replacement pair - remaining collab bugs - coalesce tracked inserts across run gaps - restore tracked change comment interactions - expose tracked mark predicate option - more cases - tc fixes - collab mode bug - floating comments fixes - make jsdoc-hygiene-ts baseline key line-independent + update wrapper docs - preserve tab underline via runProperties fallback in collab ### Changes - Merge branch 'main' into caio/sd-3315-find-replace-scroll - Merge pull request #3509 from superdoc-dev/artem/SD-3232 - Merge pull request #3555 from superdoc-dev/luccas/delete-image-content-locked-sdt - Merge branch 'main' into artem/SD-3159 - Merge pull request #3550 from superdoc-dev/luccas/sd-3302-bug-sdt-in-template-builder-shows-grey-background-highlight - Merge branch 'main' into artem/SD-3159 - SD-2676 - fix: table selection not providing a feedback (#3508) - Merge pull request #3549 from superdoc-dev/luccas/left-align-inline-sdt-label - Merge pull request #3546 from superdoc-dev/caio/sd-2647-bug-render-and-round-trip-content-wrapped-in-wsmarttag - Merge remote-tracking branch 'origin/stable' into sync/stable-to-main-20260527-230540 - Merge pull request #3539 from superdoc-dev/caio-pizzol/sd-3289-preserve-cell-level-sdt - Merge pull request #3527 from superdoc-dev/caio-pizzol/sd-3279-strip-sdblockid-from-diff-fingerprint - Merge branch 'main' into caio-pizzol/SD-js-contract-owner-audit - Merge pull request #3531 from superdoc-dev/caio-pizzol/SD-docs-snippet-typecheck-2 - Merge pull request #3526 from superdoc-dev/caio-pizzol/SD-runtime-payload-tests - Merge pull request #3521 from superdoc-dev/caio-pizzol/SD-public-method-coverage-strict-zero - Merge branch 'stable' - Merge pull request #3485 from superdoc-dev/artem/SD-3200 - Merge pull request #3517 from superdoc-dev/caio-pizzol/SD-jsdoc-hygiene-zero-flip - Merge pull request #3513 from superdoc-dev/caio-pizzol/SD-jsdoc-hygiene-cleanup - Merge branch 'stable' - Merge pull request #3511 from superdoc-dev/caio-pizzol/SD-jsdoc-hygiene-scanner - Merge pull request #3435 from superdoc-dev/nick/sd-3220-overlapping-suggestion-contract - Merge pull request #3429 from superdoc-dev/artem/underlined-tab-collab ### Documentation - note content controls in entityAt hit types (SD-3313) - clarify data uri buffer conversion - document image data uri helpers - clarify image registration comments - sync README exit semantics for js contract-owner audit - clarify wrapper-stages prose + add ts-jsdoc to check:public summary ### Features - add ui.contentControls.focus to place the caret in a control (SD-3312) - add ui.viewport.observe geometry-invalidation signal (SD-3311) - add ui.contentControls.scrollIntoView (SD-3310) - add activePath (full active stack) to content-control:active-change - expose public sdt events - select adjacent block SDT content at textblock boundaries - inherit run styles in empty block SDT placeholders - render placeholder text for empty SDTs - move caret into following block sdt on delete - move caret into preceding block sdt on backspace - disable mutation toolbar controls inside content-locked SDTs - render empty inline SDTs as a visible placeholder - add modules.contentControls.chrome - js contract-owner audit, report-only (SD-673) - add snippet typecheck for editor/superdoc/** + fix stale examples (SD-673) - add non-hover field color for sdt (#3506) - comments and tc on small screen (#3446) - overlapping tracked changes - add type-bearing JSDoc hygiene gate for .ts source (SD-673) - add anchored metadata orphan status ### Tests - resolve handleBase64 source path from package or repo root - cover locked block SDT Delete selection - cover nested block SDT boundary selection - update SDT keymap chain coverage - cover contentControlsChrome plumbing; clarify chrome-none comment - assert chrome-none hover suppression and cascade order - add v2 bridge unit + round-trip behavior coverage (SD-2647) - assert smartTag child text survives export round-trip (SD-2647) - cover nested block sdt navigation - roundtrip mixed image block sdts - cover image data uri length boundary - repaint saved sdt images through painter - cover structured content image edges - cover inline image diff fields - cover locked inline SDT beforeinput - cover inline SDT Cmd+X selection - cover inline SDT content Delete flow - clarify inline SDT boundary lock comment - cover two-step inline SDT Backspace - cover inline SDT selection meta escape - use real production payload for list-definitions-change bridge (SD-673) - tighten list-definitions-change bridge assertion (SD-673) - pin list-definitions-change bridge and DELETED comments shape (SD-673) - pin runtime event payload shapes (SD-673) ### Refactoring - consolidate type imports in dom painter - share block sdt navigation helpers - share box model between block and inline sdt labels - share structured content predicates - wrap shared tryDecodeDataUriText re-export - reuse shared data uri export policy - share image relationship export lookup - centralize image data uri parsing - share data uri text decoding - trim data uri metadata fields - extract CHECKED_FILES to shared module + drop audit from wrapper - drop list-item fragment renderer (SD-2851) (#3269) - drain 85 type-bearing JSDoc entries from .ts source (SD-673) ### Performance - avoid scanning data uri media ### Chores - 1.37.0 [skip ci] - fix import breaking delinstrtext orphans (#3535) - fix tests (#3533) - fixes - more fixes - run generate:all and fixes - more fixes - soec fixes - type fixes - ui and more - fix regression - type fixes - more fixes - more fixes - ci fixes - type fixes - add dispatch test for collab bug - review fix, type fix - add tests for metadata issue - tests for review issues
|
π This PR is included in superdoc-cli v0.15.0 The release is available on GitHub release |
|
π This PR is included in superdoc-sdk v1.14.0 |
|
π This PR is included in @superdoc-dev/mcp v0.10.0 The release is available on GitHub release |
|
π This PR is included in superdoc v1.38.0 The release is available on GitHub release |
|
π This PR is included in @superdoc-dev/react v1.9.0 The release is available on GitHub release |
|
π This PR is included in vscode-ext v2.10.0 |
Resubmit of #3528 β same commits (
57383eb0c+aae5c63f3), new branch. The previous PR's merge state got wedged on a "Head branch was modified" prompt that wouldn't clear. Opening on a fresh branch to unblock.Adds a CI-deterministic gate that catches the exact drift class #3503 had to fix manually: docs examples that teach the wrong typed shape. Existing runtime doctest extracts only the
onReadybody and runs it against a mocked host, so it never sees the destructure bug in the outer config - a typecheck does.How it works:
apps/docs/editor/superdoc/**(JS + TS fences) via the existingextractExamples()(extended to keep fence language)// @ts-check, plus a sharedplaceholders.d.tsfor docs idiomstsc --noEmit --strict(allowJs+checkJsfor JS) againstpackages/superdoc/distvia tsconfigpathscheck:public:superdoc; also available standalone viapnpm --filter @superdoc/docs run check:typesCoverage delta: real docs API drift fixes across 4 docs files; 47 in-scope Full Example snippets now typecheck clean (was
0before this PR). 1 example skipped (yjs+y-websocketupgrade-to-collaboration example: consumer BYO, not part of SuperDoc's typed surface - added toSKIP_IMPORTS).Placeholder stubs (11 ambient declarations)
Pure test-infra; doesn't change any docs. Stubbed in a shared
placeholders.d.tsso docs examples can reference common identifiers without inline declarations.yourFile,file,doc1,doc2,contentcleanup,autoSave,adjustLayout,showOnlineUsers,updateUserCursors,showLockBannerReal docs API drift fixes (4 files)
These are the typed-public-surface bugs the gate surfaced. None of them were caught at runtime because the runtime doctest doesn't typecheck.
methods.mdxonReady: (superdoc) =>βonReady: ({ superdoc }) =>in 22 examples (typed payload is{ superdoc }, not the instance)methods.mdxsetActiveEditorexample rewritten to useonEditorCreatewithqueueMicrotask(the previous version reached intoDocument.editorwhich isn't on the public type; thequeueMicrotaskdefers the switch past the in-flighteditorCreateemission - see commitaae5c63f3for the timing fix on top of the original rewrite)methods.mdxsearch/goToSearchResultnull guards on the optionalSearchMatch[] | undefinedreturnmethods.mdxaddCommentsListsignature corrected toHTMLElement(was documented asstring | HTMLElement; actual isHTMLElement)methods.mdxscrollToComment/scrollToElement:superdoc.editorβsuperdoc.activeEditorwith null guards (activeEditorisEditor | null;editordoesn't exist as a public member); narrowedBlockNodeAddressbefore.nodeId; switched.entityIdβ.idonDiscoveryItemmethods.mdxoffexample: handler annotated with/** @param {import('superdoc').SuperDocReadyPayload} payload */so the standalone-handler shape pins to the typed payloadimport-export.mdxonReady: (superdoc) =>βonReady: ({ superdoc }) =>in 5 examplesimport-export.mdxsuperdoc.activeEditorbefore.getHTML/.getJSON/.getMarkdown/.commandsaccessevents.mdxevents.mdxeditor-update: null guard on the optionaleditorfield (EditorUpdateEvent.editor?: Editor)events.mdxcontent-error: removeddocumentIdfrom destructure (SuperDocContentErrorPayloadis{ error, editor }; nodocumentId)events.mdxcomments-update:{ type, data }β{ type, comment, changes }(matchesSuperDocCommentsUpdatePayload; same drift class as #3503)events.mdxawareness-update:{ context, states }β{ states, added, removed }(runtime emitssuperdocnotcontext; same drift class)events.mdxlocked: added&& lockedByguard before reading.name(lockedBy: User | null)events.mdxpagination-update: removedpagination: truefrom Config example (no such field on the typed Config)events.mdxexception: rewrote to takepayloadand readpayload.error, with a comment on narrowing the discriminated union (document/editoronly exist on specific variants)collaboration/configuration.mdxlockedBytype cell:ObjectβUser | nullwith a null-check noteMust stay the same:
check:public:superdockeeps passing.Review:
buildand depends onpackages/superdoc/distexisting. CI builds dist before this stage; local devs needpnpm --filter superdoc build(orpnpm check:public:superdocwithout--skip-build).packages/superdoc/distbefore debugging snippet errors. The gate checks against the built.d.ts, so a stale local dist produces misleading failures unrelated to the docs themselves.skipLibCheck: trueis intentional in the temp tsconfig: this gate checks docs usage against the public surface, not library declarations (deep-type-audit-supported-rootalready covers that).Verified:
pnpm --filter @superdoc/docs run check:typesβ OK, 47/47 typechecked clean (againstaae5c63f3HEAD)pnpm check:public:superdoc --skip-buildβ PASS (12 ran, 1 skipped, 130.8s)Closes #3528 once this is merged. Recommend closing #3528 manually with a pointer to this PR (or it can be auto-closed once this one lands if you prefer).