Skip to content

require('external') is not converted to import #249

@hi-ogawa

Description

@hi-ogawa

Describe the bug

When doing ssr build with noExternal: ["@vitejs/test-cjs-peer-react"] and following inputs:

  • entry.js
import React from "react";
import testDep from "@vitejs/test-cjs-peer-react";
console.log({ React, testDep });
  • @vitejs/test-cjs-peer-react
const React = require("react");
exports.test = React.useState;

Rolldown/Vite output looks like:

import { createRequire } from "node:module";
import React from "react"; // 👈👈
// ...
var __require = /* @__PURE__ */ createRequire(import.meta.url);
var require_test_cjs_peer_react = __commonJSMin((exports) => {
	const React$1 = __require("react");   // 👈👈
	exports.test = React$1.useState;
});
var import_test_cjs_peer_react = __toESM(require_test_cjs_peer_react());
console.log({
	React,
	testDep: import_test_cjs_peer_react.default
});

Rollup/Vite output looks like:

import require$$0 from "react"; // 👈👈
function getDefaultExportFromCjs(x) {
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}
var testCjsPeerReact = {};
var hasRequiredTestCjsPeerReact;
function requireTestCjsPeerReact() {
  if (hasRequiredTestCjsPeerReact) return testCjsPeerReact;
  hasRequiredTestCjsPeerReact = 1;
  const React = require$$0;
  testCjsPeerReact.test = React.useState;
  return testCjsPeerReact;
}
var testCjsPeerReactExports = requireTestCjsPeerReact();
const testDep = /* @__PURE__ */ getDefaultExportFromCjs(testCjsPeerReactExports);
console.log({ React: require$$0, testDep });

My actual scenario is in hi-ogawa/vite-plugins#931 where I have dependency chain react-tweet -> swr -> use-sync-external-store -> react and configure noExternal: ["react-tweet"]. Project direct dependencies include only react and react-tweet, so react-tweet, swr, use-sync-external-store are bundled and only react is externalized.

I think this phenomena is same as lib mode issue raised in #223. Also a related rolldown feature is discussed in rolldown/rolldown#4575. I think we need a same behavior as rollup (perhaps commonjs plugin?) at rolldown-vite level at least.


Elaborating on why this is the issue for my use case, the reason why react-tweat is not externalized is because it's not a valid node package as it has css import inside. I could technically add noExternal: true and that should work, but I think for Vite users, there are various reasons to tweak this external/noExternal boundary for server build. Also __require("react") actually works fine when running the build output on node, but it failed on Cloudflare deployment because my build pipeline relies on 2nd build step to bundle externalized server deps.

Reproduction

https://stackblitz.com/github/hi-ogawa/reproductions/tree/main/rolldown-vite-deep-require-external

Steps to reproduce

./node_modules/rolldown-vite/bin/vite.js build
./node_modules/vite/bin/vite.js build

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 20.19.1 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.8.2 - /usr/local/bin/npm
    pnpm: 8.15.6 - /usr/local/bin/pnpm
  npmPackages:
    @vitejs/test-cjs-peer-react: file:./fixtures/test-dep => undefined 
    rolldown-vite: 6.3.18 => 6.3.18 
    vite: latest => 6.3.5

Used Package Manager

pnpm

Logs

No response

Validations

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions