Reputation: 6819
I am building an UMD library that is meant to be used only in browsers. However, it has a dependency on another UMD library/module/bundle .js file which contains require statements for nodejs modules. The statements aren't run always, but only if that sub-library detects it's running in NodeJS. For context, the required nodejs modules are os
, http
and https
but they are all required dynamically.
My rollup.config.js
is already using the @rollup/plugin-node-resolve
, @rollup/plugin-commonjs
and @rollup/plugin-typescript
. But when building it triggers some warnings.
(!) Missing shims for Node.js built-ins
Creating a browser bundle that depends on "os", "http" and "https". You might need to include https://github.com/FredKSchott/rollup-plugin-polyfill-node
(!) Missing global variable names
https://rollupjs.org/guide/en/#outputglobals
Use "output.globals" to specify browser global variable names corresponding to external modules:
os (guessing "require$$0")
http (guessing "require$$1")
https (guessing "require$$2")
The resulting generated bundle file:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('os'), require('http'), require('https')) :
typeof define === 'function' && define.amd ? define(['exports', 'os', 'http', 'https'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.ns_ = global.ns_ || {}, global.ns_.Bundle = {}), global.require$$0, global.require$$1, global.require$$2));
})(this, (function (exports, require$$0, require$$1, require$$2) { 'use strict';
// ...
// Pseudo-code
if(node) {
const os = require$$0;
//...
}
The bundler has replaced my require('os')
statements with require$$0
so it isn't calling the require statements "conditionally" as I would have expected. They have been pre-called (hoisted). Any way for Rollup to not do hoisting of that?
Upvotes: 5
Views: 921
Reputation: 11
I don't know if it's a bug or anything, but the issue occurs when the require
is used directly. When require
is used directly the commonjs
doesn't consider it as dynamic. And obviously it can not find the source in anywhere in the project. So hoist it for some reason.
To avoid the problem. You need to wrap the require
method and use the ignoreDynamicRequires
option of the commonjs
plugin.
The following code
// rollup.config.js
const { defineConfig } = require("rollup");
const commonjs = require("@rollup/plugin-commonjs");
module.exports = [
defineConfig({
input: "index.js",
context: this,
output: [
{
name: "rollup-dynamic-require",
file: `output.js`,
sourcemap: "inline",
format: "umd",
},
],
plugins: [
commonjs({
ignoreDynamicRequires: true, // this is the option for dynamic imports.
}),
],
}),
];
// index.js
const dynamicRequire = function (module) {
return require(module);
};
(function rollupDynamicRequire() {
let required;
for (const element of [0, 1, 2]) {
if (element === 0) {
required = dynamicRequire("http");
} else if (element === 1) {
required = dynamicRequire("https");
} else if (element === 2) {
required = dynamicRequire("os");
}
console.log(required.globalAgent.protocol);
}
})();
// output.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global["rollup-dynamic-require"] = factory());
})(this, (function () { 'use strict';
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var rollupDynamicRequire = {};
var hasRequiredRollupDynamicRequire;
function requireRollupDynamicRequire () {
if (hasRequiredRollupDynamicRequire) return rollupDynamicRequire;
hasRequiredRollupDynamicRequire = 1;
const dynamicRequire = function (module) {
return require(module);
};
(function rollupDynamicRequire() {
let required;
for (const element of [0, 1, 2]) {
if (element === 0) {
required = dynamicRequire("http");
} else if (element === 1) {
required = dynamicRequire("https");
} else if (element === 2) {
required = dynamicRequire("os");
}
console.log(required.globalAgent.protocol);
}
})();
return rollupDynamicRequire;
}
var rollupDynamicRequireExports = requireRollupDynamicRequire();
var index = /*@__PURE__*/getDefaultExportFromCjs(rollupDynamicRequireExports);
return index;
}));
Upvotes: 1
Reputation: 256
It is stated pretty clearly in the warnings that the os
, http
, and https
modules are being included in browser bundle and that Rollup is unable to determine the correct global variable names for these modules.
You can use the suggested output.globals
option in your Rollup configuration to specify the correct global variable names for these modules.
Something like:
export default {
output: {
globals: {
os: 'os',
http: 'http',
https: 'https',
},
},
// other options
};
You could also use @rollup/plugin-polyfill-node
(by including it as is in your Rollup configuration) to automatically include polyfills for these built-in Node.js modules.
Let me know if this does it.
Upvotes: 2