vite icon indicating copy to clipboard operation
vite copied to clipboard

CSS sourcemaps

Open Rich-Harris opened this issue 3 years ago • 5 comments

Clear and concise description of the problem

CSS sourcemaps aren't currently supported, which can make it difficult to track down where a particular style was authored. It would be cool if they were preserved in both build (<link>) and dev (<style>) mode.

You can see this with a vanilla app (npm init @vitejs/app and choose 'vanilla'), and inspecting the #app styles in both dev and serve. In dev:

image

In serve:

image

Ideally the top right would say 'style.css' in both cases.

Suggested solution

I don't pretend to know exactly what would be involved, but elsewhere in the codebase magic-string is used for a similar purpose. Presumably the relevant plugin is https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/css.ts. I could take a run at it if someone gave me a pointer or two.

Rich-Harris avatar Apr 02 '21 23:04 Rich-Harris

I think I stumbled into this a while back 😔 https://github.com/vitejs/vite/blob/fa8574921195dd03b539c150a2ae5f97121a0aea/packages/vite/src/node/plugins/css.ts#L211

the map could be available from the compileCss result a few lines above

dominikg avatar Apr 03 '21 08:04 dominikg

Spent some time on this recently and here's what i found.

  • sourcemaps didn't work in vite1 too [1]
  • simply returning the map from compileCss in vite:css [2] doesn't work [3] as it would need to be transformed to an actual SourceMap first [4]
  • returning the correct sourcemap from vite:css alone does not help much, it needs to be passed on to the client too

Figuring out how to pass on the sourcemaps is different for dev and build, both coming with their own set of challenges. During dev, css is served from special js modules managing style nodes on the fly. So i think the easiest shot at this would be to add inline sourcemaps to that.

For build it's a lot more difficult as css gets bundled too and sourcemaps have to be updated to reflect that and on top esbuild css minification needs to be accounted for too.

One Idea i had about this is to keep the css sourcemap inline as long as possible to avoid having to change any api where currently only code is passed on, extracting them to .map at the last second if it's configured for external.

Building all of this at once could prove challanging and hard to review, so i suggest to split it into smaller goals, which can be described in more detail separately and implemented on top of each other, for example

  1. add inline sourcemap for dev - which seems to be the easiest to implement and most helpful for users
  2. add build support for inline sourcemaps
  3. extend build support to cover external sourcemap options

and also state which compromises can be made, eg

  • no support for external sourcemaps on dev
  • no support for hidden sourcemap on build (not a feature of esbuild css minification)
  • ....

Would love some more :eyes: on this.

-- links [1] https://github.com/vitejs/vite/issues/649 [2] https://github.com/vitejs/vite/pull/4880 [3] https://github.com/vitejs/vite/pull/4939 [4] https://github.com/vitejs/vite/pull/4951

dominikg avatar Sep 25 '21 09:09 dominikg

I see there's a build.sourcemap option. Should that option be split into two? If there's an issue the in CSS sourcemap handling (which seems potentially likely for the first couple releases after it's added) it'd be nice to potentially be able to disable just it and leave the JS sourcemap support on.

benmccann avatar Sep 25 '21 12:09 benmccann

Just come across this issue myself. It sure would be useful if Vite could at least bundle sourcemaps during development (on production, perhaps could be lower priority as sourcemaps usually aren't used anyway).

It would actually be better right now if Vite just returned the plain css file and the plain sourcemap file. Some CSS is unlikely to change and doesn't really need to be hot content anyway.

jez9999 avatar Sep 06 '22 14:09 jez9999

I noticed in the docs there is a css.devSourcemap option, but when I enabled it, it didn't seem to change anything. Chrome dev tools still didn't recognize my CSS styling as coming from Bootstrap's .scss files, like it does in React's Webpack dev server output.

jez9999 avatar Sep 10 '22 10:09 jez9999

@jez9999 I have used the config you mentioned and for me is working well, generating the source maps and see the .scss files corresponding to every styling in dev tools:

css: {
    devSourcemap: true,
},

rodsotdia avatar Oct 19 '22 01:10 rodsotdia

With css: {devSourcemap: true} the DevTools warning comes away, however mappings do not include paths from within node_modules (clicking on source file name results Could not load content ...)

Here's my take on fixing it in dev mode:

// packages/rollup-plugin-fix-css-sourcemaps/index.mjs

/**
 * Fix css source maps in 3p packages on dev server in vite
 * Css files use relative paths to point to source maps but when running dev server these are inlined
 *
 * @param {string} base
 * @return {RollupPlugin}
 *
 * @example
 * ```js
 * // vite.config.js
 * export default defineConfig(({ command }) => ({
 *   plugins: [
 *     command === 'serve' && fixCssSourcemapsPlugin(),
 *   ],
 * })
 * ```
 */
export default function fixCssSourcemaps(base = '/') {
  const filter = createFilter('node_modules/**/*.css')
  const root = process.cwd()
  const sourceMapRegExp = new RegExp('/\\*# sourceMappingURL=([^\\s]*) \\*/')

  return {
    name: 'rollup-plugin-fix-css-sourcemaps',

    transform(src, id) {
      if (!filter(id) || !sourceMapRegExp.test(src)) {
        return
      }

      // Resolve relative directory from id (ie. /node_modules/@ionic/react/css)
      const relativeDir = base + id.slice(root.length + 1, id.lastIndexOf('/'))

      return {
        code: prependDir(src, relativeDir),
        map: null,
      }
    },
  }

  /**
   * Prepend relative directory to source map pointer
   * @param {string} src
   * @param {string} dir
   * @return {string}
   */
  function prependDir(src, dir) {
    return src.replace(
      sourceMapRegExp,
      `/*# sourceMappingURL=${dir}/$1 */`
    )
  }
}

piotr-cz avatar Oct 27 '22 12:10 piotr-cz