Skip to content

Commit

Permalink
feat(react): add format option for buildable libs
Browse files Browse the repository at this point in the history
- Clean up @nrwl/web:package executor
- Combine react package e2e tests to speed up test time
  • Loading branch information
jaysoo authored and Jack Hsu committed Aug 17, 2021
1 parent 5c1bc60 commit 8b2ac8e
Show file tree
Hide file tree
Showing 15 changed files with 238 additions and 195 deletions.
10 changes: 10 additions & 0 deletions docs/angular/api-web/executors/package.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ Type: `boolean`

CSS files will be extracted to the output folder.

### format

Alias(es): f

Default: `esm,umd`

Type: `string`

Only build the specified comma-separated formats (esm,umd,cjs)

### globals

Type: `object[]`
Expand Down
10 changes: 10 additions & 0 deletions docs/node/api-web/executors/package.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ Type: `boolean`

CSS files will be extracted to the output folder.

### format

Alias(es): f

Default: `esm,umd`

Type: `string`

Only build the specified comma-separated formats (esm,umd,cjs)

### globals

Type: `object[]`
Expand Down
10 changes: 10 additions & 0 deletions docs/react/api-web/executors/package.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ Type: `boolean`

CSS files will be extracted to the output folder.

### format

Alias(es): f

Default: `esm,umd`

Type: `string`

Only build the specified comma-separated formats (esm,umd,cjs)

### globals

Type: `object[]`
Expand Down
115 changes: 58 additions & 57 deletions e2e/react/src/react-package.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
newProject,
readFile,
readJson,
rmDist,
runCLI,
uniq,
updateFile,
Expand Down Expand Up @@ -118,9 +119,66 @@ describe('Build React libraries and apps', () => {
return JSON.stringify(json, null, 2);
});
});

afterEach(() => killPorts());

