Repository: pmndrs/valtio
Pull request #358 (2022-02-11 06:14:33)

fix(utils): do not depend on react in build
fix #356 

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).  
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: [https://vercel.com/pmndrs/valtio/BDaXYhJFS6DBWwTDQYsR1GDQssSN](https://vercel.com/pmndrs/valtio/BDaXYhJFS6DBWwTDQYsR1GDQssSN)  
✅ Preview: [https://valtio-git-fix-utilsno-react-dependency-pmndrs.vercel.app](https://valtio-git-fix-utilsno-react-dependency-pmndrs.vercel.app)



Patch:
diff --git a/package.json b/package.json
index 6c4e53a..b0972af 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,8 @@
   "sideEffects": false,
   "scripts": {
     "prebuild": "shx rm -rf dist",
-    "build": "concurrently 'yarn:build:*'",
+    "build": "concurrently -m 8 'yarn:build:*'",
+    "build-watch": "concurrently -n watch: 'yarn:build:* --watch'",
     "build:base": "rollup -c",
     "build:vanilla": "rollup -c --config-vanilla",
     "build:utils": "rollup -c --config-utils",
@@ -107,6 +108,9 @@
         "@swc/jest"
       ]
     },
+    "globals": {
+      "__DEV__": true
+    },
     "moduleNameMapper": {
       "^valtio$": "<rootDir>/src/index.ts",
       "^valtio/(.*)$": "<rootDir>/src/$1.ts"
@@ -141,6 +145,7 @@
     "@rollup/plugin-alias": "^3.1.9",
     "@rollup/plugin-babel": "^5.3.0",
     "@rollup/plugin-node-resolve": "^13.1.3",
+    "@rollup/plugin-replace": "^3.0.1",
     "@rollup/plugin-typescript": "^8.3.0",
     "@swc/core": "^1.2.137",
     "@swc/jest": "^0.2.17",
@@ -173,6 +178,7 @@
     "react-dom": "^17.0.2",
     "rollup": "^2.67.1",
     "rollup-plugin-esbuild": "^4.8.2",
+    "rollup-plugin-terser": "^7.0.2",
     "shx": "^0.3.4",
     "tslib": "^2.3.1",
     "typescript": "^4.5.5"
diff --git a/rollup.config.js b/rollup.config.js
index 450dbfa..1da8214 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -2,8 +2,10 @@ import path from 'path'
 import alias from '@rollup/plugin-alias'
 import babelPlugin from '@rollup/plugin-babel'
 import resolve from '@rollup/plugin-node-resolve'
+import replace from '@rollup/plugin-replace'
 import typescript from '@rollup/plugin-typescript'
 import esbuild from 'rollup-plugin-esbuild'
+import { terser } from 'rollup-plugin-terser'
 const createBabelConfig = require('./babel.config')
 
 const extensions = ['.js', '.ts', '.tsx']
@@ -22,9 +24,9 @@ function getBabelOptions(targets) {
   }
 }
 
-function getEsbuild(target) {
+function getEsbuild(target, env = 'development') {
   return esbuild({
-    minify: false,
+    minify: env === 'production',
     target,
     tsconfig: path.resolve('./tsconfig.json'),
   })
@@ -63,6 +65,10 @@ function createESMConfig(input, output) {
         },
       }),
       resolve({ extensions }),
+      replace({
+        __DEV__: '(import.meta.env&&import.meta.env.MODE)!=="production"',
+        preventAssignment: true,
+      }),
       getEsbuild('node12'),
     ],
   }
