cache all the things
This commit is contained in:
parent
6282e2b22e
commit
27e06c76d2
2 changed files with 111 additions and 56 deletions
|
|
@ -6,45 +6,34 @@ import { DefaultMap } from './utils/default-map'
|
|||
import { escape } from './utils/escape'
|
||||
import type { Variants } from './variants'
|
||||
|
||||
export function compileCandidates(
|
||||
rawCandidates: string[],
|
||||
export function createParsedVariants(
|
||||
designSystem: DesignSystem,
|
||||
{ throwOnInvalidCandidate = false } = {},
|
||||
) {
|
||||
let nodeSorting = new Map<
|
||||
AstNode,
|
||||
{ properties: number[]; variants: bigint; candidate: string }
|
||||
>()
|
||||
let astNodes: AstNode[] = []
|
||||
|
||||
): DefaultMap<string, Variant | null> {
|
||||
// A lazy map implementation that will return the variant if it exists. If it
|
||||
// doesn't exist yet, the raw string variant will be parsed and added to the
|
||||
// map.
|
||||
let parsedVariants: DefaultMap<string, Variant | null> = new DefaultMap((variant, map) => {
|
||||
return new DefaultMap((variant, map) => {
|
||||
return parseVariant(variant, designSystem.variants, map)
|
||||
})
|
||||
}
|
||||
|
||||
let candidates = new Map<Candidate, string>()
|
||||
|
||||
// Parse candidates and variants
|
||||
for (let rawCandidate of rawCandidates) {
|
||||
let candidate = parseCandidate(rawCandidate, designSystem.utilities, parsedVariants)
|
||||
if (candidate === null) {
|
||||
if (throwOnInvalidCandidate) {
|
||||
throw new Error(`Cannot apply unknown utility class: ${rawCandidate}`)
|
||||
}
|
||||
continue // Bail, invalid candidate
|
||||
}
|
||||
candidates.set(candidate, rawCandidate)
|
||||
}
|
||||
|
||||
// Sort the variants
|
||||
let variants = Array.from(parsedVariants.values()).sort((a, z) => {
|
||||
return designSystem.variants.compare(a, z)
|
||||
export function createParsedCandidates(
|
||||
designSystem: DesignSystem,
|
||||
parsedVariants: DefaultMap<string, Variant | null>,
|
||||
) {
|
||||
return new DefaultMap((candidate) => {
|
||||
return parseCandidate(candidate, designSystem.utilities, parsedVariants)
|
||||
})
|
||||
}
|
||||
|
||||
export function createParsedAstNodes(
|
||||
designSystem: DesignSystem,
|
||||
parsedCandidates: DefaultMap<string, Candidate | null>,
|
||||
) {
|
||||
return new DefaultMap((rawCandidate: string) => {
|
||||
let candidate = parsedCandidates.get(rawCandidate)
|
||||
if (candidate === null) return null
|
||||
|
||||
// Create the AST
|
||||
next: for (let [candidate, rawCandidate] of candidates) {
|
||||
let nodes: AstNode[] = []
|
||||
|
||||
// Handle arbitrary properties
|
||||
|
|
@ -53,12 +42,7 @@ export function compileCandidates(
|
|||
|
||||
// Build the node
|
||||
let compiledNodes = compileFn(candidate)
|
||||
if (compiledNodes === undefined) {
|
||||
if (throwOnInvalidCandidate) {
|
||||
throw new Error(`Cannot apply unknown utility class: ${rawCandidate}`)
|
||||
}
|
||||
continue next
|
||||
}
|
||||
if (compiledNodes === undefined) return null
|
||||
|
||||
nodes = compiledNodes
|
||||
}
|
||||
|
|
@ -73,12 +57,7 @@ export function compileCandidates(
|
|||
|
||||
// Build the node
|
||||
let compiledNodes = compileFn(candidate)
|
||||
if (compiledNodes === undefined) {
|
||||
if (throwOnInvalidCandidate) {
|
||||
throw new Error(`Cannot apply unknown utility class: ${rawCandidate}`)
|
||||
}
|
||||
continue next
|
||||
}
|
||||
if (compiledNodes === undefined) return null
|
||||
|
||||
nodes = compiledNodes
|
||||
}
|
||||
|
|
@ -95,23 +74,76 @@ export function compileCandidates(
|
|||
nodes,
|
||||
}
|
||||
|
||||
let variantOrder = 0n
|
||||
for (let variant of candidate.variants) {
|
||||
let result = applyVariant(node, variant, designSystem.variants)
|
||||
|
||||
// When the variant results in `null`, it means that the variant cannot be
|
||||
// applied to the rule. Discard the candidate and continue to the next
|
||||
// one.
|
||||
if (result === null) {
|
||||
if (throwOnInvalidCandidate) {
|
||||
throw new Error(`Cannot apply unknown utility class: ${rawCandidate}`)
|
||||
}
|
||||
continue next
|
||||
}
|
||||
if (result === null) return null
|
||||
}
|
||||
|
||||
// Track the variant order which is a number with each bit representing a
|
||||
// variant. This allows us to sort the rules based on the order of
|
||||
// variants used.
|
||||
return {
|
||||
node,
|
||||
propertySort,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function compileCandidates(
|
||||
rawCandidates: string[],
|
||||
designSystem: DesignSystem,
|
||||
{
|
||||
parsedVariants = createParsedVariants(designSystem),
|
||||
parsedCandidates = createParsedCandidates(designSystem, parsedVariants),
|
||||
parsedAstNodes = createParsedAstNodes(designSystem, parsedCandidates),
|
||||
invalidRawCandidates = new Set<string>(),
|
||||
throwOnInvalidCandidate = false,
|
||||
} = {},
|
||||
) {
|
||||
let nodeSorting = new Map<
|
||||
AstNode,
|
||||
{ properties: number[]; variants: bigint; candidate: string }
|
||||
>()
|
||||
let astNodes: AstNode[] = []
|
||||
let candidates = new Map<Candidate, string>()
|
||||
|
||||
// Parse candidates and variants
|
||||
for (let rawCandidate of rawCandidates) {
|
||||
let candidate = parsedCandidates.get(rawCandidate)
|
||||
if (candidate === null) {
|
||||
if (throwOnInvalidCandidate) {
|
||||
throw new Error(`Cannot apply unknown utility class: ${rawCandidate}`)
|
||||
}
|
||||
invalidRawCandidates.add(rawCandidate)
|
||||
continue // Bail, invalid candidate
|
||||
}
|
||||
candidates.set(candidate, rawCandidate)
|
||||
}
|
||||
|
||||
// Sort the variants
|
||||
let variants = Array.from(parsedVariants.values()).sort((a, z) => {
|
||||
return designSystem.variants.compare(a, z)
|
||||
})
|
||||
|
||||
// Create the AST
|
||||
next: for (let [candidate, rawCandidate] of candidates) {
|
||||
let astNode = parsedAstNodes.get(rawCandidate)
|
||||
if (astNode === null) {
|
||||
if (throwOnInvalidCandidate) {
|
||||
throw new Error(`Cannot apply unknown utility class: ${rawCandidate}`)
|
||||
}
|
||||
invalidRawCandidates.add(rawCandidate)
|
||||
continue next
|
||||
}
|
||||
|
||||
let { node, propertySort } = astNode
|
||||
|
||||
// Track the variant order which is a number with each bit representing a
|
||||
// variant. This allows us to sort the rules based on the order of
|
||||
// variants used.
|
||||
let variantOrder = 0n
|
||||
for (let variant of candidate.variants) {
|
||||
variantOrder |= 1n << BigInt(variants.indexOf(variant))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import { Features, transform } from 'lightningcss'
|
||||
import { version } from '../package.json'
|
||||
import { WalkAction, comment, decl, rule, toCss, walk, type AstNode, type Rule } from './ast'
|
||||
import { compileCandidates } from './compile'
|
||||
import {
|
||||
compileCandidates,
|
||||
createParsedAstNodes,
|
||||
createParsedCandidates,
|
||||
createParsedVariants,
|
||||
} from './compile'
|
||||
import * as CSS from './css-parser'
|
||||
import { buildDesignSystem } from './design-system'
|
||||
import { Theme } from './theme'
|
||||
|
|
@ -102,6 +107,9 @@ export function compile(
|
|||
}
|
||||
|
||||
let designSystem = buildDesignSystem(theme)
|
||||
let parsedVariants = createParsedVariants(designSystem)
|
||||
let parsedCandidates = createParsedCandidates(designSystem, parsedVariants)
|
||||
let parsedAstNodes = createParsedAstNodes(designSystem, parsedCandidates)
|
||||
|
||||
let tailwindUtilitiesNode: Rule | null = null
|
||||
|
||||
|
|
@ -112,7 +120,11 @@ export function compile(
|
|||
tailwindUtilitiesNode = node
|
||||
|
||||
// Set the `@tailwind utilities` nodes, to the actual generated CSS
|
||||
node.nodes = compileCandidates(rawCandidates, designSystem).astNodes
|
||||
node.nodes = compileCandidates(rawCandidates, designSystem, {
|
||||
parsedVariants,
|
||||
parsedCandidates,
|
||||
parsedAstNodes,
|
||||
}).astNodes
|
||||
|
||||
// Stop walking after finding `@tailwind utilities` to avoid walking all
|
||||
// of the generated CSS. This means `@tailwind utilities` can only appear
|
||||
|
|
@ -134,6 +146,9 @@ export function compile(
|
|||
{
|
||||
// Parse the candidates to an AST that we can replace the `@apply` rule with.
|
||||
let candidateAst = compileCandidates(candidates, designSystem, {
|
||||
parsedVariants,
|
||||
parsedCandidates,
|
||||
parsedAstNodes,
|
||||
throwOnInvalidCandidate: true,
|
||||
}).astNodes
|
||||
|
||||
|
|
@ -176,15 +191,23 @@ export function compile(
|
|||
|
||||
// TODO: Don't do this unless rebuilding?
|
||||
let allCandidates = new Set(rawCandidates)
|
||||
let invalidRawCandidates = new Set<string>()
|
||||
|
||||
return {
|
||||
rebuild(newRawCandidates: string[]) {
|
||||
for (let candidate of newRawCandidates) {
|
||||
allCandidates.add(candidate)
|
||||
if (!invalidRawCandidates.has(candidate)) {
|
||||
allCandidates.add(candidate)
|
||||
}
|
||||
}
|
||||
|
||||
if (tailwindUtilitiesNode) {
|
||||
let newNodes = compileCandidates(Array.from(allCandidates), designSystem).astNodes
|
||||
let newNodes = compileCandidates(Array.from(allCandidates), designSystem, {
|
||||
parsedVariants,
|
||||
parsedCandidates,
|
||||
parsedAstNodes,
|
||||
invalidRawCandidates,
|
||||
}).astNodes
|
||||
tailwindUtilitiesNode.nodes = newNodes
|
||||
}
|
||||
return toCss(ast)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue