sveltejs / vite-plugin-svelte

Svelte plugin for http://vitejs.dev/
MIT License
844 stars 103 forks source link

vitePreprocess does not reload postcss.config after full vite restart #848

Open benjaminpreiss opened 8 months ago

benjaminpreiss commented 8 months ago

Describe the bug

vite-preprocess does not reevaluate the contents of postcss.config after a full vite server restart.

That means I am logging something in postcss.config.js. When I trigger a full vite server restart via vite-plugin-restart this is not logged again, which tells me that the postcss config code is not reloaded.

Reproduction URL

https://github.com/frontline-hq/tailwind-dynamic-safelist

Reproduction

Then (requires node >= v18.13):

Then you will see a log in the console, after the server starts:

loading postcss config...

If you now edit any .svelte file in the /routes folder, you will see that the vite server is restarted in the console (I do this with the vite plugin vite-plugin-restart)

Logs

No response

System Info

System:
    OS: macOS 12.3
    CPU: (10) arm64 Apple M1 Max
    Memory: 85.88 MB / 32.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 18.17.1 - ~/.nvm/versions/node/v18.17.1/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v18.17.1/bin/yarn
    npm: 9.6.7 - ~/.nvm/versions/node/v18.17.1/bin/npm
    pnpm: 8.8.0 - ~/.nvm/versions/node/v18.17.1/bin/pnpm
    bun: 1.0.15 - ~/.bun/bin/bun
  Browsers:
    Brave Browser: 119.1.60.118
    Safari: 15.4
  npmPackages:
    @sveltejs/adapter-auto: ^3.0.0 => 3.1.1 
    @sveltejs/kit: ^2.0.0 => 2.4.3 
    @sveltejs/vite-plugin-svelte: ^3.0.0 => 3.0.1 
    svelte: ^4.2.7 => 4.2.9 
    vite: ^5.0.3 => 5.0.12
benjaminpreiss commented 8 months ago

Not even dependency re-optimization forces a postcss reload: https://github.com/frontline-hq/tailwind-dynamic-safelist/blob/main/getSafelist.js#L53

dominikg commented 8 months ago

please try using restart instead of reload here: https://github.com/frontline-hq/tailwind-dynamic-safelist/blob/main/vite.config.ts#L8

benjaminpreiss commented 8 months ago

thanks for the quick reply. I did check both restart and reload just now. Neither lead to the desired outcome.

I did check quickly by putting together a vite plugin a là:

() =>({
    name: "log-postcss",
    enforce: "pre",
    resolveId(id: string) {
        if(id.includes("postcss.config")) console.log(id)
    }
})

The postcss.config file is not processed by vite (as one can tell by checking the console logs). I wonder how I could tell vitePreprocess to "reload" postcss then?

dominikg commented 8 months ago

vite-plugin-svelte gets recreated when the vite devserver is restarted, that causes svelte.config.js to be loaded again at which point vitePreprocess() is executed again, returning a new style preprocessor built on vite's own export for preprocessCSS.

It is possible that something in vite (or maybe even postcss-load-config?) still caches the old postcss config and returns it without reading the file again. I'm not sure vite-plugin-svelte can do anything different here as we are not running postcss or load the config.

the postcss config itself would not go through resolveId as it isn't part of the users application (unless you import it yourself somewhere, which would however not influence postcss processing of your code).

cc @bluwy

benjaminpreiss commented 8 months ago

Ah, yes that might be it - maybe resetting the node require cache for postcss.config.js:

delete require.cache[require.resolve('./postcss.config.js')] ?

dominikg commented 8 months ago

btw. vite exposes server.watcher to plugins, so you would be able to implement your dynamic safelist as a vite plugin without a need for parcel watcher or jiti.

Ultimately i'd recommend against this approach though as you'll have a hard time tracking the content of the final safelist and you may end up with larger output as a result (apart from a lot of extra processing).

Clearing the require cache probably won't help, but you should be able to test this by adding that line before vitePreprocess() in svelte config. It really depends on where the postcss config is loaded and how it is cached.

