From 6febfd966a4c2c5f16a5301dfb86e619ab1f19cd Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 16 Apr 2025 17:48:26 -0400 Subject: [PATCH] Track locations when printing --- packages/tailwindcss/src/ast.bench.ts | 28 ++++++ packages/tailwindcss/src/ast.ts | 140 +++++++++++++++++++++++++- packages/tailwindcss/src/index.ts | 2 +- 3 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 packages/tailwindcss/src/ast.bench.ts diff --git a/packages/tailwindcss/src/ast.bench.ts b/packages/tailwindcss/src/ast.bench.ts new file mode 100644 index 000000000..a2d8b2892 --- /dev/null +++ b/packages/tailwindcss/src/ast.bench.ts @@ -0,0 +1,28 @@ +import { bench } from 'vitest' +import { toCss } from './ast' +import * as CSS from './css-parser' + +const css = String.raw +const input = css` + @theme { + --color-primary: #333; + } + @tailwind utilities; + .foo { + color: red; + /* comment */ + &:hover { + color: blue; + @apply font-bold; + } + } +` +const ast = CSS.parse(input) + +bench('toCss', () => { + toCss(ast) +}) + +bench('toCss with source maps', () => { + toCss(ast, true) +}) diff --git a/packages/tailwindcss/src/ast.ts b/packages/tailwindcss/src/ast.ts index a3f1835b0..e4b5d5a9a 100644 --- a/packages/tailwindcss/src/ast.ts +++ b/packages/tailwindcss/src/ast.ts @@ -714,7 +714,14 @@ export function optimizeAst( return newAst } -export function toCss(ast: AstNode[]) { +export function toCss(ast: AstNode[], track?: boolean) { + let pos = 0 + + let source: Source = { + file: null, + code: '', + } + function stringify(node: AstNode, depth = 0): string { let css = '' let indent = ' '.repeat(depth) @@ -722,15 +729,70 @@ export function toCss(ast: AstNode[]) { // Declaration if (node.kind === 'declaration') { css += `${indent}${node.property}: ${node.value}${node.important ? ' !important' : ''};\n` + + if (track) { + // indent + pos += indent.length + + // node.property + let start = pos + pos += node.property.length + + // `: ` + pos += 2 + + // node.value + pos += node.value?.length ?? 0 + + // !important + if (node.important) { + pos += 11 + } + + let end = pos + + // `;\n` + pos += 2 + + node.dst = [source, start, end] + } } // Rule else if (node.kind === 'rule') { css += `${indent}${node.selector} {\n` + + if (track) { + // indent + pos += indent.length + + // node.selector + let start = pos + pos += node.selector.length + + // ` ` + pos += 1 + + let end = pos + node.dst = [source, start, end] + + // `{\n` + pos += 2 + } + for (let child of node.nodes) { css += stringify(child, depth + 1) } + css += `${indent}}\n` + + if (track) { + // indent + pos += indent.length + + // `}\n` + pos += 2 + } } // AtRule @@ -744,19 +806,93 @@ export function toCss(ast: AstNode[]) { // ``` if (node.nodes.length === 0) { let css = `${indent}${node.name} ${node.params};\n` + + if (track) { + // indent + pos += indent.length + + // node.name + let start = pos + pos += node.name.length + + // ` ` + pos += 1 + + // node.params + pos += node.params.length + let end = pos + + // `;\n` + pos += 2 + + node.dst = [source, start, end] + } + return css } css += `${indent}${node.name}${node.params ? ` ${node.params} ` : ' '}{\n` + + if (track) { + // indent + pos += indent.length + + // node.name + let start = pos + pos += node.name.length + + if (node.params) { + // ` ` + pos += 1 + + // node.params + pos += node.params.length + } + + // ` ` + pos += 1 + + let end = pos + node.dst = [source, start, end] + + // `{\n` + pos += 2 + } + for (let child of node.nodes) { css += stringify(child, depth + 1) } + css += `${indent}}\n` + + if (track) { + // indent + pos += indent.length + + // `}\n` + pos += 2 + } } // Comment else if (node.kind === 'comment') { css += `${indent}/*${node.value}*/\n` + + if (track) { + // indent + pos += indent.length + + // The comment itself. We do this instead of just the inside because + // it seems more useful to have the entire comment span tracked. + let start = pos + pos += 2 + node.value.length + 2 + let end = pos + + node.dst = [source, start, end] + + // `\n` + pos += 1 + } } // These should've been handled already by `optimizeAst` which @@ -780,6 +916,8 @@ export function toCss(ast: AstNode[]) { css += stringify(node, 0) } + source.code = css + return css } diff --git a/packages/tailwindcss/src/index.ts b/packages/tailwindcss/src/index.ts index 04a99b927..b109713ee 100644 --- a/packages/tailwindcss/src/index.ts +++ b/packages/tailwindcss/src/index.ts @@ -814,7 +814,7 @@ export async function compile( return compiledCss } - compiledCss = toCss(newAst) + compiledCss = toCss(newAst, !!opts.from) compiledAst = newAst return compiledCss