Skip to content

core(maintenance)#1654

Open
SutuSebastian wants to merge 15 commits intomainfrom
core/maintenance
Open

core(maintenance)#1654
SutuSebastian wants to merge 15 commits intomainfrom
core/maintenance

Conversation

@SutuSebastian
Copy link
Collaborator

@SutuSebastian SutuSebastian commented Feb 5, 2026

General

  • bump Bun version to 1.3.8
  • upgrade apps/storybook packages
  • upgrade root packages

create-flowbite-react

Description

Change bundler

Changes

  • migrate tsup to tsdown

flowbite-react

Description

Shrink final bundle size from 42 MB to 8.38 MB

Changes

  • drop AST parsers in favor of oxc-parser
  • update packages
  • update plugin/modernjs to the new API

before

Screenshot 2026-02-05 at 12 02 11

after

Screenshot 2026-02-05 at 12 02 03

Summary by CodeRabbit

  • Chores
    • Substantially reduced bundle size from 42 MB to 8.38 MB, resulting in faster downloads and significantly improved performance
    • Updated core dependencies and build tooling to latest stable versions for better compatibility and security
    • Modernized internal build infrastructure and enhanced CLI utilities for improved reliability and developer experience

SutuSebastian and others added 8 commits February 1, 2026 14:56
* shrink bundle size:
- use `oxc-parser` for AST parsing
- remove previous AST related packages
- remove unused packages
- update some packages

* add tests for `compound-components` transformer

* use oxc-parser types

* infer types

* move `removeReactImport` to it's own file, add tests

* dedupe tests

* enhance `addToConfig` to inline/multiline detection and indentation

* infer types
@SutuSebastian SutuSebastian self-assigned this Feb 5, 2026
@changeset-bot
Copy link

changeset-bot bot commented Feb 5, 2026

🦋 Changeset detected

Latest commit: b6d3839

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
flowbite-react Patch
create-flowbite-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Feb 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
flowbite-react Ready Ready Preview, Comment Feb 6, 2026 1:53pm
flowbite-react-storybook Ready Ready Preview, Comment Feb 6, 2026 1:53pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

This PR migrates the CLI tooling from recast/acorn-based AST parsing to oxc-parser, replaces tsup with tsdown as the bundler for the CLI package, updates multiple dependencies including Storybook (10.0.x → 10.2.4) and Bun (1.3.1 → 1.3.8), and refactors utilities like add-to-config, compound-components, and generate-metadata to leverage the new parser while preserving public API signatures.

Changes

