vite: Bundling SSR Modules using CommonJS exports alias results in ReferenceError: exports is not defined

Describe the bug

When importing an SSR module, if

  • it uses the exports alias instead of module.exports,
  • ssr.noExternal is declared with the module

Vite is unable to evaluate it. This can also happen with the more common module.exports, but seems to not be guaranteed.

While this behavior may seem esoteric due to modern proliferation of module.exports, this is a feature of Node since v0.1.16 and has not been deprecated. In addition, there are instances of various npm modules in the wild which use this method of exporting fields, including, but not limited to:

…and much, much, more searchable on GitHub. I believe this is also an output of some bundlers.

Reproduction

https://github.com/GrygrFlzr/vite-cjs-ssr

git clone https://github.com/GrygrFlzr/vite-cjs-ssr.git
cd vite-cjs-ssr
npm i
npm run dev

Visit http://localhost:3000 to trigger SSR, which consists of a very simple script that uses the cookie npm module. The page will render exports is not defined and errors will show up in the server console:

6:29:45 PM [vite] new dependencies found: cookie, updating...
6:29:45 PM [vite] Error when evaluating SSR module /node_modules/cookie/index.js:
ReferenceError: exports is not defined
    at /node_modules/cookie/index.js:15:1
    at instantiateModule (C:\Users\GrygrFlzr\Documents\projects\vite-cjs\node_modules\vite\dist\node\chunks\dep-efe32886.js:68893:166)
ReferenceError: exports is not defined
    at /node_modules/cookie/index.js:13:1
    at instantiateModule (C:\Users\GrygrFlzr\Documents\projects\vite-cjs\node_modules\vite\dist\node\chunks\dep-efe32886.js:68893:166)

System Info

  • vite version: 2.1.2
  • Operating System: Windows 10 10.0.19042
  • Node version: 14.16.0
  • Package manager and version: npm 6.14.11

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 85
  • Comments: 45 (4 by maintainers)

Commits related to this issue

Most upvoted comments

Vite is an integral part of SvelteKit that provides many benefits such as code changes showing up instantly, code splitting for fast loading sites, etc. The Vite team has been nothing but helpful in resolving issues and have both fixed issues for us and accepted patches from us. Vite 2.7 to be released shortly will solve the majority of issues we’re aware of and we’re already working on Vite 2.8 with additional fixes. I’m aware Vite issues have been some of the most common to encounter with SvelteKit, but that’s changing very rapidly and we’re definitely not dropping Vite. Also, the majority of 1.0 blockers are in SvelteKit itself and not in Vite.

If a package bundler called vite is ruining the reputation and preventing the release of the most loved JS framework SvelteKit for several months, then why don’t Svelte team and Rich Harris drop/discard the vite thing entirely ? They can utilize a bundler which actually works, their own rollup maybe? All the blocking issues for sveltekit belong to vite. Or is this a core issue of sveltekit way of SSR implementation?

Here’s my summary of the situation, as best I understand it:

  • If ssr.noExternal includes a package, it will get loaded via ssrLoadModule during development and bundled via Vite’s plugins (plus any user plugins) during build
  • ssrLoadModule doesn’t have any special handling for CommonJS, nor does Vite’s internal set of plugins
  • Therefore it’s to be expected that CommonJS dependencies added to noExternal will fail

In other words I don’t think this is a bug. A lot of SvelteKit beta users hit it because we were fumbling around with the right config (to ensure that built serverless functions didn’t have dependencies that specific platforms struggled with), but not because Vite was doing anything wrong.

Perhaps instead of treating this as a bug we should be thinking in terms of providing a more useful error message — e.g. if ssrLoadModule results in ReferenceError: (module|require|exports|__dirname|etc) is not defined Vite replaces it with something more actionable, like ‘CommonJS dependencies cannot be added to ssr.noExternal — please remove cookie

IMPORTANT: to be extra clear about this, any SvelteKit users that hit this and have an ssr.noExternal in their svelte.config.cjs from project creation should remove it. The template no longer has this config and you should match what a newly created project now looks like.

Is there any new progress on this issue? Most svelte components have this problem. https://github.com/idris-maps/svelte-parts https://github.com/meigo/svelte-video-player/issues/10

Thanks a lot. You might be right about that @zerodevx/svelte-toast but since I got it working with the above changes applied:

