Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent compilation of CSS Modules between dev and build commands when using ?url query param #13416

Closed
7 tasks done
stefanmaric opened this issue Jun 3, 2023 · 8 comments · Fixed by #15259
Closed
7 tasks done
Labels
inconsistency Inconsistency between dev & build p3-minor-bug An edge case that only affects very specific usage (priority)

Comments

@stefanmaric
Copy link

stefanmaric commented Jun 3, 2023

Describe the bug

Hi everyone 👋

I'm trying to workaround #13401 by manually listing CSS Modules I want to manipulate on the client-side but the contents of the transformed asset differs between vite dev and vite build, to be more specific, in dev the CSS class names are property hashed as I would expect for a .module.css file but they are not after vite build.

For the reproduction project, I have an Isolated.module.css file:

.ImportantText {
  color: red;
  font-weight: bold;
}

Which I'm importing as follows:

import IsolatedUrl from './Isolated.module.css?url';

Then I'm logging the URL string, fetching such file, and redering its contents on the page:

// log the URL string to the console
console.log(IsolatedUrl);

// Get the contents for the URL string
fetch(IsolatedUrl, { headers: { Accept: 'text/css' } })
  .then(async (response) => {
    const content = await response.text();

    // Simply render it pretty on the page for easy debugging
    const container = document.createElement('div');
    container.innerHTML = `
<pre style="max-width: 28rem; overflow: auto; text-align: left;">
  <code>
${content}
  </code>
</pre>
  `;

    document.querySelector('#app').appendChild(container);
  })
  .catch(console.error);

Reproduction

https://stackblitz.com/edit/vitejs-vite-pj3wxw?file=main.js

Steps to reproduce

Open the reproduction project.

You will see the content of the CSS Module on the page. CSS Class Names will be hashed, e.g.:

._ImportantText_yuz6n_1 {
  color: red;
  font-weight: bold;
}

Open a new Terminal in the StackBlitz UI and run the preview script:

npm run preview

It will compile and serve the compiled project on port 4173 and StackBlitz should replace the browser view on the right side to point to the new port automatically.

On the new page you will see that the CSS Class Names are not hashed, e.g.:

.ImportantText {
  color: red;
  font-weight: bold;
}

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 16.14.2 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 7.17.0 - /usr/local/bin/npm
  npmPackages:
    vite: ^4.3.9 => 4.3.9

Used Package Manager

npm

Logs