dominikg commented 8 months ago

One idea i have is that you could try to import the postcss config into your vite config instead. That gets bundled and reloaded correctly...

bonus point: postcss.config.js can be esm

//postcss.config.js
import autoprefixer from 'autoprefixer';
export default {
  plugins:[autoprefixer]
}
//vite.config.js
import postcss from './postcss.config.js';
import defineConfig from 'vite';
export default defineConfig({
  css:{postcss} 
})

vite then passes postcss config along and postcss does not try to read it again itself (at least according to the docs). Some limitations might apply in terms of what you can do in postcss config, eg plugins has to be an array to be set inline in vite config https://vitejs.dev/config/shared-options.html#css-postcss

dominikg commented 8 months ago

reproduction in plain vite: https://stackblitz.com/edit/vitejs-vite-yls5ef?file=postcss.config.js,vite.config.js&terminal=dev

dominikg commented 8 months ago

filed https://github.com/vitejs/vite/issues/15745

benjaminpreiss commented 8 months ago

btw. vite exposes server.watcher to plugins, so you would be able to implement your dynamic safelist as a vite plugin without a need for parcel watcher or jiti.

Oh yes! Thanks for the tip 🔥

Ultimately i'd recommend against this approach though as you'll have a hard time tracking the content of the final safelist and you may end up with larger output as a result (apart from a lot of extra processing).

What do you mean by that? My approach in general of generating the dynamic safelist? Or using the watcher that is exposed by vite? AFAIK it is the only way to generate such a dynamic safelist without running into double build problems...

benjaminpreiss commented 8 months ago

vite then passes postcss config along and postcss does not try to read it again itself (at least according to the docs). Some limitations might apply in terms of what you can do in postcss config, eg plugins has to be an array to be set inline in vite config https://vitejs.dev/config/shared-options.html#css-postcss

That might indeed be worth a try - I just think it would be great if the postcss.config.js would also be watched (like tailwind does with their config...) and not only read once

Also, if I try your solution, that means that a developer needs to do more to setup my dynamic safelisting

dominikg commented 8 months ago

btw. vite exposes server.watcher to plugins, so you would be able to implement your dynamic safelist as a vite plugin without a need for parcel watcher or jiti.

Oh yes! Thanks for the tip 🔥

Ultimately i'd recommend against this approach though as you'll have a hard time tracking the content of the final safelist and you may end up with larger output as a result (apart from a lot of extra processing).

What do you mean by that? My approach in general of generating the dynamic safelist? Or using the watcher that is exposed by vite? AFAIK it is the only way to generate such a dynamic safelist without running into double build problems...

the dynamic safelist in general, at least if made by reading and concatenating comments in multiple source files. I can only assume you need this safelist because you construct classnames dynamically. I would suggest you limit that approach or always use the full classnames eg

const classes = someCondition ? 'bg-red-500' : 'bg-blue-500';

instead of

const classes = `bg-${someCondition ? 'red':'blue'}-500`

that way the full class should be parsable and you don't need a dynamic safelist. But this is off topic here and ofc you are free to implement such a system if you feel it makes you more productive. In my experience, this would be challenging to maintain longerm though.

benjaminpreiss commented 8 months ago

off topic:

Ah, I see. I am building this system as part of a component library where there are certain components (like a button) that have up to 30 or more different variants.

The dynamic safelisting then parses the svelte files to determine which variants are actually used and safelists these.

That way we generate only the tailwind styles that are actually used :) which will hopefully make this component library much leaner than others regarding bundle size!

I just chose the comment analysis here as an easy to understand example...

EDIT: I found a better way to achieve what I want: https://github.com/frontline-hq/tailwind-dynamic-safelist/commit/a969acdfb192570ae72681fe41fea196c6c655a6

It adds the tailwind safelist directly to the tailwind config and utilizes rewriting the config to the filesystem to restart the server (instead of server.restart()).