vite icon indicating copy to clipboard operation
vite copied to clipboard

Vite injects css assets in wrong order with dynamic import and css modules.

Open pretender91 opened this issue 4 years ago • 71 comments

Describe the bug

Vite injects css assets in wrong order with dynamic import and css modules.

Reproduction

Repo to reproduce

For example:

  1. You have component Button with default styles (text color: red)
/* button.tsx */
import React from "react"
import classnames from 'classnames'

import styles from './button.module.css'

type ButtonProps = {
	children: string
	className?: string
	onClick?: () => void
}

function Button({ children, className, onClick }: ButtonProps) {
	return <button className={classnames(styles.button, className)} onClick={onClick}>{children}</button>
}

export default Button
/* button.module.css */
.button {
	color: red
}
  1. You have Page component. This page use Button component and overrides it styles by passing custom class name as a prop (text color: green)
/* home.tsx /*
import React from "react"

import Button from "../../components/button/button"

import styles from './home.module.css'


function HomePage() {
	return <div>
		<h1>Home page</h1>
		<Button className={styles.greenTextButton}>should be green</Button>
	</div>
}

export default HomePage
/* home.module.css */
.greenTextButton {
	color: green;
}
  1. You import Page component with dynamic import.
import React, { lazy, Suspense, useState } from 'react'


const HomePage = lazy(() => import('./pages/home/home'))
const AboutPage = lazy(() => import('./pages/about/about'))


function App() {
  const [page, setPage] = useState('home')
  return (
    <div>
      <a href="#" onClick={() => setPage('home')} style={{ marginRight: '5px' }}>home page</a>
      <a href="#" onClick={() => setPage('about')}>about page</a>

      <Suspense fallback={<div>loading</div>}>
        {page === 'home' && (
          <HomePage />
        )}
        {page === 'about' && (
          <AboutPage />
        )}
      </Suspense>
    </div>
  )
}

export default App
  1. You exepect page styles will override button styles, but they are not (as vite injects styles in wrong order)
Screenshot 2021-06-23 at 19 12 44

P.S. You can reproduce this but with cssCodeSplit: true or false.

System Info

Output of npx envinfo --system --npmPackages vite,@vitejs/plugin-vue --binaries --browsers:

  System:
    OS: macOS 11.4
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 94.85 MB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 14.16.1 - ~/.volta/tools/image/node/14.16.1/bin/node
    npm: 6.14.12 - ~/.volta/tools/image/node/14.16.1/bin/npm
  Browsers:
    Chrome: 91.0.4472.114
    Firefox: 89.0
    Safari: 14.1.1
  npmPackages:
    vite: ^2.3.8 => 2.3.8 

Used package manager:

