Zum Hauptinhalt springen

.js.ts Migration Progress — details

Per-package detail for the migration plan in ./js_to_ts_progress.md. Add a subsection per package when work begins, and write per-file entries immediately so a session interrupt loses at most one entry.

Format

## packages/<name>
Session date: YYYY-MM-DD
Branch: <branch-name>

Files processed:
- `relative/path/file.ts` — short note on notable changes (caller updates, type fixes, dead code removed, etc.). If trivial, just "converted; no caller updates" is enough.

Files skipped:
- `relative/path/file.js` — reason (vendored, config, blocked by <X>, etc.)

Verification:
- `yarn tsc --noEmit`: <result>
- `yarn test` / `cd packages/<name> && yarn jest <pattern>` (where applicable): <result>
- `yarn syncFuzzer start --steps 5` (for app-cli / lib sync-target work): <result>

Notes / `review-later.md` entries added:
- <file:line><one-liner about what was logged>

Conventions for "Files processed" entries

  • Caller updates are part of the same commit as the conversion. Mention them in the entry only if there are several or they are non-obvious. A single .default add at the consumer is implicit.
  • Type fixes the conversion surfaces (e.g. a previously-any parameter that the cast reveals to be too narrow) are noted. If applied as a separate commit, mention the commit subject.
  • Dead code removed (unused private fields, unreachable branches) is noted with the original-line reference.
  • Pre-existing smells flagged to review-later.md are noted (file path + one-line summary).

packages/app-cli

Session date: 2026-05-22

Files processed (under packages/app-cli/app/):

  • command-undone.ts — converted; sibling command-done kept as require().
  • command-mknote.ts — typed local note: NoteEntity so post-Note.save reassignment type-checks; existing fire-and-forget Note.updateGeolocation marked void.
  • command-mktodo.ts — same pattern as command-mknote.
  • command-todo.ts — switched BaseModel.TYPE_NOTE to ModelType.Note to match the narrower loadItems parameter; toSave typed NoteEntity.
  • command-search.ts — converted; notebook CLI arg name forces an id-denylist disable on the args type.
  • command-status.ts — converted; no caller updates.
  • command-server.ts — Logger addTarget('console', …)TargetType.Console enum; partial console mock cast through unknown to Console.
  • command-tag.tsloadItem(ModelType.Tag as ModelType.Note, …) to work around the narrow signature; entity types NoteEntity/TagEntity.
  • gui/NoteMetadataWidget.tsexport default; consumer in app-gui.js updated to .default.
  • gui/NoteListWidget.tsexport default + consumer; itemRenderer callback typed NoteEntity.
  • gui/ConsoleWidget.tsexport default + consumer; private fields typed.
  • gui/NoteWidget.tsexport default + consumer; as string casts in the doAsync closure preserve "read at execution time" semantics that the lastLoadedNoteId_ guard relies on.
  • ResourceServer.tsexport default + consumer; LinkHandler type for the handler callback; server-destroy augmentation cast on server_.destroy().
  • help-utils.ts — destructured require() shape preserved; 'cli' string literals → AppType.Cli; eslint-disable import/prefer-default-export on the single named export. renderMetadata md parameter tightened to SettingItem in a follow-up commit. (Also a follow-up to untrack the leftover compiled help-utils.js after a staging restore left it tracked.)
  • autocompletion.tsCompletionResult = string | CompletionList (CompletionList = string[] & { prefix?: string }); yargs-parser consumed via import = require(); void on the existing fire-and-forget then(…).
  • cli-utils.tsCliUtils interface declared for the namespace object; redundant printArray empty-rows early return removed; the dead if (i >= a.length) branch in makeCommandArgs removed with a comment pointing at review-later.md; prompt synthesises a Writable & { muted: boolean } intersection.
  • app-gui.ts — biggest of the round; dropped dead inputMode_ field + the two unused INPUT_MODE_* static constants; typed processShortcutKeys explicitly as boolean; defined KeymapItem interface and NoteLink discriminated union; void on three pre-existing fire-and-forget calls. Single consumer in app.ts:442 updated to .default.

