vite icon indicating copy to clipboard operation
vite copied to clipboard

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

Open gergof opened this issue 1 year ago • 2 comments

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

gergof avatar Jan 30 '23 13:01 gergof

As a workaround you can create a variable with the URL constructor somewhere and it will force esbuild to add a suffix for the named export.

gergof avatar Jan 31 '23 08:01 gergof

Hey! Lemme try picking this up. Also is it cool if I ask questions for a little bit of help if I get stuck?

frostzt avatar Feb 04 '23 08:02 frostzt

Waiting for an ingenious solution

faga295 avatar Feb 12 '23 14:02 faga295

Tbh just was looking into it, vite is the bundler though uses Rollup I do believe its with Rollup since the transpilation happens there! I am not sure if I am correct though!

frostzt avatar Feb 16 '23 14:02 frostzt

The repro is using Vite 3, but I can confirm it happens in Vite 4 too. It's likely a bug in Rollup as it should internally alias the URL import to something else to not conflict with the global URL variable. It already does keep a list of known globals.

bluwy avatar Feb 18 '23 17:02 bluwy

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

bluwy avatar Feb 18 '23 17:02 bluwy

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'
...

sun0day avatar Feb 19 '23 04:02 sun0day

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

faga295 avatar Feb 19 '23 06:02 faga295

Would it be acceptable to switch to new globalThis.URL( instead of new URL(? Gzipped, the diff in size shouldn't be that big. And we could later optimize and remove the globalThis if there isn't a URL variable in the chunk, but I don't know if it is worth it.

patak-dev avatar Apr 10 '23 20:04 patak-dev

This happens to me on Nuxt 3 for dependencies that use the url-parse module (in my case tus-js-client). I had to override the Vite version to 4.3.1 to get it to build properly.

BlakeB415 avatar Jun 20 '23 00:06 BlakeB415

Same issue when using Vite with uuid as uuid exports URL: https://github.com/uuidjs/uuid/blob/4de23a6030e65ac72b3b015680f08e7e292681ed/src/v35.js#L17

quanglam2807 avatar Jul 06 '23 08:07 quanglam2807

I am also experiencing this issue with a project that uses react-dnd-html5-backend and uuid, both export URL. They do not collide with each other, rollup recognises that conflict, but they do collide with the global URL.

Anyone have a work around for this issue?

jjsearle avatar Jul 10 '23 08:07 jjsearle

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
  },
});

jjsearle avatar Jul 10 '23 10:07 jjsearle

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
  },
});

This workaround worked for me, thanks!

If that helps - It happens to me with SvelteKit when importing bunch of icons from $lib. I tried to make reproduction repo but it seems to be dependent on other things that go into the bundle, when I tried to make contrived example it didn't behave like that. Vite 4.4.8 Svelte 4.1.2 SvelteKit 1.22.4

import AAA from '$lib/icons/AAA.svg';
import BBB from '$lib/icons/BBB.svg';
import CCC from '$lib/icons/CCC.svg';
import DDD from '$lib/icons/DDD.svg';
import EEE from '$lib/icons/EEE.svg';
import FFF from '$lib/icons/FFF.svg';

const iconMap: Record<string, string> = {
	AAA,
	BBB,
	CCC,
	DDD,
	EEE,
	FFF
};

export default iconMap;

MaestroLegato avatar Aug 06 '23 17:08 MaestroLegato

It looks like this issue has been fixed in the latest version of Vite.

quanglam2807 avatar Feb 15 '24 03:02 quanglam2807

It looks like this issue has been fixed in the latest version of Vite.

Vite 5.1.6, the problem still exists.

oyzhen avatar Mar 13 '24 07:03 oyzhen