Logs

 vite:config bundled config file loaded in 42ms +0ms
  vite:config using resolved config: {
  vite:config   plugins: [
  vite:config     'alias',
  vite:config     'react-refresh',
  vite:config     'vite:dynamic-import-polyfill',
  vite:config     'vite:resolve',
  vite:config     'vite:html',
  vite:config     'vite:css',
  vite:config     'vite:esbuild',
  vite:config     'vite:json',
  vite:config     'vite:wasm',
  vite:config     'vite:worker',
  vite:config     'vite:asset',
  vite:config     'vite:define',
  vite:config     'vite:css-post',
  vite:config     'vite:build-html',
  vite:config     'commonjs',
  vite:config     'vite:data-uri',
  vite:config     'rollup-plugin-dynamic-import-variables',
  vite:config     'vite:import-analysis',
  vite:config     'vite:esbuild-transpile',
  vite:config     'vite:terser',
  vite:config     'vite:reporter'
  vite:config   ],
  vite:config   build: {
  vite:config     target: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ],
  vite:config     polyfillDynamicImport: false,
  vite:config     outDir: 'dist',
  vite:config     assetsDir: 'assets',
  vite:config     assetsInlineLimit: 4096,
  vite:config     cssCodeSplit: true,
  vite:config     sourcemap: false,
  vite:config     rollupOptions: {},
  vite:config     commonjsOptions: { include: [Array], extensions: [Array] },
  vite:config     minify: 'terser',
  vite:config     terserOptions: {},
  vite:config     cleanCssOptions: {},
  vite:config     write: true,
  vite:config     emptyOutDir: null,
  vite:config     manifest: false,
  vite:config     lib: false,
  vite:config     ssr: false,
  vite:config     ssrManifest: false,
  vite:config     brotliSize: true,
  vite:config     chunkSizeWarningLimit: 500,
  vite:config     watch: null
  vite:config   },
  vite:config   configFile: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order/vite.config.ts',
  vite:config   configFileDependencies: [ 'vite.config.ts' ],
  vite:config   inlineConfig: {
  vite:config     root: undefined,
  vite:config     base: undefined,
  vite:config     mode: undefined,
  vite:config     configFile: undefined,
  vite:config     logLevel: undefined,
  vite:config     clearScreen: undefined,
  vite:config     build: {}
  vite:config   },
  vite:config   root: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order',
  vite:config   base: '/',
  vite:config   resolve: { dedupe: undefined, alias: [ [Object] ] },
  vite:config   publicDir: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order/public',
  vite:config   cacheDir: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order/node_modules/.vite',
  vite:config   command: 'build',
  vite:config   mode: 'production',
  vite:config   isProduction: true,
  vite:config   server: {
  vite:config     fsServe: {
  vite:config       root: '/Users/anatoliidomaratskyi/Work/Mimy/css-code-split-order',
  vite:config       strict: false
  vite:config     }
  vite:config   },
  vite:config   env: { BASE_URL: '/', MODE: 'production', DEV: false, PROD: true },
  vite:config   assetsInclude: [Function: assetsInclude],
  vite:config   logger: {
  vite:config     hasWarned: false,
  vite:config     info: [Function: info],
  vite:config     warn: [Function: warn],
  vite:config     warnOnce: [Function: warnOnce],
  vite:config     error: [Function: error],
  vite:config     clearScreen: [Function: clearScreen]
  vite:config   },
  vite:config   createResolver: [Function: createResolver],
  vite:config   optimizeDeps: { esbuildOptions: { keepNames: undefined } }
  vite:config } +6ms
vite v2.3.8 building for production...
✓ 33 modules transformed.
dist/assets/favicon.17e50649.svg   1.49kb
dist/index.html                    0.45kb
dist/assets/home.5d3a6e0a.js       0.26kb / brotli: 0.15kb
dist/assets/button.14aa6fb9.js     0.80kb / brotli: 0.41kb
dist/assets/home.deac2baa.css      0.04kb / brotli: 0.04kb
dist/assets/about.563410d2.js      0.60kb / brotli: 0.28kb
dist/assets/button.a44dba50.css    0.03kb / brotli: 0.03kb
dist/assets/index.3938cd91.js      1.55kb / brotli: 0.60kb
dist/assets/about.d57e38e3.css     0.06kb / brotli: 0.05kb
dist/assets/vendor.cc984a25.js     127.61kb / brotli: 36.05kb


Before submitting the issue, please make sure you do the following

  • [ +] Read the Contributing Guidelines.
  • [ +] Read the docs.
  • [ +] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • [ +] Provide a description in this issue that describes the bug.
  • [+] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to https://github.com/vuejs/vue-next instead.
  • [ +] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.

pretender91 avatar Jun 23 '21 16:06 pretender91

the same issue for vue vite

iztsv avatar Jun 28 '21 06:06 iztsv

i also meet the same problem

youzizi1 avatar Jun 28 '21 09:06 youzizi1

I also have the same problem with React

FelixIncerta avatar Jun 29 '21 09:06 FelixIncerta

I also have the same problem with React

+1

zlyyyy avatar Jul 20 '21 07:07 zlyyyy

I also have the same problem with React

How did you solve it

zlyyyy avatar Jul 20 '21 07:07 zlyyyy

Same problem.

kamilic avatar Jul 23 '21 15:07 kamilic

Same problem.

cisen avatar Jul 26 '21 11:07 cisen

Same problem.

zhangzheng-zz avatar Jul 26 '21 12:07 zhangzheng-zz

Same problem.

stanfil avatar Jul 26 '21 12:07 stanfil

Same problem.

buddywang avatar Jul 26 '21 12:07 buddywang