@@ -71,7 +77,7 @@ function createESMConfig(input, output) {
 function createCommonJSConfig(input, output) {
   return {
     input,
-    output: { file: output, format: 'cjs', exports: 'named' },
+    output: { file: `${output}.js`, format: 'cjs', exports: 'named' },
     external,
     plugins: [
       alias({
@@ -81,19 +87,31 @@ function createCommonJSConfig(input, output) {
         },
       }),
       resolve({ extensions }),
+      replace({
+        __DEV__: 'process.env.NODE_ENV!=="production"',
+        preventAssignment: true,
+      }),
       babelPlugin(getBabelOptions({ ie: 11 })),
     ],
   }
 }
 
-function createUMDConfig(input, output) {
+function createUMDConfig(input, output, env) {
+  const c = output.split('/').pop()
   return {
     input,
     output: {
-      file: output,
+      file: `${output}.${env}.js`,
       format: 'umd',
       exports: 'named',
-      name: 'valtio',
+      name:
+        c === 'index'
+          ? 'valtio'
+          : `valtio${c.slice(0, 1).toUpperCase()}${c.slice(1)}`,
+      globals: {
+        react: 'React',
+        'valtio/vanilla': 'valtioVanilla',
+      },
     },
     external,
     plugins: [
@@ -104,16 +122,21 @@ function createUMDConfig(input, output) {
         },
       }),
       resolve({ extensions }),
+      replace({
+        __DEV__: env !== 'production' ? 'true' : 'false',
+        preventAssignment: true,
+      }),
       babelPlugin(getBabelOptions({ ie: 11 })),
+      ...(env === 'production' ? [terser()] : []),
     ],
   }
 }
 
-function createSystemConfig(input, output) {
+function createSystemConfig(input, output, env) {
   return {
     input,
     output: {
-      file: output,
+      file: `${output}.${env}.js`,
       format: 'system',
       exports: 'named',
     },
@@ -126,7 +149,11 @@ function createSystemConfig(input, output) {
         },
       }),
       resolve({ extensions }),
-      getEsbuild('node12'),
+      replace({
+        __DEV__: env !== 'production' ? 'true' : 'false',
+        preventAssignment: true,
+      }),
+      getEsbuild('node12', env),
     ],
   }
 }
@@ -135,18 +162,16 @@ export default function (args) {
   let c = Object.keys(args).find((key) => key.startsWith('config-'))
   if (c) {
     c = c.slice('config-'.length).replace(/_/g, '/')
-    return [
-      createCommonJSConfig(`src/${c}.ts`, `dist/${c}.js`),
-      createESMConfig(`src/${c}.ts`, `dist/esm/${c}`),
-      createUMDConfig(`src/${c}.ts`, `dist/umd/${c}.js`),
-      createSystemConfig(`src/${c}.ts`, `dist/system/${c}.js`),
-    ]
+  } else {
+    c = 'index'
   }
   return [
-    createDeclarationConfig('src/index.ts', 'dist'),
-    createCommonJSConfig('src/index.ts', 'dist/index.js'),
-    createESMConfig('src/index.ts', 'dist/esm/index'),
-    createUMDConfig('src/index.ts', 'dist/umd/index.js'),
-    createSystemConfig('src/index.ts', 'dist/system/index.js'),
+    ...(c === 'index' ? [createDeclarationConfig(`src/${c}.ts`, 'dist')] : []),
+    createCommonJSConfig(`src/${c}.ts`, `dist/${c}`),
+    createESMConfig(`src/${c}.ts`, `dist/esm/${c}`),
+    createUMDConfig(`src/${c}.ts`, `dist/umd/${c}`, 'development'),
+    createUMDConfig(`src/${c}.ts`, `dist/umd/${c}`, 'production'),
+    createSystemConfig(`src/${c}.ts`, `dist/system/${c}`, 'development'),
+    createSystemConfig(`src/${c}.ts`, `dist/system/${c}`, 'production'),
   ]
 }
diff --git a/src/react.ts b/src/react.ts
index 8a11d7e..8382ad9 100644
--- a/src/react.ts
+++ b/src/react.ts
@@ -209,7 +209,7 @@ export const useSnapshot = <T extends object>(
     snapshot,
     sub
   )
