nuxt-modules / tailwindcss

Tailwind CSS module for Nuxt
https://tailwindcss.nuxtjs.org
MIT License
1.6k stars 180 forks source link

Pre-transform error in dev mode #802

Closed kikuchan closed 3 months ago

kikuchan commented 3 months ago

Version

@nuxtjs/tailwindcss: v6.11.3 nuxt: v3.10.0

Reproduction Link

None yet (need a help)

Steps to reproduce

  1. Forge a large and slow CSS/App somehow, put a custom class in it with @layer. (It contains a lot of @import in my case, but no success to repro yet.)
  2. Starting Nuxt in dev mode by npm run dev, it sometimes complain an error with the message like;

    ERROR Pre-transform error: [postcss] /[...]/layouts/default.vue?vue&type=style&index=0&scoped=433a9abd&lang.scss:2:3: The someclass class does not exist. If someclass is a custom class, make sure it is defined within a @layer directive.

What is Expected?

None of the error shown.

What is actually happening?

Actually, this is not a bug for @nuxtjs/tailwindcss.

I've found a race-condition in Nuxt for this situation. https://github.com/nuxt/nuxt/blob/21d6dcef5dab6ca803c0ed73b1339c0abf430e2e/packages/vite/src/utils/warmup.ts#L56 This function try to warmup if nuxt.options.vite.warmupEntry is specified (default: true). Unfortunately, server.transformRequest for a Vue file failed if the main CSS has not been transformed (loaded) yet.

But the setup is typical with TailwindCSS, so I posted here.

Workaround

Request the server the transformation before the Nuxt does. Register a hook in src/module.ts like this;

--- src/module.ts
+++ src/module.ts
@@ -188,6 +188,12 @@ export default defineNuxtModule<ModuleOptions>({
         nuxt.hook('close', () => watcher.close())
       }

+      if (nuxt.options.vite.warmupEntry !== false && resolvedCss) {
+        nuxt.hook('vite:serverCreated', async (server, env) => {
+          await server.transformRequest(resolvedCss, { ssr: env.isServer })
+        })
+      }
+
       // Insert Vite plugin to work around HMR issue
       if (!moduleOptions.disableHmrHotfix) {
         addVitePlugin(vitePlugin(tailwindConfig, nuxt.options.rootDir, resolvedCss))

In this way, the server waits for our hook has been done. A bit tricky, but it resolves the issue.

kikuchan commented 3 months ago

I've not fully figured out why yet, but it only occurs and is problematic when the env.isServer is false, and the server.transformRequest on the CSS is failed when the env.isServer is true. So it has to be executed only on env.isServer is false.

ineshbose commented 3 months ago

Thanks for reporting this - sorry I didn't get to this early; this takes some understanding.

I do believe that your project may have hit this case where this was necessary, but I wouldn't be aware or confident of doing this within the module right now (the addition is quite neat; I'm unable to think how we would be hitting that race condition). You have documented the workaround, so that is very helpful for anyone that may hit this - so I'll currently close this with a "has workaround" label.

Additionally, if you want an easy form of module to do this for you in your project as this isn't added in this module (yet), here's some code 🙂

import { defineNuxtModule, createResolver, resolvePath, installModule } from '@nuxt/kit'

export default defineNuxtModule({
  async setup (_, nuxt) {
    const resolver = createResolver(import.meta.url)

    const cssPath = '~/assets/css/main.css'
    const resolvedCss = await resolvePath(cssPath, { extensions: ['.css', '.sass', '.scss', '.less', '.styl'] })

    await installModule('@nuxtjs/tailwindcss', { cssPath })

    if (nuxt.options.vite.warmupEntry !== false && resolvedCss) {
      nuxt.hook('vite:serverCreated', async (server, env) => {
        await server.transformRequest(resolvedCss, { ssr: env.isServer })
      })
    }
  }
})