Skip to content
This repository was archived by the owner on Apr 6, 2021. It is now read-only.

Better multi-build support #10

Merged
merged 3 commits into from
Mar 7, 2021
Merged

Better multi-build support #10

merged 3 commits into from
Mar 7, 2021

Conversation

adamwathan
Copy link
Member

This PR reintroduces some previously removed code necessary for reusing contexts in environments where you are processing many inputs with the same Tailwind config (for example a Vue app where you have lots of Vue components with <style> blocks).

Since build tools run PostCSS in isolation for each input, if we only track contexts by the sourcePath, every single Vue file would get its own context (including its own chokidar watchers, and copy of the class cache, etc.). This leads to lots of wasted memory and CPU.

Figuring out exactly when it is safe to reuse a context and when it is not is sort of tricky and I'm not fully confident we've worked out all the quirks here but in my testing it seems reliable.

The general idea is that we track contexts by their source path, but we also track them by a hash of the Tailwind config file. If we don't find a matching context for the source path, we look to see if we already have a context matching the config hash. If we do, we use that context for this source path, and update all relevant maps.

If we don't find a cached version we can use, but the context map does have something for the current sourcePath, we need to clean that up. We fetch the old context, then we remove the current sourcePath from a set that's storing all source paths using that context, and if after removing our source the set is empty, we know we need to GC the context, so we delete all references to it and clean up its watchers.

The big breakthrough for solving this was realizing that we don't need to invalidate a context even if the CSS file has changed as long as the CSS file contains no @tailwind rules. Prior to this things were broken af because we were considering all CSS file changes to be triggers for invalidating the context. The thinking here was that because CSS files contribute to the config, changing a CSS file was essentially changing the config. This is only true though for @layer rules, because @layer rules are converted into plugins when the context is initialized. Now since @layer rules only have any impact in files that have @tailwind rules, we can safely reuse the context for any file that doesn't contain @tailwind.

What should we do if two files contain @tailwind rules but otherwise try to share the same context? I don't know, likely error. One solution is to not process @layer rules at init time and instead always process them at run time, then the CSS file would never be a context dependency. This would be slightly slower though since we can't cache the work, which is annoying. It would also introduce complexity in code paths like processing @apply because that would need to check two possible locations for matches. Also complicates how we handle sorting since we can't calculate the bit offset as easily (may need to switch to fixed reserved bit system if we did that).

Either way this is working for any practical situation I can imagine currently. We'll let users find out where it's broken if there are situations I haven't thought to try.

This is hard, no idea if we can make this work, I can't figure out how to share contexts between Vue files that use the same config.
@adamwathan adamwathan merged commit 96850da into master Mar 7, 2021
@adamwathan adamwathan deleted the vue-support branch March 7, 2021 00:34
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant