do not add Tailwind compiled CSS in code blocks#879
Conversation
✅ Deploy Preview for livecodes ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
WalkthroughThe compiler’s postProcess loop now unconditionally skips Tailwind CSS, UnoCSS, and WindiCSS processors. Tailwind processors were updated to wrap generated CSS with any inline <style> block contents from the related HTML via a new utility, addCodeInStyleBlocks. Tailwind v4’s prepareCode now accepts HTML and applies the same wrapping. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Compiler
participant Processors
participant Tailwind as Tailwind/Uno/Windi
Compiler->>Processors: Iterate postProcess(processor)
alt processor.name in {tailwindcss, unocss, windicss}
note right of Compiler: Skip postProcess for style frameworks
Compiler-->>Tailwind: Bypassed
else
Compiler->>Processors: Execute processor.postProcess()
end
sequenceDiagram
autonumber
participant Caller
participant TW3 as Tailwind v3
participant TW4 as Tailwind v4
participant Utils as addCodeInStyleBlocks
rect rgb(235, 245, 255)
Caller->>TW3: generate(css) with html
TW3->>Utils: addCodeInStyleBlocks(css, html)
Utils-->>TW3: css+inlineStyle
TW3-->>Caller: wrapped CSS (with html reference)
end
rect rgb(240, 255, 240)
Caller->>TW4: prepareCode(css, html)
TW4->>Utils: addCodeInStyleBlocks(resultCss, html)
Utils-->>TW4: css+inlineStyle
TW4-->>Caller: wrapped CSS
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
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. Comment |
|
|
Size Change: +262 B (+0.03%) Total Size: 956 kB ℹ️ View Unchanged
|
Deploying livecodes with
|
| Latest commit: |
5eca80e
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://bc4a6d43.livecodes.pages.dev |
| Branch Preview URL: | https://tailwind-blocks.livecodes.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/livecodes/compiler/compile-blocks.ts (1)
91-93: Intent aligned, but ensure style imports still get processed when Tailwind is active.Skipping tailwindcss/unocss/windicss here achieves “don’t place compiled CSS in style blocks.” However, with Tailwind active,
postcssRequiredmay remain false (Lines 84-88), so@importrewriting could be skipped for style blocks. Recommend enabling PostCSS for style imports regardless of Tailwind’s activation to preserve import rewriting.Suggested change (outside this hunk, Lines 84-88):
// let tailwindcss handle style imports if activated, otherwise use postcss // Before: if (editorId === 'style' && hasStyleImports(code) && !tailwindcssIsActive) { postcssRequired = true; } // After: process style imports even when Tailwind is active (since Tailwind is skipped here) if (editorId === 'style' && hasStyleImports(code)) { postcssRequired = true; }src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts (1)
190-192: Include script content cautiouslyEmbedding raw script inside a <script> tag can break the tag if content contains "</script>". If that occurs, scanning still works, but consider a safer wrapper (e.g., splitting the closing tag) or defaulting to empty string when absent.
Example:
const script = (config.script?.content || '').replaceAll('</script>', '<\\/script>'); const html = `<template>${options.html}\n<script>${script}</script></template>`;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/livecodes/compiler/compile-blocks.ts(1 hunks)src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts(2 hunks)src/livecodes/languages/tailwindcss/utils.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts (2)
src/livecodes/languages/tailwindcss/utils.ts (1)
addCodeInStyleBlocks(1-13)src/livecodes/compiler/import-map.ts (1)
replaceStyleImports(244-267)
🪛 ast-grep (0.39.5)
src/livecodes/languages/tailwindcss/utils.ts
[warning] 5-5: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(pattern, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: Redirect rules - livecodes
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Header rules - livecodes
- GitHub Check: Pages changed - livecodes
- GitHub Check: build
- GitHub Check: tests (18.x, 1)
- GitHub Check: tests (18.x, 5)
- GitHub Check: tests (18.x, 3)
- GitHub Check: tests (18.x, 4)
- GitHub Check: tests (18.x, 2)
- GitHub Check: build (18.x)
- GitHub Check: build (18.x)
🔇 Additional comments (3)
src/livecodes/languages/tailwindcss/utils.ts (1)
5-5: Regex from variable is safe here; consider precompiling only if perf becomes an issue.The pattern is static and controlled; ReDoS risk is low. No action required unless profiling shows hot-path costs.
src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts (2)
9-9: LGTM: utility importImporting addCodeInStyleBlocks centralizes handling of inline styles.
203-205: Good: pass HTML into both scanning and CSS prepFeeding the same HTML to prepareCode and scanner keeps candidates and inline-style merging consistent.
| // do not place compiled css for tailwind and similar in style blocks | ||
| if (['tailwindcss', 'unocss', 'windicss'].includes(processor.name)) continue; | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
Double-check UnoCSS/WindiCSS parity
We’re skipping Unocss/WindiCSS here too. Please verify their processors (if present) also append inline <style> contents to the generated CSS (akin to Tailwind’s new utility), or else inline styles might be omitted from the final generated CSS.
🏁 Script executed:
#!/bin/bash
# Find Unocss/Windi processors and check if they use addCodeInStyleBlocks or similar
rg -n -C3 -i 'unocss|windicss|windi|create.*(uno|windi)' --type=ts
rg -n -C2 'addCodeInStyleBlocks' --type=tsLength of output: 24237
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "==== compile-blocks.ts ===="
nl -ba src/livecodes/compiler/compile-blocks.ts | sed -n '1,220p'
echo
echo "==== unocss processor ===="
nl -ba src/livecodes/languages/unocss/processor-unocss-compiler.ts | sed -n '1,320p'
echo
echo "==== windicss processor ===="
nl -ba src/livecodes/languages/windicss/processor-windicss-compiler.ts | sed -n '1,320p'
echo
echo "==== tailwind utils (for reference) ===="
nl -ba src/livecodes/languages/tailwindcss/utils.ts | sed -n '1,240p'Length of output: 68
🏁 Script executed:
#!/bin/bash
set -euo pipefail
files=(
"src/livecodes/compiler/compile-blocks.ts"
"src/livecodes/languages/unocss/processor-unocss-compiler.ts"
"src/livecodes/languages/windicss/processor-windicss-compiler.ts"
"src/livecodes/languages/tailwindcss/utils.ts"
"src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts"
)
for f in "${files[@]}"; do
echo "==== $f ===="
if [ -f "$f" ]; then
awk '{printf "%4d: %s\n", NR, $0}' "$f" | sed -n '1,240p'
else
echo "MISSING: $f"
fi
echo
doneLength of output: 21308
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "==== locate compile-in-compiler.ts ===="
rg -n --hidden "export const compileInCompiler|compileInCompiler" || true
echo
echo "==== src/livecodes/compiler/compile-in-compiler.ts ===="
if [ -f src/livecodes/compiler/compile-in-compiler.ts ]; then
awk '{printf "%4d: %s\n", NR, $0}' src/livecodes/compiler/compile-in-compiler.ts | sed -n '1,320p'
else
echo "MISSING: src/livecodes/compiler/compile-in-compiler.ts"
fi
echo
echo "==== search for options.html usage ===="
rg -n "options\\.html" || true
echo
echo "==== search for where compileInCompiler is called with options ===="
rg -n "compileInCompiler\\(" -S || trueLength of output: 9164
🏁 Script executed:
#!/bin/bash
set -euo pipefail
files=(
"src/livecodes/compiler/compile.worker.ts"
"src/livecodes/compiler/compiler-utils.ts"
"src/livecodes/compiler/compile-in-compiler.ts"
)
for f in "${files[@]}"; do
echo "==== $f ===="
if [ -f "$f" ]; then
awk '{printf "%4d: %s\n", NR, $0}' "$f" | sed -n '1,360p'
else
echo "MISSING: $f"
fi
echo
done
echo "==== locations of 'options.html' occurrences ===="
rg -n "options\\.html" || true
echo
echo "==== search for where worker handles 'compileInCompiler' or builds options ===="
rg -n "compileInCompiler|compileInWorker|compile\\(" src/livecodes -S || trueLength of output: 26160
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "==== search for 'window.compilers' ===="
rg -n "window\\.compilers" || true
echo
echo "==== search for 'compilers[' usage on window ===="
rg -n "compilers\\[" || true
echo
echo "==== list files in src/livecodes/languages ===="
ls -1 src/livecodes/languages | sed -n '1,200p'Length of output: 2569
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "==== src/livecodes/compiler/import-map.ts (lines 1-360) ===="
if [ -f src/livecodes/compiler/import-map.ts ]; then
awk '{printf "%4d: %s\n", NR, $0}' src/livecodes/compiler/import-map.ts | sed -n '1,360p'
else
echo "MISSING: src/livecodes/compiler/import-map.ts"
fi
echo
echo "==== search for 'tailwindcss' or 'unocss' in import-map.ts ===="
rg -n "tailwindcss|unocss|windicss" src/livecodes/compiler/import-map.ts || trueLength of output: 13675
Fix UnoCSS/WindiCSS inline <style> handling — compile-blocks currently skips ['tailwindcss','unocss','windicss'] (src/livecodes/compiler/compile-blocks.ts:90–92). Tailwind explicitly pulls inline <style> contents via addCodeInStyleBlocks; Unocss (src/livecodes/languages/unocss/processor-unocss-compiler.ts) generates only from options.html and won't see inline <style> content, and WindiCSS (src/livecodes/languages/windicss/processor-windicss-compiler.ts) expects the passed CSS/html but is still skipped. Either remove unocss/windicss from the skip list or update their compilers to incorporate inline <style> contents (e.g., call addCodeInStyleBlocks or equivalent) to avoid dropping inline styles.
🤖 Prompt for AI Agents
In src/livecodes/compiler/compile-blocks.ts around lines 91 to 93, the current
if-statement skips ['tailwindcss','unocss','windicss'], which drops inline
<style> content for UnoCSS and WindiCSS; change the logic so only 'tailwindcss'
is skipped, and for 'unocss' and 'windicss' ensure their processors receive
inline styles by calling addCodeInStyleBlocks (or equivalent) before compilation
so inline <style> contents are passed into their compilers rather than being
ignored.
| const prepareCode = (css: string, html: string) => { | ||
| let result = replaceStyleImports(css, [/tailwindcss/g]); | ||
| if (!result.includes('@import')) { | ||
| result = `@import "tailwindcss";${result}`; | ||
| } | ||
| return result; | ||
| return addCodeInStyleBlocks(result, html); | ||
| }; |
There was a problem hiding this comment.
Bug: Tailwind v4 import detection is too broad
if (!result.includes('@import')) skips injecting @import "tailwindcss"; when other imports exist (e.g., fonts), breaking Tailwind v4. Check specifically for Tailwind import.
Apply this diff:
-const prepareCode = (css: string, html: string) => {
- let result = replaceStyleImports(css, [/tailwindcss/g]);
- if (!result.includes('@import')) {
- result = `@import "tailwindcss";${result}`;
- }
- return addCodeInStyleBlocks(result, html);
-};
+const prepareCode = (css: string, html: string) => {
+ let result = replaceStyleImports(css, [/tailwindcss/g]);
+ const hasTailwindImport = /^\s*@import\s+["']tailwindcss["']\s*;?/m.test(result);
+ if (!hasTailwindImport) {
+ result = `@import "tailwindcss";\n${result}`;
+ }
+ return addCodeInStyleBlocks(result, html);
+};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const prepareCode = (css: string, html: string) => { | |
| let result = replaceStyleImports(css, [/tailwindcss/g]); | |
| if (!result.includes('@import')) { | |
| result = `@import "tailwindcss";${result}`; | |
| } | |
| return result; | |
| return addCodeInStyleBlocks(result, html); | |
| }; | |
| const prepareCode = (css: string, html: string) => { | |
| let result = replaceStyleImports(css, [/tailwindcss/g]); | |
| const hasTailwindImport = /^\s*@import\s+["']tailwindcss["']\s*;?/m.test(result); | |
| if (!hasTailwindImport) { | |
| result = `@import "tailwindcss";\n${result}`; | |
| } | |
| return addCodeInStyleBlocks(result, html); | |
| }; |
| export const addCodeInStyleBlocks = (css: string, html: string) => { | ||
| // from compiler/compile-blocks.ts#compileBlocks | ||
| const getBlockPattern = (el: 'style', langAttr = 'lang') => | ||
| `(<${el}\\s*)(?:([\\s\\S]*?)${langAttr}\\s*=\\s*["']([A-Za-z0-9 _]*)["'])?((?:[^>]*)>)([\\s\\S]*?)(<\\/${el}>)`; | ||
| const pattern = getBlockPattern('style'); | ||
| for (const arr of [...html.matchAll(new RegExp(pattern, 'g'))]) { | ||
| const content = arr[5]; | ||
| if (content?.trim()) { | ||
| css += `\n${content}`; | ||
| } | ||
| } | ||
| return css; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Avoid appending non-CSS (<style lang="scss"/"less">) into Tailwind input; support type="text/css".
Current implementation appends all <style> contents, including preprocessor languages, which can break Tailwind v4 compilation. Also, it doesn’t consider type="text/css". Filter to CSS-only and handle both lang and type.
Apply this diff:
export const addCodeInStyleBlocks = (css: string, html: string) => {
- // from compiler/compile-blocks.ts#compileBlocks
- const getBlockPattern = (el: 'style', langAttr = 'lang') =>
- `(<${el}\\s*)(?:([\\s\\S]*?)${langAttr}\\s*=\\s*["']([A-Za-z0-9 _]*)["'])?((?:[^>]*)>)([\\s\\S]*?)(<\\/${el}>)`;
- const pattern = getBlockPattern('style');
- for (const arr of [...html.matchAll(new RegExp(pattern, 'g'))]) {
- const content = arr[5];
- if (content?.trim()) {
- css += `\n${content}`;
- }
- }
- return css;
+ // from compiler/compile-blocks.ts#compileBlocks
+ const getBlockPattern = (el: 'style', attr: 'lang' | 'type' = 'lang') =>
+ `(<${el}\\s*)(?:([\\s\\S]*?)${attr}\\s*=\\s*["']([A-Za-z0-9 _\\/-]*)["'])?((?:[^>]*)>)([\\s\\S]*?)(<\\/${el}>)`;
+ const seen = new Set<number>();
+ const collect = (attr: 'lang' | 'type') => {
+ const pattern = getBlockPattern('style', attr);
+ for (const m of html.matchAll(new RegExp(pattern, 'g'))) {
+ // de-dup: tags without the inspected attr match both patterns
+ if (m.index != null) {
+ if (seen.has(m.index)) continue;
+ seen.add(m.index);
+ }
+ const lang = (m[3] || '').trim().toLowerCase();
+ const isCss =
+ !lang ||
+ lang === 'css' ||
+ lang === 'postcss' ||
+ lang === 'text/css' ||
+ lang === 'text/postcss';
+ const content = m[5];
+ if (isCss && content?.trim()) {
+ css += `\n${content}`;
+ }
+ }
+ };
+ collect('lang');
+ collect('type');
+ return css;
};Note: If you deliberately want to include preprocessor contents, consider compiling blocks first and passing the compiled HTML here; otherwise, restrict to CSS to avoid syntax errors in Tailwind/LightningCSS.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const addCodeInStyleBlocks = (css: string, html: string) => { | |
| // from compiler/compile-blocks.ts#compileBlocks | |
| const getBlockPattern = (el: 'style', langAttr = 'lang') => | |
| `(<${el}\\s*)(?:([\\s\\S]*?)${langAttr}\\s*=\\s*["']([A-Za-z0-9 _]*)["'])?((?:[^>]*)>)([\\s\\S]*?)(<\\/${el}>)`; | |
| const pattern = getBlockPattern('style'); | |
| for (const arr of [...html.matchAll(new RegExp(pattern, 'g'))]) { | |
| const content = arr[5]; | |
| if (content?.trim()) { | |
| css += `\n${content}`; | |
| } | |
| } | |
| return css; | |
| }; | |
| export const addCodeInStyleBlocks = (css: string, html: string) => { | |
| // from compiler/compile-blocks.ts#compileBlocks | |
| const getBlockPattern = (el: 'style', attr: 'lang' | 'type' = 'lang') => | |
| `(<${el}\\s*)(?:([\\s\\S]*?)${attr}\\s*=\\s*["']([A-Za-z0-9 _\\/-]*)["'])?((?:[^>]*)>)([\\s\\S]*?)(<\\/${el}>)`; | |
| const seen = new Set<number>(); | |
| const collect = (attr: 'lang' | 'type') => { | |
| const pattern = getBlockPattern('style', attr); | |
| for (const m of html.matchAll(new RegExp(pattern, 'g'))) { | |
| // de-dup: tags without the inspected attr match both patterns | |
| if (m.index != null) { | |
| if (seen.has(m.index)) continue; | |
| seen.add(m.index); | |
| } | |
| const lang = (m[3] || '').trim().toLowerCase(); | |
| const isCss = | |
| !lang || | |
| lang === 'css' || | |
| lang === 'postcss' || | |
| lang === 'text/css' || | |
| lang === 'text/postcss'; | |
| const content = m[5]; | |
| if (isCss && content?.trim()) { | |
| css += `\n${content}`; | |
| } | |
| } | |
| }; | |
| collect('lang'); | |
| collect('type'); | |
| return css; | |
| }; |
🧰 Tools
🪛 ast-grep (0.39.5)
[warning] 5-5: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(pattern, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)



Summary by CodeRabbit
New Features
Bug Fixes