refactor: 💡 resolve component path redundancy and public API encapsulation#798
refactor: 💡 resolve component path redundancy and public API encapsulation#798
Conversation
hoorayimhelping
left a comment
There was a problem hiding this comment.
I'm still quite against barrel files and I don't think we should be using them, even in a library. IMO, the downsides far outweigh the benefits. https://tkdodo.eu/blog/please-stop-using-barrel-files
…ts types chore: 🤖 generate exports, e.g. expose direct component imports and its types
|
Claude finished @punkbit's task in 2m 49s —— View job Code ReviewGreat PR! The goals here are well-motivated — eliminating redundant import paths, fixing circular dependencies, and tightening the public API surface are exactly the right kind of library hygiene. The benchmarks and LSP demos are excellent supporting evidence. A few things worth flagging: 🚨 Must Fix
💡 Suggestions (non-blocking)
✅ What looks good
|
📚 Storybook Preview Deployed✅ Preview URL: https://click-lxqz6w3qe-clickhouse.vercel.app Built from commit: |
…ation (#798) * chore: 🤖 update required packages * chore: 🤖 provide styled components types * fix: 🐛 linter should ignore d.ts files * fix: 🐛 use double quotes * fix: 🐛 vite > v2 separates vitest config * chore: 🤖 reorder package * chore: 🤖 remove side property (deprecated) * fix: 🐛 dropdown amends * fix: 🐛 theme prop * fix: 🐛 type * chore: 🤖 TIAS build version supported on next v16 RSC * chore: 🤖 WIP ongoing styled-component v6.1.11 (non experimental) support * chore: 🤖 strict react version for dev * fix: 🐛 missing ref on forwardRef, might have plenty of these * chore: 🤖 update lockfile * chore: 🤖 remove optional flag from ref (typo) * chore: 🤖 add comment for future ref * refactor: 💡 banner * chore: 🤖 lint amends (double quotes) * refactor: 💡 removes style prop as typed prop * chore: 🤖 remove ajv * chore: 🤖 format * test: 💍 add aria pressed to ButtonGroup * chore: 🤖 format * test: 💍 use local getByText * chore: 🤖 update lockfile * chore: 🤖 add changeset * chore: 🤖 small text amend to trigger vercel deploy * chore: 🤖 prevent running on CI * chore: 🤖 add HUSKY to preven husky runnig pre-commit hook * fix: 🐛 conflict resolution * chore: 🤖 bump rc number * docs: 📝 build esm, how to use * chore: 🤖 ESM vite builder (wip) * fix: 🐛 remove .tsx extension from import statements * fix: 🐛 remove .tsx extension from import statements * fix: 🐛 remove .tsx extension from import statements * fix: 🐛 remove .ts extension from import statements * fix: 🐛 remove .ts extension from import statements * chore: 🤖 add eslint to assess import extensions not required * chore: 🤖 format * chore: 🤖 temporary custom resolve tsconfig path * refactor: 💡 export from correct theme boundary * chore: 🤖 node externals in vite, remove alias * chore: 🤖 use relative paths * chore: 🤖 use externalize deps * chore: 🤖 for ESM compatibility, tweak/handle CJS components * chore: revert ts alias rewrite to relative * chore: lint do not allow barrel imports * chore: remove excludes from tsconfig * chore: set vite settings to preserve file struct in output * fix: solve import cycles * fix: solve import cycles in stories * fix: build amends * fix: add .js extension * chore: analyze and visualise bundle * chore: split ESM, CJS distribution * chore: format * fix: 🐛 lint code block * fix: 🐛 import Separator * chore: format * fix: 🐛 import Separator * chore: 🤖 add changeset * chore: 🤖 use 0.0.251-rc.62 * chore: 🤖 resolve conflict resolution, deleted files which were removed in main branch * chore: 🤖 resolve conflict resolution, middle truncator * chore: 🤖 resolve conflict resolution, missing container changes * refactor: 💡 FileMultiUpload to follow FileUpload due to middle truncator * fix: 🐛 prevent icon success pushed right * fix: 🐛 remove file size from multiple file upload * chore: 🤖 merge conflict amend for ButtonGroup * chore: 🤖 remove comment * refactor: 💡 reduce import path redundancy (WIP, pt1) * refactor: 💡 accordion as index * refactor: 💡 rename component by directory name to index * refactor: 💡 update import statements to prefer index * chore: 🤖 remove old indexes (this was a failed attempt, which imported the reduntant name) * fix: 🐛 import statements * fix: 🐛 import statements * fix: 🐛 import statements * fix: 🐛 icon names in types * chore: 🤖 add note * chore: 🤖 format * fix: 🐛 icon names location * fix: 🐛 icon names location * chore: 🤖 update changeset * refactor: 💡 use component level barrel, to allow devs see component name on editor * fix: 🐛 import statements * chore: 🤖 add eslint to prevent imports from index and request use of leafs when possible * fix: 🐛 dayjs version * fix: 🐛 merge from main,m incorrectly changed * chore: 🤖 prepare merge main * fix: 🐛 merge conflicts * refactor: 💡 further paths pass * chore: 🤖 add note * chore: 🤖 format * refactor: 💡 further paths pass for AutoComplete * fix: 🐛 merge conflict * refactor: 💡 further paths pass for Collapsible * fix: 🐛 merge conflict * fix: 🐛 file extension * chore: 🤖 add hmr benchmark * chore: 🤖 make component build name index to remove redundancy * chore: 🤖 generate exports, e.g. expose direct component imports and its types chore: 🤖 generate exports, e.g. expose direct component imports and its types * perf: ⚡ benchmark hmr deep nested components * chore: 🤖 link @clickhouse/click-ui to itself, required for benchmark * perf: ⚡ generate component exports, e.g. speedy component and type access * refactor: 💡 remove unused re-export files * chore: 🤖 update changeset * chore: 🤖 remove comments * test: 💍 update test (due to merge issues) * fix: 🐛 error TS2724: './IconButton' has no exported member named 'IconButtonSize'.
Why?
To resolve component path redundancy and allow public API encapsulation.
As work progressed on reducing import path verbosity, several deeper issues surfaced that were addressed as part of this PR. Component import statements previously required the component name twice, e.g. clickhouse/click-ui/components/EllipsisContent/EllipsisContent, which was unnecessary. Beyond that, the original version exposed internal implementation details, allowing consumers to directly access and depend on third-party APIs such as Radix UI components and types. This has led to applications incorrectly coupling themselves to these internals rather than the library's intended public API, a problem that now requires careful, incremental cleanup using @deprecated warnings.
While addressing the above, circular dependencies were discovered throughout the source code. These were not anticipated but were resolved as part of this PR, and new ESLint rules have been introduced to prevent them from reappearing as the library grows.
Finally, after #773 (distribute unbundled) was merged, which solved critical distribution size issues, could now confirm that tree-shaking works correctly under the revised conditions and both import strategies, e.g. top-package level and component-level.
API improvements
@clickhouse/click-ui/components/EllipsisContent/EllipsisContent🤢@deprecatedwarnings to remove them gradually! The PR addresses this by encapsulating these details, ensuring only the deliberate public API surface is accessible.Build output size improvements
The original production version of the Click UI library had a critical bundling issue, producing a build output of 1,216.21 kB with chunks exceeding the 500 kB threshold after minification.
To benchmark the improvements, a baseline Vite app without Click UI was measured at 193.30 kB. After integrating the updated PR version of Click UI, the results were as follows:
Importing a component via the main barrel file / public API produced a build output of 223.70 kB, an overhead of just ~30 kB over the baseline. Importing directly from the component-specific export path (e.g. @clickhouse/click-ui/Button) brought this down marginally further to 223.09 kB.
Both approaches represent a dramatic reduction from the original, with the PR version adding less than 30 kB over a bare Vite app regardless of import strategy.
This is made possible by several changes to resolve component paths and, of course, by the introduction of #773, which makes the package distribution unbundled and moves optimisation responsibility to the consumer side. Before, the consumer always had an unscalable bundled/unoptimizable 😬 package of 1,216.21 kB.
👇 Read below for supporting evidence
❌ The original production version (build output size 1,216.21 kB, plus chunks are larger than 500 kB after minification)
🔎 Created a base to demonstrate the performance improvements, e.g. a Vite app without Click UI (build output size 193.30KB)
The base, e.g. a Vite app shows a placeholder component (build output size 193.30KB)
👌 Vite app with Click UI PR Version imports a Click UI Component from main barrel file / Public API (build output size is 223.70 kB)
👌 Vite app with Click UI PR Version imports a Click UI Component directly from component path (build output size is 223.09 kB)
🤖 TODO: Once #773 is merged, switch base branch to main.
How?
import/no-cycle, and prevent import from barrel filesPreview?
Distribution (to keep it short shows a small example for ESM Button), if you wante more see #773
LSP direct to source or goto implementation is unaffected, e.g. you don't land in barrel but directly in the source code
Modal/terminal-based text editors
demo-lsp-editor-direct-source.mov
editor-goto-identifier.mov
VSCode-like text editors
demo-goto-source-vscode.mov
ESLint rules
Warns against importing from the main barrel within the library itself, guiding contributors towards importing directly from leaf modules instead. Also, component-level index files are reserved for cross-component imports. These rules help prevent circular dependencies from forming.
Benchmarks
To address any concerns around the use of module re-exports and show responsible and safe use of barrel files, benchmark results are available below:
NOTE: Have omitted markers to keep it short. The benchmark file can be run in your local environment and modified to your liking. Find them at here