Stale styles: Multiple `<style>` are injected on `<head>` on save, for the same component - Monorepo structure
Environment
This particular case is happening in a Monorepo configure with rush
- Linaria version:
- linaria/vite 4.5.3
- linaria/react 4.5.3
- linaria/core 4.5.3
- Bundler (+ version): Vite 4.3.9
- Node.js version: 18.12
- OS: Mac OS Ventura 13.4.1
Description
Best way to describe this is to check the video below.
Quick TLDR:
When changing styles on a component, the <head> element is injected with multiple <style> elements for the same component. Previous styles for the same component don't seem to be cleared.
This makes it look like the page doesn't react to style changes on save. A hard refresh (refresh the browser manually) clears all old styles and the page looks good.
This only happens when we change to previously used styles
Example
-
font-sizeis in the beginning3rem - update
font-sizeto4rem - app refreshes, styles are correct ✅
- update
font-sizeto3rem(previous value) - app refreshes, styles are incorrect ❌
- checking the DOM, we see multiple
<style>elements for the same component
⚠️ This only seems to happen to imported components. ⚠️ The "Dotted" styled component in the "App.tsx" file updates fine if we change its values.
Reproducible Demo
I created a minimal reproducible repository: https://github.com/JoaoFGuiomar/minimal-repo
Instructions are on the README. Will also drop a quick video below
https://github.com/callstack/linaria/assets/2249046/147dc1ec-1750-49be-a893-c7218d687c47
Thanks 👍
It seems that there is a way to workaround it, using vite plugin to transform every single JS/TS file and add this line to all those files:
import.meta.hot;
so the plugin would be as simple as:
{
name: "inject-hmr-for-linaria",
transform(code: string, id: string) {
if ([".js", ".ts", ".tsx"].some((x) => id.endsWith(x))) {
return `${code}
import.meta.hot;
`;
}
},
}
Full vite.config.ts example:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import linaria from "@linaria/vite";
const jsExtension = [".js", ".ts", ".tsx"]
export default ({ command, mode, ssrBuild }) => {
const isDev = mode === "development";
return defineConfig({
build: {
outDir: "build",
},
plugins: [
linaria(),
react({
jsxRuntime: "classic",
}),
isDev && {
name: "inject-hmr-for-linaria",
transform(code: string, id: string) {
if (jsExtension.some((x) => id.endsWith(x))) {
return `${code}
import.meta.hot;
`;
}
},
},
].filter((x) => !!x),
});
};
However, I would love that I wouldn't have to do such "workaround"