The reason why some npm packages contain invalid code is because the transpilation process of webpack-based frameworks, most notably Next.js, isn't strict and tolerates erroneous code: thus an npm package can work with Next.js while not work with Vite-based frameworks. You can learn more at Why.
The situation will improve and eventually be completely resolved as Vite-based frameworks gain popularity, which we can expect to happen quickly since all frameworks, except for Next.js, are using Vite nowadays.
Workaround
You can workaround problematic npm packages by using on of the following:
ssr.noExternal
vite-plugin-cjs-interop
SSR opt-out
The workarounds usually work. But if you struggle working around a broken npm package then feel free to reach out for help.
You may need to add nested npm packages to ssr.noExternal, because CJS/ESM issues sometimes cascade down along the "noExternal boundary" as you add npm packages to ssr.noExternal.
The section Why explains why ssr.noExternal is a workaround.
vite-plugin-cjs-interop
// vite.config.jsimport { cjsInterop } from "vite-plugin-cjs-interop"export default { plugins: [ cjsInterop({ // Add broken npm package here dependencies: [ // Apply patch to root import: // import someImport from 'some-package' "some-package", // Apply patch to all sub imports: // import someImport from 'some-package/path' // import someImport from 'some-package/sub/path' // ... "some-package/**", ] }) ]}
SyntaxError: Named export 'SomeImport' not found. The requested module 'some-library' is a CommonJS module, which may not support all module.exports as named exports.
SSR opt-out
As a last resort, if both ssr.noExternal and vite-plugin-cjs-interop doesn't work, you can opt out of SSR:
Opting out of SSR almost always works, see explanation at Why.
Why
You may ask yourself how it's possible that an npm package can publish invalid JavaScript code that doesn't get fixed for months.
The main reason is that some frameworks such as Next.js transpile the server-side code of npm packages, whereas Vite transpiles only the client-side code of npm packages. When server-side code contains invalid JavaScript then Node.js crashes and throws one of these errors, while transpilers are more tolerant and transform invalid JavaScript (that Node.js isn't able to execute) into valid JavaScript (that Node.js is able to execute).
By default, Vite doesn't transpile the server-side code of npm packages for a much faster DX, so that Node.js directly executes the server-side code without involving a slow transpilation process.
That's why adding an npm package to ssr.noExternal is usually a workaround when the npm package contains invalid JavaScript.
By adding an npm package to ssr.noExternal, you replicate the behavior of frameworks like Next.js.
The issue is most widespread in the React ecosystem because of Next.js's prevalence, but this is going to less and less of an issue as Vite-based React frameworks gain popularity. All frameworks other than Next.js are now Vite-based (e.g. Remix), thus the situation will quickly improve.
Common errors
Common invalid JavaScript code published by npm packages.
Cannot use import statement outside a module
(node:30335) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
node_modules/some-library/dist/esm/index.js:1
SyntaxError: Cannot use import statement outside a module
at Object.compileFunction (node:vm:352:18)
at wrapSafe (node:internal/modules/cjs/loader:1033:15)
at Module._compile (node:internal/modules/cjs/loader:1069:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Module._load (node:internal/modules/cjs/loader:827:12)
at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:170:29)
at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:409:24)
Node.js v18.0.0
Node.js's message set "type": "module" in package.json or use the .mjs extension is misleading because it means the library's node_modules/some-library/package.json, not your package.json. It isn't really actionable (unless you patch the library).
import { SomeImport } from "some-library";
^^^^^^^^^^
SyntaxError: Named export 'SomeImport' not found. The requested module 'some-library' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'some-library';
const { SomeImport } = pkg;
at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
at async ModuleJob.run (node:internal/modules/esm/module_job:190:5)
Node.js v18.0.0
For default exports, the workaround proposed by Node.js may not work and you may need to do this instead:
import DefaultImport from "some-library"; import pkg from 'some-library'; const DefaultImport = pkg.default ?? pkg;
node:internal/process/esm_loader:91
internalBinding('errors').triggerUncaughtException(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'node_modules/some-library/dist/esm/some-file' imported from node_modules/some-library/dist/esm/index.js
Did you mean to import some-file.js?
at new NodeError (node:internal/errors:372:5)
at finalizeResolution (node:internal/modules/esm/resolve:405:11)
at moduleResolve (node:internal/modules/esm/resolve:966:10)
at defaultResolve (node:internal/modules/esm/resolve:1176:11)
at ESMLoader.resolve (node:internal/modules/esm/loader:605:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:318:18)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:80:40)
at link (node:internal/modules/esm/module_job:78:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js v18.0.0
The error is usually thrown when the library's ESM code contains import './some-file'. (It should be import './some-file.js' instead, as imports in ESM code are required to include the file extension.)
Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import 'node_modules/some-library/dist/some-dir/' is not supported resolving ES modules imported from node_modules/some-library/dist/index.js
Did you mean to import ./some-dir/index.js?
at finalizeResolution (node:internal/modules/esm/resolve:412:17)
ESM doesn't allow directory imports: all import paths need to point to a filename instead.
Another common problem is code importing CSS which makes Node.js crash:
Error: ERR_UNKNOWN_FILE_EXTENSION .css node_modules/some-library/dist/main.css
at someFunction (node_modules/some-library/dist/main.js)
at nextLoad (node:internal/modules/esm/loader:163:28)
at ESMLoader.load (node:internal/modules/esm/loader:605:26)
The error Cannot read properties of undefined is often caused by ESM/CJS issues.
TypeError: Cannot read properties of undefined (reading 'someProp')
at someFunction (node_modules/some-good-lib/dist/index.js:1000:3)
at someHook (renderer/+someHook.js:13:37)
The underlying issue is often this:
// node_modules/some-good-lib/dist/index.js// Because of CJS/ESM issues, someImport is undefinedimport { someImport } from 'some-broken-lib'// ...function someFunction() { // TypeError: Cannot read properties of undefined (reading 'someProp') someImport.someProp}
Sometimes, when dependency injection is used, the import to some-broken-lib isn't in the file in which the
exception is being raised, making it harder to understand which library is broken. See
here an example of this.
Adding some-broken-lib to ssr.noExternal usually solves the issue.
Alternatively, you can add some-good-lib to ssr.noExternal while adding some-broken-lib to vite-plugin-cjs-interop.
React invalid component
The following is a common React error and the root cause is usually a CJS/ESM issue.
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
at renderElement (node_modules/react-dom/...)
at renderNodeDestructiveImpl (node_modules/react-dom/...)
at renderNodeDestructive (node_modules/react-dom/...)
...
Or got: object.
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
React usually logs a component trace before throwing the error:
Check your code at +Page.tsx:26.
at Page
at div
at div
at Layout (/pages/+Layout.tsx:66:19)
Sometimes React doesn't log a component trace. In that case you can use this temporary patch to get a component trace.
You can also use the temporary patch to get a more precise component trace. (For example the component trace above says Check your code at +Page.tsx:26 but there can be hundreds of +Page.tsx files.)
Use the component trace to find out which component is undefined / an object. You'll likely see that the component is imported and that the import value is undefined / an object (instead of a React component) because of CJS/ESM interoperability quirks.
A local workaround is usually this:
import SomeComponent from "some-npm-package"import pkg from "some-npm-package"// This:const { SomeComponent } = pkg // Or that:const SomeComponent = pkg.default