-  if (typeof process === 'object' && process.env.NODE_ENV !== 'production') {
+  if (__DEV__) {
     // eslint-disable-next-line react-hooks/rules-of-hooks
     useAffectedDebugValue(currSnapshot, affected)
   }
diff --git a/src/types.d.ts b/src/types.d.ts
new file mode 100644
index 0000000..4781b52
--- /dev/null
+++ b/src/types.d.ts
@@ -0,0 +1,2 @@
+// eslint-disable-next-line no-var
+declare var __DEV__: boolean
diff --git a/src/utils/devtools.ts b/src/utils/devtools.ts
index 1518365..a66efcc 100644
--- a/src/utils/devtools.ts
+++ b/src/utils/devtools.ts
@@ -23,11 +23,7 @@ export const devtools = <T extends object>(proxyObject: T, name?: string) => {
     // ignored
   }
   if (!extension) {
-    if (
-      typeof process === 'object' &&
-      process.env.NODE_ENV === 'development' &&
-      typeof window !== 'undefined'
-    ) {
+    if (__DEV__ && typeof window !== 'undefined') {
       console.warn('[Warning] Please install/enable Redux devtools extension')
     }
     return
diff --git a/src/utils/proxyMap.ts b/src/utils/proxyMap.ts
index 75f6ea7..0e29d8b 100644
--- a/src/utils/proxyMap.ts
+++ b/src/utils/proxyMap.ts
@@ -1,4 +1,4 @@
-import { proxy } from 'valtio'
+import { proxy } from '../vanilla'
 
 type KeyValRecord<K, V> = [key: K, value: V]
 
diff --git a/src/utils/proxySet.ts b/src/utils/proxySet.ts
index 2f0ac17..3101d2c 100644
--- a/src/utils/proxySet.ts
+++ b/src/utils/proxySet.ts
@@ -1,4 +1,4 @@
-import { proxy } from 'valtio'
+import { proxy } from '../vanilla'
 
 // properies that we don't want to expose to the end-user
 type InternalProxySet<T> = Set<T> & {
diff --git a/src/vanilla.ts b/src/vanilla.ts
index e21e9e0..f90e8f5 100644
--- a/src/vanilla.ts
+++ b/src/vanilla.ts
@@ -219,11 +219,7 @@ export const subscribe = <T extends object>(
   callback: (ops: Op[]) => void,
   notifyInSync?: boolean
 ) => {
-  if (
-    typeof process === 'object' &&
-    process.env.NODE_ENV !== 'production' &&
-    !(proxyObject as any)?.[LISTENERS]
-  ) {
+  if (__DEV__ && !(proxyObject as any)?.[LISTENERS]) {
     console.warn('Please use proxy object')
   }
   let promise: Promise<void> | undefined
@@ -274,22 +270,14 @@ type Snapshot<T> = T extends AnyFunction
     }
 
 export const snapshot = <T extends object>(proxyObject: T): Snapshot<T> => {
-  if (
-    typeof process === 'object' &&
-    process.env.NODE_ENV !== 'production' &&
-    !(proxyObject as any)?.[SNAPSHOT]
-  ) {
+  if (__DEV__ && !(proxyObject as any)?.[SNAPSHOT]) {
     console.warn('Please use proxy object')
   }
   return (proxyObject as any)[SNAPSHOT]
 }
 
 export const getHandler = <T extends object>(proxyObject: T) => {
-  if (
-    typeof process === 'object' &&
-    process.env.NODE_ENV !== 'production' &&
-    !(proxyObject as any)?.[HANDLER]
-  ) {
+  if (__DEV__ && !(proxyObject as any)?.[HANDLER]) {
     console.warn('Please use proxy object')
   }
   return (proxyObject as any)[HANDLER]
diff --git a/yarn.lock b/yarn.lock
index f5965eb..dcec6f3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1247,6 +1247,14 @@
     is-module "^1.0.0"
     resolve "^1.19.0"
 
+"@rollup/plugin-replace@^3.0.1":
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-3.0.1.tgz#f774550f482091719e52e9f14f67ffc0046a883d"
+  integrity sha512-989J5oRzf3mm0pO/0djTijdfEh9U3n63BIXN5X7T4U9BP+fN4oxQ6DvDuBvFaHA6scaHQRclqmKQEkBhB7k7Hg==
+  dependencies:
+    "@rollup/pluginutils" "^3.1.0"
+    magic-string "^0.25.7"
+
 "@rollup/plugin-typescript@^8.3.0":
   version "8.3.0"
   resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz#bc1077fa5897b980fc27e376c4e377882c63e68b"
@@ -2158,6 +2166,11 @@ combined-stream@^1.0.8:
   dependencies:
     delayed-stream "~1.0.0"
 
+commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
 commander@^8.3.0:
   version "8.3.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
@@ -3749,6 +3762,15 @@ jest-watcher@^27.5.1:
     jest-util "^27.5.1"
     string-length "^4.0.1"
 
+jest-worker@^26.2.1:
+  version "26.6.2"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
+  integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
+  dependencies:
+    "@types/node" "*"
+    merge-stream "^2.0.0"
+    supports-color "^7.0.0"
+
 jest-worker@^27.5.1:
   version "27.5.1"
   resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
@@ -4015,6 +4037,13 @@ lz-string@^1.4.4:
   resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
   integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
 
+magic-string@^0.25.7:
+  version "0.25.7"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
+  integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
+  dependencies:
+    sourcemap-codec "^1.4.4"
+
 make-dir@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@@ -4418,6 +4447,13 @@ queue-microtask@^1.2.2:
   resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
   integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
 
+randombytes@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+  integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+  dependencies:
+    safe-buffer "^5.1.0"
+
 react-dom@^17.0.2:
   version "17.0.2"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
@@ -4593,6 +4629,16 @@ rollup-plugin-esbuild@^4.8.2:
     joycon "^3.0.1"
     jsonc-parser "^3.0.0"
 
+rollup-plugin-terser@^7.0.2:
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d"
+  integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==
+  dependencies:
+    "@babel/code-frame" "^7.10.4"
+    jest-worker "^26.2.1"
+    serialize-javascript "^4.0.0"
+    terser "^5.0.0"
+
 rollup@^2.67.1:
   version "2.67.1"
   resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.67.1.tgz#4402665706fa00f321d446ce45f880e02cf54f01"
@@ -4621,6 +4667,11 @@ rxjs@^7.5.2:
   dependencies:
     tslib "^2.1.0"
 
+safe-buffer@^5.1.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
 safe-buffer@~5.1.1:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@@ -4663,6 +4714,13 @@ semver@^7.3.2, semver@^7.3.5:
   dependencies:
     lru-cache "^6.0.0"
 
+serialize-javascript@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
+  integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==
+  dependencies:
+    randombytes "^2.1.0"
+
 shebang-command@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -4742,7 +4800,7 @@ slice-ansi@^5.0.0:
     ansi-styles "^6.0.0"
     is-fullwidth-code-point "^4.0.0"
 
-source-map-support@^0.5.6:
+source-map-support@^0.5.6, source-map-support@~0.5.20:
   version "0.5.21"
   resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
   integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
@@ -4760,11 +4818,16 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
-source-map@^0.7.3:
+source-map@^0.7.3, source-map@~0.7.2:
   version "0.7.3"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
   integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
 
+sourcemap-codec@^1.4.4:
+  version "1.4.8"
+  resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
+  integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
+
 spawn-command@^0.0.2-1:
   version "0.0.2-1"
   resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
@@ -4936,6 +4999,15 @@ terminal-link@^2.0.0:
     ansi-escapes "^4.2.1"
     supports-hyperlinks "^2.0.0"
 
+terser@^5.0.0:
+  version "5.10.0"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc"
+  integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==
+  dependencies:
+    commander "^2.20.0"
+    source-map "~0.7.2"
+    source-map-support "~0.5.20"
+
 test-exclude@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"


Test patch:
diff --git a/.github/workflows/test-multiple-builds.yml b/.github/workflows/test-multiple-builds.yml
new file mode 100644
index 0000000..68c6ff2
--- /dev/null
+++ b/.github/workflows/test-multiple-builds.yml
@@ -0,0 +1,57 @@
+name: Test Multiple Builds
+
+on:
+  push:
+    branches: [main]
+  pull_request:
+    types: [opened, synchronize]
+
+jobs:
+  test_matrix:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        build: [cjs, umd] # [cjs, esm, umd, system]
+        env: [development, production]
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions/setup-node@v2
+        with:
+          node-version: '12'
+          cache: yarn
+      - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
+      - run: yarn install --frozen-lockfile --check-files
+      - run: yarn build
+      - name: Patch for DEV-ONLY
+        if: ${{ matrix.env == 'development' }}
+        run: |
+          sed -i~ "s/it[a-zA-Z]*('\[PRD-ONLY\]/it.skip('/" tests/*.tsx
+      - name: Patch for PRD-ONLY
+        if: ${{ matrix.env == 'production' }}
+        run: |
+          sed -i~ "s/it[a-zA-Z]*('\[DEV-ONLY\]/it.skip('/" tests/*.tsx
+      - name: Patch for CJS
+        if: ${{ matrix.build == 'cjs' }}
+        run: |
+          sed -i~ "s/<rootDir>\/src\(.*\)\.ts/<rootDir>\/dist\1.js/" package.json
+      - name: Patch for ESM
+        if: ${{ matrix.build == 'esm' }}
+        run: |
+          sed -i~ "s/<rootDir>\/src\(.*\)\.ts/<rootDir>\/dist\/esm\1.js" package.json
+          sed -i~ "1s/^/import.meta.env=import.meta.env||{};import.meta.env.MODE='${NODE_ENV}';/" tests/*.tsx
+        env:
+          NODE_ENV: ${{ matrix.env }}
+      - name: Patch for UMD/SystemJS
+        if: ${{ matrix.build == 'umd' || matrix.build == 'system' }}
+        run: |
+          sed -i~ "s/<rootDir>\/src\(.*\)\.ts/<rootDir>\/dist\/${BUILD}\1.${NODE_ENV}.js/" package.json
+        env:
+          BUILD: ${{ matrix.build }}
+          NODE_ENV: ${{ matrix.env }}
+      - name: Test ${{ matrix.build }} ${{ matrix.env }}
+        run: |
+          cat package.json
+          yarn test:ci
+        env:
+          NODE_ENV: ${{ matrix.env }}
diff --git a/tests/devtools.test.tsx b/tests/devtools.test.tsx
index ced1d8e..54c260c 100644
--- a/tests/devtools.test.tsx
+++ b/tests/devtools.test.tsx
@@ -3,6 +3,22 @@ import { act, fireEvent, render } from '@testing-library/react'
 import { proxy, useSnapshot } from 'valtio'
 import { devtools } from 'valtio/utils'
 
+const consoleError = console.error
+beforeEach(() => {
+  console.error = jest.fn((message) => {
+    if (
+      process.env.NODE_ENV === 'production' &&
+      message.startsWith('act(...) is not supported in production')
+    ) {
+      return
+    }
+    consoleError(message)
+  })
+})
+afterEach(() => {
+  console.error = consoleError
+})
+
 let extensionSubscriber: ((message: any) => void) | undefined
 
 const extension = {
@@ -48,10 +64,13 @@ it('connects to the extension by initialiing', () => {
 })
 
 describe('If there is no extension installed...', () => {
+  let savedDEV: boolean
   beforeAll(() => {
+    savedDEV = __DEV__
     ;(window as any).__REDUX_DEVTOOLS_EXTENSION__ = undefined
   })
   afterAll(() => {
+    __DEV__ = savedDEV
     ;(window as any).__REDUX_DEVTOOLS_EXTENSION__ = extensionConnector
   })
 
@@ -69,15 +88,15 @@ describe('If there is no extension installed...', () => {
   }
 
   it('does not throw', () => {
+    __DEV__ = false
     devtools(obj)
     expect(() => {
       render(<Counter />)
     }).not.toThrow()
   })
 
-  it('warns in dev env', () => {
-    const originalNodeEnv = process.env.NODE_ENV
-    process.env.NODE_ENV = 'development'
+  it('[DEV-ONLY] warns in dev env', () => {
+    __DEV__ = true
     const originalConsoleWarn = console.warn
     console.warn = jest.fn()
     devtools(obj)
@@ -87,11 +106,11 @@ describe('If there is no extension installed...', () => {
       '[Warning] Please install/enable Redux devtools extension'
     )
 
-    process.env.NODE_ENV = originalNodeEnv
     console.warn = originalConsoleWarn
   })
 
-  it('does not warn if not in dev env', () => {
+  it('[PRD-ONLY] does not warn if not in dev env', () => {
+    __DEV__ = false
     const consoleWarn = jest.spyOn(console, 'warn')
 
     render(<Counter />)