content icon indicating copy to clipboard operation
content copied to clipboard

Support invalidating Vite Styling when content changes (Tailwind, Uno, etc)

Open atinux opened this issue 3 years ago • 11 comments

I think a video is better to understand:

https://user-images.githubusercontent.com/904724/166730285-d28417e5-5b91-4559-8b3f-b89f802d6a7d.mp4

Adding classes that are not generated by Tailwind at first does not work when changing

Example for Tailwind: https://stackblitz.com/edit/nuxt-starter-omgw5t?file=app.vue

I am also trying to make UnoCSS works but not sure what I did wrong (cc @antfu): https://stackblitz.com/edit/nuxt-starter-rbqpf3?file=nuxt.config.ts

atinux avatar May 23 '22 12:05 atinux

Do we have a resource that describes how Nuxt Content handles the markdown compiling somewhere? Did a quick check, and it seems that the markdowns are not been passed to Vite's plugins pipeline. Which UnoCSS is relying on, and probably also how TW handles HMR.

By adding this to config:

 vite: {
    plugins: [
      {
        name: 'foo',
        transform(code, id) {
          if (!id.match('node_modules'))
            console.log(id)
        },
      },
    ],
  },

You will see the contents are not presented in the log.

antfu avatar May 23 '22 13:05 antfu

Indeed @antfu, the Markdown files are not handled through Vite at all, this is the power of Nuxt Content, the Markdown is part of runtime, like any other CMS.

The main thing I need is the ability to tell Vite to tell its plugins (Uno, Tailwind, etc) to "recompile" by reading their sources (content/** is included in them). Basically, it will be here that I would like to ping Vite to re-run the plugin and do a HMR: https://github.com/nuxt/content/blob/docs/marketing/src/module.ts#L421

Or it could be handled on client-side here: https://github.com/nuxt/content/blob/docs/marketing/src/runtime/composables/web-socket.ts#L25

Something like import.meta.hot.refreshStyles() could be possible? (https://vitejs.dev/guide/api-plugin.html#client-to-server)

atinux avatar May 23 '22 14:05 atinux

Tailwind is probably reading the changes from the disk, in this case, you might need a custom extractor for it to understand MDC? Other than that, you might just need to invalidate the style. You can use configureServer hook from a Vite plugin, and send a ws event to vite client manually:

server.ws.send({
  type: 'update',
  updates: [{
    acceptedPath: id, // id is the CSS file contains `@tailwind`
    path: id,
    timestamp: +Date.now(),
    type: 'js-update',
  }],
})

For UnoCSS it's a bit different, we read and extract utils usage from the plugin's transform hook instead of disk for efficiency. It means in this case UnoCSS didn't know about the contents at all. You might need to use cross-plugin API.

Unless we fake the vite plugin hook calls, we might more or less need to have some specific logic for handling Uno or Tailwind (probably a few other integrations in the future).

antfu avatar May 23 '22 15:05 antfu

Nuxt content v1 also had this issue with Windi, I fixed it with this code.

Basically, hook into before the markdown is parsed, extract the classes from the markdown, generate the classes for just that file and insert them at the end of the .md within a style block.

Not an ideal solution by any means but it's simple and worked. Since the scope was just the HMR update I wasn't too worried, the build isn't effected. Not sure if the same hook is provided for v2.

Seems like we'll need a custom extractor either way

harlan-zw avatar May 26 '22 14:05 harlan-zw

Can we expose this hook @farnabaz ?

atinux avatar May 26 '22 14:05 atinux

We can provide a similar hook. Not in module scope but inside nitro scope. Windi module could inject a nitro plugin to use this hook.

// windi-nitro-plugin.ts
export default defineNitroPlugin((nitroApp) => {
 nitroApp.hooks.hook('content:file:beforeParse', () => {
   ...
 })
})

farnabaz avatar May 26 '22 14:05 farnabaz

Sounds good, I'm guessing we would be able to inject this nitro plugin via the nitro:context hook?

harlan-zw avatar May 26 '22 15:05 harlan-zw

'nitro:context' removed in favor of 'nitro:config'. You can use this hook to update nitro configs

farnabaz avatar May 26 '22 15:05 farnabaz

It's a bit hacky and there are sometimes some weird h3 and worker errors popping up, but nuxt-windicss now supports nuxt/content v2

Nitro plugin: https://github.com/windicss/nuxt-windicss/blob/main/packages/nuxt-windicss/src/runtime/server/class-extractor.mjs Setup: https://github.com/windicss/nuxt-windicss/blob/main/packages/nuxt-windicss/src/module.ts#L310

harlan-zw avatar Jun 01 '22 11:06 harlan-zw

Right now is not working together - MDC and UnoCSS: Example from @Atinux but newest version: https://stackblitz.com/edit/nuxt-starter-9s9zsj?file=content%2Findex.md,app.vue,package.json

    "@nuxt/content": "npm:@nuxt/content-edge@latest",
    "@unocss/nuxt": "^0.45.21",
    "nuxt": "3.0.0-rc.9"

We have problem with tailwind by refresh loop. On UnoCSS is not updating at all on rc9.

Triloworld avatar Sep 14 '22 10:09 Triloworld

for those who looking current workaround for unocss, you can include your content dir to uno.config.ts

export default defineConfig({
  filesystem: ['content/**'],
  pipeline: {
    include: [
      // the file extensions are limited by default. In my case, I need to add .json extension
      /\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html|json)($|\?)/,
    ]
  }
})

references: https://unocss.dev/guide/extracting#extracting-from-filesystem

anhzf avatar Feb 29 '24 10:02 anhzf