This PR improves the performance of the `@tailwindcss/postcss` and `@tailwindcss/vite` implementations. The issue is that in some scenarios, if you have multiple `.css` files, then all of the CSS files are ran through the Tailwind CSS compiler. The issue with this is that in a lot of cases, the CSS files aren't even related to Tailwind CSS at all. E.g.: in a Next.js project, if you use the `next/font/local` tool, then every font you used will be in a separate CSS file. This means that we run Tailwind CSS in all these files as well. That said, running Tailwind CSS on these files isn't the end of the world because we still need to handle `@import` in case `@tailwind utilities` is being used. However, we also run the auto source detection logic for every CSS file in the system. This part is bad. To solve this, this PR introduces an internal `features` to collect what CSS features are used throughout the system (`@import`, `@plugin`, `@apply`, `@tailwind utilities`, etc…) The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that information to decide if they can take some shortcuts or not. --- Overall, this means that we don't run the slow parts of Tailwind CSS if we don't need to. --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
45 lines
1 KiB
TypeScript
45 lines
1 KiB
TypeScript
export const enum WalkAction {
|
|
// Continue walking the tree. Default behavior.
|
|
Continue,
|
|
|
|
// Skip walking into the current node.
|
|
Skip,
|
|
|
|
// Stop walking the tree entirely.
|
|
Stop,
|
|
}
|
|
|
|
interface Walkable<T> {
|
|
each(cb: (node: T, index: number) => void): void
|
|
}
|
|
|
|
// Custom walk implementation where we can skip going into nodes when we don't
|
|
// need to process them.
|
|
export function walk<T>(
|
|
rule: Walkable<T>,
|
|
cb: (rule: T, idx: number, parent: Walkable<T>) => void | WalkAction,
|
|
): undefined | false {
|
|
let result: undefined | false = undefined
|
|
|
|
rule.each?.((node, idx) => {
|
|
let action = cb(node, idx, rule) ?? WalkAction.Continue
|
|
if (action === WalkAction.Stop) {
|
|
result = false
|
|
return result
|
|
}
|
|
if (action !== WalkAction.Skip) {
|
|
result = walk(node as Walkable<T>, cb)
|
|
return result
|
|
}
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// Depth first walk reversal implementation.
|
|
export function walkDepth<T>(rule: Walkable<T>, cb: (rule: T) => void) {
|
|
rule?.each?.((node) => {
|
|
walkDepth(node as Walkable<T>, cb)
|
|
cb(node)
|
|
})
|
|
}
|