describe('Publishable libraries', () => {
it('should build libraries with and without dependencies', () => {
/*
* 1. Without dependencies
*/
const childLibOutput = runCLI(`build ${childLib}`)
// FIX for windows and OSX where output names might get broken to multipleline
.replace(/\s\s\s││\s\s\s/gm, '');
const childLib2Output = runCLI(`build ${childLib2}`)
// FIX for windows and OSX where output names might get broken to multipleline
.replace(/\s\s\s││\s\s\s/gm, '');

expect(childLibOutput).toContain(`${childLib}.umd.js`);
expect(childLibOutput).toContain(`${childLib}.esm.js`);
expect(childLibOutput).toContain(`Bundle complete: ${childLib}`);

expect(childLib2Output).toContain(`${childLib2}.esm.js`);
expect(childLib2Output).toContain(`${childLib2}.umd.js`);
expect(childLib2Output).toContain(`Bundle complete: ${childLib2}`);

checkFilesExist(`dist/libs/${childLib}/assets/hello.txt`);
checkFilesExist(`dist/libs/${childLib}/README.md`);

/*
* 2. With dependencies
*/
let parentLibOutput = runCLI(`build ${parentLib}`)
// FIX for windows and OSX where output names might get broken to multipleline
.replace(/\s\s\s││\s\s\s/gm, '');

expect(parentLibOutput).toContain(`${parentLib}.esm.js`);
expect(parentLibOutput).toContain(`${parentLib}.umd.js`);
expect(parentLibOutput).toContain(`Bundle complete: ${parentLib}`);

const jsonFile = readJson(`dist/libs/${parentLib}/package.json`);
expect(jsonFile.peerDependencies).toEqual(
expect.objectContaining({
[`@${proj}/${childLib}`]: '0.0.1',
[`@${proj}/${childLib2}`]: '0.0.1',
})
);

/*
* 3. With dependencies without existing dist
*/
rmDist();

parentLibOutput = runCLI(`build ${parentLib} --with-deps`).replace(
/\s\s\s││\s\s\s/gm,
''
);

expect(parentLibOutput).toContain(`Bundle complete: ${parentLib}`);
expect(parentLibOutput).toContain(`Bundle complete: ${childLib}`);
expect(parentLibOutput).toContain(`Bundle complete: ${childLib2}`);
});

it('should preserve the tsconfig target set by user', () => {
// Setup
const myLib = uniq('my-lib');
Expand Down Expand Up @@ -172,53 +230,6 @@ describe('Build React libraries and apps', () => {
expect(content).toContain('function __generator(thisArg, body) {');
});

it('should build the library when it does not have any deps', () => {
const output = runCLI(`build ${childLib}`)
// FIX for windows and OSX where output names might get broken to multipleline
.replace(/\s\s\s││\s\s\s/gm, '');
expect(output).toContain(`${childLib}.esm.js`);
expect(output).toContain(`Bundle complete: ${childLib}`);
checkFilesExist(`dist/libs/${childLib}/assets/hello.txt`);
});

it('should copy the README to dist', () => {
const output = runCLI(`build ${childLib2}`);
expect(output).toContain(`Bundle complete: ${childLib2}`);
checkFilesExist(`dist/libs/${childLib2}/README.md`);
});

it('should properly add references to any dependency into the parent package.json', () => {
const childLibOutput = runCLI(`build ${childLib}`)
// FIX for windows and OSX where output names might get broken to multipleline
.replace(/\s\s\s││\s\s\s/gm, '');
const childLib2Output = runCLI(`build ${childLib2}`)
// FIX for windows and OSX where output names might get broken to multipleline
.replace(/\s\s\s││\s\s\s/gm, '');
const parentLibOutput = runCLI(`build ${parentLib}`)
// FIX for windows and OSX where output names might get broken to multipleline
.replace(/\s\s\s││\s\s\s/gm, '');

expect(childLibOutput).toContain(`${childLib}.esm.js`);
expect(childLibOutput).toContain(`${childLib}.umd.js`);
expect(childLibOutput).toContain(`Bundle complete: ${childLib}`);

expect(childLib2Output).toContain(`${childLib2}.esm.js`);
expect(childLib2Output).toContain(`${childLib2}.umd.js`);
expect(childLib2Output).toContain(`Bundle complete: ${childLib2}`);

expect(parentLibOutput).toContain(`${parentLib}.esm.js`);
expect(parentLibOutput).toContain(`${parentLib}.umd.js`);
expect(parentLibOutput).toContain(`Bundle complete: ${parentLib}`);

const jsonFile = readJson(`dist/libs/${parentLib}/package.json`);
expect(jsonFile.peerDependencies).toEqual(
expect.objectContaining({
[`@${proj}/${childLib}`]: '0.0.1',
[`@${proj}/${childLib2}`]: '0.0.1',
})
);
});

it('should build an app composed out of publishable libs', () => {
const buildWithDeps = runCLI(
`build ${app} --with-deps --buildLibsFromSource=false`
Expand All @@ -235,14 +246,4 @@ describe('Build React libraries and apps', () => {
expect(failedBuild).toContain(`Can't resolve`);
}, 1000000);
});

describe('Buildable libraries', () => {
it('should build dependent libraries', () => {
const parentLibOutput = runCLI(`build ${parentLib} --with-deps`);

expect(parentLibOutput).toContain(`Bundle complete: ${parentLib}`);
expect(parentLibOutput).toContain(`Bundle complete: ${childLib}`);
expect(parentLibOutput).toContain(`Bundle complete: ${childLib2}`);
});
});
});
3 changes: 2 additions & 1 deletion packages/web/babel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module.exports = function (api: any, options: NxReactBabelPresetOptions = {}) {
// All other options will fail in Jest since Node does not support some ES features
// such as import syntax.
process.env.NODE_ENV === 'test'
? { targets: { node: 'current' } }
? { targets: { node: 'current' }, loose: true }
: {
// Allow importing core-js in entrypoint and use browserlist to select polyfills.
// This is needed for differential loading as well.
Expand All @@ -48,6 +48,7 @@ module.exports = function (api: any, options: NxReactBabelPresetOptions = {}) {
bugfixes: true,
// Exclude transforms that make all code slower
exclude: ['transform-typeof-symbol'],
loose: true,
},
],
require.resolve('@babel/preset-typescript'),
Expand Down
10 changes: 5 additions & 5 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@
"@babel/plugin-transform-runtime": "^7.15.0",
"@babel/runtime": "^7.14.8",
"@babel/preset-typescript": "^7.15.0",
"@rollup/plugin-commonjs": "11.0.2",
"@rollup/plugin-babel": "5.0.2",
"@rollup/plugin-image": "2.0.4",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-image": "^2.1.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "7.1.1",
"@rollup/plugin-node-resolve": "^13.0.4",
"autoprefixer": "^10.2.5",
"babel-loader": "8.1.0",
"babel-plugin-const-enum": "^1.0.1",
Expand Down Expand Up @@ -80,7 +80,7 @@
"rxjs": "^6.5.4",
"rxjs-for-await": "0.0.2",
"rimraf": "^3.0.2",
"rollup": "1.31.1",
"rollup": "^2.56.2",
"rollup-plugin-copy": "^3.3.0",
"rollup-plugin-filesize": "^9.0.0",
"rollup-plugin-local-resolve": "^1.0.7",
Expand Down
55 changes: 55 additions & 0 deletions packages/web/src/executors/package/lib/normalize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { dirname } from 'path';

import { AssetGlobPattern } from '../../../utils/types';
import { normalizeAssets, normalizePluginPath } from '../../../utils/normalize';
import { WebPackageOptions } from '../schema';

export interface NormalizedWebPackageOptions extends WebPackageOptions {
entryRoot: string;
projectRoot: string;
assets: AssetGlobPattern[];
rollupConfig: string[];
formats: string[];
}

export function normalizePackageOptions(
options: WebPackageOptions,
root: string,
sourceRoot: string
): NormalizedWebPackageOptions {
const entryFile = `${root}/${options.entryFile}`;
const entryRoot = dirname(entryFile);
const project = `${root}/${options.project}`;
const projectRoot = dirname(project);
const outputPath = `${root}/${options.outputPath}`;

let formats = [];
for (const format of options.format.split(',')) {
if (format !== 'cjs' && format !== 'esm' && format !== 'umd')
throw new Error(`Invalid format specified: ${format}`);
formats.push(format);
}
// de-dupe formats
formats = Array.from(new Set(formats));

if (options.buildableProjectDepsInPackageJsonType == undefined) {
options.buildableProjectDepsInPackageJsonType = 'peerDependencies';
}

return {
...options,
formats,
rollupConfig: []
.concat(options.rollupConfig)
.filter(Boolean)
.map((p) => normalizePluginPath(p, root)),
assets: options.assets
? normalizeAssets(options.assets, root, sourceRoot)
: undefined,
entryFile,
entryRoot,
project,
projectRoot,
outputPath,
};
}
45 changes: 31 additions & 14 deletions packages/web/src/executors/package/package.impl.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
let mockCopyPlugin = jest.fn();
jest.mock('rollup-plugin-copy', () => mockCopyPlugin);
jest.mock('tsconfig-paths-webpack-plugin');

import { ExecutorContext } from '@nrwl/devkit';
import { createRollupOptions } from './package.impl';
import { PackageBuilderOptions } from '../../utils/types';
import { normalizePackageOptions } from '../../utils/normalize';
import * as rollup from 'rollup';
import { WebPackageOptions } from './schema';
import { createRollupOptions } from './package.impl';
import { normalizePackageOptions } from './lib/normalize';

jest.mock('rollup-plugin-copy', () => jest.fn());
jest.mock('tsconfig-paths-webpack-plugin');

describe('packageExecutor', () => {
let context: ExecutorContext;
let testOptions: PackageBuilderOptions;
let testOptions: WebPackageOptions;

beforeEach(async () => {
context = {
Expand All @@ -30,11 +29,12 @@ describe('packageExecutor', () => {
project: 'libs/ui/package.json',
tsConfig: 'libs/ui/tsconfig.json',
watch: false,
format: 'esm,umd',
};
});

describe('createRollupOptions', () => {
it('should work', () => {
it('should create rollup options for valid config', () => {
const result: any = createRollupOptions(
normalizePackageOptions(testOptions, '/root', '/root/src'),
[],
Expand All @@ -43,22 +43,38 @@ describe('packageExecutor', () => {
'/root/src',
[]
);

expect(result.map((x) => x.output)).toEqual([
{
file: '/root/dist/ui/example.umd.js',
format: 'umd',
file: '/root/dist/ui/example.esm.js',
format: 'esm',
globals: {},
name: 'Example',
},
{
file: '/root/dist/ui/example.esm.js',
format: 'esm',
file: '/root/dist/ui/example.umd.js',
format: 'umd',
globals: {},
name: 'Example',
},
]);
});

it('should handle invalid formats', () => {
testOptions.format = 'invalid,format';

expect(() =>
createRollupOptions(
normalizePackageOptions(testOptions, '/root', '/root/src'),
[],
context,
{ name: 'example' },
'/root/src',
[]
)
).toThrow(/Invalid/);
});

it('should handle custom config path', async () => {
jest.mock(
'/root/custom-rollup.config.ts',
Expand Down Expand Up @@ -138,7 +154,8 @@ describe('packageExecutor', () => {
'/root/src',
[]
);
expect(mockCopyPlugin).toHaveBeenCalledWith({

expect(require('rollup-plugin-copy')).toHaveBeenCalledWith({
targets: [{ dest: '/root/dist/ui', src: 'C:/windows/path/README.md' }],
});
});
Expand Down
Loading

0 comments on commit 8b2ac8e

Please sign in to comment.