Skip to content

CommonJS plugin strictRequires settings always hoists NodeJS conditional require calls from an imported UMD file #1380

@Bloodsucker

Description

@Bloodsucker

Hello everyone,

Given a entry-point index.js:

import { doSomething } from "./umd.cjs";

export function entryPoint() {
    doSomething(false);
}

And a UMD bundle ( umd.cjs ) with "dynamic" require calls to NodeJS builtins that are called only under certain conditions:

// Third-party library UMD library are bundled into this file.
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.umd_bundle = {}));
})(this, (function (exports) { 'use strict';
    function doSomething(isNode) {
        if (isNode) {
            return require('os'); // This require is called "dynamically".
        }
    }
    exports.doSomething = doSomething;
}));

A rollup config rollup.config.js:

export default defineConfig([
	{
		input: 'src/index2.js',
		output: [
			{
				file: 'dist/main_bundle.js',
				format: 'umd',
				name: 'mainBundle',
			},
		],
		plugins: [commonjs(), nodeResolve()],
	},
]);

This results in the following final UMD bundle file:

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('os')) :
	typeof define === 'function' && define.amd ? define(['exports', 'os'], factory) :
	(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.mainBundle = {}, global.require$$0));
})(this, (function (exports, require$$0) { 'use strict';

	var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

	var umd = {exports: {}};

	(function (module, exports) {
		// Third-party library UMD library are bundled into this file.
		(function (global, factory) {
		    factory(exports) ;
		})(commonjsGlobal, (function (exports) {	    function doSomething(isNode) {
		        if (isNode) {
		            return require$$0; // This require is called "dynamically".
		        }
		    }
		    exports.doSomething = doSomething;
		}));
	} (umd, umd.exports));

	function entryPoint() {
	    umd.exports.doSomething(false);
	}

	exports.entryPoint = entryPoint;

}));

The umd.cjs require require('os'); has been replaced with a return require$$0; which changes the logic that is happening inside the bundle. This also causes the following errors:

src/index.js → dist/main_bundle.js...
(!) Missing shims for Node.js built-ins
Creating a browser bundle that depends on "os". You might need to include https://github.com/FredKSchott/rollup-plugin-polyfill-node
(!) Missing global variable name
https://rollupjs.org/guide/en/#outputglobals
Use "output.globals" to specify browser global variable names corresponding to external modules:
os (guessing "require$$0")

I believe this is somehow expected. However, if I use the setting strictRequires from the plugin @rollup/plugin-commonjs in the rollup.config.js:
plugins: [commonjs({strictRequires: true}), nodeResolve()],

The expectation was to NOT hoist the require('os') call in the resulting final UMD file. In other words. From:

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('os')) : // problem!
	typeof define === 'function' && define.amd ? define(['exports', 'os'], factory) :
	(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.mainBundle = {}, global.require$$0));
})(this, (function (exports, require$$0) { 'use strict';

// ...

to something like (modified manually):

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
	typeof define === 'function' && define.amd ? define(['exports', 'os'], factory) :
	(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.mainBundle = {}));
})(this, (function (exports) { 'use strict';
// ...

Paradoxically, an imported ES6 module file with dynamic require doesn't result in a hoisted require call.

Does anybody know how to avoid the hoisting of the file and how to fix the bundling errors?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions