vitest
vitest copied to clipboard
Can not run tests when using preact compat alias
Describe the bug
When package tries to import react by alias, it picks CJS version of preact specific module, which leads to following error:
FAIL src/app.test.tsx > should render app
TypeError: Cannot read properties of undefined (reading '__H')
❯ l node_modules/preact/hooks/dist/hooks.js:1:201
❯ d node_modules/preact/hooks/dist/hooks.js:1:600
❯ Object.exports.useRef node_modules/preact/hooks/dist/hooks.js:1:2323
❯ d.BrowserRouter [as constructor] node_modules/react-router-dom/umd/react-router-dom.development.js:80:28
❯ d.O [as render] ../../../../../../../../../C:/Users/wight/Documents/preact-vitest-repro/node_modules/preact/dist/preact.mjs:1:8158
❯ j ../../../../../../../../../C:/Users/wight/Documents/preact-vitest-repro/node_modules/preact/dist/preact.mjs:1:5870
❯ w ../../../../../../../../../C:/Users/wight/Documents/preact-vitest-repro/node_modules/preact/dist/preact.mjs:1:2137
❯ j ../../../../../../../../../C:/Users/wight/Documents/preact-vitest-repro/node_modules/preact/dist/preact.mjs:1:6138
❯ P ../../../../../../../../../C:/Users/wight/Documents/preact-vitest-repro/node_modules/preact/dist/preact.mjs:1:8272
❯ ../../../../../../../../../C:/Users/wight/Documents/preact-vitest-repro/node_modules/@testing-library/preact/dist/esm/pure.mjs:52:7
Reproduction
https://github.com/wight554/preact-vitest-repro/
System Info
System:
OS: Windows 10 10.0.22621
CPU: (16) x64 AMD Ryzen 7 3700X 8-Core Processor
Memory: 16.89 GB / 31.93 GB
Binaries:
Node: 16.14.0 - c:\program files\nodejs\node.EXE
Yarn: 1.22.4 - ~\AppData\Roaming\npm\yarn.CMD
npm: 8.3.1 - c:\program files\nodejs\npm.CMD
Browsers:
Chrome: 103.0.5060.114
Edge: Spartan (44.22621.290.0), Chromium (103.0.1264.49)
Internet Explorer: 11.0.22621.1
npmPackages:
vite: ^3.0.0 => 3.0.0
vitest: ^0.18.0 => 0.18.0
Used Package Manager
yarn
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
I've checked https://github.com/preactjs/compat-alias-package, it seems to have proper exports. Can't really tell why this happens
I see that the error is coming from react-router-dom
. Have you tried using deps.inline
with it? Currently files inside node_modules
are not processed by Vite. They are executed by Native Node without any transforming.
I see that the error is coming from
react-router-dom
. Have you tried usingdeps.inline
with it? Currently files insidenode_modules
are not processed by Vite. They are executed by Native Node without any transforming.
This config:
deps: {
inline: ['react-router-dom'],
}
Produces exactly the same error, with this config:
deps: {
inline: true,
}
I get error:
FAIL src/app.test.tsx > should render app
Unknown Error: undefined
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed (1)
Time 2.13s (in thread 6ms, 35558.00%)
Other than react-router-dom I faced this error with styled components and recoil and probably other libraries that rely on react
Tried https://preactjs.com/guide/v10/getting-started#aliasing-in-rollup instead of alias package - this works in vite, but doesn't work in vitest even with https://github.com/vitest-dev/vitest/pull/1634
Tried https://preactjs.com/guide/v10/getting-started#aliasing-in-rollup instead of alias package - this works in vite, but doesn't work in vitest even with https://github.com/vitest-dev/vitest/pull/1634
Please, read again: Currently files inside node_modules are not processed by Vite. They are executed by Native Node without any transforming.
If deps.inline
doesn't work, then it needs investigating. Aliasing WILL NOT WORK, if file is not processed by Vite.
One of the solutions from https://vitejs.dev/guide/ssr.html#ssr-externals:
If you have configured aliases that redirects one package to another, you may want to alias the actual node_modules packages instead to make it work for SSR externalized dependencies. Both Yarn and pnpm support aliasing via the npm: prefix.
One of the solutions from https://vitejs.dev/guide/ssr.html#ssr-externals:
If you have configured aliases that redirects one package to another, you may want to alias the actual node_modules packages instead to make it work for SSR externalized dependencies. Both Yarn and pnpm support aliasing via the npm: prefix.
tried all possible ways to add react
or @preact/compat
to deps.inline/ssr.noExternal, this doesn't work, tried these:
['@preact/compat', 'react', 'react-dom', "npm:@preact/compat"]
always same error
tried all possible ways to add
react
or@preact/compat
to deps.inline/ssr.noExternal, this doesn't work, tried these:['@preact/compat', 'react', 'react-dom', "npm:@preact/compat"]
always same error
No, Vite recommends defining react inside package.json
with npm
prefix. Please, follow provided links to yarn or pnpm documentation.
Note: I understand deps.inline
throws error, this is unrelated to aliasing.
If I add this to /node_modules/react-router/package.json
, everything works fine:
"exports": {
".": {
"import": "./index.js"
}
}
This means that react-router
doesn't package their library with Node ESM in mind. I checked they bundle ESM for browser, but it's incompatible with Node, as it doesn't have .mjs
extension or package.json
with type: module
. See more here.
I wan't able to make your reproduction run, so I would recommend opening an issue in react-router
package for now, and regenerate their package.json
in post install hook.
I wan't able to make your reproduction run
Weird, yarn install
+ npx vitest run
works fine for me on several devices
I would recommend opening an issue in
react-router
The thing is, there at least 4 more libraries in my main project with such issue and dealing with all of those this way takes ages to complete, would be way better to see some workaround (at least temporal)
Weird, yarn install + npx vitest run works fine for me on several devices
I mean, I wasn't able to fix it.
The thing is, there at least 4 more libraries in my main project with such issue and dealing with all of those this way takes ages to complete, would be way better to see some workaround (at least temporal)
I proposed several workarounds. The main problem with all of them I guess is the wrong package.json
configuration. You can try and manually setup test.alias
for your packages. PR https://github.com/vitest-dev/vitest/pull/1673 should help with the problem, but it won't fix it, if package has wrong package.json
, and you will still need to add aliases. Until we merge the PR, alias will only be applied if files are inlined
.
If I add this to
/node_modules/react-router/package.json
, everything works fine
tried doing this, getting this error:
FAIL src/app.test.tsx [ src/app.test.tsx ]Error: No "exports" main defined in C:\Users\wight\Documents\preact-vitest-repro\node_modules\react-router\package.json
❯ node_modules/react-router-dom/umd/react-router-dom.development.js:12:121
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯Serialized Error: {
"code": "ERR_PACKAGE_PATH_NOT_EXPORTED",
}
Managed to fix most errors in main package codebase by patching package.json files in node_modules 8 files packages (would take months to get it fixed in those in best case), but even this way some packages can not be patched, e.g. this:
Error: No "exports" main defined in C:\Users\wight\Documents\blog-template\node_modules\@mui\system\package.json
❯ Object.<anonymous> node_modules/@mui/material/node/styles/adaptV4Theme.js:14:15
package.json of node_modules\@mui\material\styles\package.json
{
"sideEffects": false,
"module": "./index.js",
"main": "../node/styles/index.js",
"types": "./index.d.ts"
}
definitely needs some workaround
Upd. was able to force esm for most deps using alias and https://github.com/vitest-dev/vitest/pull/1673 But some dependencies can not into esm at all, e.g. styled-components, see
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Vitest caught 1 unhandled error during the test run. This might cause false positive tests.
Please, resolve all the errors to make sure your tests are not affected.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Error ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
TypeError: Cannot read properties of null (reading 'context')
❯ Module.q ../../../../../../../../../C:/Users/wight/Documents/blog-template/node_modules/preact/hooks/dist/hooks.mjs:1:1014
❯ node_modules/styled-components/src/utils/interleave.js:1:18828
❯ O node_modules/styled-components/src/utils/interleave.js:1:19499
❯ d.t [as constructor] ../../../../../../../../../C:/Users/wight/Documents/blog-template/node_modules/preact/compat/dist/compat.mjs:1:1363
❯ d.O [as render] ../../../../../../../../../C:/Users/wight/Documents/blog-template/node_modules/preact/dist/preact.mjs:1:8158
❯ I node_modules/preact/dist/preact.js:1:5716
❯ b node_modules/preact/dist/preact.js:1:2103
❯ I node_modules/preact/dist/preact.js:1:5984
❯ b node_modules/preact/dist/preact.js:1:2103
❯ I node_modules/preact/dist/preact.js:1:5984
My changes: https://github.com/wight554/blog-template/pull/55/files#diff-6a3b01ba97829c9566ef2d8dc466ffcffb4bdac08706d3d6319e42e0aa6890dd
Is there any way I can force preact to be CJS @sheremet-va, tried aliasing but no luck? Unfortunately most react UI libraries aren't into supporting node native esm, they prefer bundlers to do they work
@wight554 yes, I also tried this MR agains your repro with no luck.
Is there any way I can force preact to be CJS
Unfortunately Vite's resolution algorithm is asynchronous, so it's not possible to reuse it. Maybe you can try and hijack require
yourself, using node's module
module? If it's successful we can try and find a way to integrate in inside Vitest, maybe under deps.cjs
or something.
@wight554 yes, I also tried this MR agains your repro with no luck.
Is there any way I can force preact to be CJS
Unfortunately Vite's resolution algorithm is asynchronous, so it's not possible to reuse it. Maybe you can try and hijack
require
yourself, using node'smodule
module? If it's successful we can try and find a way to integrate in inside Vitest, maybe underdeps.cjs
or something.
I was able to workaround it before by using TS CJS interop https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#commonjs-interop Used it for '@testing-library/preact', so it imported CJS version of preact https://github.com/vitest-dev/vitest/issues/747#issuecomment-1140225294 But it doesn't works since vite 3 version of vitest:
FAIL test/src/components/Header/Header.test.ts > Header > user is authenticated > user menu is opened > logout click > user service success > should set user state to null
FAIL test/src/components/Header/Header.test.ts > Header > user is authenticated > user menu is opened > logout click > user service error > should set snackbar state to error
TypeError: Cannot read properties of undefined (reading '__H')
❯ p ../../../../../../../../../C:/Users/wight/Documents/blog-template/node_modules/preact/hooks/dist/hooks.mjs:1:158
❯ Module._ ../../../../../../../../../C:/Users/wight/Documents/blog-template/node_modules/preact/hooks/dist/hooks.mjs:1:469
❯ y.RecoilObserver [as constructor] test/src/testUtils/components/RecoilObserver/RecoilObserver.ts:17:2
15| }: RenderableProps<RecoilObserverProps<T>>) => {
16| const value = useRecoilValueLoadable(node);
17| useEffect(() => onChange(async ? value : value.contents), [onChange, async, value]);
| ^
18| return null;
19| };
❯ y.M [as render] node_modules/preact/dist/preact.js:1:8004
❯ I node_modules/preact/dist/preact.js:1:5716
❯ b node_modules/preact/dist/preact.js:1:2103
❯ I node_modules/preact/dist/preact.js:1:5984
❯ b node_modules/preact/dist/preact.js:1:2103
❯ I node_modules/preact/dist/preact.js:1:5984
❯ b node_modules/preact/dist/preact.js:1:2103
Upd. was able to fix it by using cjs interop for all 'preact/hooks' (all packages with multiple version) imports and testing library In my case I can fix it on transform stage (I'm using custom transformer), but I'd be happy to see some general solution, e.g. deps.cjsPackages flag that'll do some transforms for imports on vitest side example: https://github.com/wight554/blog-template/pull/55/commits/e9c6659fa05ff972291b36ce65b35e54e712b130
@wight554 cjs interop is the same as using const mod = require('path')
, so I don't see why you really want to use it over simple require in this case. The syntax is hideous.
Using require
bypasses Vitest entirely, and uses Native Node.js (note, that loader from #1673 will not work in this cases). Please, can you formulate more clearly what you expect from Vitest in this issue?
@wight554 cjs interop is the same as using
const mod = require('path')
, so I don't see why you really want to use it over simple require in this case. The syntax is hideous.Using
require
bypasses Vitest entirely, and uses Native Node.js (note, that loader from #1673 will not work in this cases). Please, can you formulate more clearly what you expect from Vitest in this issue?
I wonder if it's possible to force configurable deps list to use require instead of import from vitest side
E.g. instead of doing changes I made higher I can add ['preact/hooks', '@testing-library/preact']
to some new configuration option like deps.forceCJS: ['preact/hooks', '@testing-library/preact']
i've seen nodejs createRequire, but idk how can I try to add it to vitest https://nodejs.org/api/module.html#modulecreaterequirefilename
i've seen nodejs createRequire, but idk how can I try to add it to vitest https://nodejs.org/api/module.html#modulecreaterequirefilename
Vitest injects require
in your files, you can use it directly.
Upd. was able to fix it by using cjs interop for all 'preact/hooks' (all packages with multiple version) imports and testing library In my case I can fix it on transform stage (I'm using custom transformer), but I'd be happy to see some general solution, e.g. deps.cjsPackages flag that'll do some transforms for imports on vitest side example: wight554/blog-template@e9c6659
managed to achieve same result with
alias: [
{
find: 'preact/hooks',
replacement: path.resolve(__dirname, './node_modules/preact/hooks/dist/hooks.js'),
},
{
find: '@testing-library/preact',
replacement: path.resolve(
__dirname,
'./node_modules/@testing-library/preact/dist/cjs/index.js',
),
},
],
this is way more DX friendly it is possible to cover this internally?
Upd. cleanest solution I was able to implement:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
alias: [
{
find: 'preact/hooks',
replacement: require.resolve('preact/hooks'),
},
{
find: '@testing-library/preact',
replacement: require.resolve('@testing-library/preact'),
},
],
Upd2. Can be splitted to simple:
const generateCjsAlias = (cjsPackages: Array<string>) => {
const require = createRequire(import.meta.url);
return cjsPackages.map((p) => ({
find: p,
replacement: require.resolve(p),
}));
};
alias: [...generateCjsAlias(['preact/hooks', '@testing-library/preact'])],
I don't mind having this in codebase or as separate plugin, but still would be nicer to see smth like this in vitest itself, either way issue can be closed since there's acceptable fix
would be nicer to see smth like this in vitest itself
Vitest has both alias
and test.alias
config options, what else do you need? How users populate it is not Vitest authority.
would be nicer to see smth like this in vitest itself
Vitest has both
alias
andtest.alias
config options, what else do you need? How users populate it is not Vitest authority.
I meant having a separate option that'll use alias internally to simplify logic, but that's not really necessary
Just a note -> I got it to work with the solution from @wight554 🙏 - but only when combined with https://github.com/preactjs/compat-alias-package
@acapro got it working as well with the mixture of test.alias
and compat alias package.
All my tests pass expect the ones testing custom hooks. Did you have any like that? am using @testing-library/react-hooks
and get this error
[log] ❯ Object.<anonymous> node_modules/@testing-library/react-hooks/lib/dom/pure.js:48:18
[error] Error: Package subpath './test-utils' is not defined by "exports" in node_modules/react-dom/package.json
[log] ❯ getRenderer node_modules/@testing-library/react-hooks/lib/pure.js:54:12
[log] ❯ Object.<anonymous> node_modules/@testing-library/react-hooks/lib/pure.js:70:5
[log] ❯ Object.<anonymous> node_modules/@testing-library/react-hooks/lib/index.js:11:13
[log] Serialized Error: {
"code": "ERR_PACKAGE_PATH_NOT_EXPORTED",
}
I'm facing similar issue with styled-components/native
Error: Cannot find module 'react-native'
Require stack:
- ./styled-components/native/dist/styled-components.native.cjs.js
Serialized Error: {
"code": "MODULE_NOT_FOUND",
"requireStack": [
"./node_modules/styled-components/native/dist/styled-components.native.cjs.js",
],
}
styled-components/native
internally uses require like so:
// inside styled-components.native.cjs.js
var reactNative = require('react-native');
I'm using an alias for react-native -> react-native-web, but adding test.alias or test.deps.inline didn't help. I guess vite can't transpile this? Manually patching the dependency solves the problem, but it would be nice if vite could override the require
inside styled-components/native
itself.
// inside styled-components.native.cjs.js
-var reactNative = require('react-native');
+var reactNative = require('react-native-web');
Here's my config:
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'react-native': 'react-native-web',
},
},
test: {
environment: 'jsdom',
setupFiles: './tests/setup.ts',
},
});
I guess vite can't transpile this?
Vitest doesn't support aliasing require
calls, yes. There is an open issue: https://github.com/vitest-dev/vitest/issues/1910
I had a similar problem with a require("react")
inside one of my dependencies. I was able to get around it by adding a Vitest setup script…
//vitest.config.ts
setupFiles: ['setup.js']
And then in the setup file I manually created an override for the library's React version by changing require.cache
:
require('react'); // to force load it
const modulePath = path.join('node_modules', 'react', 'index.js');
const source = path.resolve(__dirname, '..', 'my-lib', modulePath);
const target = path.resolve(__dirname, modulePath);
require.cache[source] = require.cache[target];