question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

When using SWC, code wrapped in `if (typeof window === 'undefined')` is not removed from the browser bundle.

See original GitHub issue

What version of Next.js are you using?

12.0.2

What version of Node.js are you using?

16.8.0

What browser are you using?

Chrome / N/A

What operating system are you using?

macOS

How are you deploying your application?

other

Describe the Bug

When using SWC, code wrapped in if (typeof window === 'undefined') is not removed from the browser bundle.

Without a .babelrc in my project (and confirming that it’s using SWC via lack of warn - Disabled SWC as replacement for Babel warning), I see that server-side libraries are loaded in the browser bundle.

See repro.

Expected Behavior

Code wrapped in if (typeof window === 'undefined') is not included in the browser bundle.

To Reproduce

Original code:

import { getDocumentData } from 'utils/data/documentData';
import type { QGetServerSidePropsContext } from 'utils/serverSideProps/types';

export async function withServerSideTranslations(
  context: QGetServerSidePropsContext
) {
  if (typeof window === 'undefined') {
    const serverSideTranslations = (
      await import('next-i18next/serverSideTranslations')
    ).serverSideTranslations;
    const data = await getDocumentData(context.req);
    return {
      props: await serverSideTranslations(
        data.localePreference || context.locale!
      ),
    };
  }
  return { props: {} };
}

Browser output using SWC:

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
  /* harmony export */ withServerSideTranslations: function () {
    return /* binding */ withServerSideTranslations;
  },
  /* harmony export */
});
/* harmony import */ var regenerator_runtime__WEBPACK_IMPORTED_MODULE_0__ =
  __webpack_require__(
    /*! regenerator-runtime */ "../../node_modules/regenerator-runtime/runtime.js"
  );
/* harmony import */ var regenerator_runtime__WEBPACK_IMPORTED_MODULE_0___default =
  /*#__PURE__*/ __webpack_require__.n(
    regenerator_runtime__WEBPACK_IMPORTED_MODULE_0__
  );
/* harmony import */ var utils_data_documentData__WEBPACK_IMPORTED_MODULE_1__ =
  __webpack_require__(
    /*! utils/data/documentData */ "./utils/data/documentData.ts"
  );
/* module decorator */ module = __webpack_require__.hmd(module);
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}
function _asyncToGenerator(fn) {
  return function () {
    var self = this,
      args = arguments;
    return new Promise(function (resolve, reject) {
      var gen = fn.apply(self, args);
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
      }
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
      }
      _next(undefined);
    });
  };
}
function _withServerSideTranslations() {
  _withServerSideTranslations = _asyncToGenerator(
    regenerator_runtime__WEBPACK_IMPORTED_MODULE_0___default().mark(
      function _callee(context) {
        var serverSideTranslations, data;
        return regenerator_runtime__WEBPACK_IMPORTED_MODULE_0___default().wrap(
          function _callee$(_ctx) {
            while (1)
              switch ((_ctx.prev = _ctx.next)) {
                case 0:
                  if (true) {
                    _ctx.next = 11;
                    break;
                  }
                  _ctx.next = 3;
                  return __webpack_require__
                    .e(
                      /*! import() */ "node_modules_next-i18next_serverSideTranslations_js"
                    )
                    .then(
                      __webpack_require__.t.bind(
                        __webpack_require__,
                        /*! next-i18next/serverSideTranslations */ "../../node_modules/next-i18next/serverSideTranslations.js",
                        23
                      )
                    );
                case 3:
                  serverSideTranslations = _ctx.sent.serverSideTranslations;
                  _ctx.next = 6;
                  return (0,
                  utils_data_documentData__WEBPACK_IMPORTED_MODULE_1__.getDocumentData)(
                    context.req
                  );
                case 6:
                  data = _ctx.sent;
                  _ctx.next = 9;
                  return serverSideTranslations(
                    data.localePreference || context.locale
                  );
                case 9:
                  _ctx.t0 = _ctx.sent;
                  return _ctx.abrupt("return", {
                    props: _ctx.t0,
                  });
                case 11:
                  return _ctx.abrupt("return", {
                    props: {},
                  });
                case 12:
                case "end":
                  return _ctx.stop();
              }
          },
          _callee
        );
      }
    )
  );
  return _withServerSideTranslations.apply(this, arguments);
}

