feat(bundling): rollup should support ESM config files (#21999)

This commit is contained in:
Colum Ferry 2024-02-28 17:26:34 +00:00 committed by GitHub
parent 6576325f7b
commit f046c52e5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 95 additions and 21 deletions

View File

@ -13,7 +13,7 @@ import {
import { join } from 'path'; import { join } from 'path';
describe('Rollup Plugin', () => { describe('Rollup Plugin', () => {
beforeAll(() => newProject()); beforeAll(() => newProject({ packages: ['@nx/rollup', '@nx/js'] }));
afterAll(() => cleanupProject()); afterAll(() => cleanupProject());
it('should be able to setup project to build node programs with rollup and different compilers', async () => { it('should be able to setup project to build node programs with rollup and different compilers', async () => {
@ -119,4 +119,46 @@ describe('Rollup Plugin', () => {
runCLI(`generate @nx/js:lib ${jsLib} --bundler rollup`); runCLI(`generate @nx/js:lib ${jsLib} --bundler rollup`);
expect(() => runCLI(`build ${jsLib}`)).not.toThrow(); expect(() => runCLI(`build ${jsLib}`)).not.toThrow();
}); });
it('should be able to build libs generated with @nx/js:lib --bundler rollup with a custom rollup.config.{cjs|mjs}', () => {
const jsLib = uniq('jslib');
runCLI(`generate @nx/js:lib ${jsLib} --bundler rollup`);
updateFile(
`libs/${jsLib}/rollup.config.cjs`,
`module.exports = {
output: {
format: "cjs",
dir: "dist/test",
name: "Mylib",
entryFileNames: "[name].cjs.js",
chunkFileNames: "[name].cjs.js"
}
}`
);
updateJson(join('libs', jsLib, 'project.json'), (config) => {
config.targets.build.options.rollupConfig = `libs/${jsLib}/rollup.config.cjs`;
return config;
});
expect(() => runCLI(`build ${jsLib}`)).not.toThrow();
checkFilesExist(`dist/test/index.cjs.js`);
updateFile(
`libs/${jsLib}/rollup.config.mjs`,
`export default {
output: {
format: "es",
dir: "dist/test",
name: "Mylib",
entryFileNames: "[name].mjs.js",
chunkFileNames: "[name].mjs.js"
}
}`
);
updateJson(join('libs', jsLib, 'project.json'), (config) => {
config.targets.build.options.rollupConfig = `libs/${jsLib}/rollup.config.mjs`;
return config;
});
expect(() => runCLI(`build ${jsLib}`)).not.toThrow();
checkFilesExist(`dist/test/index.mjs.js`);
});
}); });

View File

@ -1,11 +1,15 @@
import { ExecutorContext } from '@nx/devkit'; import { ExecutorContext } from '@nx/devkit';
import * as fs from 'fs';
import * as rollup from 'rollup'; import * as rollup from 'rollup';
import { RollupExecutorOptions } from './schema'; import { RollupExecutorOptions } from './schema';
import { createRollupOptions } from './rollup.impl'; import { createRollupOptions } from './rollup.impl';
import { normalizeRollupExecutorOptions } from './lib/normalize'; import { normalizeRollupExecutorOptions } from './lib/normalize';
jest.mock('rollup-plugin-copy', () => jest.fn()); jest.mock('rollup-plugin-copy', () => jest.fn());
jest.mock('fs', () => ({
...jest.requireActual('fs'),
readdirSync: () => [],
}));
describe('rollupExecutor', () => { describe('rollupExecutor', () => {
let context: ExecutorContext; let context: ExecutorContext;
let testOptions: RollupExecutorOptions; let testOptions: RollupExecutorOptions;
@ -35,8 +39,8 @@ describe('rollupExecutor', () => {
}); });
describe('createRollupOptions', () => { describe('createRollupOptions', () => {
it('should create rollup options for valid config', () => { it('should create rollup options for valid config', async () => {
const result: any = createRollupOptions( const result: any = await createRollupOptions(
normalizeRollupExecutorOptions( normalizeRollupExecutorOptions(
testOptions, testOptions,
{ root: '/root' } as any, { root: '/root' } as any,
@ -73,7 +77,7 @@ describe('rollupExecutor', () => {
() => (o) => ({ ...o, prop: 'my-val' }), () => (o) => ({ ...o, prop: 'my-val' }),
{ virtual: true } { virtual: true }
); );
const result: any = createRollupOptions( const result: any = await createRollupOptions(
normalizeRollupExecutorOptions( normalizeRollupExecutorOptions(
{ ...testOptions, rollupConfig: 'custom-rollup.config.ts' }, { ...testOptions, rollupConfig: 'custom-rollup.config.ts' },
{ root: '/root' } as any, { root: '/root' } as any,
@ -85,6 +89,7 @@ describe('rollupExecutor', () => {
'/root/src', '/root/src',
[] []
); );
expect(result.map((x) => x.prop)).toEqual(['my-val', 'my-val']); expect(result.map((x) => x.prop)).toEqual(['my-val', 'my-val']);
}); });
@ -103,7 +108,7 @@ describe('rollupExecutor', () => {
}), }),
{ virtual: true } { virtual: true }
); );
const result: any = createRollupOptions( const result: any = await createRollupOptions(
normalizeRollupExecutorOptions( normalizeRollupExecutorOptions(
{ {
...testOptions, ...testOptions,
@ -121,6 +126,7 @@ describe('rollupExecutor', () => {
'/root/src', '/root/src',
[] []
); );
expect(result.map((x) => x.prop1)).toEqual([ expect(result.map((x) => x.prop1)).toEqual([
'my-val-my-val-2', 'my-val-my-val-2',
'my-val-my-val-2', 'my-val-my-val-2',
@ -128,8 +134,8 @@ describe('rollupExecutor', () => {
expect(result.map((x) => x.prop2)).toEqual(['my-val-2', 'my-val-2']); expect(result.map((x) => x.prop2)).toEqual(['my-val-2', 'my-val-2']);
}); });
it(`should always use forward slashes for asset paths`, () => { it(`should always use forward slashes for asset paths`, async () => {
createRollupOptions( await createRollupOptions(
{ {
...normalizeRollupExecutorOptions( ...normalizeRollupExecutorOptions(
testOptions, testOptions,
@ -156,8 +162,8 @@ describe('rollupExecutor', () => {
}); });
}); });
it(`should treat npm dependencies as external if external is all`, () => { it(`should treat npm dependencies as external if external is all`, async () => {
const options = createRollupOptions( const options = await createRollupOptions(
normalizeRollupExecutorOptions( normalizeRollupExecutorOptions(
{ ...testOptions, external: 'all' }, { ...testOptions, external: 'all' },
{ root: '/root' } as any, { root: '/root' } as any,
@ -177,8 +183,8 @@ describe('rollupExecutor', () => {
expect(external('rxjs', '', false)).toBe(false); expect(external('rxjs', '', false)).toBe(false);
}); });
it(`should not treat npm dependencies as external if external is none`, () => { it(`should not treat npm dependencies as external if external is none`, async () => {
const options = createRollupOptions( const options = await createRollupOptions(
normalizeRollupExecutorOptions( normalizeRollupExecutorOptions(
{ ...testOptions, external: 'none' }, { ...testOptions, external: 'none' },
{ root: '/root' } as any, { root: '/root' } as any,
@ -198,8 +204,8 @@ describe('rollupExecutor', () => {
expect(external('rxjs', '', false)).toBe(false); expect(external('rxjs', '', false)).toBe(false);
}); });
it(`should set external based on options`, () => { it(`should set external based on options`, async () => {
const options = createRollupOptions( const options = await createRollupOptions(
normalizeRollupExecutorOptions( normalizeRollupExecutorOptions(
{ ...testOptions, external: ['rxjs'] }, { ...testOptions, external: ['rxjs'] },
{ root: '/root' } as any, { root: '/root' } as any,

View File

@ -28,6 +28,7 @@ import { analyze } from './lib/analyze-plugin';
import { deleteOutputDir } from '../../utils/fs'; import { deleteOutputDir } from '../../utils/fs';
import { swc } from './lib/swc-plugin'; import { swc } from './lib/swc-plugin';
import { updatePackageJson } from './lib/update-package-json'; import { updatePackageJson } from './lib/update-package-json';
import { loadConfigFile } from '@nx/devkit/src/utils/config-utils';
export type RollupExecutorEvent = { export type RollupExecutorEvent = {
success: boolean; success: boolean;
@ -74,7 +75,7 @@ export async function* rollupExecutor(
.filter((d) => d.target.startsWith('npm:')) .filter((d) => d.target.startsWith('npm:'))
.map((d) => d.target.slice(4)); .map((d) => d.target.slice(4));
const rollupOptions = createRollupOptions( const rollupOptions = await createRollupOptions(
options, options,
dependencies, dependencies,
context, context,
@ -155,14 +156,14 @@ export async function* rollupExecutor(
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
export function createRollupOptions( export async function createRollupOptions(
options: NormalizedRollupExecutorOptions, options: NormalizedRollupExecutorOptions,
dependencies: DependentBuildableProjectNode[], dependencies: DependentBuildableProjectNode[],
context: ExecutorContext, context: ExecutorContext,
packageJson: PackageJson, packageJson: PackageJson,
sourceRoot: string, sourceRoot: string,
npmDeps: string[] npmDeps: string[]
): rollup.InputOptions[] { ): Promise<rollup.InputOptions[]> {
const useBabel = options.compiler === 'babel'; const useBabel = options.compiler === 'babel';
const useTsc = options.compiler === 'tsc'; const useTsc = options.compiler === 'tsc';
const useSwc = options.compiler === 'swc'; const useSwc = options.compiler === 'swc';
@ -195,7 +196,7 @@ export function createRollupOptions(
options.format = ['cjs']; options.format = ['cjs'];
} }
return options.format.map((format, idx) => { const _rollupOptions = options.format.map(async (format, idx) => {
// Either we're generating only one format, so we should bundle types // Either we're generating only one format, so we should bundle types
// OR we are generating dual formats, so only bundle types for CJS. // OR we are generating dual formats, so only bundle types for CJS.
const shouldBundleTypes = options.format.length === 1 || format === 'cjs'; const shouldBundleTypes = options.format.length === 1 || format === 'cjs';
@ -310,10 +311,35 @@ export function createRollupOptions(
plugins, plugins,
}; };
return options.rollupConfig.reduce((currentConfig, plugin) => { const userDefinedRollupConfigs = options.rollupConfig.map((plugin) =>
return require(plugin)(currentConfig, options); loadConfigFile(plugin)
}, rollupConfig); );
let finalConfig: rollup.InputOptions = rollupConfig;
for (const _config of userDefinedRollupConfigs) {
const config = await _config;
if (typeof config === 'function') {
finalConfig = config(finalConfig, options);
} else {
finalConfig = {
...finalConfig,
...config,
plugins: [
...(finalConfig.plugins?.length > 0 ? finalConfig.plugins : []),
...(config.plugins?.length > 0 ? config.plugins : []),
],
};
}
}
return finalConfig;
}); });
const rollupOptions = [];
for (const rollupOption of _rollupOptions) {
rollupOptions.push(await rollupOption);
}
return rollupOptions;
} }
function createTsCompilerOptions( function createTsCompilerOptions(