Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Jordan Pittman
b66ba4fdb7 fix 2024-03-06 18:41:12 -05:00
Jordan Pittman
61a622fa3c Add candidate cache implementation 2024-03-06 18:36:42 -05:00
3 changed files with 37 additions and 8 deletions

View file

@ -57,6 +57,8 @@ export function options() {
} satisfies Arg
}
let cache: Map<string, unknown> = new Map()
export async function handle(args: Result<ReturnType<typeof options>>) {
let base = path.resolve(args['--cwd'])
@ -96,7 +98,7 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
)
// Compile the input
let result = optimizeCss(compile(input, candidates), {
let result = optimizeCss(compile(input, candidates, cache), {
file: args['--input'] ?? 'input.css',
minify: args['--minify'],
})

View file

@ -6,18 +6,27 @@ import { DefaultMap } from './utils/default-map'
import { escape } from './utils/escape'
import type { Variants } from './variants'
type SortDetails = {
properties: number[]
variants: bigint
candidate: string
}
type CompileOptions = {
throwOnInvalidCandidate?: boolean
cache?: Map<string, [Candidate, AstNode, SortDetails]>
}
export function compileCandidates(
rawCandidates: string[],
designSystem: DesignSystem,
{ throwOnInvalidCandidate = false } = {},
{ throwOnInvalidCandidate = false, cache }: CompileOptions = {},
) {
// Ensure the candidates are sorted alphabetically
rawCandidates.sort()
let nodeSorting = new Map<
AstNode,
{ properties: number[]; variants: bigint; candidate: string }
>()
let nodeSorting = new Map<AstNode, SortDetails>()
let astNodes: AstNode[] = []
// A lazy map implementation that will return the variant if it exists. If it
@ -31,6 +40,14 @@ export function compileCandidates(
// Parse candidates and variants
for (let rawCandidate of rawCandidates) {
let existing = cache?.get(rawCandidate)
if (existing) {
candidates.set(existing[0], rawCandidate)
astNodes.push(existing[1])
nodeSorting.set(existing[1], existing[2])
continue
}
let candidate = parseCandidate(rawCandidate, designSystem.utilities, parsedVariants)
if (candidate === null) {
if (throwOnInvalidCandidate) {
@ -48,6 +65,9 @@ export function compileCandidates(
// Create the AST
next: for (let [candidate, rawCandidate] of candidates) {
let existing = cache?.get(rawCandidate)
if (existing) continue
let nodes: AstNode[] = []
// Handle arbitrary properties
@ -124,6 +144,8 @@ export function compileCandidates(
candidate: rawCandidate,
})
astNodes.push(node)
cache?.set(rawCandidate, [candidate, node, nodeSorting.get(node)!])
}
astNodes.sort((a, z) => {

View file

@ -6,7 +6,11 @@ import * as CSS from './css-parser'
import { buildDesignSystem } from './design-system'
import { Theme } from './theme'
export function compile(css: string, rawCandidates: string[]) {
export function compile(
css: string,
rawCandidates: string[],
cache: Map<string, unknown> = new Map(),
) {
let ast = CSS.parse(css)
if (process.env.NODE_ENV !== 'test') {
@ -90,7 +94,7 @@ export function compile(css: string, rawCandidates: string[]) {
// class CSS.
walk(ast, (node, { replaceWith }) => {
if (node.kind === 'rule' && node.selector === '@tailwind utilities') {
replaceWith(compileCandidates(rawCandidates, designSystem).astNodes)
replaceWith(compileCandidates(rawCandidates, designSystem, { cache: cache as any }).astNodes)
// Stop walking after finding `@tailwind utilities` to avoid walking all
// of the generated CSS. This means `@tailwind utilities` can only appear
// once per file but that's the intended usage at this point in time.
@ -110,6 +114,7 @@ export function compile(css: string, rawCandidates: string[]) {
{
// Parse the candidates to an AST that we can replace the `@apply` rule with.
let candidateAst = compileCandidates(candidates, designSystem, {
cache: cache as any,
throwOnInvalidCandidate: true,
}).astNodes