prisma: After upgrading from 3.2.1 to 3.3.0 I get a 'Error: Cannot find module 'os''

Bug description

I have an issue where my webpack-built cli application throws an error Error: Cannot find module 'os' after upgrading Prisma from 3.2.1 to 3.3.0.

When googling I found another thread which seems to be the same issue: https://answers.netlify.com/t/nextjs-page-with-server-side-logic-throws-error-cannot-find-module-os/46431

When looking at the build output it looks like there is something going wierd / different with the imports.

When using Prisma 3.2.1 the output for using ‘supports-color’ looks like this (this is the working code):

// ../../node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js
var require_supports_color2 = __commonJS2((exports2, module2) => {
  "use strict";
  var os = __webpack_require__(22037);
  var tty = __webpack_require__(76224);
  var hasFlag = require_has_flag2();
  var {env} = process;
  var forceColor;
  if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
    forceColor = 0;
  } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
    forceColor = 1;
  }
  if ("FORCE_COLOR" in env) {
    if (env.FORCE_COLOR === "true") {
      forceColor = 1;
    } else if (env.FORCE_COLOR === "false") {
      forceColor = 0;
    } else {
      forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3);
    }
  }
  function translateLevel(level) {
    if (level === 0) {
      return false;
    }
    return {
      level,
      hasBasic: true,
      has256: level >= 2,
      has16m: level >= 3
    };
  }
  __name(translateLevel, "translateLevel");
  function supportsColor(haveStream, streamIsTTY) {
    if (forceColor === 0) {
      return 0;
    }
    if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
      return 3;
    }
    if (hasFlag("color=256")) {
      return 2;
    }
    if (haveStream && !streamIsTTY && forceColor === void 0) {
      return 0;
    }
    const min2 = forceColor || 0;
    if (env.TERM === "dumb") {
      return min2;
    }
    if (process.platform === "win32") {
      const osRelease = os.release().split(".");
      if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
        return Number(osRelease[2]) >= 14931 ? 3 : 2;
      }
      return 1;
    }
    if ("CI" in env) {
      if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE"].some((sign2) => sign2 in env) || env.CI_NAME === "codeship") {
        return 1;
      }
      return min2;
    }
    if ("TEAMCITY_VERSION" in env) {
      return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
    }
    if (env.COLORTERM === "truecolor") {
      return 3;
    }
    if ("TERM_PROGRAM" in env) {
      const version = parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
      switch (env.TERM_PROGRAM) {
        case "iTerm.app":
          return version >= 3 ? 3 : 2;
        case "Apple_Terminal":
          return 2;
      }
    }
    if (/-256(color)?$/i.test(env.TERM)) {
      return 2;
    }
    if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
      return 1;
    }
    if ("COLORTERM" in env) {
      return 1;
    }
    return min2;
  }
  __name(supportsColor, "supportsColor");
  function getSupportLevel(stream) {
    const level = supportsColor(stream, stream && stream.isTTY);
    return translateLevel(level);
  }
  __name(getSupportLevel, "getSupportLevel");
  module2.exports = {
    supportsColor: getSupportLevel,
    stdout: translateLevel(supportsColor(true, tty.isatty(1))),
    stderr: translateLevel(supportsColor(true, tty.isatty(2)))
  };
});

When using Prisma 3.3.0 the output for using ‘supports-color’ looks like this (the __require(‘os’) fails, so would the tty import):

var require_supports_color2 = __commonJS2({
  "../../node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports2, module2) {
    "use strict";
    var os2 = __require("os");
    var tty = __require("tty");
    var hasFlag = require_has_flag2();
    var { env } = process;
    var forceColor;
    if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
      forceColor = 0;
    } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
      forceColor = 1;
    }
    if ("FORCE_COLOR" in env) {
      if (env.FORCE_COLOR === "true") {
        forceColor = 1;
      } else if (env.FORCE_COLOR === "false") {
        forceColor = 0;
      } else {
        forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3);
      }
    }
    function translateLevel(level) {
      if (level === 0) {
        return false;
      }
      return {
        level,
        hasBasic: true,
        has256: level >= 2,
        has16m: level >= 3
      };
    }
    __name(translateLevel, "translateLevel");
    __name2(translateLevel, "translateLevel");
    function supportsColor(haveStream, streamIsTTY) {
      if (forceColor === 0) {
        return 0;
      }
      if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
        return 3;
      }
      if (hasFlag("color=256")) {
        return 2;
      }
      if (haveStream && !streamIsTTY && forceColor === void 0) {
        return 0;
      }
      const min2 = forceColor || 0;
      if (env.TERM === "dumb") {
        return min2;
      }
      if (process.platform === "win32") {
        const osRelease = os2.release().split(".");
        if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
          return Number(osRelease[2]) >= 14931 ? 3 : 2;
        }
        return 1;
      }
      if ("CI" in env) {
        if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE"].some((sign2) => sign2 in env) || env.CI_NAME === "codeship") {
          return 1;
        }
        return min2;
      }
      if ("TEAMCITY_VERSION" in env) {
        return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
      }
      if (env.COLORTERM === "truecolor") {
        return 3;
      }
      if ("TERM_PROGRAM" in env) {
        const version = parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
        switch (env.TERM_PROGRAM) {
          case "iTerm.app":
            return version >= 3 ? 3 : 2;
          case "Apple_Terminal":
            return 2;
        }
      }
      if (/-256(color)?$/i.test(env.TERM)) {
        return 2;
      }
      if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
        return 1;
      }
      if ("COLORTERM" in env) {
        return 1;
      }
      return min2;
    }
    __name(supportsColor, "supportsColor");
    __name2(supportsColor, "supportsColor");
    function getSupportLevel(stream3) {
      const level = supportsColor(stream3, stream3 && stream3.isTTY);
      return translateLevel(level);
    }
    __name(getSupportLevel, "getSupportLevel");
    __name2(getSupportLevel, "getSupportLevel");
    module2.exports = {
      supportsColor: getSupportLevel,
      stdout: translateLevel(supportsColor(true, tty.isatty(1))),
      stderr: translateLevel(supportsColor(true, tty.isatty(2)))
    };
  }
});