$ vite build --debug
  vite:config bundled config file loaded in 244.44ms +0ms
  vite:config using resolved config: {
  vite:config   build: {
  vite:config     target: [ 'es2020', 'edge88', 'firefox78', 'chrome87', 'safari14' ],
  vite:config     cssTarget: [ 'es2020', 'edge88', 'firefox78', 'chrome87', 'safari14' ],
  vite:config     outDir: 'dist',
  vite:config     assetsDir: 'assets',
  vite:config     assetsInlineLimit: 4096,
  vite:config     cssCodeSplit: true,
  vite:config     sourcemap: true,
  vite:config     rollupOptions: {},
  vite:config     minify: false,
  vite:config     terserOptions: {},
  vite:config     write: true,
  vite:config     emptyOutDir: null,
  vite:config     copyPublicDir: true,
  vite:config     manifest: true,
  vite:config     lib: false,
  vite:config     ssr: false,
  vite:config     ssrManifest: false,
  vite:config     ssrEmitAssets: false,
  vite:config     reportCompressedSize: true,
  vite:config     chunkSizeWarningLimit: 500,
  vite:config     watch: null,
  vite:config     cssMinify: false,
  vite:config     commonjsOptions: { include: [Array], extensions: [Array] },
  vite:config     dynamicImportVarsOptions: { warnOnError: true, exclude: [Array] },
  vite:config     modulePreload: { polyfill: true }
  vite:config   },
  vite:config   optimizeDeps: {
  vite:config     disabled: 'build',
  vite:config     force: undefined,
  vite:config     esbuildOptions: { preserveSymlinks: false }
  vite:config   },
  vite:config   configFile: '/home/projects/vitejs-vite-pj3wxw/vite.config.ts',
  vite:config   configFileDependencies: [ '/home/projects/vitejs-vite-pj3wxw/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     optimizeDeps: { force: undefined },
  vite:config     build: {}
  vite:config   },
  vite:config   root: '/home/projects/vitejs-vite-pj3wxw',
  vite:config   base: '/',
  vite:config   rawBase: '/',
  vite:config   resolve: {
  vite:config     mainFields: [ 'module', 'jsnext:main', 'jsnext' ],
  vite:config     browserField: true,
  vite:config     conditions: [],
  vite:config     extensions: [
  vite:config       '.mjs',  '.js',
  vite:config       '.mts',  '.ts',
  vite:config       '.jsx',  '.tsx',
  vite:config       '.json'
  vite:config     ],
  vite:config     dedupe: [],
  vite:config     preserveSymlinks: false,
  vite:config     alias: [ [Object], [Object] ]
  vite:config   },
  vite:config   publicDir: '/home/projects/vitejs-vite-pj3wxw/public',
  vite:config   cacheDir: '/home/projects/vitejs-vite-pj3wxw/node_modules/.vite',
  vite:config   command: 'build',
  vite:config   mode: 'production',
  vite:config   ssr: {
  vite:config     format: 'esm',
  vite:config     target: 'node',
  vite:config     optimizeDeps: { disabled: true, esbuildOptions: [Object] }
  vite:config   },
  vite:config   isWorker: false,
  vite:config   mainConfig: null,
  vite:config   isProduction: true,
  vite:config   plugins: [
  vite:config     'vite:build-metadata',
  vite:config     'vite:watch-package-data',
  vite:config     'vite:pre-alias',
  vite:config     'alias',
  vite:config     'vite:modulepreload-polyfill',
  vite:config     'vite:resolve',
  vite:config     'vite:html-inline-proxy',
  vite:config     'vite:css',
  vite:config     'vite:esbuild',
  vite:config     'vite:json',
  vite:config     'vite:wasm-helper',
  vite:config     'vite:worker',
  vite:config     'vite:asset',
  vite:config     'vite:wasm-fallback',
  vite:config     'vite:define',
  vite:config     'vite:css-post',
  vite:config     'vite:build-html',
  vite:config     'vite:worker-import-meta-url',
  vite:config     'vite:asset-import-meta-url',
  vite:config     'vite:force-systemjs-wrap-complete',
  vite:config     'commonjs',
  vite:config     'vite:data-uri',
  vite:config     'vite:dynamic-import-vars',
  vite:config     'vite:import-glob',
  vite:config     'vite:build-import-analysis',
  vite:config     'vite:esbuild-transpile',
  vite:config     'vite:manifest',
  vite:config     'vite:reporter',
  vite:config     'vite:load-fallback'
  vite:config   ],
  vite:config   esbuild: { jsxDev: false },
  vite:config   server: {
  vite:config     preTransformRequests: true,
  vite:config     sourcemapIgnoreList: [Function: isInNodeModules],
  vite:config     middlewareMode: false,
  vite:config     fs: { strict: true, allow: [Array], deny: [Array] }
  vite:config   },
  vite:config   preview: {
  vite:config     port: undefined,
  vite:config     strictPort: undefined,
  vite:config     host: undefined,
  vite:config     https: undefined,
  vite:config     open: undefined,
  vite:config     proxy: undefined,
  vite:config     cors: undefined,
  vite:config     headers: undefined
  vite:config   },
  vite:config   envDir: '/home/projects/vitejs-vite-pj3wxw',
  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     hasErrorLogged: [Function: hasErrorLogged]
  vite:config   },
  vite:config   packageCache: Map(1) {
  vite:config     'fnpd_/home/projects/vitejs-vite-pj3wxw' => {
  vite:config       dir: '/home/projects/vitejs-vite-pj3wxw',
  vite:config       data: [Object],
  vite:config       hasSideEffects: [Function: hasSideEffects],
  vite:config       webResolvedImports: {},
  vite:config       nodeResolvedImports: {},
  vite:config       setResolvedCache: [Function: setResolvedCache],
  vite:config       getResolvedCache: [Function: getResolvedCache]
  vite:config     },
  vite:config     set: [Function (anonymous)]
  vite:config   },
  vite:config   createResolver: [Function: createResolver],
  vite:config   worker: {
  vite:config     format: 'iife',
  vite:config     plugins: [
  vite:config       'vite:build-metadata',
  vite:config       'vite:watch-package-data',
  vite:config       'vite:pre-alias',
  vite:config       'alias',
  vite:config       'vite:modulepreload-polyfill',
  vite:config       'vite:resolve',
  vite:config       'vite:html-inline-proxy',
  vite:config       'vite:css',
  vite:config       'vite:esbuild',
  vite:config       'vite:json',
  vite:config       'vite:wasm-helper',
  vite:config       'vite:worker',
  vite:config       'vite:asset',
  vite:config       'vite:wasm-fallback',
  vite:config       'vite:define',
  vite:config       'vite:css-post',
  vite:config       'vite:build-html',
  vite:config       'vite:worker-import-meta-url',
  vite:config       'vite:asset-import-meta-url',
  vite:config       'vite:force-systemjs-wrap-complete',
  vite:config       'commonjs',
  vite:config       'vite:data-uri',
  vite:config       'vite:worker-post',
  vite:config       'vite:dynamic-import-vars',
  vite:config       'vite:import-glob',
  vite:config       'vite:build-import-analysis',
  vite:config       'vite:esbuild-transpile',
  vite:config       'vite:load-fallback'
  vite:config     ],
  vite:config     rollupOptions: {},
  vite:config     getSortedPlugins: [Function: getSortedPlugins],
  vite:config     getSortedPluginHooks: [Function: getSortedPluginHooks]
  vite:config   },
  vite:config   appType: 'spa',
  vite:config   experimental: { importGlobRestoreExtension: false, hmrPartialAccept: false },
  vite:config   getSortedPlugins: [Function: getSortedPlugins],
  vite:config   getSortedPluginHooks: [Function: getSortedPluginHooks]
  vite:config } +18ms