function withServerSideTranslations(context) {
  return _withServerSideTranslations.apply(this, arguments);
}
var _a, _b;
// Legacy CSS implementations will `eval` browser code in a Node.js context
// to extract CSS. For backwards compatibility, we need to check we're in a
// browser context before continuing.
if (
  typeof self !== "undefined" &&
  // AMP / No-JS mode does not inject these helpers:
  "$RefreshHelpers$" in self
) {
  var currentExports = module.__proto__.exports;
  var prevExports =
    (_b =
      (_a = module.hot.data) === null || _a === void 0
        ? void 0
        : _a.prevExports) !== null && _b !== void 0
      ? _b
      : null;
  // This cannot happen in MainTemplate because the exports mismatch between
  // templating and execution.
  self.$RefreshHelpers$.registerExportsForReactRefresh(
    currentExports,
    module.id
  );
  // A module can be accepted automatically based on its exports, e.g. when
  // it is a Refresh Boundary.
  if (self.$RefreshHelpers$.isReactRefreshBoundary(currentExports)) {
    // Save the previous exports on update so we can compare the boundary
    // signatures.
    module.hot.dispose(function (data) {
      data.prevExports = currentExports;
    });
    // Unconditionally accept an update to this module, we'll check if it's
    // still a Refresh Boundary later.
    module.hot.accept();
    // This field is set when the previous version of this module was a
    // Refresh Boundary, letting us know we need to check for invalidation or
    // enqueue an update.
    if (prevExports !== null) {
      // A boundary can become ineligible if its exports are incompatible
      // with the previous exports.
      //
      // For example, if you add/remove/change exports, we'll want to
      // re-execute the importing modules, and force those components to
      // re-render. Similarly, if you convert a class component to a
      // function, we want to invalidate the boundary.
      if (
        self.$RefreshHelpers$.shouldInvalidateReactRefreshBoundary(
          prevExports,
          currentExports
        )
      ) {
        module.hot.invalidate();
      } else {
        self.$RefreshHelpers$.scheduleUpdate();
      }
    }
  } else {
    // Since we just executed the code for the module, it's possible that the
    // new exports made it ineligible for being a boundary.
    // We only care about the case when we were _previously_ a boundary,
    // because we already accepted this update (accidental side effect).
    var isNoLongerABoundary = prevExports !== null;
    if (isNoLongerABoundary) {
      module.hot.invalidate();
    }
  }
}

Browser output using Babel:

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
  /* harmony export */ withServerSideTranslations: function () {
    return /* binding */ withServerSideTranslations;
  },
  /* harmony export */
});
/* harmony import */ var _opt_projects_quizlet_node_modules_next_node_modules_babel_runtime_helpers_esm_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ =
  __webpack_require__(
    /*! ../../node_modules/next/node_modules/@babel/runtime/helpers/esm/asyncToGenerator */ "../../node_modules/next/node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js"
  );
/* harmony import */ var core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_1__ =
  __webpack_require__(
    /*! core-js/modules/es.promise.js */ "../../node_modules/core-js/modules/es.promise.js"
  );
/* harmony import */ var core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_1___default =
  /*#__PURE__*/ __webpack_require__.n(
    core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_1__
  );
/* harmony import */ var utils_data_documentData__WEBPACK_IMPORTED_MODULE_2__ =
  __webpack_require__(
    /*! utils/data/documentData */ "./utils/data/documentData.ts"
  );