vite: { optimizeDeps: { include: ['clipboard-copy', 'detect-node', 'unload', 'broadcast-channel'] }}

I wouldn’t dare to check it in isolation again. 😸

Having a loader being able to recognize it as a CJS problem and hinting to optimizeDeps.include would have been great. I think optimizeDeps name is a bit misleading in this context. Messaging or a lack of proper hinting is/was a regular problem with installation of svelte packages (dev or not to dev).

For me changing to module.exports also does not solve the problem: I get module is not defined error.

I was having this issue and fixed with https://github.com/originjs/vite-plugins/tree/main/packages/vite-plugin-commonjs

Working on a sveltekit project may help someone in the future

I have working WIP sapper project that I tried to migrate into svelte-kit. The above error and couple - I assume related - hit me hard while adding any meaningful(to me with existing codebase) svelte component, namely:

I tried many possible configs mentioned here and in other topics, with no success. How could I and how could we as developers be able to use/adapt/wrap libraries using cjs? Requiring every package to add ejs support and not use any cjs packages seems unrealistic. cjs in itself is one of those billion dollars in development wasted problems but we had a decade to understand it good enough to be able to solve related issues efficiently.

How would one go about using cjs modules or ejs modules that use cjs modules with vitejs? Is it currently supported? If yes, then what are the correct settings to do that efficiently or good enough? If it isn’t currently supported then would that be supported in the future or is it intentionally not supported/out of scope of project? If so, then why?

Thank you very much in advance. Keep up the good work folks. Let me try to help you, even if that only means asking the right questions. Are those questions the right ones? 😉

If Netlify requires dependencies to be bundled then we should prefer to do that with the Netlify tooling in the SvelteKit adapter and not with Vite. I’d filed an issue for this in https://github.com/sveltejs/kit/issues/1016. I closed it because it’s unclear to me whether there are currently any issues that people are experiencing, but if there are issues then I think that would be the proper solution to try to implement

Just did a test on d3 with the latest kit, requires ssr: { noExternal: ["d3"] } to run the build. I tried no options, then all the variations listed in the faq. Repo

Managed to get around a similar issue by using the vite-plugin-commonjs plugin:

// vite.config.ts
import { esbuildCommonjs, viteCommonjs } from "@originjs/vite-plugin-commonjs";
import react from "@vitejs/plugin-react";
import { UserConfig } from "vite";
import ssr from "vite-plugin-ssr/plugin";

const config: UserConfig = {
  plugins: [viteCommonjs(), react(), ssr()],
  optimizeDeps: {
    esbuildOptions: {
      plugins: [esbuildCommonjs(["@app/config"])],  // the problematic cjs module
    },
    include: ["@app/config"],  // also here
  },
};

export default config;

I have PRs pending to convert the CommonJS dependencies of first two of those libraries to ESM: https://github.com/feross/clipboard-copy/pull/49 and https://github.com/iliakan/detect-node/pull/17

In the meantime you can work around this by adding those CommonJS dependencies to optimizeDeps.include. There are issues in the @sveltestack/svelte-query and carbon-components-svelte repos showing how to do this.

@zerodevx/svelte-toast does not use CommonJS so I’m not sure what issue you’re having with that library, but it might be a little different than the other two

That’s a good summary. Another option might be to have Vite optimize these dependencies and utilize the optimized ESM version. Svelte components need to be in ssr.noExternal. That means if they have a CommonJS dependency they will fail unless that dependency is converted to ESM by including it in optimizeDeps.include. It’s possible that any CommonJS dependencies in noExternal could work if they’re in optimizeDeps.include though I haven’t tested that. I’m not sure whether Vite’s automatic dependency discovery can be made to work with vite-plugin-svelte, but if it’s possible that would be a nice quality of life improvement (tracked in https://github.com/vitejs/vite/issues/3024). Svelte component authors should try to make their dependencies ESM if possible in either case

I believe this is also an output of some bundlers.

It turns out that Rollup creates output that uses just exports instead of module.exports so this is probably quite common to hit. A user reported this issue with apollo-link-prismic which is written in ESM but then bundled to CJS with Rollup

have you tried

import {* as somename} from 'module';
//tried with cookie and it worked
import * as cookie from 'cookie';

Probably #2157 can resolve this issue. I am looking forward to its merge.