Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Use the right import base path when using the CLI to reading files from stdin ([#14522](https://github.com/tailwindlabs/tailwindcss/pull/14522))
- Ensure that `@utility` is top-level and cannot be nested ([#14525](https://github.com/tailwindlabs/tailwindcss/pull/14525))
- Editing imported CSS files should trigger a rebuild ([#14561](https://github.com/tailwindlabs/tailwindcss/pull/14561))
- Only setup a single compiler in `@tailwindcss/postcss` for initial builds ([#14565](https://github.com/tailwindlabs/tailwindcss/pull/14565))
- _Experimental_: Improve codemod output, keep CSS after last Tailwind directive unlayered ([#14512](https://github.com/tailwindlabs/tailwindcss/pull/14512))
- _Experimental_: Fix incorrect empty `layer()` at the end of `@import` at-rules when running codemods ([#14513](https://github.com/tailwindlabs/tailwindcss/pull/14513))
- _Experimental_: Do not wrap comment nodes in `@layer` when running codemods ([#14517](https://github.com/tailwindlabs/tailwindcss/pull/14517))
Expand Down
7 changes: 5 additions & 2 deletions packages/@tailwindcss-cli/src/commands/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,16 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
: process.cwd()
let fullRebuildPaths: string[] = []

function createCompiler(css: string) {
return compile(css, {
async function createCompiler(css: string) {
env.DEBUG && console.time('[@tailwindcss/cli] Setup compiler')
let compiler = await compile(css, {
base: inputBasePath,
onDependency(path) {
fullRebuildPaths.push(path)
},
})
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Setup compiler')
return compiler
}

// Compile the input
Expand Down
5 changes: 2 additions & 3 deletions packages/@tailwindcss-node/src/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import {
} from 'tailwindcss'
import { getModuleDependencies } from './get-module-dependencies'

export async function compile(
export function compile(
css: string,
{ base, onDependency }: { base: string; onDependency: (path: string) => void },
) {
return await _compile(css, {
return _compile(css, {
base,
async loadModule(id, base) {
return loadModule(id, base, onDependency)
Expand Down Expand Up @@ -60,7 +60,6 @@ export async function loadModule(id: string, base: string, onDependency: (path:
getModuleDependencies(resolvedPath),
])

onDependency(resolvedPath)
for (let file of moduleDependencies) {
onDependency(file)
}
Expand Down
33 changes: 21 additions & 12 deletions packages/@tailwindcss-postcss/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ export type PluginOptions = {
optimize?: boolean | { minify?: boolean }
}

let cache = new DefaultMap(() => {
return {
mtimes: new Map<string, number>(),
compiler: null as null | Awaited<ReturnType<typeof compile>>,
css: '',
optimizedCss: '',
fullRebuildPaths: [] as string[],
}
})
Copy link
Member

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:

image

Do we know with absolute certainty it was getting re-created from scratch on every save? 🤔

Copy link
Member Author

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:
image

You have the initial build, then 2 builds because of watch mode and because I made 2 changes.

Some notes / observations:

  1. Not only is the Setup caches here log called every time we save, it's called twice for an unknown reason.
  2. 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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can confirm that this behaviour also exists in a vite project with the PostCSS plugin:
image

Copy link
Member Author

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')()
  ],
}


function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
let base = opts.base ?? process.cwd()
let optimize = opts.optimize ?? process.env.NODE_ENV === 'production'

let cache = new DefaultMap(() => {
return {
mtimes: new Map<string, number>(),
compiler: null as null | Awaited<ReturnType<typeof compile>>,
css: '',
optimizedCss: '',
fullRebuildPaths: [] as string[],
}
})

return {
postcssPlugin: '@tailwindcss/postcss',
plugins: [
Expand All @@ -72,7 +72,7 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {

context.fullRebuildPaths = []

let compiler = compile(root.toString(), {
let compiler = await compile(root.toString(), {
base: inputBasePath,
onDependency: (path) => {
context.fullRebuildPaths.push(path)
Expand All @@ -83,6 +83,10 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
return compiler
}

// Whether this is the first build or not, if it is, then we can
// optimize the build by not creating the compiler until we need it.
let isInitialBuild = context.compiler === null

// Setup the compiler if it doesn't exist yet. This way we can
// guarantee a `build()` function is available.
context.compiler ??= await createCompiler()
Expand Down Expand Up @@ -158,7 +162,12 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
})
}

if (rebuildStrategy === 'full') {
if (
rebuildStrategy === 'full' &&
// We can re-use the compiler if it was created during the
// initial build. If it wasn't, we need to create a new one.
!isInitialBuild
) {
context.compiler = await createCompiler()
}

Expand Down
2 changes: 2 additions & 0 deletions packages/@tailwindcss-vite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,15 @@ class Root {
clearRequireCache(Array.from(this.dependencies))
this.dependencies = new Set([idToPath(inputPath)])

env.DEBUG && console.time('[@tailwindcss/vite] Setup compiler')
this.compiler = await compile(content, {
base: inputBase,
onDependency: (path) => {
addWatchFile(path)
this.dependencies.add(path)
},
})
env.DEBUG && console.timeEnd('[@tailwindcss/vite] Setup compiler')

this.scanner = new Scanner({
sources: this.compiler.globs,
Expand Down