canonicalizeAst() — tailwindcss Function Reference
Architecture documentation for the canonicalizeAst() function in canonicalize-candidates.ts from the tailwindcss codebase.
Entity Profile
Dependency Diagram
graph TD 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01["canonicalizeAst()"] 6bc468c3_f692_fb93_b3e2_cf2a6051b874["createUtilitySignatureCache()"] 6bc468c3_f692_fb93_b3e2_cf2a6051b874 -->|calls| 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01 69cc8435_e606_a151_8be0_cd5f1378ad52["createUtilityPropertiesCache()"] 69cc8435_e606_a151_8be0_cd5f1378ad52 -->|calls| 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01 5e4110d6_8dee_8a97_307e_d0021e0225de["expandDeclaration()"] 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01 -->|calls| 5e4110d6_8dee_8a97_307e_d0021e0225de 88e17ede_e457_4c87_d3c8_c491a1795810["resolveVariablesInValue()"] 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01 -->|calls| 88e17ede_e457_4c87_d3c8_c491a1795810 3a1a48b0_f593_6ef7_a2a2_e2d97f76468f["constantFoldDeclaration()"] 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01 -->|calls| 3a1a48b0_f593_6ef7_a2a2_e2d97f76468f c6f44294_1faa_f8d2_1ba3_f57256e33fc7["printArbitraryValue()"] 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01 -->|calls| c6f44294_1faa_f8d2_1ba3_f57256e33fc7 24d95be4_356f_a1f9_9702_2a4f413db0f5["add()"] 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01 -->|calls| 24d95be4_356f_a1f9_9702_2a4f413db0f5 fd9d5dae_4dbc_46b8_74f1_c7c51a381d0a["walk()"] 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01 -->|calls| fd9d5dae_4dbc_46b8_74f1_c7c51a381d0a style 3c29a5ca_8e68_4fa2_4e91_392cf24c5a01 fill:#6366f1,stroke:#818cf8,color:#fff
Relationship Graph
Source Code
packages/tailwindcss/src/canonicalize-candidates.ts lines 2070–2171
function canonicalizeAst(designSystem: DesignSystem, ast: AstNode[], options: SignatureOptions) {
let { rem } = options
walk(ast, {
enter(node, ctx) {
// Optimize declarations
if (node.kind === 'declaration') {
if (node.value === undefined || node.property === '--tw-sort') {
return WalkAction.Replace([])
}
// Ignore `--tw-{property}` if `{property}` exists with the same value
if (node.property.startsWith('--tw-')) {
if (
(ctx.parent?.nodes ?? []).some(
(sibling) =>
sibling.kind === 'declaration' &&
node.value === sibling.value &&
node.important === sibling.important &&
!sibling.property.startsWith('--tw-'),
)
) {
return WalkAction.Replace([])
}
}
if (options.features & SignatureFeatures.ExpandProperties) {
let replacement = expandDeclaration(node, options.features)
if (replacement) return WalkAction.Replace(replacement)
}
// Resolve theme values to their inlined value.
if (node.value.includes('var(')) {
node.value = resolveVariablesInValue(node.value, designSystem)
}
// Very basic `calc(…)` constant folding to handle the spacing scale
// multiplier:
//
// Input: `--spacing(4)`
// → `calc(var(--spacing, 0.25rem) * 4)`
// → `calc(0.25rem * 4)` ← this is the case we will see
// after inlining the variable
// → `1rem`
node.value = constantFoldDeclaration(node.value, rem)
// We will normalize the `node.value`, this is the same kind of logic
// we use when printing arbitrary values. It will remove unnecessary
// whitespace.
//
// Essentially normalizing the `node.value` to a canonical form.
node.value = printArbitraryValue(node.value)
}
// Replace special nodes with its children
else if (node.kind === 'context' || node.kind === 'at-root') {
return WalkAction.Replace(node.nodes)
}
// Remove comments
else if (node.kind === 'comment') {
return WalkAction.Replace([])
}
// Remove at-rules that are not needed for the signature
else if (node.kind === 'at-rule' && node.name === '@property') {
return WalkAction.Replace([])
}
},
exit(node) {
if (node.kind === 'rule' || node.kind === 'at-rule') {
// Remove declarations that are re-defined again later.
//
// This could maybe result in unwanted behavior (because similar
// properties typically exist for backwards compatibility), but for
// signature purposes we can assume that the last declaration wins.
if (node.nodes.length > 1) {
let seen = new Set<string>()
for (let i = node.nodes.length - 1; i >= 0; i--) {
let child = node.nodes[i]
if (child.kind !== 'declaration') continue
if (child.value === undefined) continue
if (seen.has(child.property)) {
node.nodes.splice(i, 1)
}
seen.add(child.property)
}
}
// Sort declarations alphabetically by property name
node.nodes.sort((a, b) => {
if (a.kind !== 'declaration') return 0
if (b.kind !== 'declaration') return 0
return a.property.localeCompare(b.property)
})
}
},
})
return ast
}
Domain
Subdomains
Calls
Source
Frequently Asked Questions
What does canonicalizeAst() do?
canonicalizeAst() is a function in the tailwindcss codebase.
What does canonicalizeAst() call?
canonicalizeAst() calls 6 function(s): add, constantFoldDeclaration, expandDeclaration, printArbitraryValue, resolveVariablesInValue, walk.
What calls canonicalizeAst()?
canonicalizeAst() is called by 2 function(s): createUtilityPropertiesCache, createUtilitySignatureCache.
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free