build(typecheck): check Vue components with vue-tsc against a baseline; fix broken dependency ranges - #2362
Merged
Conversation
The 0.26.46 bump replaced the minor and patch of babel-loader, eslint and jsdom with the app's own, and every bump since has carried them along: "^10.26.60" for eslint, a version npm has never published. The lock file still pins what was installed (10.1.1, 10.10.0, 30.0.1), so a plain build went on working, but npm ci and npm install have to reconcile the two and hang for as long as they look for a release that does not exist. These are the ranges from before that bump. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
…inst a baseline npm run typecheck ran tsc over the stores, services and utilities only: tsc cannot resolve a .vue import, so the components, the larger half of the frontend, were checked against nothing. vue-tsc can, but over the whole of src/ it reports 719 errors in 89 files today, which is more than one change should fix. So the check is a ratchet, the way the psalm baseline is. vue-tsc runs over every .js and .vue file in src/ with the options jsconfig.json already uses (jsconfig.vue.json), and tools/typecheck.mjs compares what it reports with tests/js/typecheck-baseline.json. An error the baseline does not list for that file, or one more of an error than it counts, fails; errors that went away pass with a note to run npm run typecheck:baseline. The baseline keys on file, error code and message, not line and column, so an edit elsewhere in a file does not make old errors look new, and component instance types in messages are elided because they spell out every field and change with each one added. The tsc check is unchanged and still runs first, allowing no errors. CI runs both through the existing npm run typecheck step. The test runs the comparison on recorded output and runs vue-tsc on a component that assigns a misspelt field, to show the mistake is reported and fails the check. Fixes #2306 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
…ery component Every entry point sets t, n, OC and OCA on app.config.globalProperties, and the templates call t() and n() throughout, but nothing told the type checker those properties exist. vue-tsc therefore reported every translated string in a template as a missing property: 280 of the 719 errors in the baseline were that one cause. Vue's ComponentCustomProperties is the interface globalProperties are meant to be declared on. The augmentation is its own module file, because in the script-style globals.d.ts a `declare module 'vue'` would replace the real module rather than add to it. The baseline drops from 719 to 439 errors. Refs #2306 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
Thirteen components kept timers, event handlers, observers and gesture state on `this` by assigning them in mounted() or created() without declaring them anywhere. It works at run time, but nothing knows the fields exist: a misspelt name is a silent new field rather than an error, and vue-tsc reported every use, 117 of the 439 errors in the baseline. Timers, handler functions, observers and unsubscribe functions go in data(), which is where the other components already keep theirs. None of them is rendered, so nothing re-renders when they change, and Vue does not wrap functions, numbers or DOM observers in a proxy, so each reads back as the value that was stored. Two sets are returned from setup() instead, which exposes them on `this` without making them reactive. ZoomableImage's pointer map, gesture and pinch change on every pointermove and nothing renders from them; created() kept them raw on purpose. The composer's Tribute instance is a plain class instance, which data() would wrap in a deep reactive proxy that tributejs' attach() and detach() would then be handed. Tests pin both as unwrapped. The baseline drops from 439 to 322 errors. Refs #2306 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
… code reads The typedefs said less than the server sends, so vue-tsc flagged reads of fields that are really there. ServerData lacked needsAccount, the admin setup checks, the sections, the sensitive-media policy and choice, and the handles the account-creation screen offers, all of which NavigationController puts in the page. Status lacked view_count, which the server sends on the author's own copy only. And the moderation table's rows were typed as nothing at all: its reports prop was a bare Array, so every cell read a property of unknown; they are now ModerationReport, the shape Report::moderationRow() builds, in both components that pass it along. Fields absent from some pages (the public one, a reader who already has an account, anyone who is not an administrator) are optional. The baseline drops from 322 to 284 errors. Refs #2306 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
The settings-order test pinned the last three headings to Migration, Keyboard shortcuts and the account deletion. Authorized apps has since been added between Migration and the shortcuts, so the test failed on every run that got far enough to reach it. What the test is about is the reference list above the one irreversible action, so it now checks exactly those two. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com>
karlitschek
force-pushed
the
build/typecheck-ratchet
branch
from
September 25, 2026 06:55
e6284c7 to
6fef936
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.
What each commit addresses
fix(deps)—package.jsoncarriedbabel-loader ^10.26.60,eslint ^10.26.60andjsdom ^30.26.60since the 0.26.46 bump rewrote their ranges with the app version;eslint@10.26.60does not exist, andnpm cihangs reconciling them with the lock. Restored to the pre-bump ranges (^10.1.1,^10.10.0,^30.0.1), which the lock already pins.npm cinow completes in about 5 s.build(typecheck)— Frontend typecheck excludes all Vue single-file components #2306:npm run typechecknow also runsvue-tscover everysrc/**/*.{js,vue}and compares with a committed baseline (tests/js/typecheck-baseline.json, keyed by file, code and message, not line): new errors fail, removed ones pass with a note to runnpm run typecheck:baseline(tools/typecheck.mjs, tested intests/js/typecheck.test.js).fix(types)—t,n,OC,OCAdeclared on every component (src/types/vue.d.ts): −280 errors.fix(components)— fields components assign in hooks are now declared (13 files;ZoomableImagegesture state and Composer's Tribute stay non-reactive viasetup(), with tests): −117 errors.fix(types)—ServerData,Status.view_countand aModerationReporttypedef: −38 errors.chore(release)— 0.26.62 with the rebuilt bundle (0.26.61 is fix: paging, stale-state, direct-message, nid and remote-media fixes (19 issues) #2361).Baseline: 719 errors in 89 files before, 284 in 81 files after.
Checks run locally
phpunit (6585), vitest (3102), eslint, stylelint,
npm run typecheck(tsc clean; vue-tsc 284, none beyond baseline).AI disclosure
The code, tests and this index were produced with Claude Code (Claude Opus 5.5) and are awaiting the author's review.
Signed-off-byis added by the author.🤖 Generated with Claude Code