vitejs / vite

Next generation frontend tooling. It's fast!
http://vitejs.dev
MIT License
67.19k stars 6.04k forks source link

Vite build produces development version when using the --mode parameter AND when NODE_ENV is not set to production #11617

Closed MetaMmodern closed 1 year ago

MetaMmodern commented 1 year ago

Describe the bug

Our application is using Vite and React and has 3 deployments: development, qa and production. Each of those have different settings for authorization(different auth URLs) so I put those links into .env.development, .env.qa and .env.production files and use them through import.meta.env. The variables work fine, the problem is that react (or react plugin) generate a development build when running build command for qa or development modes.

To build I wrote this command:

{  
  "scripts": {
    "build": "vite build -m=${NODE_ENV=development}"
   }
}

(the =development in command is just for default value, it's not setting NODE_ENV to development when NODE_ENV exists) and my CI runs it like NODE_ENV=development npm run build (or qa, or production)

I ran it the same way locally and when I preview my build I see that React DevTools tell me, that website is using the development build.

I solved this issue like this:

{  
  "scripts": {
    "build": "MODE=\"$NODE_ENV\" NODE_ENV=production; vite build -m=${MODE=development}"
   }
}

So basically I copy the mode and then I set NODE_ENV to prod so that vite builds it for prod.

While documentation clearly states that this has to work, it doesn't:

image

https://main.vitejs.dev/guide/migration.html#production-builds-by-default

image

Also, I know that some similar issues existed before(#10024 and #9203) but those are closed and did not provide any explanation on why this still happens.

Reproduction

-

Steps to reproduce

create a vite react project, create 3 env files for each env as in description and try to build each of them with NODE_ENV env variable and using the first script from description. Then open each build in browser or using vite preview and check what react devtools tell you.

I tried to create a repro on stackblitz, but it did not allow me to create any env variables, so try locally.

System Info

System:
    OS: macOS 12.5.1
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 1.98 GB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 18.12.1 - ~/Library/Caches/fnm_multishells/69390_1672934733978/bin/node
    Yarn: 1.22.18 - /usr/local/bin/yarn
    npm: 8.19.2 - ~/Library/Caches/fnm_multishells/69390_1672934733978/bin/npm
  Browsers:
    Chrome: 108.0.5359.124
    Firefox: 108.0.1
    Safari: 15.6.1

Used Package Manager

npm

Logs

Click to expand! ```shell vite:config bundled config file loaded in 455.86ms +0ms vite:esbuild init tsconfck (root: /Users/metamodern/Work/Github/leiapix-admin-panel) +0ms vite:esbuild init tsconfck (root: /Users/metamodern/Work/Github/leiapix-admin-panel) +1ms vite:esbuild init tsconfck (root: /Users/metamodern/Work/Github/leiapix-admin-panel) +0ms vite:esbuild init tsconfck (root: /Users/metamodern/Work/Github/leiapix-admin-panel) +0ms vite:esbuild init tsconfck end +19ms vite:esbuild init tsconfck end +0ms vite:esbuild init tsconfck end +1ms vite:esbuild init tsconfck end +0ms vite:config using resolved config: { vite:config server: { vite:config preTransformRequests: true, vite:config proxy: { '/api': [Object] }, vite:config middlewareMode: false, vite:config fs: { strict: true, allow: [Array], deny: [Array] } vite:config }, vite:config plugins: [ vite:config 'vite:build-metadata', vite:config 'vite:pre-alias', vite:config 'alias', vite:config 'vite:react-babel', vite:config 'vite:react-refresh', vite:config 'vite:react-jsx', vite:config 'vite-tsconfig-paths', 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 'vite:watch-package-data', 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:terser', vite:config 'vite:reporter', vite:config 'vite:load-fallback' vite:config ], vite:config mode: 'qa', vite:config optimizeDeps: { vite:config disabled: 'build', vite:config force: undefined, vite:config include: [ 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react' ], vite:config esbuildOptions: { preserveSymlinks: false } vite: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: false, vite:config rollupOptions: {}, vite:config minify: 'esbuild', vite:config terserOptions: {}, vite:config write: true, vite:config emptyOutDir: null, vite:config copyPublicDir: true, vite:config manifest: false, vite:config lib: false, vite:config ssr: false, vite:config ssrManifest: false, vite:config reportCompressedSize: true, vite:config chunkSizeWarningLimit: 500, vite:config watch: null, vite:config commonjsOptions: { include: [Array], extensions: [Array] }, vite:config dynamicImportVarsOptions: { warnOnError: true, exclude: [Array] }, vite:config modulePreload: { polyfill: true } vite:config }, vite:config esbuild: { vite:config jsxDev: true, vite:config jsx: 'automatic', vite:config jsxImportSource: undefined, vite:config jsxSideEffects: false vite:config }, 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: [ 'react', 'react-dom' ], vite:config preserveSymlinks: false, vite:config alias: [ [Object], [Object] ] vite:config }, vite:config configFile: '/Users/metamodern/Work/Github/leiapix-admin-panel/client/vite.config.ts', vite:config configFileDependencies: [ vite:config '/Users/metamodern/Work/Github/leiapix-admin-panel/client/vite.config.ts' vite:config ], vite:config inlineConfig: { vite:config root: undefined, vite:config base: undefined, vite:config mode: 'qa', 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: '/Users/metamodern/Work/Github/leiapix-admin-panel/client', vite:config base: '/', vite:config rawBase: '/', vite:config publicDir: '/Users/metamodern/Work/Github/leiapix-admin-panel/client/public', vite:config cacheDir: '/Users/metamodern/Work/Github/leiapix-admin-panel/client/node_modules/.vite', vite:config command: 'build', 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: false, 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: { '/api': [Object] }, vite:config cors: undefined, vite:config headers: undefined vite:config }, vite:config env: { vite:config VITE_CLIENT_ID: 'holopix-admin-panel', vite:config VITE_AUTH_URL: 'https://auth.leialoft-qa.com/auth', vite:config VITE_REALM: 'leialoft', vite:config VITE_USER_NODE_ENV: 'production', vite:config BASE_URL: '/', vite:config MODE: 'qa', vite:config DEV: true, vite:config PROD: false vite:config }, 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(0) { set: [Function (anonymous)] }, vite:config createResolver: [Function: createResolver], vite:config worker: { vite:config format: 'iife', vite:config plugins: [ vite:config 'vite:build-metadata', 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 'vite:watch-package-data', 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:terser', 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 } +36ms vite v4.0.3 building for qa... transforming (12976) ../node_modules/lodash/_overArg.jsUse of eval in "../node_modules/js-sha256/src/sha256.js" is strongly discouraged as it poses security risks and may cause issues with minification. Use of eval in "../node_modules/js-sha256/src/sha256.js" is strongly discouraged as it poses security risks and may cause issues with minification. ✓ 13044 modules transformed. dist/index.html 0.99 kB dist/assets/favicon-cba45305.svg 1.69 kB dist/assets/web-vitals-d4fac006.js 4.06 kB │ gzip: 1.56 kB dist/assets/index-4e4d742f.js 1,803.06 kB │ gzip: 505.07 kB (!) Some chunks are larger than 500 kBs after minification. Consider: - Using dynamic import() to code-split the application - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/guide/en/#outputmanualchunks - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit. ```

Validations

MetaMmodern commented 1 year ago

Okay, I've found a bit more explanation for this in doc again:

So if you've set process.env.NODE_ENV = 'development' before building, it will also build for development. 
This gives more control when running multiple builds or dev servers in parallel

However, this doesn't explain why NODE_ENV=qa makes development build.

sapphi-red commented 1 year ago

In part of this change, vite dev and vite build will not override process.env.NODE_ENV anymore if it is already defined. https://main.vitejs.dev/guide/migration.html#production-builds-by-default

If you are setting NODE_ENV, Vite won't override NODE_ENV as described above.

React uses production build when process.env.NODE_ENV === 'production' is true. https://cdn.jsdelivr.net/npm/react@18.2.0/index.js

Because you are setting NODE_ENV=qa, this condition is false and the development build will be included.

MetaMmodern commented 1 year ago

@sapphi-red alright, sounds fine I guess. And sounds more like an issue of integration between vite and react or vite and react plugin. I mean, if Vite claims, that by default when running build command it builds for production(it turns to dev only when having NODE_ENV===dev), why does the underlying tool (react or react plugin in our case) decides how to build on it's own will? Why doesn't vite force it to build for production regardless of NODE_ENV?

Anyway, all of this aside: is the proposed workaround alright or there may be a better way to achieve what I'm doing?

sapphi-red commented 1 year ago

I mean, if Vite claims, that by default when running build command it builds for production(it turns to dev only when having NODE_ENV===dev), why does the underlying tool (react or react plugin in our case) decides how to build on it's own will? Why doesn't vite force it to build for production regardless of NODE_ENV?

I don't get what you mean. Maybe you are mixing NODE_ENV and mode (these are not the same thing)? Vite builds for production by default and even if any mode is used. You can set NODE_ENV if you want to override the value. If Vite set's NODE_ENV even if it's already set by a user, that will disallow users to use NODE_ENV other than 'production'.

Anyway, all of this aside: is the proposed workaround alright or there may be a better way to achieve what I'm doing?

If your intent is to build with 'development' in qa mode, I think using NODE_ENV=development vite build -m=qa (or you can use .env.qa file) is more intuitive.