Skip to content

refactor(lib): stop exporting 23 symbols that never leave their module - #444

Merged
PathGao merged 1 commit into
masterfrom
chore/delete-unreferenced-exports
Aug 3, 2026
Merged

refactor(lib): stop exporting 23 symbols that never leave their module#444
PathGao merged 1 commit into
masterfrom
chore/delete-unreferenced-exports

Conversation

@PathGao

@PathGao PathGao commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

This fixes no bug. Nothing here changes behaviour, output, or performance. 23 declarations lose the export keyword and keep everything else. The benefit is that a reader opening one of these modules sees an interface that reflects what the module actually promises, instead of one where two thirds of the surface is noise.

npm test 565/565 (unchanged from 6b58cd5), npm run check 637 files / 0 errors, npm run build clean. No Rust touched.

The starting list was right about references and wrong about what follows

The candidates came from a scan for exported symbols in src/lib/**/*.ts with no consumer outside their defining file — 49 of them, after setting aside 29 that only scripts/ consumes. Re-checked here across src/, scripts/, src-tauri/, and the root config files: all 49 do have zero external references. That part held.

What did not hold is the conclusion. The scan excluded the defining file from its own search, so "no external reference" was being read as "dead". Printing every in-file occurrence of all 49 settles it:

Every one of the 49 is used inside its own file. Not one is an orphan. So the delete bucket is empty, and the whole question is export-removal versus keep.

classification count
used inside its own file → export removed, declaration kept 23
used nowhere at all → declaration deleted 0
kept exported, with reason 26

The line

A type keeps export when it is directly the parameter type or the return type of an exported function, or the declared type of an exported constant. That is the module's stated interface: a caller who builds the argument in a separate statement (const ctx: PdfExportContext = {…}) or holds the result in a typed field has to be able to name it, and no importer existing yet is not evidence that none should.

A type loses export when it is only reachable inside such a type — a field of an options bag — or appears only in value positions internal to the module. Structural typing and inference cover those; a caller never has to write the name. A function loses export when every call to it is in its own file.

That rule decides all 49 without a tie-break, and re-exporting any of them later is a one-word change.

export removed (23)

symbol file why it is internal
SUPPORTED_LANGUAGES stores/settings.svelte.ts value position only — feeds SUPPORTED_LANGUAGE_CODES
SUPPORTED_LANGUAGE_CODES stores/settings.svelte.ts value position only — feeds isSupportedLanguage
isSupportedLanguage stores/settings.svelte.ts one call, :751, validating the persisted language key
loadPersistedSettings stores/settings.svelte.ts two calls, both in this file (:343, :423)
installPersistedSettings stores/settings.svelte.ts one call, :435
UpdatePhase stores/update.svelte.ts type of a field on a non-exported class; UpdateDialog.svelte compares updateStore.phase to string literals and never names the type
ErrorSource stores/update.svelte.ts same
EditorToolbarGroup utils/editorToolbar.ts field of EditorToolbarTool
EDITOR_TOOLBAR_TOOLS utils/editorToolbar.ts value position only — callers go through getEditorToolbarTools() and DEFAULT_EDITOR_TOOLBAR_ORDER
KatexFamilyRule utils/exportFonts.ts return type of parseKatexFamilyRules, which is itself internal
parseKatexFamilyRules utils/exportFonts.ts one call, :109, inside collectUsedKatexFamilies
collectClassNames utils/exportFonts.ts one call, :106, same
bytesToBase64 utils/exportFonts.ts one call, :247, inside fetchFontDataUrls
FrontMatterValueKind utils/frontMatter.ts field of FrontMatterField
parseFrontMatterTagInput utils/frontMatter.ts two calls, both in this file
isAbsoluteMarkdownPath utils/markdownLinks.ts one call, :54
MermaidRenderer utils/mermaidPrint.ts field of PrintDiagramContext (which stays exported)
OpenExportedFileLabels utils/openExportedFile.ts optional field of OpenExportedFileDeps (which stays exported)
PASTE_PROBE_CHARACTER utils/pasteContext.ts value position only, :122
PasteContextTokenizer utils/pasteContext.ts field of PasteCaretContext (which stays exported)
isCodeAtOffset utils/pasteContext.ts one call, :156
TabFileActionId utils/tabFileActions.ts field of TabFileAction (which stays exported)
TITLEBAR_TOOLBAR_ACTIONS utils/titlebarToolbar.ts value position only — callers go through getTitlebarToolbarActions() and the two DEFAULT_TITLEBAR_TOOLBAR_* constants

Kept exported (26)

24 of them are the vocabulary of a module's public interface — the shape a caller has to name to build an argument or hold a result:

symbol the exported signature it appears in
ExternalChangeOutcome return of resolveExternalChange, which createDocumentSession hands back (documentSession.svelte.ts:630)
PersistedSetting param of two helpers and return of createSettingsPersistence()
EditorToolbarMove param of applyEditorToolbarMove, return of getEditorToolbar{Reorder,Adjacent}Move
ExportHtmlResult return of exportAsHtml
PdfExportContext param of exportAsPdf
ExportDocumentInput param of buildExportDocument
KatexFontFace return of findKatexFontFaces
PrintDiagramContext param of renderDiagramsForPrint
ExportedFileFormat param of askToOpenExportedFile
OpenExportedFileResult return of askToOpenExportedFile
OpenExportedFileDeps param of askToOpenExportedFile
PasteCaretContext param of shouldLinkifyPastedUrl
PathIdentity params of isSameFilePath
LineRange return of parseSourceposLineRange
AnchorNode param of findAnchorElement
AnchorMatch return of findAnchorElement
RenderRichContentOptions param of renderRichContent
TabFileAction return of getTabFileActions
FileHistoryState params and returns across six exported functions
FileHistoryNavigationState params of replaceCurrentHistoryEntry, navigateFileHistory
FileHistoryNavigationResult return of goBackInHistory, goForwardInHistory
TitlebarToolbarAction return of getTitlebarToolbarActions
TitlebarToolbarMove param of applyTitlebarToolbarMove, return of getTitlebarToolbar{Reorder,Adjacent}Move
ConfiguredTitlebarToolbarIds return of getConfiguredTitlebarToolbarIds
DefaultFonts declared type of the exported DEFAULT_FONTS, which Settings.svelte reads

