rollup-cache icon indicating copy to clipboard operation
rollup-cache copied to clipboard

Improving handling of exports in prebuilt CommonJS dependencies

Open robertknight opened this issue 4 years ago • 2 comments

If a CommonJS package is prebundled using the prebuild configuration setting, the exports in the generated bundle may not be what the user expects.

Given a CommonJS npm package test-pkg with an entry point which contains:

function functionA() {
  return 'functionA';
}

function functionB() {
  return 'functionB';
}

module.exports = { functionA, functionB };

If test-pkg is added to the list of npm dependencies to prebuild, then rollup-cache will generate the following test-pkg.bundle.js ESM bundle:

function functionA() {
  return 'functionA';
}

function functionB() {
  return 'functionB';
}

var testPkg = { functionA, functionB };

export { testPkg as default };

This behavior matches how Node handles importing a CommonJS package in an ESM module. However it is often not what existing in projects may expect. If a project using the above package has code in it like:

import { functionA, functionB } from 'test-pkg';

Then the generated project bundle will reference the prebuilt bundle using:

import { functionA, functionB } from './npm/test-pkg.bundle.js';

This import will then fail at runtime because the named exports functionA and functionB do not actually exist.

I can see a few ways to resolve this:

  1. Disallow prebuilding of non-ESM packages. This is probably the simplest and most predictable approach, but will prevent prebuilding from being used with a lot of existing packages.
  2. Change the way the exports are generated in the generated CommonJS package.

robertknight avatar Nov 11 '21 08:11 robertknight

If on the other hand the CommonJS entry point contains:

function functionA() {
  return 'functionA';
}

function functionB() {
  return 'functionB';
}

exports.functionA = functionA;
exports.functionB = functionB;

Then the generated bundle is:

var testPkg = {};

function functionA() {
  return 'functionA';
}

function functionB() {
  return 'functionB';
}

var functionA_1 = testPkg.functionA = functionA;
var functionB_1 = testPkg.functionB = functionB;

export { testPkg as default, functionA_1 as functionA, functionB_1 as functionB };

This will work with the code in the original post.

robertknight avatar Nov 11 '21 08:11 robertknight

The above scenarios are covered by the "Entry points" section of https://github.com/rollup/plugins/issues/481. See "Entry point which assigns to module.exports" and "Entry point which adds properties to exports".

robertknight avatar Nov 11 '21 08:11 robertknight