The __require function above looks like this:

var __require = /* @__PURE__ */ ((x) =>  true ? __webpack_require__(91426) : 0)(function(x) {
  if (true)
    return __webpack_require__(91426).apply(this, arguments);
  throw new Error('Dynamic require of "' + x + '" is not supported');
});

and 91426

/***/ 91426:
/***/ ((module) => {

function webpackEmptyContext(req) {
	var e = new Error("Cannot find module '" + req + "'");
	e.code = 'MODULE_NOT_FOUND';
	throw e;
}
webpackEmptyContext.keys = () => ([]);
webpackEmptyContext.resolve = webpackEmptyContext;
webpackEmptyContext.id = 91426;
module.exports = webpackEmptyContext;

/***/ }),

How to reproduce

The setup is pretty specfic but I hope the file examples show the reason why its failing.

Expected behavior

Native node.js imports should just import without throwing an error.

Prisma information

The error happends whenever you have prisma 3.3.0 inside your build.

Environment & setup

OS: Ubuntu NodeVersion: Tried 12 and 16. Webpack 5

Prisma Version

3.3.0

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 13
  • Comments: 26 (6 by maintainers)

Most upvoted comments

In @prisma/client@3.2.0 code was var os = require("os"); (i.e. require), so webpack can handle it, because it is statically analyzable, in v3.3.0 and above code is var os2 = __require("os"); (i.e. no more simple require, now it is helper), so webpack can’t handle it, because it is not statically analyzable.

That is correct, this will be fixed in the next release.

Thank you all for the feedback, it was really useful. And @tonivj5, your reported issue/comment has also been fixed.

This should be fixed now in 3.5.0-integration-fix-bundlers-broken-require.2 (@tonivj5 @quinnjr), would you mind to give it another try? Thanks 😃

I’ll upgrade our e2e tests so that this does not happen again. We do test webpack but don’t perform a full bundle.

In @prisma/client@3.2.0 code was var os = require("os"); (i.e. require), so webpack can handle it, because it is statically analyzable, in v3.3.0 and above code is var os2 = __require("os"); (i.e. no more simple require, now it is helper), so webpack can’t handle it, because it is not statically analyzable.

What is solution?

  • Do not bundle client, because bundler on application side will do it (avoid double bundling)
  • Avoid using __require helper, maybe esbuild has option for this
  • polyfill Node.js modules on esbuild side (don’t know possible it or not)
  • Avoid usage Node.js modules on browser side (maybe hard), but it is good solution, most of them from dependencies, for example supports-color package, why you need it in browser? Also it reduces bundle size very well

Note - this problems is not related webpack itself, it happens with other bundlers too, because bundlers can’t bundle non statically analyzable code, you will faced with this using rollup/parcel/vite/etc too

Note - double bundling is always bad idea, some optimizations will be loses, bad caching, big bundle size

@millsp it works to me too! 🚀

But these warnings appears only If I use local client generation


WARNING in ./libs/.../database/client/runtime/index.js 81:73-80
Critical dependency: require function is used in a way in which dependencies cannot be statically extracted

WARNING in ./libs/.../database/client/runtime/index.js 85:15-22
Critical dependency: require function is used in a way in which dependencies cannot be statically extracted

webpack compiled with 2 warnings (a1699459dc9c42dd)

If I import from @prisma/client no warnings are showed

Confirmed working on my application.

Thanks for the quick turn-around time @millsp.

Additional reproduction repo: https://github.com/quinnjr/webpack-os-error-issue

Steps to observer:

  1. Install dependencies and setup Prisma correctly
  2. Generate Prisma client
  3. run npm run build:ssr
  4. Wait for webpack to finish bundling frontend and backend.
  5. Run npm run serve:ssr

The same for 3.4.0.