When using SWC, code wrapped in `if (typeof window === 'undefined')` is not removed from the browser bundle.
See original GitHub issueWhat 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:
- Created 2 years ago
- Reactions:5
- Comments:6 (3 by maintainers)
Top 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 >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@balazsorban44: Here’s a simple reproduction: https://github.com/SirensOfTitan/next-30892
npm run dev
and hithttp://localhost:3000/
, notice a blank page and an error related tofs
not being found (in browser context).babelrc.works
to.babelrc
and rerun project, notice that index renders successfully.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.