Any updates?(

Maybe @sodatea will provide some thoughts where to find problem in project and i will try to make pull request to vite?

pretender91 avatar Aug 02 '21 12:08 pretender91

I'm seeing this issue as well, now that I've started to use storybook's new dynamic loading feature, my styles are broken in production stories. I've found that the css files are directly after the component in the network tab. So, in one example, I have a RadioButton component that imports Label (which has its own styles that I want to override), and then it imports some css module with the overrides. However, when building now, I see this:

image

Which puts the RadioButton styles higher in the cascade, and they are overwritten by the Label styles, which is backwards from what I want.

IanVS avatar Dec 09 '21 20:12 IanVS

Here is a minimal stackblitz reproduction: https://stackblitz.com/edit/vite-gu874q?file=main.jsx

vite: 2.7.1 @vitejs/plugin-react: 1.1.1

IanVS avatar Dec 09 '21 21:12 IanVS

Great work @IanVS! Really hope this helps resolving the issue. I would help, but have no idea on where to start unfortunately.

laurentvd avatar Dec 10 '21 07:12 laurentvd

I finally found the culprit, and FOR a while I thought vite-plugin-pages and unplugin-vue-components were causing the problem, my bad. Glad to use them again.

My temporary solution is to load influenced page synchronously in router configuration.

davidlin88 avatar Dec 20 '21 08:12 davidlin88

any idea when will #6301 be merged?

mgiraldo avatar Jan 13 '22 19:01 mgiraldo

As far as I know it's not ready, @mgiraldo. This is a sticky problem to solve. If you have any ideas or can help out, please do!

IanVS avatar Jan 13 '22 19:01 IanVS

i understand. thanks for all this work!

mgiraldo avatar Jan 13 '22 19:01 mgiraldo

I wonder whether Vite could maybe look for a __vite__injectStyle global hook to allow overriding the head.appendChild behavior.

With Webpack's style-loader one can specify a function/hook that will be called to place the style tag into the DOM (by default it will just append it to the head like Vite does).

Webpack Configuration
// webpack.config.js
      {
        test: /\.css$/,
        use: [
          {
            loader: "style-loader",
            options: {
              injectType: "singletonStyleTag",
              // this hook will be 
              insert: require.resolve("./tools/insertStyleTag.ts"),
            },
          },
          { loader: "css-loader" },
        ],
      },
// insertStyleTag.ts
function insertStyleTag(element) {
  element.dataset.styleLoader = "";

  // Custom logic, could be more complicated. Here I added a marker
  // to ensure `import ...` styles are added before `styled-component`
  // styles.
  document.head.insertBefore(
    element,
    document.head.querySelector("[data-styled]")
  );
}

module.exports = insertStyleTag;

mrcljx avatar Jan 13 '22 22:01 mrcljx

For those who (like me) had to ship and need a temporary workaround: Depending on your project, you may be able to use build.cssCodeSplit to extract a single css file which has the classes in the correct order.

laurentvd avatar Jan 29 '22 08:01 laurentvd

sadly this didnt work out for me @laurentvd 😔 i might need to refactor my components 🤔

mgiraldo avatar Jan 31 '22 20:01 mgiraldo

hay!

It's almost finished now, but it's stuck at one point. Now the loading order should be as expected, but there is still a problem with the execution order

Why pre-load blue.module.css but execute first __vite__updateStyle is post-load red.module.css 🥲

However change the import order I do, it always load order by red -> green -> blue in dev mod.

poyoho avatar Feb 21 '22 15:02 poyoho

This is a pretty big issue. The production build styling order also differs in order compared with the dev build making working with Vite a nightmare for me. Is there no workaround I can use in the meanwhile, it seems we are a long way from having this fixed?

Winston-Guess avatar Apr 20 '22 07:04 Winston-Guess

same issue

zhuweiyou avatar Apr 29 '22 02:04 zhuweiyou

Same

kadiryazici avatar May 05 '22 13:05 kadiryazici

@poyoho Any progress?

ghost avatar May 09 '22 17:05 ghost

Same problem.

geng130127 avatar May 18 '22 09:05 geng130127

I tried out the latest vite 2.9.14 version, and the situation seems to be improved (not as many style errors as before), but not quite yet resolved. I rebased https://github.com/vitejs/vite/pull/6301 onto the latest 2.9 branch as well, but its test is still failing.

IanVS avatar Jul 08 '22 19:07 IanVS

I have this problem i add antd css in main file and other css in component but in dev ant last style and component css before antd. Any have solution until fix it?

hmz22 avatar Jul 11 '22 19:07 hmz22

While this is frustrating, I was able to work around it using CSS layers.

<style>
  @import url('../path/to/css') layer(layer-1);
  @import url('../path/to/css-2') layer(layer-2);
  /* unlayered styles take preference over any layers */
</style>

If you're worried about browser support, there is a PostCSS plugin to help with that.

mayank99 avatar Jul 15 '22 20:07 mayank99