From d35cf46fb5f274e8d4366ded95ea70220afa4127 Mon Sep 17 00:00:00 2001
From: ssz
Date: Sat, 17 May 2025 17:29:04 +0200
Subject: [PATCH 1/3] change the tailwind browser and make it importable
---
packages/@tailwindcss-browser/package.json | 13 +-
packages/@tailwindcss-browser/src/index.ts | 119 ++++---------------
packages/@tailwindcss-browser/tsconfig.json | 9 ++
packages/@tailwindcss-browser/tsup.config.ts | 4 +-
4 files changed, 40 insertions(+), 105 deletions(-)
diff --git a/packages/@tailwindcss-browser/package.json b/packages/@tailwindcss-browser/package.json
index 700cad815c0c..20ce420b82f7 100644
--- a/packages/@tailwindcss-browser/package.json
+++ b/packages/@tailwindcss-browser/package.json
@@ -18,10 +18,6 @@
"dev": "pnpm run build -- --watch",
"test:ui": "playwright test"
},
- "exports": {
- ".": "./dist/index.global.js",
- "./package.json": "./package.json"
- },
"files": [
"dist"
],
@@ -33,5 +29,12 @@
"h3": "^1.15.3",
"listhen": "^1.9.0",
"tailwindcss": "workspace:*"
+ },
+ "exports": {
+ ".": {
+ "require": "./dist/index.js",
+ "import": "./dist/index.mjs"
+ },
+ "./package.json": "./package.json"
}
-}
+}
\ No newline at end of file
diff --git a/packages/@tailwindcss-browser/src/index.ts b/packages/@tailwindcss-browser/src/index.ts
index 74992157f5d5..3b5a771c9aa0 100644
--- a/packages/@tailwindcss-browser/src/index.ts
+++ b/packages/@tailwindcss-browser/src/index.ts
@@ -8,11 +8,6 @@ console.warn(
'The browser build of Tailwind CSS should not be used in production. To use Tailwind CSS in production, use the Tailwind CLI, Vite plugin, or PostCSS plugin: https://tailwindcss.com/docs/installation',
)
-/**
- * The type used by `` tags that contain input CSS.
- */
-const STYLE_TYPE = 'text/tailwindcss'
-
/**
* The current Tailwind CSS compiler.
*
@@ -33,16 +28,12 @@ let classes = new Set()
*/
let lastCss = ''
-/**
- * The stylesheet that we use to inject the compiled CSS into the page.
- */
-let sheet = document.createElement('style')
/**
* The queue of build tasks that need to be run. This is used to ensure that we
* don't run multiple builds concurrently.
*/
-let buildQueue = Promise.resolve()
+let buildQueue = Promise.resolve('')
/**
* What build this is
@@ -63,21 +54,10 @@ let I = new Instrumentation()
* This does **not** imply that the CSS is actually built. That happens in the
* `build` function and is a separate scheduled task.
*/
-async function createCompiler() {
+async function createCompiler(css: string) {
I.start(`Create compiler`)
I.start('Reading Stylesheets')
- // The stylesheets may have changed causing a full rebuild so we'll need to
- // gather the latest list of stylesheets.
- let stylesheets: Iterable = document.querySelectorAll(
- `style[type="${STYLE_TYPE}"]`,
- )
-
- let css = ''
- for (let sheet of stylesheets) {
- observeSheet(sheet)
- css += sheet.textContent + '\n'
- }
// The user might have no stylesheets, or a some stylesheets without `@import`
// because they want to customize their theme so we'll inject the main import
@@ -180,7 +160,7 @@ async function loadModule(): Promise {
throw new Error(`The browser build does not support plugins or config files.`)
}
-async function build(kind: 'full' | 'incremental') {
+async function build() {
if (!compiler) return
// 1. Refresh the known list of classes
@@ -201,100 +181,41 @@ async function build(kind: 'full' | 'incremental') {
count: newClasses.size,
})
- if (newClasses.size === 0 && kind === 'incremental') return
-
// 2. Compile the CSS
I.start(`Build utilities`)
- sheet.textContent = compiler.build(Array.from(newClasses))
+ const result = compiler.build(Array.from(newClasses))
I.end(`Build utilities`)
+
+ return result;
}
-function rebuild(kind: 'full' | 'incremental') {
+async function rebuild(css: string) {
async function run() {
- if (!compiler && kind !== 'full') {
- return
- }
let buildId = nextBuildId++
- I.start(`Build #${buildId} (${kind})`)
+ I.start(`Build #${buildId}`)
+
+ await createCompiler(css)
- if (kind === 'full') {
- await createCompiler()
- }
I.start(`Build`)
- await build(kind)
+ const result = await build();
I.end(`Build`)
- I.end(`Build #${buildId} (${kind})`)
- }
-
- buildQueue = buildQueue.then(run).catch((err) => I.error(err))
-}
+ I.end(`Build #${buildId}`)
-// Handle changes to known stylesheets
-let styleObserver = new MutationObserver(() => rebuild('full'))
-
-function observeSheet(sheet: HTMLStyleElement) {
- styleObserver.observe(sheet, {
- attributes: true,
- attributeFilter: ['type'],
- characterData: true,
- subtree: true,
- childList: true,
- })
-}
-
-// Handle changes to the document that could affect the styles
-// - Changes to any element's class attribute
-// - New stylesheets being added to the page
-// - New elements (with classes) being added to the page
-new MutationObserver((records) => {
- let full = 0
- let incremental = 0
-
- for (let record of records) {
- // New stylesheets == tracking + full rebuild
- for (let node of record.addedNodes as Iterable) {
- if (node.nodeType !== Node.ELEMENT_NODE) continue
- if (node.tagName !== 'STYLE') continue
- if (node.getAttribute('type') !== STYLE_TYPE) continue
-
- observeSheet(node as HTMLStyleElement)
- full++
- }
-
- // New nodes require an incremental rebuild
- for (let node of record.addedNodes) {
- if (node.nodeType !== 1) continue
-
- // Skip the output stylesheet itself to prevent loops
- if (node === sheet) continue
-
- incremental++
- }
-
- // Changes to class attributes require an incremental rebuild
- if (record.type === 'attributes') {
- incremental++
- }
+ return result ?? '';
}
- if (full > 0) {
- return rebuild('full')
- } else if (incremental > 0) {
- return rebuild('incremental')
+ try {
+ buildQueue = buildQueue.then(run);
+ return await buildQueue;
+ } catch (error) {
+ I.error(error);
}
-}).observe(document.documentElement, {
- attributes: true,
- attributeFilter: ['class'],
- childList: true,
- subtree: true,
-})
-
-rebuild('full')
+}
+export const tailwindCompiler = rebuild;
-document.head.append(sheet)
diff --git a/packages/@tailwindcss-browser/tsconfig.json b/packages/@tailwindcss-browser/tsconfig.json
index 8ea040daf8d3..b9e1ccb07257 100644
--- a/packages/@tailwindcss-browser/tsconfig.json
+++ b/packages/@tailwindcss-browser/tsconfig.json
@@ -2,5 +2,14 @@
"extends": "../tsconfig.base.json",
"compilerOptions": {
"lib": ["es2022", "esnext.disposable", "dom", "dom.iterable"],
+ "target": "ESNext",
+ "module": "ESNext",
+ "declaration": true,
+ "declarationDir": "dist/types",
+ "outDir": "dist",
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "strict": true,
},
+ "include": ["src"]
}
diff --git a/packages/@tailwindcss-browser/tsup.config.ts b/packages/@tailwindcss-browser/tsup.config.ts
index bb0ddc39cde4..654a9c0db5bc 100644
--- a/packages/@tailwindcss-browser/tsup.config.ts
+++ b/packages/@tailwindcss-browser/tsup.config.ts
@@ -1,9 +1,11 @@
import { defineConfig } from 'tsup'
export default defineConfig({
- format: ['iife'],
+ format: ['esm', 'cjs'],
clean: true,
minify: true,
+ sourcemap: true,
+ dts: true,
entry: ['src/index.ts'],
noExternal: [/.*/],
loader: {
From 10e683461bee99e8deeca6cbd2749bd20017a155 Mon Sep 17 00:00:00 2001
From: ssz
Date: Sat, 17 May 2025 17:34:19 +0200
Subject: [PATCH 2/3] add description
---
README.md | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/README.md b/README.md
index 95ec9d87ddcc..d713e64827fe 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,19 @@
+## Tailwind in browser
+changed the packages/@tailwindcss-browser project to use it as a library
+
+- clone it
+- add it in the package.js dependencies
+
+ `"@tailwindcss-browser": "file:../../tailwindcss-browser/packages/@tailwindcss-browser"`
+- install
+
+ `pnpm install`
+- import
+
+ `import { tailwindCompiler } from "@tailwindcss-browser";`
+
+
+---
From d7b996dfbbb3e4162628855f80f384fb41fed9a1 Mon Sep 17 00:00:00 2001
From: ssz
Date: Sat, 17 May 2025 20:18:21 +0200
Subject: [PATCH 3/3] simplify
---
packages/@tailwindcss-browser/src/index.ts | 94 +++-------------------
1 file changed, 9 insertions(+), 85 deletions(-)
diff --git a/packages/@tailwindcss-browser/src/index.ts b/packages/@tailwindcss-browser/src/index.ts
index 3b5a771c9aa0..290f435dc4df 100644
--- a/packages/@tailwindcss-browser/src/index.ts
+++ b/packages/@tailwindcss-browser/src/index.ts
@@ -16,29 +16,6 @@ console.warn(
*/
let compiler: Awaited>
-/**
- * The list of all seen classes on the page so far. The compiler already has a
- * cache of classes but this lets us only pass new classes to `build(…)`.
- */
-let classes = new Set()
-
-/**
- * The last input CSS that was compiled. If stylesheets "change" without
- * actually changing, we can avoid a full rebuild.
- */
-let lastCss = ''
-
-
-/**
- * The queue of build tasks that need to be run. This is used to ensure that we
- * don't run multiple builds concurrently.
- */
-let buildQueue = Promise.resolve('')
-
-/**
- * What build this is
- */
-let nextBuildId = 1
/**
* Used for instrumenting the build process. This data shows up in the
@@ -54,28 +31,11 @@ let I = new Instrumentation()
* This does **not** imply that the CSS is actually built. That happens in the
* `build` function and is a separate scheduled task.
*/
-async function createCompiler(css: string) {
+async function createCompiler() {
I.start(`Create compiler`)
I.start('Reading Stylesheets')
-
- // The user might have no stylesheets, or a some stylesheets without `@import`
- // because they want to customize their theme so we'll inject the main import
- // for them. However, if they start using `@import` we'll let them control
- // the build completely.
- if (!css.includes('@import')) {
- css = `@import "tailwindcss";${css}`
- }
-
- I.end('Reading Stylesheets', {
- size: css.length,
- changed: lastCss !== css,
- })
-
- // The input CSS did not change so the compiler does not need to be recreated
- if (lastCss === css) return
-
- lastCss = css
+ const css = `@import "tailwindcss";`
I.start('Compile CSS')
try {
@@ -88,8 +48,6 @@ async function createCompiler(css: string) {
I.end('Compile CSS')
I.end(`Create compiler`)
}
-
- classes.clear()
}
async function loadStylesheet(id: string, base: string) {
@@ -160,59 +118,25 @@ async function loadModule(): Promise {
throw new Error(`The browser build does not support plugins or config files.`)
}
-async function build() {
- if (!compiler) return
- // 1. Refresh the known list of classes
- let newClasses = new Set()
-
- I.start(`Collect classes`)
-
- for (let element of document.querySelectorAll('[class]')) {
- for (let c of element.classList) {
- if (classes.has(c)) continue
-
- classes.add(c)
- newClasses.add(c)
- }
- }
-
- I.end(`Collect classes`, {
- count: newClasses.size,
- })
-
- // 2. Compile the CSS
- I.start(`Build utilities`)
-
- const result = compiler.build(Array.from(newClasses))
-
- I.end(`Build utilities`)
-
- return result;
-}
-
-async function rebuild(css: string) {
+async function rebuild(classes: Set) {
async function run() {
- let buildId = nextBuildId++
-
- I.start(`Build #${buildId}`)
-
- await createCompiler(css)
-
+ if (!compiler) {
+ await createCompiler()
+ }
I.start(`Build`)
- const result = await build();
+ const result = compiler.build(Array.from(classes))
I.end(`Build`)
- I.end(`Build #${buildId}`)
+ I.end(`Build`)
return result ?? '';
}
try {
- buildQueue = buildQueue.then(run);
- return await buildQueue;
+ return await Promise.resolve().then(run);;
} catch (error) {
I.error(error);
}