pretty print generated CSS by default (#13086)

The pretty printing is fairly basic, but it adds some space between
properties `color: red;`, adds newlines after `;`, `{`, and `}`.

Last but not least, it indents nested CSS based on depth with 2 spaces.
This commit is contained in:
Robin Malfait 2024-03-05 17:58:17 +01:00 • committed by GitHub
parent 86e71194e7
commit 191643f663
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 34 deletions

View file

@ -0,0 +1,15 @@
import { expect, it } from 'vitest'
import { toCss } from './ast'
import * as CSS from './css-parser'
it('should pretty print an AST', () => {
expect(toCss(CSS.parse('.foo{color:red;&:hover{color:blue;}}'))).toMatchInlineSnapshot(`
".foo {
color: red;
&:hover {
color: blue;
}
}
"
`)
})

View file

@ -73,50 +73,53 @@ export function walk(
export function toCss(ast: AstNode[]) {
let atRoots: string[] = []
return ast
.map(function stringify(node: AstNode): string {
let css = ''
// Rule
if (node.kind === 'rule') {
// Pull out `@at-root` rules to append later
if (node.selector === '@at-root') {
for (let child of node.nodes) {
atRoots.push(stringify(child))
}
return css
}
function stringify(node: AstNode, depth = 0): string {
let css = ''
// Print at-rules without nodes with a `;` instead of an empty block.
//
// E.g.:
//
// ```css
// @layer base, components, utilities;
// ```
if (node.selector[0] === '@' && node.nodes.length === 0) {
return `${node.selector};`
}
css += `${node.selector}{`
// Rule
if (node.kind === 'rule') {
// Pull out `@at-root` rules to append later
if (node.selector === '@at-root') {
for (let child of node.nodes) {
css += stringify(child)
atRoots.push(stringify(child, 0))
}
css += '}'
return css
}
// Comment
else if (node.kind === 'comment') {
css += `/*${node.value}*/\n`
// Print at-rules without nodes with a `;` instead of an empty block.
//
// E.g.:
//
// ```css
// @layer base, components, utilities;
// ```
if (node.selector[0] === '@' && node.nodes.length === 0) {
return `${' '.repeat(depth)}${node.selector};\n`
}
// Declaration
else if (node.property !== '--tw-sort' && node.value !== undefined && node.value !== null) {
css += `${node.property}:${node.value}${node.important ? '!important' : ''};`
css += `${' '.repeat(depth)}${node.selector} {\n`
for (let child of node.nodes) {
css += stringify(child, depth + 1)
}
css += `${' '.repeat(depth)}}\n`
}
return css
})
// Comment
else if (node.kind === 'comment') {
css += `${' '.repeat(depth)}/*${node.value}*/\n`
}
// Declaration
else if (node.property !== '--tw-sort' && node.value !== undefined && node.value !== null) {
css += `${' '.repeat(depth)}${node.property}: ${node.value}${node.important ? '!important' : ''};\n`
}
return css
}
return ast
.map((node) => stringify(node))
.concat(atRoots)
.join('\n')
}