vite v4.3.9 building for production...
transforming (1) index.html  vite:esbuild 334.94ms tsconfck init /home/projects/vitejs-vite-pj3wxw +0ms
✓ 9 modules transformed.
dist/manifest.json                   0.41 kB │ gzip: 0.19 kB
dist/index.html                      0.45 kB │ gzip: 0.30 kB
dist/assets/javascript-8dac5379.svg  1.00 kB │ gzip: 0.60 kB
dist/assets/index-c8247773.css       1.63 kB │ gzip: 0.75 kB
dist/assets/index-d9330be8.js        2.95 kB │ gzip: 1.29 kB │ map: 3.06 kB
✓ built in 720ms

Validations

@stackblitz
Copy link

stackblitz bot commented Jun 3, 2023

Fix this issue in StackBlitz Codeflow Start a new pull request in StackBlitz Codeflow.

@sapphi-red sapphi-red added p3-minor-bug An edge case that only affects very specific usage (priority) inconsistency Inconsistency between dev & build labels Jun 4, 2023
@sun0day
Copy link
Member

sun0day commented Jun 23, 2023

Seems like vite has to transform css contents before they are emitted 🤔

const referenceId = pluginContext.emitFile({
// Ignore directory structure for asset file names
name: path.basename(file),
type: 'asset',
source: content,
})

@stefanmaric
Copy link
Author

Seems like vite has to transform css contents before they are emitted 🤔

The weird thing is that it works in dev, so it is a different "asset" treatment.

@minht11
Copy link

minht11 commented Jul 9, 2023

I am experiencing the same issue with css file url. In DEV mode tailwind (postcss) processing of the file is working fine, but not when built.

new URL('./styles.css', import.meta.url).href

@markdalgleish
Copy link
Contributor

markdalgleish commented Oct 29, 2023

We're running into this issue in the context of Remix + Vite because the ?url style of CSS import is the default in the original Remix compiler (see https://remix.run/docs/en/main/styling/css). Anyone migrating a Remix app to Vite is likely to run into this if they're relying on PostCSS transforms, as @minht11 is also running into.

I think we'll recommend that people avoid using ?url for anything other than raw CSS, but the fact that the styles only break in the production build makes this a pretty big footgun.

@sapphi-red
Copy link
Member

Using ?url with JS/CSS is currently ambiguous in Vite (#2522, #7842). For now, I'll recommend avoid using ?url with JS or CSS, but I'll bring this to the next team meeting for discussion.

@izznatsir
Copy link

Any chance the inconsistencies will be fixed before v5 release?

@sapphi-red
Copy link
Member

No, this won't be included in 5.0.0.


We discussed in the last meeting about this and decided to go with #2522 (comment).

@github-actions github-actions bot locked and limited conversation to collaborators Jan 27, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
inconsistency Inconsistency between dev & build p3-minor-bug An edge case that only affects very specific usage (priority)
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

6 participants