ampproject / rollup-plugin-closure-compiler

Leverage Closure Compiler to minify and optimize JavaScript with Rollup.
Apache License 2.0
292 stars 27 forks source link

Support rollup output.preserveModules #470

Open icy0307 opened 1 year ago

icy0307 commented 1 year ago

Please only file bugs/feature requests for rollup-plugin-closure-compiler here. Rollup provides an output option called preserveModules.

Instead of creating as few chunks as possible, this mode will create separate chunks for all modules using the original module names as file names.

Generating one big fat esm chunk is less effective than multiple small modules because of latter can benefit from sideEffects, while a single file can only rely on dead code elimination for unused export.

Is your feature request related to a problem? Please describe.

However, when I add this flag when aggregating exports like this happens, test/aggregating-export/fixtures/re-export.js

export * as dep from './foo-dep'
export * as dep1 from './bar-dep'

the below error occurs:

✖ aggregating-export › re-export › re-export works correctly with  Promise returned by test never resolved

  Unhandled rejection in test/aggregating-export/re-export.test.js

  /workspaces/rollup-plugin-closure-compiler/node_modules/acorn/dist/acorn.js:2927

  SyntaxError: Export 'fooDep' is not defined (1:7)

  Parser.pp$4.raise (node_modules/acorn/dist/acorn.js:2927:15)
  Parser.pp$1.parseTopLevel (node_modules/acorn/dist/acorn.js:763:16)
  Parser.parse (node_modules/acorn/dist/acorn.js:555:17)
  Function.parse (node_modules/acorn/dist/acorn.js:578:37)
  Object.parse (node_modules/acorn/dist/acorn.js:5095:19)
  Object.parse (src/acorn.ts:52:19)
  ImportTransform.post (src/transformers/chunk/imports.ts:159:27)
  Object.chunkLifecycle (src/transform.ts:92:50)
  processTicksAndRejections (node:internal/process/task_queues:95:5)
  Object.postCompilation (src/transformers/chunk/transforms.ts:100:10)

I add a mini repo to reproduce this problem test/aggregating-export/re-export.test.js

const rollup = require('rollup');
const fs = require('fs');
const test = require('ava');
const { default: compiler } = require('../../transpile-tests/index');
async function compile() {
  const rollupConfig = {
    input: 'test/aggregating-export/fixtures/re-export.js',
    output: {
      format: 'esm',
      dir: 'lib',
      preserveModules: true,
    },
    plugins: [compiler()],
  };
  const bundle = await rollup.rollup(rollupConfig);
  const bundles = await bundle.generate(rollupConfig.output);
  return bundles.output;
}

async function testReExport() {
  const output = await compile();
  const code = output.find((ele) => ele.fileName === 're-export.js').code;
  const minified = await fs.promises.readFile('test/aggregating-export/fixtures/re-export.esm.default.js', 'utf8');
  return { code, minified };
}

test('re-export works correctly with ', async (t) => {
  await testReExport();
  t.is(code, minified);
});

test/aggregating-export/fixtures/foo-dep.js test/aggregating-export/fixtures/bar-dep.js

export const export1 = 1;
export function export2() {
    return 2;
}

Describe the solution you'd like

Turns out that the order of chunk transformers causes this issue.

const TRANSFORMS: Array<typeof ChunkTransform> = [
  HashbangRemoveTransform,
  // Acorn can parse content starting here
  ConstTransform,
  IifeTransform,
  CJSTransform,
  LiteralComputedKeys,
  StrictTransform,
  ExportTransform,
  ImportTransform,
  ASITransform,
  // Acorn cannot parse content starting here.
  HashbangApplyTransform,
];

rollup transpiles the above code into:

import * as fooDep from './foo-dep.js';
export { fooDep as dep };
import * as barDep from './bar-dep.js';
export { barDep as dep1 };

In the posting closure compiler phrase,ExportTransform which adds the exports back first generates something like

export{fooDep as dep,barDep as dep1}

which lacks the form keyword

Describe alternatives you've considered

Change the transform order to add back import first. But I can't find a way to do it elegantly.

Additional context

The Rollup version must be close to 2.28.1, the higher version produces more problems due to the tests being quite fragile. Simple string comparison highly relies on rollup's implementation for output. However, the package.json file states that all versions above 1.27 are compatible.

  "peerDependencies": {
    "rollup": ">=1.27"
  },