/* module decorator */ module = __webpack_require__.hmd(module);
function withServerSideTranslations(_x) {
  return _withServerSideTranslations.apply(this, arguments);
}
function _withServerSideTranslations() {
  _withServerSideTranslations = (0,
  _opt_projects_quizlet_node_modules_next_node_modules_babel_runtime_helpers_esm_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[
    "default"
  ])(function* (context) {
    if (false) {
      var data, serverSideTranslations;
    }
    return {
      props: {},
    };
  });
  return _withServerSideTranslations.apply(this, arguments);
}
var _a, _b;
// Legacy CSS implementations will `eval` browser code in a Node.js context
// to extract CSS. For backwards compatibility, we need to check we're in a
// browser context before continuing.
if (
  typeof self !== "undefined" &&
  // AMP / No-JS mode does not inject these helpers:
  "$RefreshHelpers$" in self
) {
  var currentExports = module.__proto__.exports;
  var prevExports =
    (_b =
      (_a = module.hot.data) === null || _a === void 0
        ? void 0
        : _a.prevExports) !== null && _b !== void 0
      ? _b
      : null;
  // This cannot happen in MainTemplate because the exports mismatch between
  // templating and execution.
  self.$RefreshHelpers$.registerExportsForReactRefresh(
    currentExports,
    module.id
  );
  // A module can be accepted automatically based on its exports, e.g. when
  // it is a Refresh Boundary.
  if (self.$RefreshHelpers$.isReactRefreshBoundary(currentExports)) {
    // Save the previous exports on update so we can compare the boundary
    // signatures.
    module.hot.dispose(function (data) {
      data.prevExports = currentExports;
    });
    // Unconditionally accept an update to this module, we'll check if it's
    // still a Refresh Boundary later.
    module.hot.accept();
    // This field is set when the previous version of this module was a
    // Refresh Boundary, letting us know we need to check for invalidation or
    // enqueue an update.
    if (prevExports !== null) {
      // A boundary can become ineligible if its exports are incompatible
      // with the previous exports.
      //
      // For example, if you add/remove/change exports, we'll want to
      // re-execute the importing modules, and force those components to
      // re-render. Similarly, if you convert a class component to a
      // function, we want to invalidate the boundary.
      if (
        self.$RefreshHelpers$.shouldInvalidateReactRefreshBoundary(
          prevExports,
          currentExports
        )
      ) {
        module.hot.invalidate();
      } else {
        self.$RefreshHelpers$.scheduleUpdate();
      }
    }
  } else {
    // Since we just executed the code for the module, it's possible that the
    // new exports made it ineligible for being a boundary.
    // We only care about the case when we were _previously_ a boundary,
    // because we already accepted this update (accidental side effect).
    var isNoLongerABoundary = prevExports !== null;
    if (isNoLongerABoundary) {
      module.hot.invalidate();
    }
  }
}

Babel config for reference:

{
  "presets": ["next/babel"],
  "plugins": [
    "@babel/plugin-proposal-logical-assignment-operators",
    ["@babel/plugin-proposal-private-methods", { "loose": false }],
    ["@babel/plugin-proposal-private-property-in-object", { "loose": false }]
  ]
}

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:5
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
SirensOfTitancommented, Feb 2, 2022

@balazsorban44: Here’s a simple reproduction: https://github.com/SirensOfTitan/next-30892

  1. Run the project in dev mode: npm run dev and hit http://localhost:3000/, notice a blank page and an error related to fs not being found (in browser context)
  2. Move .babelrc.works to .babelrc and rerun project, notice that index renders successfully.
0reactions
AndersDJohnsoncommented, Aug 19, 2022

Had the same issue. Seems like a possible workaround is to refactor code to instead use typeof window !== 'object'. I think perhaps this might even work with both Terser and SWC, but to be determined.

Read more comments on GitHub >

github_iconTop Results From Across the Web

What's the purpose of if (typeof window !== 'undefined')
It's an idiomatic check to see if the script is being run in a web-page inside a web-browser or not. One might assume...
Read more >
TerserWebpackPlugin | webpack
This plugin uses terser to minify/minimize your JavaScript. Getting Started. Webpack v5 comes with the latest terser-webpack-plugin out of the box. If you...
Read more >
Using Non-SSR Friendly Components with Next.js
1. Use next/dynamic imports · 2. Check if the window object is defined · 3. Using react-no-ssr package.
Read more >
bootstrap.bundle.min.js.map - Google Git
userAgent);\n\n/**\n * Determines if the browser is Internet Explorer\n ... do not use `remove` because IE11 doesn't support it\n if (this.options.
Read more >
https://www.icaro2000.com/fallback/js/bootstrap.bu...
parentNode)\n },\n\n jQueryDetection() {\n if (typeof $ === 'undefined') {\n ... do not use `remove` because IE11 doesn't support it\n if (this.options.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found