Repository navigation
⬆️ chore: Upgrade Click UI, i18next, TypeScript, Vitest and the Dev Tooling to Their New Majors - #194
Open
danny-avila wants to merge 8 commits into
Open
danny-avila wants to merge 8 commits into
danny-avila wants to merge 8 commits into
Conversation
i18next 25.10.10 -> 26.5.10 and react-i18next 16.6.6 -> 17.1.8, resolved with
`--minimum-release-age=86400` like the rest of the refresh, so i18next 26.5.11 and react-i18next
17.1.9 (both from the last day) stay out. react-i18next 17 needs i18next >= 26.2.0, so the two
move together. i18next no longer depends on @babel/runtime, and react-i18next's html-parse-stringify
moves to 4.0.1, which drops void-elements from the tree. i18next-browser-languagedetector 8.2.2
has no i18next peer and works unchanged.
Nothing in the app needed to change. i18next 26 removes initImmediate, the legacy
interpolation.format function, showSupportNotice and simplifyPluralSuffix, none of which
src/locales/i18n.ts sets. The console support notice goes with it: every page load printed the
Locize notice in the browser console before. react-i18next 17's one breaking change is the key
<Trans> generates for kept HTML nodes, and the panel does not use <Trans>.
src/locales/i18n.test.ts renders every key of translation.json through the app's i18n instance
with a value for each {{placeholder}} and expects its own text, so no key can fall back to its id
or come out escaped. It also checks that each _zero/_one/_other base picks the right form for
0, 1, 2 and 21, and that useLocalize interpolates and pluralizes. The test passes on i18next 25 /
react-i18next 16 as well, and fails with escapeValue: true or with empty resources.
Click UI 0.9.1 -> 0.15.0, read through every release in between (0.10.0, 0.11.0, 0.12.0, 0.13.0, 0.14.0, 0.15.0). None of the API breaks reach this app, so no component code changes: - 0.10 drops styled-components and wraps all Click UI CSS in one `clickui` cascade layer. The app never imported styled-components itself (no ThemeProvider bridge, linkStyles or useTheme), so it leaves package.json and the Vite optimizeDeps list, and @emotion/*, stylis, camelize and css-to-react-native leave the tree. styles.css loads first, so the layer order is properties, theme, base, components, utilities, clickui in the build and in `vite dev`: Click UI still beats Tailwind utilities and preflight as its unlayered styles did, and the unlayered rules in styles.css (focus rings, switch and dark-accent tokens) still beat Click UI. Every --click-* token the app overrides still exists in 0.15. - 0.11 bumps Radix to the 1.6.7 set. The react-dialog 1.3.1 and dismissable-layer 1.2.1 override pins still leave one dismissable-layer; react-popper now has one copy instead of two, and the focus-scope/focus-guards copies nested under menu and popover are the same split the tree had before. IconButton, Toast role and Table header changes touch nothing the app renders or tests. - 0.13's DropdownItemProps union, FileTab/Dropdown.Sub prop types and token semicolons, and 0.15's Tooltip.Trigger asChild forwarding, are unused here. - 0.13 stops IconWrapper stacking a menu item's icon above its label at 768px and below, so the styles.css rule that forced those rows back to one line goes. The dialog close-button naming and the Dropdown.Trigger aria-expanded workarounds stay: 0.15 still renders the close button unnamed and still puts aria-expanded on the trigger's wrapper. Visible change: 0.15 keeps Dropdown menus 8px from the viewport edge instead of 100px, so the user menu opens flush with the left edge (over the icon rail) and, on a phone, row action menus are wider and wrap less. Checked against the 0.9.1 build on a live stack, light and dark at 1440 and 390 wide (login and its error, configuration tabs, scope selector, endpoint drawer and its select, row action menu, preview drawer, provenance hover card, review dialog, discard toast, Import YAML, access roles/groups, role dialog tabs, delete confirmation, create role, grants and capability dialog, audit log, date picker, command menu, user menu): 156 of 174 screenshot pairs are pixel-identical; the rest are those menus and audit-log loading timing. Dropping the menu-row rule changed no pixel at either width. The production client bundle grows by 62 kB gzipped, mostly new logos and flags in the icons chunk. lucide-react ^0.545.0 -> ^1.61.0. Nothing in src imports it; v1 drops brand icons and sets aria-hidden by default, neither of which applies. Resolved with `bun install --minimum-release-age=86400`; bun.lock keeps lockfileVersion 1. The .claude click-ui skill no longer tells agents to extend components with styled-components, and its examples only use Tailwind for properties the clickui layer leaves unset, since a utility can't override one Click UI sets. The ESLint plugin's comment on the disabled require-css-import rule now gives the CSS Modules reason.
…ypeScript 7 Compiler TypeScript 7.0.2 is not adopted: typescript-eslint 8.72 peers on typescript >=4.8.4 <6.1.0 and its parser throws "typescript-eslint does not support TS 7.0" at load, because 7.0 ships no JS compiler API. 6.0.3 is inside that range and is the step toward 7: - Drop baseUrl from tsconfig. 6.0 rejects it as deprecated (TS5101) and 7.0 removes it (TS5102); paths already resolve from the tsconfig folder. - Annotate the CSRF middleware in start.ts. Under 7.0's stable type ordering (and 6.0 with --stableTypeOrdering) its inferred type cycles through the generated Register config (TS7022/TS7024). The built client and server bundles are byte-identical to the 5.9 build apart from the start manifest's absolute paths.
jsdom 30 computes box-sizing ("content-box"; 27 returned an empty
string), so react-textarea-autosize now mounts its hidden measuring
textarea in document.body and keeps it there between tests. A
ListField test that read the first textarea in the document got that
one (value "x") instead of the stop sequence; it and the test beside
it now find the item's textarea by its accessible name.
Vitest 5 needs Node 22.12+, jsdom 30 Node 22.22.2+ and jest-dom 7
Node 22+; the ubuntu-latest CI image ships Node 22.23.3.
Version 6 drops its Babel pipeline and leaves the React Refresh transform to Vite 8's Oxc. The panel passes the plugin no Babel options, and the production client bundle is byte-identical to the one 5.2 builds.
Version 17 needs Node 22.22.1+ and Git 2.32+, and makes yaml an optional dependency. .lintstagedrc.json stays JSON, so nothing else changes.
… Piping Off devtools-vite 0.8 pipes server console output into the browser and browser console output back to the dev server by default. Vite 8 forwards browser warnings and errors to the terminal whenever it runs under a coding agent, so the two features feed each other: a single server warning on the login page re-logs itself with a growing [Server] prefix until the tab closes. 0.3.12 had no console piping, so turning it off keeps the dev server as it was. 0.8 also moves its event bus to an OS-assigned port when the default one is taken, where 0.3.12 crashed `vite dev` with EADDRINUSE on port 42069 whenever a second dev server was already running. Production builds still strip the devtools from __root.tsx, and the client bundle is byte-identical to 0.3.12's.
…4 Reports - dotenv ^17.4.2 -> ^18.0.9. Nothing imports it (Bun and Vite load .env themselves), so the bump changes no runtime path. - @types/node ^22.20.5 -> ^26.6.5. The image runs on oven/bun:1.4.3, whose process.version is v26.3.0, and bun-types takes @types/node "*"; the types now describe the Node API the production runtime reports. Its undici-types moves to 8.9.0 and stays a single copy. Left on their current majors: - js-yaml 4: librechat-data-provider keeps js-yaml 4 in the client chunk, so 5 would ship both parsers (+17 KB gzip), would need the global js-yaml 4.3.2 security override dropped, and needs a compat layer to read and write YAML as LibreChat's own js-yaml 4 does. - mongoose 8: @librechat/data-schemas (0.0.74 and the latest 0.0.77) peers on mongoose ^8.24.1, as LibreChat itself does; the panel only lists it to pin the patched 8.24.x and never loads it at runtime.
Contributor
Author
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Chef's kiss. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
7 tasks done
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.
Pull Request Template
Summary
Takes the major upgrades that the in-range refresh left out: Click UI 0.15, i18next 26 / react-i18next 17, TypeScript 6, Vitest 5, jsdom 30, jest-dom 7,
@vitejs/plugin-react6, lint-staged 17,@tanstack/devtools-vite0.8, dotenv 18, lucide-react 1.x and@types/node26.styled-componentsleaves the tree, because Click UI no longer uses it.TypeScript 7, js-yaml 5 and mongoose 9 are not upgraded; Not upgraded says why.
Stacked on #189 (the in-range refresh, branch
pr/deps-refresh). This PR targets that branch and is built on its head, 4e129c9, so it only adds the eight commits below. Merge #189 first. If #189 is squash-merged, rebase this branch ontomain(git rebase --onto main 4e129c9) instead of only retargeting the PR, or #189's two commits come back with it.There are eight commits, one for each upgrade or group of packages that have to move together. Every commit installs with
bun install --frozen-lockfileand passestscon its own, so each can be reviewed or reverted separately:styled-components)@vitejs/plugin-react6@tanstack/devtools-vite0.8, with its console piping kept off@types/node26What moved
@clickhouse/click-uistyled-componentslucide-reacti18nextreact-i18nexttypescriptvitestjsdom@testing-library/jest-dom@vitejs/plugin-reactlint-staged@tanstack/devtools-vite(and its override pin)dotenv@types/nodeSource and config changes are small:
src/start.ts: one type annotation.tsconfig.json:baseUrlremoved.vite.config.ts:styled-componentsremoved fromoptimizeDeps, the devtools option added, and the file Prettier-formatted for the first time.src/styles.css: one Click UI workaround removed..claude/skills/click-ui.mdrewritten so it no longer recommendsstyled-components, and its examples only use Tailwind for properties Click UI leaves unset.tools/eslint-plugin-click-ui/index.js: the comment on the disabledrequire-css-importrule now gives the CSS Modules reason instead ofstyled-components.i18next 26 and react-i18next 17
react-i18next 17 requires i18next 26.2.0 or newer, so the two move together.
Breaking changes and how they were handled:
initImmediate, the legacyinterpolation.formatfunction,showSupportNoticeandsimplifyPluralSuffix.src/locales/i18n.tssets none of them, so no app code changes.showSupportNoticegone, the Locize notice that i18next 25 printed to the browser console on every page load is gone too. "locize" no longer appears indist/client.<Trans>generates for kept HTML nodes. The panel doesn't use<Trans>.@babel/runtime.html-parse-stringifymoves to 4.0.1, which dropsvoid-elements.New test
src/locales/i18n.test.tshas three cases:translation.jsongoes through the app's own i18n instance, with a value for each{{placeholder}}, and must come out as its own text: never its id, never escaped._zero/_one/_otherbase picks the right form for 0, 1, 2 and 21.useLocalizeinterpolates and pluralizes.The test also passes on i18next 25. It fails if
escapeValueis set totrueor the resources are emptied.Both packages now accept TypeScript
^5 || ^6 || ^7as an optional peer.Click UI 0.9.1 → 0.15.0
I went through every release in between (0.10 to 0.15). None of the API breaks reach this app, so no component code changes:
styled-componentsand puts all Click UI CSS in one@layer clickui.styled-componentsitself: no ThemeProvider bridge,linkStylesoruseTheme. It leavespackage.jsonand the ViteoptimizeDepslist, and@emotion/*,stylis,camelizeandcss-to-react-nativeleave the tree.vite devis properties, theme, base, components, utilities, clickui. So Click UI still overrides Tailwind's preflight and utilities, as its unlayered CSS did before, and the unlayered rules instyles.css(focus rings, switch and dark-accent tokens) still override Click UI. Every--click-*token the panel overrides still exists in 0.15.clickuicomes afterutilities, a Tailwind class can't override a property Click UI sets. On this branch's build,grid grid-cols-2on aTabs.TriggersListstill computes todisplay: flex. The skill doc's Tabs example relied on that override; it now splits the row withflex-1on each trigger (two 200px triggers in a 400px list) and says to use an unlayered rule instyles.cssfor anything Click UI already sets.react-dialog1.3.1 anddismissable-layer1.2.1 override pins still leave a singledismissable-layer, andreact-popperdrops from two copies to one.DropdownItemPropsunion,FileTabandDropdown.Subprops, andTooltip.TriggerasChildforwarding.styles.cssrule that forced those rows back onto one line is removed. At 390 and 1440 px wide, removing it changed no pixel.useDialogCloseLabelsanduseStripAriaExpanded. 0.15 still renders the dialog close button without a name and still putsaria-expandedon the trigger's wrapper.vite devon this branch: properties, theme, base, components, utilities, clickui, the same order as the build.lucide-react 0.545 → 1.61
Nothing in
srcore2eimports it. v1 removes the brand icons and setsaria-hiddenby default, neither of which matters here. It could be removed outright in a follow-up.TypeScript 5.9 → 6.0.3
baseUrlis deprecated. TypeScript 6 fails on it with TS5101, and 7 removes it (TS5102). It is removed fromtsconfig.json.pathsalready resolve@/*from the tsconfig folder.vite-tsconfig-pathsin Vitest still resolve@/.src/start.tsannotates the CSRF middleware asReturnType<typeof createCsrfMiddleware>.Registerconfig inrouteTree.gen.ts. TypeScript 7's stable type ordering, and 6.0 with--stableTypeOrdering, report it as TS7022/TS7024.tsc --noEmit --stableTypeOrderingpasses on this branch.typescript-eslint8.72 accepts TypeScript>=4.8.4 <6.1.0, so 6.0.3 is inside its range.Vitest 4 → 5
Vitest 5 needs Node 22.12+ and Vite 6.4+. These breaking changes could have affected this suite, and none needed a code change:
expectthat is never awaited now fails its test.sequentialoption is removed..vitest/.155 files and 3,341 tests pass with no unhandled errors.
Vite still warns that
vite-tsconfig-pathsinvitest.config.tsis no longer needed. #189's branch already shows the same warning.jsdom 27 → 30
undici8.12.3 comes back into the tree, as a dev-only dependency of jsdom. ⬆️ chore: Refresh Dependencies Within Their Majors and Clear the Fixable Audit Findings #189 dropped the old 7.29.1 override because nothing used it then.cssstylewith jsdom's own CSSOM.getComputedStylenow reports a textarea'sbox-sizingascontent-box, where 27 returned"".react-textarea-autosizenow leaves its hidden measuring<textarea>indocument.bodybetween tests.ListFieldtest incollections.test.tsx, "keeps whitespace-only stop sequences", read the first textarea in the document, got that hidden one and failed (expected'\n', got'x'). It and the test beside it, which read its first value the same way but still passed, now find the item's textarea by its accessible name.^22.22.2 || ^24.15.0 || >=26. CI'subuntu-latestimage ships Node 22.23.3.jest-dom 6 → 7
7.0 makes
@testing-library/doma required peer, and it is already a direct devDependency (10.4.2). It also requires Node 22 or newer. 7.0.1 makes Vitest an optional peer. Nothing else changes.@vitejs/plugin-react 5 → 6
Version 6 drops Babel. React Refresh is now done by Vite 8's Oxc, and the plugin's
babeloption is gone.vite.config.tspasses no Babel options, so nothing changes.Built on its own, this commit produced a production client bundle byte-identical to the one 5.2 builds.
On this branch's
vite dev, I editedToolbarFilter.tsxand then reverted it. Both changes hot-reloaded without a page reload, and the filter text and awindowmarker survived.lint-staged 16 → 17
Version 17 needs Node 22.22.1+ and Git 2.32+. It makes
yamloptional, which doesn't matter because the config is.lintstagedrc.json. After running its tasks it re-stages the files withgit update-index --againinstead ofgit add.Tried directly when the upgrade was made:
eslint --fixand re-staged.@tanstack/start-server-coreimport failed the commit, and the original state was restored.@tanstack/devtools-vite 0.3.12 → 0.8.5
This bumps the devDependency and its override pin together.
Console piping is kept off. 0.8 pipes the server console into the browser and the browser console back to the dev server, both by default. Vite 8 forwards browser warnings and errors to the terminal whenever it runs under a coding agent, so the two feed each other:
[checkOpenIdFn]warning on/loginre-logged itself with a growing[Server]prefix.vite.config.tsnow callsdevtools({ consolePiping: { enabled: false } }). 0.3.12 had no console piping, so this keeps the dev server as it was.0.8 also moves its event bus to an OS-assigned port when port 42069 is taken. 0.3.12 crashed
vite devwithEADDRINUSEwhenever another dev server held that port.Production builds still strip the devtools from
__root.tsx, and no devtools code is left indist/client.Checked on this branch's
vite devwhile running under a coding agent:data-tsd-source.[Server]loop.dotenv 17 → 18
18 removes
.env.vaultsupport and preloading with-r dotenv/config, writes its injection message to stderr, and adds a CLI. Nothing in the repo imports or preloads dotenv: Bun and Vite read.envthemselves. Like lucide-react, it could be removed in a follow-up.@types/node 22 → 26
The Docker image runs
oven/bun:1.4.3, and itsprocess.versionis v26.3.0, so the types now describe the Node API the production runtime reports.undici-typesmoves from 6.21.0 to 8.9.0, andbun-types(which takes@types/nodeandundici-typesat*) uses the same single copy.server.ts, whichtsconfig.jsonexcludes, type-checks againstbuntypes and@types/node26.Lockfile
bun.lockstays at"lockfileVersion": 1, the same asmainand #189.bun installfrom the previous commit's lockfile, never edited by hand. The resolved versions are the ones each upgrade was tested with.bun installkept a stale nestedbun-types/undici-types@6.21.0beside the new 8.9.0. Removing and re-adding@types/bunwith Bun let it resolve again to the single 8.9.0 copy.gitmerged a commit's lockfile cleanly, a plainbun installleaves it unchanged. Regenerating it from the previous commit's lockfile gives the same file, except for the Click UI commit, where Bun would pick lucide-react 1.62.0 over the tested 1.61.0.Not upgraded
TypeScript 7 (7.0.2).
typescript-eslint8.72, the latest release, accepts TypeScript>=4.8.4 <6.1.0. Its parser deliberately throws "typescript-eslint does not support TS 7.0" at load, because 7.0 ships no JavaScript compiler API. There is no 9.x yet, and support is tracked in typescript-eslint#10940.baseUrlandstart.tschanges,tsc7.0.2 passes, and so dovitestandvite build, which don't loadtypescriptat all. Only ESLint fails.typescript-eslintsupports 7."typescript": "npm:@typescript/typescript6@^6.0.2"plus"@typescript/native": "npm:typescript@^7.0.2". With that,tsc,tsc6and ESLint all passed.js-yaml 5. A migration was built and tested with a small compatibility layer: every input js-yaml 4 accepts reads the same, and output is byte-identical for the existing tests and six real configs. It is not adopted for three reasons:
librechat-data-provider0.8.524 keeps js-yaml 4 in the client bundle, so the browser would ship both parsers (+17 kB gzipped).js-yaml4.3.2 security override would have to go, because Bun overrides apply to direct dependencies too.librechat.yamlwith js-yaml 4, so staying on 4 is the simplest way to read and write YAML exactly as it does.Revisit once
librechat-data-providerand LibreChat move to js-yaml 5.mongoose 9.
@librechat/data-schemas, both 0.0.74 and the latest 0.0.77, declares a peer dependency onmongoose ^8.24.1, and so does LibreChat. The panel lists mongoose only to pin the patched 8.24.x and never loads it at runtime: neither built bundle imports it. It stays at 8.24.7 untildata-schemaswidens its range to 9.Unchanged from ⬆️ chore: Refresh Dependencies Within Their Majors and Clear the Fixable Audit Findings #189:
zod3.25.76 underlibrechat-data-provider, and@librechat/data-schemas0.0.74.bun auditreports the same single finding as ⬆️ chore: Refresh Dependencies Within Their Majors and Clear the Fixable Audit Findings #189, GHSA-mrhx-c7xc-m3w5 on that zod 3 copy.Newer patches inside the new ranges aren't taken: i18next 26.5.11, react-i18next 17.1.9 and lucide-react 1.62.0. Each upgrade keeps the version it was tested with, and the next in-range refresh will pick these up.
Follow-ups, not done here:
dotenvandlucide-react.vite-tsconfig-pathswith Vite's nativeresolve.tsconfigPaths.collisionPaddingon itsDropdown.Contentbrings back the old position).server.forwardConsole: false.Change Type
Testing
Each check below exited 0, except
bun audit. After this branch moved onto #189's head and the Click UI commit's skill doc and ESLint comment were corrected, I ran them again on the final head. Four I did not repeat: the cleanrm -rf node_modulesinstall, theserver.tscheck, thebun --bunbuild and the Docker install path. Those ran on the previous head, which differs from this one only in.claude/skills/click-ui.mdand that comment.rm -rf node_modules && bun install --frozen-lockfile. Thereact-movablepatch still applies, and a plainbun installleavesbun.lockunchanged.npx tsc --noEmit, andnpx tsc --noEmit --stableTypeOrdering.server.tstype-checked separately with"types": ["bun"].npx eslint src/ e2e/ --max-warnings 0bun run format:check, plusprettier --checkon every changed file outsidesrc(package.json,tsconfig.json,vite.config.ts,.claude/skills/click-ui.md,tools/eslint-plugin-click-ui/index.js).bun run test:lint-rules: 19 of 19 pass.npx vitest run --maxWorkers=3: 155 files and 3,341 tests pass, with no unhandled errors. That is ⬆️ chore: Refresh Dependencies Within Their Majors and Clear the Fixable Audit Findings #189's 154 files and 3,338 tests plus the new i18n test.bun run build, under Node and underbun --bun(the Docker image builds with Bun). The Docker install path also works:bun install --frozen-lockfile, then--production.bun install --frozen-lockfile, abun install --lockfile-onlythat leavesbun.lockbyte-identical, andtsc --noEmit.bun auditexits 1 on the same single zod 3 finding as ⬆️ chore: Refresh Dependencies Within Their Majors and Clear the Fixable Audit Findings #189.Live click-through
This is the production build (
bun server.ts) against a LibreChat backend with existing admin overrides, at 1440px, and I looked at the screenshots./.25 (librechat.yaml) → 33. I saved, and the value was still 33 after a reload.openAI: streamRate: 33. The page's pointer events come back after it closes..yamlfile withstartBalance: 0x2710andprompts: False. The preview showedStarting credits 110,474 → 10,000and listedpromptsas not importable ("managed with role permissions")./usersredirects to/, as intended.Every server-function call answered 200: 114 GETs and 4 POSTs in the run that saved, and 108 GETs and 2 POSTs in each of the others.
The only console error was an avatar URL pointing at a LibreChat on
:3080that wasn't running. Besides that, one run had a single server-function GET aborted by a page navigation.The Bun-built bundle was served and clicked through the same way.
Playwright e2e (not run by CI)
The whole suite ran against
e2e/mock-backend.mjswith--workers=2: 30 passed, 46 failed, 137 skipped. All 46 failures are theaccess.spec.tsandgrants.spec.tstests that also fail onmain: since #185, the mock doesn't serveGET /api/admin/grants/effective, so those pages show "Could not verify permissions".login.spec.tspasses in full, including the 2FA separator test.I also ran those two specs with #190's mock fix applied locally (not committed): 41 failures, the same count #190 reports for
mainwith that fix.Test Configuration:
bun run build+bun server.ts) withSESSION_COOKIE_SECURE=falseover plain HTTP.dev(f7a56e8), before the Zod v4 merge.Checklist