Cohort / File(s) Summary
Changelog & Release Notes
.changeset/khaki-rooms-film.md, .changeset/social-papayas-ring.md
Document patch releases: bundle size reduction (42 MB → 8.38 MB), AST parser migration to oxc-parser, and tsup-to-tsdown migration for CLI.
Bun & GitHub Actions
.github/actions/setup/action.yml, package.json
Updated Bun version from 1.3.1 → 1.3.8 in GitHub Actions setup and root package manager specification.
Storybook & Build Tool Updates
apps/storybook/package.json, apps/web/package.json
Upgraded Storybook addons/core from 10.0.x → 10.2.4 (storybook, addon-docs, addon-links, addon-themes, react-vite), and updated autoprefixer, postcss, vite, and related tooling.
Root & CLI Dependencies
package.json, packages/cli/package.json, packages/cli/tsdown.config.ts
Bumped devDependencies (@changesets/cli, @types/bun, prettier, turbo, etc.); migrated CLI build tool from tsup → tsdown with updated build script.
UI Package Dependencies
packages/ui/package.json
Added oxc-parser, magic-string, and package-manager-detector; replaced older AST libraries (acorn, ast-types, estree-walker); updated rolldown, webpack, next, rsbuild/rslib/rspack; adjusted tailwind-merge to use npm aliases.
Parser Migration – Build Config
packages/ui/rollup.config.js, packages/ui/src/cli/utils/update-build-config.ts
Replaced @rslib/core build() with createRslib() API; refactored to use oxc-parser (parseSync) with Visitor-based AST traversal for plugin insertion and build-config modifications.
Parser Migration – Metadata Generation
packages/ui/scripts/generate-metadata.ts, packages/ui/scripts/generate-metadata.test.ts
Replaced Bun transpiler & acorn with oxc-parser; switched to Visitor-based AST traversal for class and dependency extraction; added comprehensive test coverage for TypeScript features, type-only imports, and generic parameters.
Parser Migration – Setup & Init
packages/ui/src/cli/commands/setup-init.ts
Swapped parser from Recaster → oxc-parser (parseSync); extracted removeReactImport into a shared utility module instead of local implementation.
Parser Migration – Compound Components
packages/ui/src/cli/transformers/compound-components.ts, packages/ui/src/cli/transformers/compound-components.test.ts
Replaced recast & @typescript-eslint/typescript-estree with oxc-parser & MagicString; added ImportDeclaration and JSXMemberExpression visitors; introduced comprehensive test suite covering component mappings, edge cases, and error handling.
Parser Migration – Config Utilities
packages/ui/src/cli/utils/add-to-config.ts, packages/ui/src/cli/utils/add-to-config.test.ts, packages/ui/src/cli/utils/compare-nodes.test.ts
Replaced recast with oxc-parser & MagicString; introduced new public exports (builders, ValueGenerator type); refactored to support array style detection and indentation preservation; added extensive test coverage for nested arrays and indentation handling.
React Import Removal Utility
packages/ui/src/cli/utils/remove-react-import.ts, packages/ui/src/cli/utils/remove-react-import.test.ts
Added new shared utility to remove React imports from AST; includes comprehensive test suite covering default imports, namespace imports, mixed imports, type-only scenarios, and edge cases.
Helper Library Updates
packages/ui/src/helpers/drag-scroll.tsx, packages/ui/src/helpers/tailwind-merge.ts, packages/ui/src/plugin/modernjs.ts
Replaced classnames with twMerge in drag-scroll; updated tailwind-merge imports to use npm specifiers (@2, @3); refactored modernjs plugin API from returned hooks to api.onBeforeBuild/onBeforeDev callbacks.
Component Formatting
packages/ui/src/components/Avatar/Avatar.tsx, packages/ui/src/components/Button/ButtonGroup.tsx, packages/ui/src/components/.../\*
Minor whitespace and line-wrapping adjustments to interface extends clauses (ButtonGroup, Checkbox, Datepicker, Dropdown, FileInput, List, MegaMenu, Progress, RangeSlider, Sidebar, Spinner) with no semantic changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement, dependencies, refactor

Suggested reviewers

  • rluders
  • robert1508

🐰 A rabbit hops through parsers new,
Where oxc replaces old AST's brew,
From tsup to tsdown we swiftly bound,
Bundle shrinks—eight meg, not forty-two!
MagicString weaves where Visitors play,

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'core(maintenance)' is vague and generic, failing to convey the specific nature of this substantial bundle optimization and modernization effort. Consider a more descriptive title like 'core: reduce bundle size with oxc-parser and modernize tooling' or 'core: migrate to oxc-parser and tsdown for significant bundle optimization'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch core/maintenance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/helpers/tailwind-merge.ts (1)

18-18: ⚠️ Potential issue | 🔴 Critical

Bug: version-to-import mapping is inverted.

When version === 3, this selects extendTailwindMerge_v2 (from tailwind-merge@2), and vice versa. This means Tailwind CSS v3 users get the v2 merge logic, and v2 users get the v3 merge logic — producing incorrect class merging behavior for all users.