Follow-up tightening commits (separate from each file's main conversion):

  • Tighten BaseCommand.description() return type to string — base method throws but every subclass returns a string; tightened so help-utils doesn't need a cast.
  • Tighten app-cli help-utils renderMetadata md parameteranySettingItem.
  • Tighten app-gui Redux store/state any typesstore_: anyStore<State>; four (state: any) => … mappers → (state: State) => ….
  • Tighten app-gui App any type to Application — required adding export to the Application class in app.ts; surfaced a real type mismatch (ResourceServer.setLogger(this.app().logger()) was passing LoggerWrapper into a Logger-typed setter — fixed by widening ResourceServer's logger types).
  • Simplify ResourceServer logger types to just LoggerWrapperLogger | LoggerWrapper collapses to LoggerWrapper since Logger is structurally assignable to it.
  • Tighten remaining app-gui any typesnoteLinks → discriminated union; updateNoteText note: anyNoteEntity | null.
  • Tighten cli-utils prompt mutableStdout any to a typed intersection — replaced two any casts with Writable & { muted: boolean } and a typed this on the write callback.
  • Extract PromptOptions interface from StatusBarWidget.prompt — same anonymous { cursorPosition?: number; secure?: boolean } shape was duplicated across three places; extracted as PromptOptions exported from StatusBarWidget.
  • Final-review cleanup on app-cli conversions — mirrored the LoggerWrapper widening to app-gui.ts logger_/setLogger; reordered the NoteEntity import to group with @joplin/lib/*; trimmed the 5-line closure-cast comment in NoteWidget.
  • Trim cli-utils explanatory comments — removed the printArray empty-rows comment entirely; shortened the prompt _initialText rationale.
  • Use typed LinkSelector import in app-cli app-guiLinkSelector was already a typed .ts class; replaced the require('./LinkSelector.js').default + any field with a typed import and dropped the stale eslint-disable. Caught during a final consistency-check pass over the branch.
  • Trim block comments in app-cli autocompletion and cli-utils — same consistency-check pass: shortened the CompletionList rationale block to one line and removed the cli-utils makeCommandArgs dead-branch post-mortem (the dead branch is already gone; the note lives on in review-later.md).

Files skipped:

  • app/main.js — shebang preservation issue (see "Files to never touch" in the plan).
  • app/build-doc.js — dev-time documentation generator, low value.
  • app/fuzzing.js — 2400+ lines, exploratory test runner.
  • tests/feature_*.js, tests/HtmlToHtml.js, tests/support/createSyncTargetSnapshot.js — feature tests / test helpers; deferred to a follow-up round.
  • tests/support/plugins/**/*.js — intentionally JS plugin fixtures.

Verification:

  • yarn tsc --noEmit from the repo root: clean after every commit.
  • yarn syncFuzzer start --steps 5 from the repo root: green at three checkpoints (after the first batch of commands, after the gui widget round, after the cli-utils / help-utils / autocompletion round).
  • cd packages/app-cli && yarn jest command-done.test: 1 passing (smoke check after command-undone).
  • cd packages/lib && yarn jest synchronizer.basics.test: 24/24 (baseline before the round; not re-run after every commit).

Notes / review-later entries added during this round (entries record generic locations; an on-disk review-later.md is per-environment and not committed):

  • packages/app-cli/app/app.ts loadItem/loadItems — the typed parameter (Note | Folder | 'folderOrNote') is narrower than the runtime support, which falls through to BaseItem.itemClass(type).loadByTitle for other types including ModelType.Tag. command-tag casts at the call site to compensate.
  • packages/app-cli/app/autocompletion.ts:103 — in the 'item' argument completion branch, notes.map(n => n.title) is spread but folders.map(n => n.title) is not — folder titles end up pushed into the completion list as a single nested array element. Likely-bug; the as unknown as string cast in the converted file preserves the behaviour.
  • packages/app-cli/app/cli-utils.ts makeCommandArgsif (i >= a.length) where a is { required, name } is unreachable (a.length is undefined). The else branch always ran; preserved by removing the dead conditional. Likely intended args['_'].length.

packages/renderer

Session date: 2026-05-25Branch: claude/chore/renderer--js-to-ts

Files processed (under packages/renderer/):

  • stringUtils.tsmodule.exports = { surroundKeywords } → named export; require('html-entities').AllHtmlEntities swapped for the typed import { AllHtmlEntities } from 'html-entities'; new Keyword = string | RegexKeyword | StringKeyword discriminated union typed off the in-code comment that documented the keyword shapes. cSpell ignore block added around the diacritic-replacement table. Existing require('../../stringUtils.js') consumers in highlight_keywords.ts unchanged (named exports remain reachable from the namespace).
  • urlUtils.tsmodule.exports = urlUtils namespace object → named exports (urlDecode, isResourceUrl, parseResourceUrl, ParsedResourceUrl). Internal self-references switched from urlUtils.X(...) to bare calls. Existing require('../urlUtils.js') consumers in linkReplacement.ts and link_open.ts unchanged.
  • defaultNoteStyle.tsmodule.exports = {...}export default {...}. Single consumer in MdToHtml.ts:76 updated to a top-of-file import defaultNoteStyle from './defaultNoteStyle';.
  • Tools/buildAssets.ts — dev-time script. Three function signatures typed (dirname(path: string), copyFile(source: string, dest: string), main()); fs-extra kept as const fs = require('fs-extra') (implicit any from require per project convention; renderer has no @types/fs-extra and adding the dep is out of scope for a mechanical conversion). Verified by running yarn buildAssets from packages/renderer/ — outputs in assets/{abc,mermaid,katex,highlight.js} matched the prior structure.

Files skipped:

  • MdToHtml/rules/abc_render.js and MdToHtml/rules/mermaid_render.js — shipped raw to browsers by Tools/buildAssets.js (lines 49/52 fs.copy the source .js to assets/{mermaid,abc}/). Matches the same hazard pattern documented for packages/lib/markJsUtils.js in the plan's "Files to never touch" section: TS-emit CommonJS wrappers (Object.defineProperty(exports, '__esModule', …)) break in a raw <script> context. mermaid_render.js additionally assigns to Event.target (a readonly DOM property in lib.dom.d.ts), so strict TS would require a cast. Skip until the build pipeline is updated (e.g. compile these via a webpack/esbuild step that emits a browser-friendly IIFE, or change buildAssets.js to copy the compiled output instead of the source).
  • MdToHtml/rules/katex_mhchem.js — 1700+ lines vendored from the KaTeX repo (https://github.com/KaTeX/KaTeX/blob/master/contrib/mhchem/mhchem.js); already preceded by /* eslint-disable */. Falls under the plan's "Vendored / forked third-party code — preserved verbatim" rule.
  • lib/renderer.js — 7-line // TODO stub with no callers found in repo. Flag for removal as dead code in a separate PR; not in scope for a mechanical conversion.
  • tests/test-utils.js — empty file (0 bytes), no callers. Same: candidate for deletion, not conversion.
  • jest.config.js — config file kept as .js by design (plan "Files to never touch").

Verification:

  • yarn tsc --noEmit from the repo root: zero new errors in @joplin/renderer. (Pre-existing errors in @joplin/app-desktop WhiteboardEditor/* and @joplin/app-mobile services/e2ee/crypto.ts were present on the branch base and are unrelated.)
  • cd packages/renderer && yarn jest: 35/35 passing (5 suites).
  • cd packages/app-cli && yarn test MdToHtml: 13/13 passing (cross-package renderer integration tests).
  • cd packages/renderer && yarn buildAssets: ran clean; regenerated assets under assets/{abc,mermaid,katex,highlight.js} matched the prior structure. Note: regenerating overwrote a committed-stale assets/abc/abcjs-basic-min.js (v6.5.2 on disk vs v6.6.2 in package.json); restored before commit and noted as a pre-existing concern.

Notes / review-later entries added (generic locations; the on-disk review-later.md is per-environment and not committed):

  • packages/renderer/stringUtils.ts surroundKeywordsvalueRegex from RegexKeyword entries is passed verbatim into new RegExp(...); only pregQuote-escaped string keywords are safe. A pathological pattern ((a+)+$) compiled gi and run against note text causes catastrophic backtracking on every render. Source of valueRegex is search input — primarily self-inflicted but persists into saved searches.
  • packages/renderer/assets/abc/abcjs-basic-min.js — committed v6.5.2 disagrees with package.json's abcjs: 6.6.2; running yarn buildAssets regenerates and produces a dirty working tree.
  • packages/renderer/lib/renderer.js — 7-line // TODO stub, no callers in repo; deletion candidate.
  • packages/renderer/tests/test-utils.js — 0-byte file, no callers in repo; deletion candidate.

packages/lib

Session date: 2026-05-26Branch: claude/chore/lib--js-to-ts

Source files processed (under packages/lib/):

  • envFromArgs.ts — signature tightened to (args: string[] | null | undefined) => 'dev' | 'prod'.
  • parameters.ts — introduced Env, AppCredentials, ParametersForEnv types; parameters_ typed Record<Env, ParametersForEnv>; Setting.value('env') cast to Env.
  • randomClipperPort.ts — added ClipperPortState interface and local Env alias.
  • SyncTargetMemory.tsdefault export class extends BaseSyncTarget; callers updated to default-import.
  • SyncTargetNextcloud.ts — sibling SyncTargetWebDAV kept as require() (still JS at this point); checkConfig temporarily typed options: any with an eslint-disable.
  • SyncTargetDropbox.ts — removed unused private api_ field (set in constructor, never read); fixed a spelling slip flagged by cSpell; callers updated to default-import.
  • SyncTargetWebDAV.ts — added exported WebDavFileApiOptions interface shared by checkConfig and newFileApi_; sibling file-api-driver-webdav kept as require().
  • resourceUtils.ts — split previous module.exports = { ... } object into individual named exports; both destructured-require and namespace-style callers continue to work.
  • import-enex-html-gen.ts — removed an unused options parameter from enexXmlToHtml (forwarded to enexXmlToHtml_ which never read it, and no caller passed it); string-to-stream/@joplin/fork-sax kept as require() (no types); sax event-handler node/stream-output values stay any.

Test files processed (production-code counterpart was already TS):

  • database.test.ts — converted; benefits from existing TS types in test-utils and BaseModel.
  • TaskQueue.test.ts — constructor now passed a name; push ids switched from numeric to string to match TaskQueue.ts's typed signature (JS coerced the numbers at runtime).
  • ArrayUtils.test.ts — dropped a second arg to expect().toEqual() in the mergeOverlappingIntervals case (rejected by Jest typings; silently ignored at runtime).
  • services/KvStore.test.ts — no notable type adjustments.
  • eventManager.test.ts — simplified test states cast through unknown as AppState at appStateEmit call sites; callbacks typed via the AppStateChangeCallback generic; dropped the now-redundant 'use strict'.
  • urlUtils.test.ts — added explicit row types for the parseResourceUrl/extractResourceUrls test-case arrays; switched to import * as urlUtils from './urlUtils' to preserve namespace-style call sites.
  • mimeUtils.test.ts, pathUtils.test.ts, timeUtils.test.ts — no notable type adjustments.
  • services/KeymapService.test.ts — negative-test data for "required properties missing" cast through unknown as KeymapItem[] so the type checker accepts the deliberately malformed entries; pre-existing accelerator: null discrepancy left as-is (masked by root strict: false).
  • import-enex-html-gen.test.ts — imports ResourceEntity and types fixtures explicitly; one attachment-image fixture has a width field not on ResourceEntity, cast as ResourceEntity locally (field is unread by the converter; removing it would alter long-standing data).
  • models/Note.customSortOrder.test.ts — converted and renamed from Note_CustomSortOrder.test.js (and describe title updated) to match the Foo.aspect.test.ts convention; Note.insertNotesAt calls now pass all 5 args (false, false for uncompletedTodosOnTop/showCompletedTodos, preserving prior falsy-undefined behaviour); is_todo: true switched to is_todo: 1 to match the typed numeric schema; originalTimestamps typed as Record<string, {…}>.

Dead code removed (separate commit):

  • parseUri.js — deleted; no callers in repo.

Follow-up tightening commits:

  • Type SyncTargetNextcloud.checkConfig with WebDavFileApiOptions — replaced options: any with the WebDavFileApiOptions import from SyncTargetWebDAV, dropping the eslint-disable. Done as a separate commit because the type wasn't exported until SyncTargetWebDAV was itself converted.
  • Simplify eventManager.test by dropping redundant generics — removed explicit <string> and <{ name: string }> args on appStateOn/appStateOff; the callback's event.value already lets TS infer the generic.

Files attempted but reverted:

  • markJsUtils.ts — converted in 5de93c318 and reverted in 6e2a1b8ec. The TS-emit CommonJS wrappers (Object.defineProperty(exports, '__esModule', …)) broke the desktop note viewer at runtime (yarn test-ui markdownEditor failed). yarn tsc --noEmit and yarn jest did not catch it. Same hazard pattern as the renderer's abc_render.js / mermaid_render.js: the file is shipped to a browser context where the CJS wrapper isn't valid. Documented in the plan's "Files to never touch" section; don't retry without first identifying the bundling path.

Files not yet processed (deferred to a follow-up round):

  • Remaining packages/lib/ source .js files not in this batch (models, services, sync helpers, etc.) — convert per the order in the plan's per-package strategy.
  • markJsUtils.js — see above; blocked on the bundler/runtime fix.

Verification:

  • yarn tsc --noEmit from the repo root: clean after each commit (and after the merge with upstream/dev).
  • cd packages/lib && yarn jest <suite> against the touched test files: green per-conversion.
  • yarn syncFuzzer start --steps 5 from the repo root: green after each SyncTarget* conversion (primary verification path for sync-target files; yarn jest does not meaningfully exercise them).

Notes / review-later entries added during this round (generic locations; on-disk review-later.md is per-environment and not committed):

  • packages/lib/markJsUtils.js — see "Files attempted but reverted" above; the CJS-wrapper-in-browser hazard applies to any other file consumed by the desktop note viewer via a raw <script>-style include.
  • packages/lib/SyncTargetDropbox.ts constructor — removed unused api_ field that was assigned but never read; verify no out-of-tree consumer (mobile/desktop overrides) relied on the field name before the next release.
  • packages/lib/import-enex-html-gen.ts enexXmlToHtml — dropped unused options parameter; if a future plugin or external caller passes options it will now silently be ignored at compile time (the parameter was already a no-op at runtime).
  • packages/lib/services/KeymapService.test.ts — pre-existing accelerator: null in test fixtures disagrees with KeymapItem (only present at root strict: false); flag for cleanup when strict is tightened.

Merge with upstream/dev (2026-05-26)

upstream/dev landed two overlapping PRs while this branch was in progress: #15523 (app-cli conversions) and #15532 (renderer conversions). Both equivalently converted files we had also converted locally. Merge resolution:

  • packages/renderer/stringUtils.ts — true content conflict (different stylistic choices: arrow-function vs function-declaration; module-scope vs function-scope diacriticReplacements; discriminated-union vs separate-interfaces for Keyword). Resolved by accepting upstream's version verbatim, since it was already reviewed and merged in #15532. Local commit 19893150f becomes redundant content-wise (the rename is preserved by the merge).
  • All app-cli .js → .ts renames and the renderer defaultNoteStyle.ts / urlUtils.ts conversions resolved without conflict (identical content on both sides; git's rename detection collapsed them).
  • .eslintignore / .gitignore — merge cleanly absorbed upstream's # AUTO-GENERATED additions for app-cli; re-ran yarn updateIgnored post-merge as a sanity check (no further diff).
  • yarn tsc --noEmit post-merge: clean.

packages/lib (round 2)

Session date: 2026-05-26Branch: claude/chore/lib--js-to-ts-2

Continuation of the first packages/lib round. Picks up the small / mid-size remaining .js files; the larger sync-target drivers (SyncTargetAmazonS3.js, DropboxApi.js, file-api-driver-*.js) are still deferred and need fuzzer-led conversion per the plan's sync-target verification rule.

Source files processed (under packages/lib/):

  • reserved-ids.tsmodule.exports = Object.freeze({...}) → named export const ALL_NOTES_FILTER_ID. Single export with file/symbol-name divergence, so eslint-disable-next-line import/prefer-default-export per project guidance. No caller updates needed (destructured require continues to work).
  • fs-driver-dummy.tsclass FsDriverDummy made to extend FsDriverBase; readFile typed async. Default-exported; consumer models/Resource.ts switched from destructured require('../fs-driver-dummy.js') to a typed default import.
  • react-logger.tsmodule.exports = { ReactLogger }export class ReactLogger extends Logger. No callers found in the repo; conversion was mechanical. Single-export disable for import/prefer-default-export.
  • lib/lib.ts — 7-line TODO stub with no callers; converted to export default function lib(). Deletion candidate flagged below.
  • Cache.ts — added a minimal Storage interface for the two node-persist methods used here (the package is untyped); module.exports = Cacheexport default class Cache. catch (error)catch (_error) to satisfy unused-binding rule; no-op behaviour preserved. Follow-up commit 6b50d7193 fixed the app-cli caller (app/app.ts:21 had a const Cache = require('@joplin/lib/Cache') that survived the conversion; at runtime the TS-emit CJS shape made new Cache() throw "Cache is not a constructor"). yarn tsc did not catch this; the sync fuzzer did. Lesson: when converting module.exports = Xexport default X, grep all require() callers (including other packages and compiled JS that won't be regenerated) in the same commit.
  • migrations/20.ts, migrations/27.ts, migrations/33.ts, migrations/35.ts — match the shape of the existing migrations/42.ts (local Script interface, export default script). Consumer models/Migration.ts switched from require('../migrations/20.js') (×4) to typed default imports.
  • services/plugins/sandboxProxy.ts — typed the proxy target as (path: string, args: unknown[]) => unknown with a __joplinNamespace?: string[] marker; the dynamic Proxy can't be honestly typed, so the function takes a generic T for callers to supply the surface type (PluginRunner in app-cli now does sandboxProxy<Joplin>(target)).
  • mime-utils-types.ts — added an exported MimeType interface ({ t: string; e: string[] }); module.exports = mimeTypesexport default. Wrapped the array body in // cSpell:disable / // cSpell:enable because hundreds of MIME subtype tokens are not real words. Consumer mime-utils.ts switched from require() to a typed default import.
  • welcomeAssets.ts — initially converted in a3fb2b92b, then reverted in 8008a3158 after spotting that packages/tools/build-welcome.ts:165 regenerates the file via writeFileSync(.../welcomeAssets.js, ...). Any conversion would be silently undone the next time yarn build-welcome runs. Should be added to the plan's "files to never touch" list, or build-welcome.ts updated to emit a .ts file (and an export default) first.
  • services/PluginManager.tsmodule.exports = PluginManagerexport default class. Introduced explicit interfaces: PluginMenuItemDeclaration (manifest shape, accelerator is a function), PluginMenuItem (menuItems() return shape — accelerator resolved to string, click required), PluginManifest, PluginInstance, PluginClass. The static Dialog property is typed unknown to keep React out of lib/; consumers cast it to React.ComponentType<…> at the render site. Three callers (app.ts, gotoAnything.ts, WindowCommandsAndDialogs.tsx in app-desktop) switched from require() to typed default imports.
  • database-driver-node.tsmodule.exports = { DatabaseDriverNode }export class DatabaseDriverNode (single-export disable as above). Added a local SqliteDatabase interface for the methods touched (get/all/run/close/loadExtension). sqliteErrorToJsError keeps the msg array as unknown[] so Array#join's String() coercion of each element preserves the original error.toString(): sql: params formatting (where params is an array; switching to JSON.stringify would change the output). Four consumers (BaseApplication, testing/test-utils, server/joplinUtils, app-cli cli-integration-tests) switched to typed named imports.
  • onedrive-api-node-utils.tsapi: OneDriveApi parameter typed against the existing TS class; targetConsole callback typed { log(message: string): void }; DestroyableServer interface to apply the server-destroy augmentation on top of http.Server. Two consumers (app-cli command-sync.ts, app-desktop OneDriveLoginScreen.tsx) switched from destructured require() to typed named imports. Tightened command-sync's oneDriveApiUtils_: any to OneDriveApiNodeUtils | null (drops one no-explicit-any disable).
  • components/shared/dropbox-login-shared.ts — typed the small DropboxApi surface this module uses (loginUrl/setAuthToken/execAuthToken) since the full DropboxApi is still JS. The host-component parameter is kept any (with a comment) rather than modelling React.Component<P, S>.setState — its generic Pick<S, K> signature is awkward at this seam and the desktop/mobile callers each have their own Props shape. MessageBox typed loosely ((msg) => unknown) since desktop's bridge().showInfoMessageBox returns boolean and mobile's shim.showMessageBox returns a Promise. Three pre-existing fire-and-forget promise calls (reg.scheduleSync, two UNSAFE_componentWillMount calls of refreshUrl) now need void since the wrapping methods are typed async. Two consumers (DropboxLoginScreen.tsx desktop, dropbox-login.tsx mobile) switched to typed default imports and dropped their private shared_: any disables.

Files skipped (intentionally JS):

  • markJsUtils.js — see prior round's "Files attempted but reverted" note.
  • string-utils-common.js — has an explicit "leave as JavaScript" comment; consumed by packages/editor/CodeMirror/* files that ship to browser contexts. Same TS-emit CJS-wrapper hazard as markJsUtils.js.
  • renderers/webviewLib.js — read as raw text by packages/app-mobile/tools/buildInjectedJs/copyAssets.ts (gulp task copyWebviewLib) and inlined into packages/app-mobile/lib/rnInjectedJs/webviewLib.js. After conversion, copyAssets would inline the TS-compiled .js (with Object.defineProperty(exports, '__esModule', …) wrappers) into the webview context. Same hazard pattern as markJsUtils.js. Skip until the build pipeline reads the .ts source or emits a browser-friendly IIFE.

Files not yet processed (deferred to a follow-up round):

  • DropboxApi.js (213 lines) — touch point for dropbox-login-shared (which declares a minimal local interface in lieu of converting). Convert next so the local interface can be replaced.
  • SyncTargetAmazonS3.js (154 lines) — sync-target file; needs fuzzer-led conversion per the plan.
  • file-api-driver-amazon-s3.js (406 lines), file-api-driver-dropbox.js (255), file-api-driver-webdav.js (237) — sync-target drivers; same fuzzer rule.
  • utils/types/pdfJs.js — already a compiled output of utils/types/pdfJs.ts but accidentally tracked in git. Needs git rm --cached; flagged as a separate fix (not a conversion).

Verification:

  • yarn tsc --noEmit from the repo root: clean after every commit.
  • yarn tsc from the repo root: clean after every commit.
  • cd packages/lib && yarn jest mimeUtils: 6/6 (post mime-utils-types conversion).
  • cd packages/lib && yarn jest synchronizer.basics.test: 24/24 (baseline check after database-driver-node conversion).
  • MAX_TIME_DRIFT=0 yarn syncFuzzer start --steps 5 from the repo root: 5/5 with clean cleanup, after the Cache caller fix (6b50d7193). MAX_TIME_DRIFT=0 was needed because of a local device-clock drift unrelated to the conversion.

Notes / review-later.md entries added during this round:

  • packages/lib/utils/types/pdfJs.js — compiled output of pdfJs.ts is tracked in git (present in .gitignore + .eslintignore AUTO-GENERATED block but still in the index). git rm --cached packages/lib/utils/types/pdfJs.js packages/lib/utils/types/pdfJs.js.map would clean it; separate from any conversion.
  • packages/lib/react-logger.ts — class has no body and no callers in the repo. Deletion candidate.
  • packages/lib/lib/lib.ts — 7-line empty // TODO function stub with no callers. Deletion candidate.
  • packages/lib/services/PluginManager.ts pluginDialogToShow — returns a Dialog: unknown that desktop's WindowCommandsAndDialogs.tsx casts to React.ComponentType<Record<string, unknown>> to render. The right long-term fix is to add a generic-typed Dialog parameter (or refactor away the legacy plugin system as the existing TODO comment in that file already notes).
  • packages/lib/components/shared/dropbox-login-shared.ts — host-component parameter stayed any to avoid a fragile React-types shim; revisit if DropboxApi.js is converted so the local DropboxApi interface can be removed.