diff --git a/.changeset/hungry-toys-care.md b/.changeset/hungry-toys-care.md new file mode 100644 index 000000000..1e6974802 --- /dev/null +++ b/.changeset/hungry-toys-care.md @@ -0,0 +1,63 @@ +--- +"flowbite-react": minor +--- + +## Rework build process using `rollup` and `tsc` + +### Summary + +In order to bring more performance to the build process of `flowbite-react` package, we have to consider transpiling the files using other tools rather than `tsc`, which is very slow. + +After evaluating various tools including `tsup`, `tshy`, and `bun build`, we chose `rollup` with the `esbuild` plugin for transpiling due to its performance and flexibility. We continue to use `tsc` solely for generating `*.d.ts` declaration files. + +### Changes + +- added `rollup` + `esbuild` for transpiling files + - all files in the `cjs` directory now have `.cjs` extension + - all files in the `esm` directory now have `.mjs` extension + - declaration file types (`*.d.ts`) have been moved from `dist/esm` to `dist/types` +- changed the build output dir from `lib` to `dist` +- created a facade layer for easier management of the `content` path as well as the `plugin` +- fixed turbo repo dependency tree configs in order for `apps/web` to properly pipe and require the build output of `packages/ui` in certain script steps such as `build` and `dev` + +### Breaking changes + +`tailwind.config.js` `content` path: + +old: `"node_modules/flowbite-react/lib/esm/**/*.js"` + +new: `"node_modules/flowbite-react/dist/esm/**/*.mjs"` - (`flowbite.content()` returns it) + +Before + +```js {5,9} +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + // ... + "node_modules/flowbite-react/lib/esm/**/*.js", + ], + plugins: [ + // ... + require("flowbite/plugin"), + ], +}; +``` + +After + +```js {1,7,11} +const flowbite = require("flowbite-react/tailwind"); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + // ... + flowbite.content(), + ], + plugins: [ + // ... + flowbite.plugin(), + ], +}; +``` diff --git a/apps/web/content/docs/getting-started/introduction.mdx b/apps/web/content/docs/getting-started/introduction.mdx index 282311ed5..1a863ff73 100644 --- a/apps/web/content/docs/getting-started/introduction.mdx +++ b/apps/web/content/docs/getting-started/introduction.mdx @@ -13,32 +13,7 @@ Learn how to get started with Flowbite React and start leveraging the interactiv ### Setup Tailwind CSS -Install Tailwind CSS: - -```bash -npm i autoprefixer postcss tailwindcss -npx tailwindcss init -p -``` - -Point Tailwind CSS to files you have `className=".."` in: - -```javascript -module.exports = { - content: ["./src/**/*.{js,jsx,ts,tsx}" /* src folder, for example */], - theme: { - extend: {}, - }, - plugins: [], -}; -``` - -Add Tailwind CSS to a CSS file: - -```css -@tailwind base; -@tailwind components; -@tailwind utilities; -``` +Install Tailwind CSS by following the [official installation guide](https://tailwindcss.com/docs/installation). ### Install Flowbite React @@ -48,18 +23,20 @@ Add Tailwind CSS to a CSS file: npm i flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.js`, and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `tailwind.config.js`: + +```js {1,7,11} +const flowbite = require("flowbite-react/tailwind"); -```js {5,9} /** @type {import('tailwindcss').Config} */ -export default { +module.exports = { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` diff --git a/apps/web/content/docs/guides/astro.mdx b/apps/web/content/docs/guides/astro.mdx index 52ebc4374..e1f5d7d21 100644 --- a/apps/web/content/docs/guides/astro.mdx +++ b/apps/web/content/docs/guides/astro.mdx @@ -57,18 +57,20 @@ npx astro add tailwind npm i flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.mjs` and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `tailwind.config.mjs`: + +```js {1,7,11} +import flowbite from "flowbite-react/tailwind"; -```js {5,9} /** @type {import('tailwindcss').Config} */ export default { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` diff --git a/apps/web/content/docs/guides/create-react-app.mdx b/apps/web/content/docs/guides/create-react-app.mdx index 4ef06c1d9..b585ca2a9 100644 --- a/apps/web/content/docs/guides/create-react-app.mdx +++ b/apps/web/content/docs/guides/create-react-app.mdx @@ -74,18 +74,20 @@ module.exports = { npm i flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.js` and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `tailwind.config.js`: + +```js {1,7,11} +const flowbite = require("flowbite-react/tailwind"); -```js {5,9} /** @type {import('tailwindcss').Config} */ -export default { +module.exports = { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` diff --git a/apps/web/content/docs/guides/gatsby.mdx b/apps/web/content/docs/guides/gatsby.mdx index 82ac4525b..1cfef8148 100644 --- a/apps/web/content/docs/guides/gatsby.mdx +++ b/apps/web/content/docs/guides/gatsby.mdx @@ -41,18 +41,20 @@ npm init gatsby npm i flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.js` and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `tailwind.config.js`: + +```js {1,7,11} +import flowbite from "flowbite-react/tailwind"; -```js {5,9} /** @type {import('tailwindcss').Config} */ export default { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` diff --git a/apps/web/content/docs/guides/laravel.mdx b/apps/web/content/docs/guides/laravel.mdx index 2c06b7f7c..4e138b220 100644 --- a/apps/web/content/docs/guides/laravel.mdx +++ b/apps/web/content/docs/guides/laravel.mdx @@ -41,18 +41,20 @@ laravel new flowbite-react-laravel --breeze --stack=react npm i flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.js` and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `tailwind.config.js`: + +```js {1,7,11} +import flowbite from "flowbite-react/tailwind"; -```js {5,9} /** @type {import('tailwindcss').Config} */ export default { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` diff --git a/apps/web/content/docs/guides/next-js.mdx b/apps/web/content/docs/guides/next-js.mdx index 01632a575..4ac78f41c 100644 --- a/apps/web/content/docs/guides/next-js.mdx +++ b/apps/web/content/docs/guides/next-js.mdx @@ -45,18 +45,20 @@ npm i flowbite-react ### Configure Tailwind CSS -Open `tailwind.config.js` and update `content` and `plugins` to include Flowbite React: +Open `tailwind.config.ts` and update `content` and `plugins` to include Flowbite React: + +```js {1,7,11} +import flowbite from "flowbite-react/tailwind"; -```js {5,9} /** @type {import('tailwindcss').Config} */ export default { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` @@ -115,18 +117,20 @@ module.exports = { npm i flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.js` and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `tailwind.config.js`: + +```js {1,7,11} +const flowbite = require("flowbite-react/tailwind"); -```js {5,9} /** @type {import('tailwindcss').Config} */ -export default { +module.exports = { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` diff --git a/apps/web/content/docs/guides/parcel.mdx b/apps/web/content/docs/guides/parcel.mdx index 22d5bb0fa..f6ba23b16 100644 --- a/apps/web/content/docs/guides/parcel.mdx +++ b/apps/web/content/docs/guides/parcel.mdx @@ -199,18 +199,20 @@ module.exports = { npm i flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.js` and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `tailwind.config.js`: + +```js {1,7,11} +const flowbite = require("flowbite-react/tailwind"); -```js {5,9} /** @type {import('tailwindcss').Config} */ -export default { +module.exports = { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` diff --git a/apps/web/content/docs/guides/redwood-js.mdx b/apps/web/content/docs/guides/redwood-js.mdx index 7b8e8975c..fdcaf6880 100644 --- a/apps/web/content/docs/guides/redwood-js.mdx +++ b/apps/web/content/docs/guides/redwood-js.mdx @@ -53,18 +53,20 @@ yarn rw setup ui tailwindcss yarn workspace web add flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.js` and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `web/config/tailwind.config.js`: + +```js {1,7,11} +const flowbite = require("flowbite-react/tailwind"); -```js {5,9} /** @type {import('tailwindcss').Config} */ module.exports = { content: [ // ... - "../node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content({ base: "../" }), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` diff --git a/apps/web/content/docs/guides/remix.mdx b/apps/web/content/docs/guides/remix.mdx index 2e21f6f06..79645a6ef 100644 --- a/apps/web/content/docs/guides/remix.mdx +++ b/apps/web/content/docs/guides/remix.mdx @@ -86,21 +86,21 @@ export const links: LinksFunction = () => [ npm i flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.ts` and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `tailwind.config.ts`: ```ts {1,8,12} -import flowbite from "flowbite/plugin"; +import flowbite from "flowbite-react/tailwind"; import type { Config } from "tailwindcss"; export default { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - flowbite, + flowbite.plugin(), ], } satisfies Config; ``` diff --git a/apps/web/content/docs/guides/vite.mdx b/apps/web/content/docs/guides/vite.mdx index 351818f3e..e0484d16a 100644 --- a/apps/web/content/docs/guides/vite.mdx +++ b/apps/web/content/docs/guides/vite.mdx @@ -74,18 +74,20 @@ module.exports = { npm i flowbite-react ``` -2. Add the Flowbite plugin to `tailwind.config.js` and include content from `flowbite-react`: +2. Add the Flowbite React `content` path and `plugin` to `tailwind.config.js`: + +```js {1,7,11} +const flowbite = require("flowbite-react/tailwind"); -```js {5,9} /** @type {import('tailwindcss').Config} */ -export default { +module.exports = { content: [ // ... - "node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content(), ], plugins: [ // ... - require("flowbite/plugin"), + flowbite.plugin(), ], }; ``` diff --git a/apps/web/tailwind.config.cjs b/apps/web/tailwind.config.cjs index 18be9a2b0..ba3d6a775 100644 --- a/apps/web/tailwind.config.cjs +++ b/apps/web/tailwind.config.cjs @@ -1,3 +1,5 @@ +const flowbite = require("flowbite-react/tailwind"); + /** @type {import('tailwindcss').Config} */ module.exports = { darkMode: ["variant", "&:is(.dark *)&:not(.light *)"], @@ -6,7 +8,7 @@ module.exports = { "./components/**/*.{js,jsx,md,mdx,ts,tsx}", "./data/**/*.{js,jsx,ts,tsx}", "./examples/**/*.{js,jsx,ts,tsx}", - "../../node_modules/flowbite-react/lib/esm/**/*.js", + flowbite.content({ base: "../../" }), ], theme: { extend: { @@ -89,5 +91,5 @@ module.exports = { ], }, }, - plugins: [require("flowbite/plugin")], + plugins: [flowbite.plugin()], }; diff --git a/apps/web/turbo.json b/apps/web/turbo.json index efbfa99f5..5a62a2cf8 100644 --- a/apps/web/turbo.json +++ b/apps/web/turbo.json @@ -2,7 +2,20 @@ "extends": ["//"], "pipeline": { "build": { + "dependsOn": ["^build"], "outputs": [".next/**", "!.next/cache/**"] + }, + "dev": { + "dependsOn": ["^build"] + }, + "format": { + "dependsOn": ["^build"] + }, + "format:check": { + "dependsOn": ["^build"] + }, + "typecheck": { + "dependsOn": ["^build"] } } } diff --git a/bun.lockb b/bun.lockb index 9099591d3..88152d042 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index cd533024d..d2feb2eb4 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "dev:web": "turbo dev --filter=web", "format": "turbo format", "format:check": "turbo format:check", - "postinstall": "bun build:ui", "lint": "turbo lint", "lint:fix": "turbo lint:fix", "release": "changeset publish", diff --git a/packages/ui/.eslintrc.cjs b/packages/ui/.eslintrc.cjs index e147886ea..b9a18cbf0 100644 --- a/packages/ui/.eslintrc.cjs +++ b/packages/ui/.eslintrc.cjs @@ -40,7 +40,7 @@ module.exports = { classRegex: "^(class(Name)|theme)?$", }, }, - ignorePatterns: ["lib"], + ignorePatterns: ["dist"], rules: { "no-undef": "off", "react/prop-types": "off", diff --git a/packages/ui/.gitignore b/packages/ui/.gitignore index d63643222..29f44348c 100644 --- a/packages/ui/.gitignore +++ b/packages/ui/.gitignore @@ -1,2 +1,2 @@ coverage -lib +dist diff --git a/packages/ui/package.json b/packages/ui/package.json index 320a36f48..ac7da2155 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -21,31 +21,32 @@ "license": "MIT", "exports": { ".": { - "types": "./lib/types/index.d.ts", - "import": "./lib/esm/index.js", - "require": "./lib/cjs/index.js" + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/index.mjs", + "require": "./dist/cjs/index.cjs" }, "./components/*": { - "types": "./lib/types/components/*/index.d.ts", - "import": "./lib/esm/components/*/index.js", - "require": "./lib/cjs/components/*/index.js" + "types": "./dist/types/components/*/index.d.ts", + "import": "./dist/esm/components/*/index.mjs", + "require": "./dist/cjs/components/*/index.cjs" + }, + "./tailwind": { + "types": "./dist/types/tailwind.d.ts", + "import": "./dist/esm/tailwind.mjs", + "require": "./dist/cjs/tailwind.cjs" }, "./package.json": "./package.json" }, - "main": "lib/cjs/index.js", - "module": "lib/esm/index.js", - "types": "lib/types/index.d.ts", + "main": "dist/cjs/index.cjs", + "module": "dist/esm/index.mjs", + "types": "dist/types/index.d.ts", "files": [ - "lib" + "dist" ], "scripts": { - "build": "rimraf lib && bun build:fast", - "build:cjs": "tsc -p tsconfig.build.json --module CommonJS --outDir lib/cjs", - "build:esm": "tsc -p tsconfig.build.json --module ESNext --outDir lib/esm", - "build:fast": "bun build:cjs && bun build:esm && bun build:types", - "build:types": "tsc -p tsconfig.build.json --declaration --emitDeclarationOnly --outDir lib/types", - "clean": "rimraf .turbo coverage lib node_modules tsconfig.tsbuildinfo", - "dev": "bun build:fast --watch", + "build": "bun --bun rollup -c", + "clean": "rimraf .turbo coverage dist node_modules tsconfig.tsbuildinfo", + "dev": "bun run build --watch", "format": "prettier . --write", "format:check": "prettier . --check", "lint": "eslint .", @@ -78,8 +79,11 @@ "eslint-plugin-react": "7.34.1", "eslint-plugin-storybook": "0.8.0", "eslint-plugin-vitest": "0.4.1", + "fast-glob": "3.3.2", "jsdom": "24.0.0", - "mime": "^4.0.1", + "rollup": "4.13.2", + "rollup-plugin-esbuild": "6.1.1", + "rollup-plugin-use-client": "1.4.0", "typescript": "5.4.3", "vitest": "1.4.0" }, diff --git a/packages/ui/rollup.config.mjs b/packages/ui/rollup.config.mjs new file mode 100644 index 000000000..3223a9388 --- /dev/null +++ b/packages/ui/rollup.config.mjs @@ -0,0 +1,76 @@ +import { $ } from "bun"; +import glob from "fast-glob"; +import { rimraf } from "rimraf"; +import esbuild from "rollup-plugin-esbuild"; +import { rollupPluginUseClient } from "rollup-plugin-use-client"; +import packageJson from "./package.json"; + +const componentEntries = await glob("src/components/**/index.ts"); +const entries = ["src/index.ts", "src/tailwind.ts", ...componentEntries]; +const external = [ + "flowbite/plugin", + "react-icons/fa", + "react-icons/hi", + "react/jsx-runtime", + ...Object.keys({ + ...packageJson.dependencies, + ...packageJson.peerDependencies, + }), +]; +const outputDir = "dist"; + +/** + * @type {import('rollup').RollupOptions} + */ +export default { + input: entries, + output: [ + { + format: "es", + dir: `${outputDir}/esm`, + entryFileNames: "[name].mjs", + preserveModules: true, + sourcemap: true, + }, + { + format: "cjs", + dir: `${outputDir}/cjs`, + entryFileNames: "[name].cjs", + preserveModules: true, + sourcemap: true, + }, + ], + external, + plugins: [ + cleanOutputDir(), + esbuild({ + sourceMap: false, + }), + rollupPluginUseClient(), + generateDts(), + ], + onwarn(warning, warn) { + if (warning.code === "MODULE_LEVEL_DIRECTIVE") { + return; + } + warn(warning); + }, +}; + +function cleanOutputDir() { + return { + name: "clean-output-dir", + async buildStart() { + await rimraf(outputDir); + }, + }; +} + +function generateDts() { + return { + name: "generate-dts", + async closeBundle() { + await $`tsc -p tsconfig.build.json --outDir ${outputDir}/types`; + }, + }; +} diff --git a/packages/ui/src/tailwind.ts b/packages/ui/src/tailwind.ts new file mode 100644 index 000000000..3450756b0 --- /dev/null +++ b/packages/ui/src/tailwind.ts @@ -0,0 +1,44 @@ +import flowbitePlugin from "flowbite/plugin"; + +interface Content { + /** + * Path to `node_modules` where `flowbite-react` is installed + * + * =============================================== + * + * For monorepo setup where `flowbite-react` is installed in the root `node_modules` but used in `apps/web` directory + * @example + * ``` + * // tailwind.config.(js|cjs|mjs) file + * + * // cjs + * const flowbite = require("flowbite-react/tailwind"); + * // esm + * import flowbite from "flowbite-react/tailwind"; + * + * { + * content: [ + * // ... + * flowbite.content({ base: "../../" }) + * ], + * plugins: [ + * // ... + * flowbite.plugin() + * ] + * } + * ``` + * + * @default "./" + */ + base?: string; +} + +export function content({ base = "./" }: Content = {}) { + const path = "node_modules/flowbite-react/dist/esm/**/*.mjs"; + + return `${base}${path}`; +} + +export function plugin() { + return flowbitePlugin; +} diff --git a/packages/ui/tsconfig.build.json b/packages/ui/tsconfig.build.json index d9ac60ce3..94d1746d0 100644 --- a/packages/ui/tsconfig.build.json +++ b/packages/ui/tsconfig.build.json @@ -1,10 +1,9 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "incremental": false, - "sourceMap": true, - "jsx": "react-jsx" + "declaration": true, + "emitDeclarationOnly": true }, "include": ["src"], - "exclude": ["lib", "**/*.spec.ts", "**/*.spec.tsx", "**/*.stories.tsx"] + "exclude": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.stories.tsx"] } diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index af269a94d..df8824655 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -1,24 +1,30 @@ { "compilerOptions": { - "allowJs": true, + /* Language and Environment */ + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "jsx": "react-jsx", + + /* Modules */ + "module": "NodeNext", + "moduleResolution": "NodeNext", + "resolveJsonModule": true, + + /* Interop Constraints */ + "isolatedModules": true, + "allowSyntheticDefaultImports": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "incremental": true, - "isolatedModules": true, - "jsx": "preserve", - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "module": "ESNext", - "moduleResolution": "node", - "noEmit": false, + + /* Type Checking */ + "strict": true, "noImplicitAny": true, - "noImplicitThis": true, "noUnusedLocals": true, "noUnusedParameters": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "ESNext" + "noFallthroughCasesInSwitch": true, + + /* Completeness */ + "skipLibCheck": true }, - "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["lib"] + "include": ["**/*.ts", "**/*.tsx"] } diff --git a/packages/ui/turbo.json b/packages/ui/turbo.json index dad78ed11..1ca1faa83 100644 --- a/packages/ui/turbo.json +++ b/packages/ui/turbo.json @@ -2,7 +2,7 @@ "extends": ["//"], "pipeline": { "build": { - "outputs": ["lib/**"] + "outputs": ["dist/**"] } } }