-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve @tailwindcss/postcss
performance for initial builds
#14565
Conversation
ca1390d
to
931f927
Compare
5b74a25
to
b6eb194
Compare
Pushed a few more changes to make CI pass. Some things I noticed and that the new commits solve:
So these new changes need a second pair of eyes before we can merge this. |
I am not 100% sure, but I believe this gets rid of at least 1 macroTask.
Otherwise it means that we: 1. Setup 2 compilers for an initial build 2. The second time we clear require caches which means that _everything_ will be loaded from scratch.
aea94a6
to
67b2f51
Compare
During debugging I noticed that some of the files were in the list multiple times, using a Set solves that issues. While this is a little band-aid it helps for now. Following commits will include a more proper fix.
During debugging, I noticed that every time the PostCSS code runs, the full plugin re-runs. This means that caches are thrown away. I'm currently not sure if this is a test specific issue or an actual issue. That said, the integration tests do run postcss-cli for example directly so I think this is a real issue. This change should be safe since the cache is unique per input file (which it was before as well). Worst case is that we use a bit more memory.
Removing this means that the main file (e.g.: `tailwind.config.js`) is only in the list once. This also means that we only have to register it as a PostCSS dependency and it also means that we will `fs.stat` the file once.
67b2f51
to
0c1a873
Compare
let cache = new DefaultMap(() => { | ||
return { | ||
mtimes: new Map<string, number>(), | ||
compiler: null as null | Awaited<ReturnType<typeof compile>>, | ||
css: '', | ||
optimizedCss: '', | ||
fullRebuildPaths: [] as string[], | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing I can think of here is that previously this was scoped for the opts
argument to the tailwindcss
function and now it's not, so now the cache is shared for the same key even if the options are different.
Interestingly the PostCSS docs explicitly say to set up caches in the place we were setting it up before:
Do we know with absolute certainty it was getting re-created from scratch on every save? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I apply this diff to our plugin:
diff --git a/packages/@tailwindcss-postcss/src/index.ts b/packages/@tailwindcss-postcss/src/index.ts
index 7d18ddc36..43b824d2f 100644
--- a/packages/@tailwindcss-postcss/src/index.ts
+++ b/packages/@tailwindcss-postcss/src/index.ts
@@ -50,6 +50,8 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
let base = opts.base ?? process.cwd()
let optimize = opts.optimize ?? process.env.NODE_ENV === 'production'
+ console.count('Setup caches here')
+
return {
postcssPlugin: '@tailwindcss/postcss',
plugins: [
@@ -65,6 +67,7 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
let inputFile = result.opts.from ?? ''
let context = cache.get(inputFile)
let inputBasePath = path.dirname(path.resolve(inputFile))
+ console.count('Run @tailwindcss/postcss')
async function createCompiler() {
env.DEBUG && console.time('[@tailwindcss/postcss] Setup compiler')
If I then use it, the output looks like this:
You have the initial build, then 2 builds because of watch mode and because I made 2 changes.
Some notes / observations:
- Not only is the
Setup caches here
log called every time we save, it's called twice for an unknown reason. - This is using the
postcss-cli
package
Next steps are to verify if this behaviour also happens in other environments where the PostCSS plugin is being used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some updates for completeness:
If you structure your PostCSS config like this:
module.exports = {
plugins: {
'@tailwindcss/postcss': {},
},
}
or this:
module.exports = {
plugins: [
require('@tailwindcss/postcss')
],
}
Then the above behavior can be observer. If you call your function (even without options) then you see the correct behavior:
module.exports = {
plugins: [
require('@tailwindcss/postcss')()
],
}
I might be running ahead of the train, but can this change (theoretically) be ported to v3? If so, what do maintainers think about it? Every standalone run (read CI/CD cases) should be faster with it. IMO it deserves to be present in the latest stable branch |
@ZuBB This isn't really relevant in v3 — completely different codebase and all of the caching we do there is already at the module scope level, so this is just an improvement over how v4 was working prior to this. |
okay, I get it. thanks for the response |
There’ve been some improvements in compatibility and perf in a few of them
0b3f8e7
to
15bf21b
Compare
detectSources: { base }, | ||
sources: context.compiler.globs, | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to recreate the scanner when doing a full rebuild too, right? Since the globs might've changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
basically after line 166 we also want to re-create the scanner
We accidentally removed this in #14565.
This PR improves the performance of the
@tailwindcss/postcss
plugin. Before this change we created 2 compiler instances instead of a single one. On a project where atailwindcss.config.ts
file is used, this means that the timings look like this:This means that with this small change, we can easily shave of ~50ms for initial PostCSS builds.