🐛 Proposed fix
-  const twMergeFn = (version === 3 ? extendTailwindMerge_v2 : extendTailwindMerge_v3)({
+  const twMergeFn = (version === 3 ? extendTailwindMerge_v3 : extendTailwindMerge_v2)({
🤖 Fix all issues with AI agents
In `@packages/ui/src/cli/transformers/compound-components.ts`:
- Around line 209-221: The rewrite of import specifiers currently discards
aliases by rebuilding the specifier list from names; update the logic around
newImportsToAdd/finalImportInfo so existing specifiers use their original source
text (use content.slice(specifier.start, specifier.end) from the stored
finalImportInfo.specifiers) while only appending newly added identifiers by
name; then build allSpecifiers from the preserved original specifier texts plus
the new import names before joining and calling
s.overwrite(finalImportInfo.specifiersStart, finalImportInfo.specifiersEnd,
newSpecifiersText), preserving multiline formatting logic as-is.
- Around line 136-166: The code overwrites flowbiteImportInfo for each matching
ImportDeclaration so only the last import receives new specifiers; update the
logic in importVisitor.ImportDeclaration to collect multiple import metadata
(e.g., change flowbiteImportInfo to an array like flowbiteImportInfos) or pick a
deterministic target import (prefer the base "flowbite-react" import over
"flowbite-react/components/...") when multiple matches exist, push each
specifier into flowbiteImportSpecifiers and also record/specify which
flowbiteImportInfo should receive new specifiers (or merge specifiers into the
preferred import entry); adjust later code that writes the rewritten import to
use the chosen flowbiteImportInfo entry (or consolidated entry) so all added
specifiers are inserted into the correct ImportDeclaration.
🧹 Nitpick comments (7)
packages/ui/src/cli/utils/remove-react-import.ts (1)

6-25: specifiers[0] check only matches default-import-only statements.

The filter checks only specifiers[0]?.local?.name === "React", so a combined import like import React, { useState } from "react" would have the entire declaration removed (losing named imports). This is fine for the current usage in setup-init.ts where the generated content is controlled, but worth noting if this utility is intended to be general-purpose.

packages/ui/src/cli/utils/add-plugin.test.ts (1)

197-203: Consider verifying plugin insertion order in the multi-plugin test.

Other tests (lines 92, 111, 130, 152) use regex to verify that flowbiteReact() is appended after existing plugins. This test only checks presence via [\s\S]*flowbiteReact\(\)[\s\S]*, not ordering relative to tsConfigPaths, vitePluginRequire, or vuePlugin.

packages/ui/scripts/generate-metadata.ts (1)

90-92: Consider logging parse errors instead of silently returning empty.

Both extractClassList and extractDependencyList return [] on parse errors with no diagnostic output. If a theme or component file has a syntax error, this silently produces incomplete metadata. A console.warn with the file context would help debugging build issues.

💡 Proposed diagnostic logging
  if (result.errors.length > 0) {
+   console.warn(`Failed to parse content, skipping. Errors: ${result.errors.map(e => e.message).join(", ")}`);
    return [];
  }
packages/ui/src/cli/transformers/compound-components.test.ts (1)

904-1076: Consider adding a test for multiple flowbite-react import declarations.

The edge cases section is solid, but there's no test for a file that has both import { Accordion } from "flowbite-react" and import { Modal } from "flowbite-react/components/Modal" simultaneously. This would exercise the "last import wins" behavior flagged in the transformer review.

packages/ui/src/cli/utils/update-build-config.ts (2)

162-167: Hardcoded 8-space indentation when inserting plugins property.

Line 164 uses ",\n plugins: [${pluginName}]" (8 spaces), which won't match files using 2-space, 4-space, or tab indentation. Compare with add-to-config.ts which has detectIndentUnit for this purpose.

💡 Suggestion: reuse indent detection
+  const indent = detectIndentUnit(content);
   if (finalBuildOptions.hasProperties && finalBuildOptions.lastPropertyEnd) {
-      s.appendLeft(finalBuildOptions.lastPropertyEnd, `,\n        plugins: [${pluginName}]`);
+      // Detect indentation from existing properties
+      const beforeObj = content.slice(Math.max(0, finalBuildOptions.objectStart), finalBuildOptions.lastPropertyEnd);
+      const indentMatch = beforeObj.match(/\n([ \t]+)[^\n]*$/);
+      const propIndent = indentMatch ? indentMatch[1] : '        ';
+      s.appendLeft(finalBuildOptions.lastPropertyEnd, `,\n${propIndent}plugins: [${pluginName}]`);
   } else {
       s.appendLeft(finalBuildOptions.objectStart + 1, ` plugins: [${pluginName}] `);
   }

57-61: Silent error handling differs from compound-components.ts.

compound-components.ts logs console.warn(...) on parse errors, but this function silently returns the original content. Minor inconsistency — consider adding a console.warn for debuggability.

packages/ui/src/cli/utils/add-to-config.ts (1)

449-508: Indent detection heuristic filters out indentation > 4 characters.

Line 464 discards indentation strings longer than 4 characters. In files that are heavily nested (e.g., most lines at 6+ spaces), the 2-space base indent may appear infrequently and the heuristic could fall back to a wrong unit. This is unlikely for typical config files, but worth keeping in mind.

Comment on lines +136 to 166
const importVisitor = new Visitor({
ImportDeclaration(node) {
if (
node.type === "ImportDeclaration" &&
(node.source.value === "flowbite-react" || node.source.value.startsWith("flowbite-react/components/")) &&
Array.isArray(node.specifiers) &&
node.specifiers.every((s) => s.type === "ImportSpecifier")
node.source?.value === "flowbite-react" ||
(typeof node.source?.value === "string" && node.source.value.startsWith("flowbite-react/components/"))
) {
flowbiteImportPath = node;
node.specifiers.forEach((specifier) => {
if (specifier.imported.type === "Identifier") {
flowbiteImportSpecifiers.push(specifier.imported.name);
if (Array.isArray(node.specifiers) && node.specifiers.every((s) => s.type === "ImportSpecifier")) {
const importText = content.slice(node.start, node.end);
const braceStart = importText.indexOf("{");
const braceEnd = importText.lastIndexOf("}");

if (braceStart !== -1 && braceEnd !== -1) {
flowbiteImportInfo = {
start: node.start,
end: node.end,
specifiersStart: node.start + braceStart + 1,
specifiersEnd: node.start + braceEnd,
specifiers: [],
};

node.specifiers.forEach((specifier) => {
if (specifier.imported?.type === "Identifier") {
flowbiteImportSpecifiers.push(specifier.imported.name);
flowbiteImportInfo!.specifiers.push(specifier.imported.name);
}
});
}
});
}
}
return false;
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Multiple flowbite-react imports: only the last import declaration receives new specifiers.

flowbiteImportInfo is overwritten on each matching ImportDeclaration, so if a file has both import { Accordion } from "flowbite-react" and import { Modal } from "flowbite-react/components/Modal", new specifiers are appended only to whichever import was visited last. flowbiteImportSpecifiers is cumulative so JSX replacements still happen correctly, but the rewritten import block may not land in the expected declaration.

This is a narrow edge case for a migration tool, but worth noting.

🤖 Prompt for AI Agents
In `@packages/ui/src/cli/transformers/compound-components.ts` around lines 136 -
166, The code overwrites flowbiteImportInfo for each matching ImportDeclaration
so only the last import receives new specifiers; update the logic in
importVisitor.ImportDeclaration to collect multiple import metadata (e.g.,
change flowbiteImportInfo to an array like flowbiteImportInfos) or pick a
deterministic target import (prefer the base "flowbite-react" import over
"flowbite-react/components/...") when multiple matches exist, push each
specifier into flowbiteImportSpecifiers and also record/specify which
flowbiteImportInfo should receive new specifiers (or merge specifiers into the
preferred import entry); adjust later code that writes the rewritten import to
use the chosen flowbiteImportInfo entry (or consolidated entry) so all added
specifiers are inserted into the correct ImportDeclaration.

Comment on lines +209 to +221
if (newImportsToAdd.size > 0 && finalImportInfo) {
const allSpecifiers = [...finalImportInfo.specifiers, ...newImportsToAdd].sort((a, b) => a.localeCompare(b));
const originalImportText = content.slice(finalImportInfo.specifiersStart, finalImportInfo.specifiersEnd);
const isMultiline = originalImportText.includes("\n");

let newSpecifiersText: string;
if (isMultiline) {
newSpecifiersText = "\n " + allSpecifiers.join(",\n ") + ",\n";
} else {
newSpecifiersText = " " + allSpecifiers.join(", ") + " ";
}

s.overwrite(finalImportInfo.specifiersStart, finalImportInfo.specifiersEnd, newSpecifiersText);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Import alias information is lost when rewriting specifiers.

If the original import used aliases (e.g., import { Accordion as Acc } from "flowbite-react"), the rewrite at Line 210 rebuilds the specifier list using only specifier.imported.name, discarding the alias. This would change the user's code semantics.

Again a narrow edge case for typical flowbite-react usage, but could silently break files with aliased imports.

💡 Sketch: preserve original specifier text instead of just the name

One approach is to store the original text of each specifier (via content.slice(specifier.start, specifier.end)) in flowbiteImportInfo.specifiers and only use identifier names for the newly added imports.

🤖 Prompt for AI Agents
In `@packages/ui/src/cli/transformers/compound-components.ts` around lines 209 -
221, The rewrite of import specifiers currently discards aliases by rebuilding
the specifier list from names; update the logic around
newImportsToAdd/finalImportInfo so existing specifiers use their original source
text (use content.slice(specifier.start, specifier.end) from the stored
finalImportInfo.specifiers) while only appending newly added identifiers by
name; then build allSpecifiers from the preserved original specifier texts plus
the new import names before joining and calling
s.overwrite(finalImportInfo.specifiersStart, finalImportInfo.specifiersEnd,
newSpecifiersText), preserving multiline formatting logic as-is.

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