Skip to content

Feat/typescript migration - #862

Closed
sroucheray wants to merge 121 commits into
rapi-doc:masterfrom
PerfectMemory:feat/typescript-migration
Closed

sroucheray wants to merge 121 commits into
rapi-doc:masterfrom
PerfectMemory:feat/typescript-migration

Conversation

@sroucheray

@sroucheray sroucheray commented Dec 13, 2022 •

Copy link
Copy Markdown
Contributor

Summary

Migrate packages/rapidoc from JavaScript to TypeScript. All sources under src/ are now .ts (57 files, no .js left), type-checked with strict mode. The built bundle (dist/rapidoc-min.js) and the public API (<rapi-doc> attributes, methods, events, CSS parts and custom properties) are meant to be unchanged.

This PR is meant to be reviewed after the bug-fix PR (#NNN). The branch merges it, so the first commits of the diff are those fixes; the migration itself is the commits listed below.

What changed

The migration is split in small, reviewable commits:

  1. build: add TypeScript toolchain for packages/rapidoc: typescript, typescript-eslint, tsconfig.json, a typecheck script.
  2. refactor: rename packages/rapidoc sources from .js to .ts: a pure rename, to keep the git history of each file.
  3. build: enforce erasable TS syntax, add @scalar/openapi-types for typings: no enums, namespaces or parameter properties, so the code can still be run or stripped by Node, esbuild and Vite as-is.
  4. refactor(ts): type utils, styles and shared spec/schema types: shared types in src/types/ (element.ts, spec.ts, schema.ts, theme.ts).
  5. refactor(ts): type components, templates and main elements: rapidoc.ts, rapidoc-mini.ts, json-schema-viewer.ts and the templates.
  6. build: relax lit-analyzer binding rules to warnings, fix unknown attribute binding.

Design choices

  • strict: true, plus noImplicitOverride and noFallthroughCasesInSwitch.
  • erasableSyntaxOnly: TypeScript is only type annotations, nothing that changes runtime semantics.
  • useDefineForClassFields: false: required for Lit's reactive properties declared with static properties.
  • No any as a shortcut: where a type could not be established without changing behavior, the code keeps a narrow cast and a TODO(ts-migration) comment (7 left), so these places can be found with one grep.
  • No runtime changes were intended. The few real bugs found while typing (undefined accesses) were fixed separately, each in its own commit with a unit test where possible.

Notes for reviewers

  • The rename commit has no content change; review the following commits for the typing.
  • lit-analyzer binding rules are downgraded to warnings in the analyze script, because the analyzer cannot see types across the Lit templates' attribute bindings.
  • Vite config and the published files are unchanged: dist/rapidoc-min.js is still the only artifact.

Checks

  • npm run typecheck --workspace=rapidoc (tsc -p tsconfig.json)
  • npm run lint
  • npm run test:unit
  • npm run build:rapidoc and the docs site (npm run dev) load with no console error
  • Bundle size compared with master (npm run build:size)

sroucheray and others added 28 commits October 9, 2026 17:32
Removes @ts-nocheck from utils/* and styles/*; adds types/schema.ts,
types/spec.ts and types/theme.ts. Pure typing: the unminified bundle only
differs by comments, two let->const and two equivalent spread rewrites.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Removes the last @ts-nocheck headers. Adds types/element.ts (host element
types). tsc strict passes with 0 errors, eslint 0 errors, unit tests 124/124.
Emitted JS only differs by equivalent rewrites (local aliases of
e.target/e.currentTarget, bracket dataset access, an unused parameter) and
template whitespace; upstream bugs spotted during typing are left unchanged
and marked with TODO(ts-migration).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ibute binding

Typed templates expose 'string | undefined' attribute bindings that the
untyped JS hid; tracked as warnings for later cleanup.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- global marked override: in-page #anchors become absolute URLs of the current
  page (<base> tag support) with the anchor-link class (+ unit tests)
- 'select' CSS part on dropdowns, exported through api-request/api-response
- .security-scheme CSS class, toolbar-btn border-radius via --border-radius,
  vertically centered parameter labels
- fetch failures set responseStatus to 'error' and hide the response tabs
- curl syntax refreshes after fill/clear of request parameters

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
setTheme read the misspelled `theme.navAccenttextColor` whenever
`navAccentTextColor` was set, so a custom value always resolved to undefined
(for both the dark and the light theme).
The storage listener called `this.receiveStorage`, which does not exist, so it
threw a TypeError on every storage event instead of relaying the OAuth data
to the parent window.
The api-key security scheme was pushed with an `apiKeyId` field while the
lookup just above matches on `securitySchemeId`. The entry was therefore never
found again and every api-key attribute change pushed a duplicate scheme.
The relative refreshUrl branch concatenated the server URL with the tokenUrl
(copy/paste of the tokenUrl branch), so when the server URL ends with a slash
the refresh URL silently became the token endpoint.
The element itself was passed as `targetOrigin`, which browsers reject with a
SyntaxError. Starting a new OAuth flow while a previous single-use message
listener was still active therefore threw before the authorization window was
opened. The fake message is sent to the current window and carries no data, so
'*' is safe.
…tions

ProcessSpec takes (attrApiKey, attrApiKeyLocation, attrApiKeyValue, serverUrl)
before (matchPaths, matchType, removeEndpointsWithBadgeLabelAs), but the viewer
passed the match-* attributes directly after sortEndpointsBy, so they landed in
the api-key parameters and the match parameters kept their defaults.
generateExample takes 8 parameters but the call passed 9: the extra `true`
shifted 'text' into includeGeneratedExample and put `true` in outputType. The
generated sample of an object-typed parameter was thus returned as an object
instead of JSON text, and `data-example="${example.exampleVal}"` rendered
"[object Object]" which the "Fill Example" button then wrote into the input.
Two heading renderers (read-mode endpoint descriptions and the info
description headings in the nav bar) still used the pre-v5
`heading(text, level, raw)` signature. marked 18 passes a single token
object, so `level` and `raw` were undefined and headings rendered as
`<hundefined ... id="...--undefined">`. Align them with the renderers already
updated in focused-endpoint-template and overview-template.
`typeof null === 'object'`, so a valid `example: null` (or a null entry in an
examples map) reached `ex.value` / `obj.value` and threw a TypeError, breaking
the rendering of the whole endpoint.
…onse

`exportparts` takes `inner:outer`; the entries read `tab-coontent:tab-content`
(typo), so no inner part matched and the documented `tab-content` part was
never re-exported to the rapi-doc host.
- downloadResource/viewResource/onApiKeyChange are called with an event
  argument they do not accept
- unsafeHTML() takes a single argument; the `{ USE_PROFILES }` options (a
  DOMPurify leftover) were silently ignored since sanitizeHTML already runs
- endpoint ordering by method compared indices as strings (via toString +
  localeCompare); compare them numerically

No behavior change.
Brings in upstream bug fixes (developed on top of rapidoc/master so they can
be proposed upstream as-is): theme nav-accent-text-color, oauth-receiver
storage relay, rapidoc-mini api-key scheme id, oauth refreshUrl and fake
postMessage origin, json-schema-viewer ProcessSpec arguments, request-params
generateExample call, marked 18 heading renderers, null example handling,
tab-content part export and a dead-argument cleanup.

The matching TODO(ts-migration) notes and @ts-expect-error directives are
removed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eme tokens

The viewer built its theme object with the misspelled key `navAccenttextColor`,
so setTheme never saw the configured nav-accent-text-color.
jsonSchemaViewerTemplate only takes `isMini`; the three extra arguments were
leftovers of the former endpoint template signature.
Second sync: navAccentTextColor spelling in the json-schema-viewer theme tokens
and removal of ignored arguments in json-schema-viewer render().

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…<base> tag

When the host page has an HTML <base> tag, a bare `#section` link in a markdown
description resolves against the base URL, so clicking it navigates away from
the page instead of jumping to the section.

Markdown in-page anchors are now rendered as absolute URLs of the current page
(with an `anchor-link` class). The fix is applied to the global marked instance
and to the custom renderers (headings in the nav bar, read mode, focused mode),
which replace the global renderer when passed per call.

Covered by unit tests and by a browser test on a page with a <base> tag.
Brings in the <base>-tag anchor link fix (global marked override plus the
custom heading renderers). markdown-utils stays in TypeScript here.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The icon click passed a div as event target, so searchVal became undefined and
the component search crashed on undefined.toLowerCase().

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…pec is loading

resolvedSpec is a placeholder without securitySchemes during loadSpec, so
attributeChangedCallback threw on securitySchemes.find.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@mrin9

mrin9 commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator

code-base has been refactored therefore the PR lost its relevance

@mrin9 mrin9 closed this Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants