UNPKG

@0x1js/tailwind-handler

Version:

Lightning-fast Tailwind CSS processor achieving <50ms builds through intelligent caching and incremental processing. Built for Bun 1.2+ with native performance optimizations.

14 lines (12 loc) 12.5 kB
// @bun class N{projectPath;config;constructor(q,x){this.projectPath=q,this.config=x}async process(){let q=performance.now();try{return{css:await this.generateRealTailwindCSS(),fromCache:!1,processingTime:performance.now()-q}}catch(x){return console.debug(`[TailwindHandler] All strategies failed: ${x}`),{css:this.getMinimalCSS(),fromCache:!1,processingTime:performance.now()-q}}}async generateRealTailwindCSS(){try{let q=await this.tryTailwindV4PostCSS();if(q&&q.length>5000)return console.debug(`[TailwindHandler] Tailwind v4 success: ${q.length} bytes`),q}catch(q){console.debug(`[TailwindHandler] Tailwind v4 failed: ${q}`)}try{let{cssContent:q,filePath:x}=await this.findExistingTailwindCSS();if(q&&x){if(console.debug(`[TailwindHandler] Found CSS file: ${x} (${q.length} bytes)`),this.containsTailwindDirectives(q)){console.debug("[TailwindHandler] CSS contains Tailwind directives, processing..."),console.debug(`[TailwindHandler] Detected directives: ${this.detectDirectives(q)}`);try{console.debug("[TailwindHandler] About to call processCSSWithTailwind...");let y=await this.processCSSWithTailwind(q,x);if(console.debug(`[TailwindHandler] processCSSWithTailwind returned: ${y?.length||0} bytes`),y&&y.length>5000)return console.debug(`[TailwindHandler] Processed CSS success: ${y.length} bytes`),y;else console.debug(`[TailwindHandler] Processed CSS too small: ${y?.length||0} bytes`)}catch(y){console.debug(`[TailwindHandler] CSS processing failed: ${y}`),console.debug(`[TailwindHandler] Error stack: ${y.stack}`)}}else console.debug("[TailwindHandler] No Tailwind directives detected in CSS"),console.debug(`[TailwindHandler] CSS preview: ${q.substring(0,200)}...`);if(q.length>5000)return console.debug(`[TailwindHandler] Using raw CSS: ${q.length} bytes`),q}}catch(q){console.debug(`[TailwindHandler] Existing CSS strategy failed: ${q}`)}try{let q=await this.tryTailwindCLI();if(q&&q.length>5000)return console.debug(`[TailwindHandler] CLI success: ${q.length} bytes`),q}catch(q){console.debug(`[TailwindHandler] CLI failed: ${q}`)}try{let q=await this.tryTailwindPostCSS();if(q&&q.length>5000)return console.debug(`[TailwindHandler] PostCSS success: ${q.length} bytes`),q}catch(q){console.debug(`[TailwindHandler] PostCSS failed: ${q}`)}throw new Error("No Tailwind strategies worked")}async detectTailwindV4(){let q=[`${this.projectPath}/package.json`,`${this.projectPath}/../0x1/package.json`];for(let x of q)try{console.debug(`[TailwindHandler] Checking package.json at: ${x}`);let y=await Bun.file(x).json(),z={...y.dependencies,...y.devDependencies};if(console.debug(`[TailwindHandler] Found deps at ${x}: tailwindcss=${z.tailwindcss}, @tailwindcss/postcss=${z["@tailwindcss/postcss"]}, @tailwindcss/cli=${z["@tailwindcss/cli"]}`),!!(z["@tailwindcss/postcss"]||z["@tailwindcss/cli"]||z.tailwindcss&&(z.tailwindcss.includes("4.")||z.tailwindcss.includes("^4.")||z.tailwindcss.includes("next"))))return console.debug(`[TailwindHandler] Tailwind v4 detected at: ${x}`),!0}catch(y){console.debug(`[TailwindHandler] Error checking ${x}: ${y}`);continue}return console.debug("[TailwindHandler] No Tailwind v4 detected in any package.json"),!1}async tryTailwindV4PostCSS(){try{if(!await this.detectTailwindV4())throw new Error("Tailwind v4 not detected");console.debug("[TailwindHandler] Detected Tailwind v4, trying PostCSS processing");let x=this.findV4InputFile();if(!x)throw new Error("No v4 CSS input file found");console.debug(`[TailwindHandler] Using v4 input file: ${x}`);let y=`${this.projectPath}/node_modules/postcss`,z=`${this.projectPath}/node_modules/@tailwindcss/postcss`,D=await import(y),E=await import(z),A=await Bun.file(x).text();console.debug(`[TailwindHandler] Processing v4 CSS: ${A.length} bytes input`);let K=await D.default([E.default]).process(A,{from:x,to:void 0});return console.debug(`[TailwindHandler] v4 processing complete: ${K.css.length} bytes output`),K.css}catch(q){throw new Error(`Tailwind v4 PostCSS failed: ${q}`)}}findV4InputFile(){let q=["app/globals.css","src/app/globals.css","src/globals.css","styles/globals.css","app/global.css","styles/main.css","css/main.css","public/styles.css"];for(let x of q)try{let y=`${this.projectPath}/${x}`;try{return Bun.file(y).stream(),y}catch{continue}}catch{continue}return null}async tryTailwindCLI(){let q=[["npx","@tailwindcss/cli"],["bunx","@tailwindcss/cli"],["npx","tailwindcss"],["bunx","tailwindcss"],["./node_modules/.bin/tailwindcss"],["tailwindcss"]];for(let x of q)try{console.debug(`[TailwindHandler] Trying command: ${x.join(" ")}`);let y=Bun.spawn([...x,"--input","-","--output","-"],{stdin:"pipe",stdout:"pipe",stderr:"pipe",cwd:this.projectPath}),D=x.includes("@tailwindcss/cli")?'@import "tailwindcss";':`@tailwind base; @tailwind components; @tailwind utilities; /* Ensure we get substantial output */ @layer utilities { .content-auto { content-visibility: auto; } }`;if(y.stdin?.write(D),y.stdin?.end(),await y.exited===0){let A=await new Response(y.stdout).text();if(A.length>5000)return A}else{let A=await new Response(y.stderr).text();console.debug(`[TailwindHandler] ${x.join(" ")} failed: ${A}`)}}catch(y){console.debug(`[TailwindHandler] ${x.join(" ")} error: ${y}`);continue}throw new Error("All CLI commands failed")}async tryTailwindPostCSS(){try{let q=`${this.projectPath}/node_modules/postcss`,x=`${this.projectPath}/node_modules/tailwindcss`,y=await import(q),z=await import(x),D=y.default([z.default({content:this.config.content||["**/*.{html,js,ts,jsx,tsx}"],theme:{extend:{}}})]),E=`@tailwind base; @tailwind components; @tailwind utilities;`;return(await D.process(`@tailwind base; @tailwind components; @tailwind utilities;`,{from:void 0})).css}catch(q){throw new Error(`PostCSS import failed: ${q}`)}}async findExistingTailwindCSS(){let q=["app/globals.css","src/app/globals.css","src/globals.css","styles/globals.css","app/global.css","styles/main.css","css/main.css","dist/styles.css","public/styles.css","build/styles.css",".next/static/css/app/layout.css","styles/tailwind.css","css/tailwind.css"];for(let x of q)try{let y=`${this.projectPath}/${x}`,z=Bun.file(y);try{z.stream();let D=await z.text();if(D.length>100)return{cssContent:D,filePath:y}}catch{continue}}catch{continue}return{cssContent:null,filePath:null}}containsTailwindDirectives(q){return q.includes('@import "tailwindcss"')||q.includes("@tailwind")||q.includes("@layer")||q.includes("@apply")}async processCSSWithTailwind(q,x){let y=await this.detectTailwindV4();if(console.debug(`[TailwindHandler] Processing CSS with ${y?"Tailwind v4":"Tailwind v3"}`),y)try{let z=[`${this.projectPath}/node_modules/postcss`,`${this.projectPath}/../0x1/node_modules/postcss`,"postcss"],D=[`${this.projectPath}/node_modules/@tailwindcss/postcss`,`${this.projectPath}/../0x1/node_modules/@tailwindcss/postcss`,"@tailwindcss/postcss"],E,A,M,K;for(let I of z)try{console.debug(`[TailwindHandler] Trying PostCSS from: ${I}`),E=await import(I),M=I,console.debug(`[TailwindHandler] PostCSS imported successfully from: ${I}`);break}catch(Q){console.debug(`[TailwindHandler] PostCSS import failed from ${I}: ${Q}`)}for(let I of D)try{console.debug(`[TailwindHandler] Trying Tailwind v4 from: ${I}`),A=await import(I),K=I,console.debug(`[TailwindHandler] Tailwind v4 plugin imported successfully from: ${I}`);break}catch(Q){console.debug(`[TailwindHandler] Tailwind v4 import failed from ${I}: ${Q}`)}if(!E||!A)throw new Error(`Missing dependencies: postcss=${!!E}, tailwind=${!!A}`);console.debug("[TailwindHandler] Creating PostCSS processor...");let G=E.default([A.default]);console.debug("[TailwindHandler] PostCSS processor created");let L=await G.process(q,{from:x,to:void 0});return console.debug(`[TailwindHandler] PostCSS processing complete: ${L.css.length} bytes`),L.css}catch(z){throw console.debug(`[TailwindHandler] v4 processing detailed error: ${z}`),new Error(`Tailwind v4 processing failed: ${z}`)}else try{let z=[`${this.projectPath}/node_modules/postcss`,`${this.projectPath}/../0x1/node_modules/postcss`,"postcss"],D=[`${this.projectPath}/node_modules/tailwindcss`,`${this.projectPath}/../0x1/node_modules/tailwindcss`,"tailwindcss"],E,A;for(let G of z)try{console.debug(`[TailwindHandler] Trying PostCSS from: ${G}`),E=await import(G),console.debug(`[TailwindHandler] PostCSS imported successfully from: ${G}`);break}catch(L){console.debug(`[TailwindHandler] PostCSS import failed from ${G}: ${L}`)}for(let G of D)try{console.debug(`[TailwindHandler] Trying Tailwind v3 from: ${G}`),A=await import(G),console.debug(`[TailwindHandler] Tailwind v3 imported successfully from: ${G}`);break}catch(L){console.debug(`[TailwindHandler] Tailwind v3 import failed from ${G}: ${L}`)}if(!E||!A)throw new Error(`Missing dependencies: postcss=${!!E}, tailwind=${!!A}`);console.debug("[TailwindHandler] Creating PostCSS processor...");let M=E.default([A.default({content:this.config.content||["**/*.{html,js,ts,jsx,tsx}"],theme:{extend:{}}})]);console.debug("[TailwindHandler] PostCSS processor created");let K=await M.process(q,{from:x});return console.debug(`[TailwindHandler] PostCSS processing complete: ${K.css.length} bytes`),K.css}catch(z){throw console.debug(`[TailwindHandler] v3 processing detailed error: ${z}`),new Error(`Tailwind v3 processing failed: ${z}`)}}getMinimalCSS(){return"*,*::before,*::after{box-sizing:border-box;border:0 solid #e5e7eb}html{line-height:1.5;font-family:ui-sans-serif,system-ui,sans-serif}body{margin:0;line-height:inherit}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-4{gap:1rem}.p-2{padding:0.5rem}.p-3{padding:0.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-3{padding-left:0.75rem;padding-right:0.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:0.5rem;padding-bottom:0.5rem}.py-3{padding-top:0.75rem;padding-bottom:0.75rem}.m-2{margin:0.5rem}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-4{margin-bottom:1rem}.mt-4{margin-top:1rem}.w-full{width:100%}.w-auto{width:auto}.h-full{height:100%}.text-sm{font-size:0.875rem}.text-base{font-size:1rem}.text-lg{font-size:1.125rem}.text-xl{font-size:1.25rem}.text-2xl{font-size:1.5rem}.font-medium{font-weight:500}.font-bold{font-weight:700}.text-center{text-align:center}.text-white{color:#fff}.text-black{color:#000}.text-gray-600{color:#4b5563}.text-gray-900{color:#111827}.text-blue-500{color:#3b82f6}.bg-white{background-color:#fff}.bg-gray-100{background-color:#f3f4f6}.bg-blue-500{background-color:#3b82f6}.border{border-width:1px}.border-gray-200{border-color:#e5e7eb}.rounded{border-radius:0.25rem}.rounded-lg{border-radius:0.5rem}.shadow{box-shadow:0 1px 3px 0 rgb(0 0 0 / 0.1)}.shadow-lg{box-shadow:0 10px 15px -3px rgb(0 0 0 / 0.1)}.transition{transition-property:all;transition-duration:150ms}.cursor-pointer{cursor:pointer}@media (min-width:768px){.md\\:flex{display:flex}.md\\:text-xl{font-size:1.25rem}}.hover\\:bg-gray-100:hover{background-color:#f3f4f6}.hover\\:bg-blue-600:hover{background-color:#2563eb}.btn{display:inline-flex;align-items:center;padding:0.5rem 1rem;border-radius:0.375rem;font-weight:500;border:none;cursor:pointer}.btn-primary{background-color:#3b82f6;color:#fff}.card{background:#fff;border:1px solid #e5e7eb;border-radius:0.5rem;padding:1.5rem;box-shadow:0 1px 3px 0 rgb(0 0 0 / 0.1)}.container{width:100%;max-width:1200px;margin:0 auto;padding:0 1rem}"}async writeOutput(q,x){if(q)await Bun.write(q,x)}detectDirectives(q){let x=[];if(q.includes('@import "tailwindcss"'))x.push('@import "tailwindcss"');if(q.includes("@tailwind"))x.push("@tailwind");if(q.includes("@layer"))x.push("@layer");if(q.includes("@apply"))x.push("@apply");return x.join(", ")||"none"}}async function U(q,x={}){return new N(q,{content:x.content||["**/*.{html,js,ts,jsx,tsx}"],outputPath:x.outputPath})}async function W(q,x){try{let y=new N(q,{content:x.config?.content||x.content||["**/*.{html,js,ts,jsx,tsx}"],outputPath:x.outputPath}),z=await y.process();if(z.css&&x.outputPath)await y.writeOutput(x.outputPath,z.css);return{success:!0,css:z.css,processingTime:z.processingTime,fromCache:z.fromCache}}catch{return{success:!1,css:".flex{display:flex}.p-4{padding:1rem}",processingTime:0,fromCache:!1}}}var Y=N;export{W as processTailwindFast,Y as default,U as createTailwindHandler,N as TailwindHandler};