PersistedSetting is the clearest of these. scripts/settingsPersistence.test.ts:100 needs that type and cannot import it, so it writes ReturnType<typeof createSettingsPersistence>[number] instead. The export is doing work even with no import naming it.

The 26th is TOC_WIDTH_RANGE, kept for a different reason — see below.

Two things found while classifying

settings.svelte.ts carries a second, stale copy of the language catalogue. The group of 8 the scan flagged in that file is not one feature's leftovers; it is two unrelated things, and only the language half is interesting. SUPPORTED_LANGUAGES (:55) duplicates getSupportedLanguages() in utils/i18n.ts:33 — same 26 entries, same code/name/nativeName shape — and settings.svelte.ts:27 duplicates i18n.ts:1's LanguageCode union. The live one is i18n.ts: Settings.svelte:1166-1168 renders the language <select> from it.

The two copies have already drifted. For pt, i18n.ts says Portuguese / Português; settings.svelte.ts says Portuguese (European) / Português (Europeu). Nothing catches that, because the settings copy is only consumed for its code column — .map((entry) => entry.code) on the next line. Its name and nativeName are never read by anything.

So the chain is live but only one column deep: SUPPORTED_LANGUAGESSUPPORTED_LANGUAGE_CODESisSupportedLanguage → validating the persisted language key at :751. Un-exporting is correct and is all this PR does. Deleting the duplicate — or deriving the codes from i18n.ts — is a real change with a real reason and belongs in its own PR.

TOC_WIDTH_RANGE is unreferenced because its consumer re-typed the numbers. It is one of five sibling NumericSettingRange constants; the other four are imported and consumed by Settings.svelte. NumericSettingRange's own doc comment (settings.svelte.ts:262-266) states the contract: "Single source of truth: the settings UI renders min/max/step from it and every write path clamps against the same object."

The TOC width control is the drag handle in MarkdownViewer.svelte, not a settings slider, and it does not import the range. It declares its own:

const TOC_MIN_WIDTH = 180;   // MarkdownViewer.svelte:267
const TOC_MAX_WIDTH = 420;   // MarkdownViewer.svelte:268

Identical to TOC_WIDTH_RANGE's min/max (:209), and used for the clamp at :430, the keyboard jumps at :444/:447, and aria-valuemin/aria-valuemax at :3468/:3469. The store's own setTocWidth clamps against TOC_WIDTH_RANGE as well, so the numbers agree today by coincidence of them being typed twice.

TOC_WIDTH_RANGE therefore keeps its export. Its unreferenced-ness is a defect in the caller, not surplus surface, and un-exporting it would cement the duplication and make the obvious fix start by re-adding the keyword. Not fixed here — importing the range into MarkdownViewer.svelte changes what a wrong number would do, which is more than a visibility PR should carry.

Not done

  • No lint rule or test guards against the surface growing back. Nothing stops the next export with no importer. A scripts/ check in the shape of singleImplementationConvention.test.ts could assert it, but that is a source-text assertion with its own design questions and does not belong in the same change as the removals it would police.
  • The 29 symbols referenced only from scripts/ are untouched, as intended. Removing export there would delete executable tests, which is the wrong trade in this repo.
  • .svelte files were not scanned for their own unreferenced exports — only src/lib/**/*.ts was in scope.
  • SUPPORTED_LANGUAGES's unread name/nativeName columns are left in place. Deleting data from a declaration is a different action from narrowing its visibility, and the whole constant is a duplicate that wants resolving as one decision.
  • Verification is the compiler, not a behaviour run. npm run check covers src/ and scripts/ (637 files, 0 errors) and would name any broken reference; npm test is unchanged at 565 because none of these 23 were reachable from a test. The app was not launched — for a change that only removes a keyword from a declaration, there is nothing at runtime for it to observe.

🤖 Generated with Claude Code

Fixes no bug. Every one of these compiles, runs, and behaves exactly as
before -- the only change is that a reader opening one of these modules
now sees an interface that matches what the module actually promises.

23 declarations lose the `export` keyword. None are deleted: all 23 are
called or referenced inside their own file, so the declaration stays and
only its visibility narrows.

The line drawn here: a type keeps `export` when it is directly the
parameter type or the return type of an exported function, or the
declared type of an exported constant -- a caller who builds the argument
in a separate statement, or holds the result in a typed field, has to be
able to name it. It loses `export` when it is only reachable inside such
a type (a field of an options bag), or appears only in value positions
internal to the module. A function loses `export` when every call to it
is in its own file. 26 of the 49 candidates are kept on that basis.

No renames, no reordering, no adjacent cleanup. `npm test` 565/565
unchanged, `npm run check` 637 files / 0 errors, `npm run build` clean.
No Rust touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@PathGao
PathGao merged commit a15d54a into master Aug 3, 2026
4 checks passed
@PathGao
PathGao deleted the chore/delete-unreferenced-exports branch August 3, 2026 12:54
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.

1 participant