vite: 'URL' named export breaks static file import in production build

Describe the bug

When using a package (react-dnd-html5-backend in my case) that exports ‘URL’ as a named export, it will break the static file import (which uses new URL(...) in the production build).

Depending on the import order you will get “Cannot access ‘URL’ before initialization” or “‘URL’ is not a constructor” when running the packaged application.

It works perfectly fine in dev mode, but it breaks in production build.

Reproduction

https://stackblitz.com/edit/vitejs-vite-zguhve

Steps to reproduce

npm install

Then if you run npm run dev you will see the vite logo and under it the “URL named export” string.

But if you run npm run build and npm run preview you will see a blank white page indicating a broken react application. If you open the console, you can see the “Cannot access ‘URL’ before initialization” error message.

System Info

System:
    OS: Linux 4.19 Debian GNU/Linux 10 (buster) 10 (buster)
    CPU: (8) x64 Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
    Memory: 6.19 GB / 15.36 GB
    Container: Yes
    Shell: 5.0.3 - /bin/bash
  Binaries:
    Node: 16.19.0 - /usr/bin/node
    npm: 8.5.0 - /usr/bin/npm
  Browsers:
    Chrome: 109.0.5414.119
    Firefox: 102.7.0esr
  npmPackages:
    @vitejs/plugin-react: ^2.1.0 => 2.1.0 
    vite: ^3.2.5 => 3.2.5

Used Package Manager

npm

Logs

No response

Validations

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 1
  • Comments: 16 (4 by maintainers)

Most upvoted comments

Managed to workaround the issue by using @rollup-plugins/replace. It is less than ideal but got me going again.

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr'
import replace from '@rollup/plugin-replace'

export default defineConfig({
  base: './',
  plugins: [
    react(),
    svgr(),
    replace({
      'new URL(': 'new globalThis.URL(',
      delimiters: ['', ''],
      preventAssignment: true
    })
  ],
  server: {
    port: 3000,
    host: true
  },
  build: {
    outDir: './build',
    manifest: true,
    minify: true
  },
});

https://github.com/rollup/rollup/blob/master/src/utils/deconflictChunk.ts#L210 seems rollup resolve global variable conflict with this function

Hmm actually looks like Rollup is working fine, perhaps Vite’s dynamic usage of URL confuses Rollup, but I’m not sure how.

The problem seems vite render the new URL(...) with const URL = .... together, which may let rollup think they are the same variable.

...
const image = "__VITE_ASSET__24400c48__"; // __VITE_ASSET__ will be replace to new URL via asset plugin
const URL = 'xxxx'
...