nx/e2e/esbuild/src/esbuild.test.ts
2023-10-03 11:18:12 -06:00

234 lines
7.5 KiB
TypeScript

import {
checkFilesDoNotExist,
checkFilesExist,
cleanupProject,
detectPackageManager,
newProject,
packageInstall,
packageManagerLockFile,
readFile,
readJson,
runCLI,
runCommand,
runCommandUntil,
tmpProjPath,
uniq,
updateFile,
updateJson,
waitUntil,
} from '@nx/e2e/utils';
import { join } from 'path';
describe('EsBuild Plugin', () => {
let proj: string;
beforeEach(() => (proj = newProject()));
afterEach(() => cleanupProject());
it('should setup and build projects using build', async () => {
const myPkg = uniq('my-pkg');
runCLI(`generate @nx/js:lib ${myPkg} --bundler=esbuild`);
updateFile(`libs/${myPkg}/src/index.ts`, `console.log('Hello');\n`);
updateJson(join('libs', myPkg, 'project.json'), (json) => {
json.targets.build.options.assets = [`libs/${myPkg}/assets/*`];
return json;
});
updateFile(`libs/${myPkg}/assets/a.md`, 'file a');
updateFile(`libs/${myPkg}/assets/b.md`, 'file b');
// Copy package.json as asset rather than generate with Nx-detected fields.
runCLI(`build ${myPkg} --generatePackageJson=false`);
const packageJson = readJson(`libs/${myPkg}/package.json`);
// This is the file that is generated by lib generator (no deps, no main, etc.).
expect(packageJson).toEqual({
name: `@proj/${myPkg}`,
version: '0.0.1',
type: 'commonjs',
main: './index.cjs',
dependencies: {},
});
// Build normally with package.json generation.
runCLI(`build ${myPkg}`);
expect(runCommand(`node dist/libs/${myPkg}/index.cjs`)).toMatch(/Hello/);
// main field should be set correctly in package.json
checkFilesExist(
`dist/libs/${myPkg}/package.json`,
`dist/libs/${myPkg}/${
packageManagerLockFile[detectPackageManager(tmpProjPath())]
}`
);
expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/);
expect(runCommand(`node dist/libs/${myPkg}/index.cjs`)).toMatch(/Hello/);
// main field should be set correctly in package.json
expect(readFile(`dist/libs/${myPkg}/assets/a.md`)).toMatch(/file a/);
expect(readFile(`dist/libs/${myPkg}/assets/b.md`)).toMatch(/file b/);
/* Metafile is not generated by default, but passing --metafile generates it.
*/
checkFilesDoNotExist(`dist/libs/${myPkg}/meta.json`);
runCLI(`build ${myPkg} --metafile`);
checkFilesExist(`dist/libs/${myPkg}/meta.json`);
/* Type errors are turned on by default
*/
updateFile(
`libs/${myPkg}/src/index.ts`,
`
const x: number = 'a'; // type error
console.log('Bye');
`
);
expect(() => runCLI(`build ${myPkg}`)).toThrow();
expect(() => runCLI(`build ${myPkg} --skipTypeCheck`)).not.toThrow();
expect(runCommand(`node dist/libs/${myPkg}/index.cjs`)).toMatch(/Bye/);
// Reset file
updateFile(
`libs/${myPkg}/src/index.ts`,
`
console.log('Hello');
`
);
/* Test that watch mode copies assets on start, and again on update.
*/
updateFile(`libs/${myPkg}/assets/a.md`, 'initial a');
const watchProcess = await runCommandUntil(
`build ${myPkg} --watch`,
(output) => {
return output.includes('watching for changes');
}
);
readFile(`dist/libs/${myPkg}/assets/a.md`).includes('initial a');
updateFile(`libs/${myPkg}/assets/a.md`, 'updated a');
await expect(
waitUntil(
() => readFile(`dist/libs/${myPkg}/assets/a.md`).includes('updated a'),
{
timeout: 20_000,
ms: 500,
}
)
).resolves.not.toThrow();
watchProcess.kill();
}, 300_000);
it('should support bundling everything or only workspace libs', async () => {
packageInstall('rambda', undefined, '~7.3.0', 'prod');
packageInstall('lodash', undefined, '~4.14.0', 'prod');
const parentLib = uniq('parent-lib');
const childLib = uniq('child-lib');
runCLI(`generate @nx/js:lib ${parentLib} --bundler=esbuild`);
runCLI(`generate @nx/js:lib ${childLib} --bundler=none`);
updateFile(
`libs/${parentLib}/src/index.ts`,
`
// @ts-ignore
import _ from 'lodash';
import { greet } from '@${proj}/${childLib}';
console.log(_.upperFirst('hello world'));
console.log(greet());
`
);
updateFile(
`libs/${childLib}/src/index.ts`,
`
import { always } from 'rambda';
export const greet = always('Hello from child lib');
`
);
// Bundle child lib and third-party packages
runCLI(`build ${parentLib}`);
expect(
readJson(`dist/libs/${parentLib}/package.json`).dependencies?.['dayjs']
).not.toBeDefined();
let runResult = runCommand(`node dist/libs/${parentLib}/index.cjs`);
expect(runResult).toMatch(/Hello world/);
expect(runResult).toMatch(/Hello from child lib/);
// Bundle only child lib
runCLI(`build ${parentLib} --third-party=false`);
expect(
readJson(`dist/libs/${parentLib}/package.json`).dependencies
).toEqual({
// Don't care about the versions, just that they exist
rambda: expect.any(String),
lodash: expect.any(String),
});
runResult = runCommand(`node dist/libs/${parentLib}/index.cjs`);
expect(runResult).toMatch(/Hello world/);
expect(runResult).toMatch(/Hello from child lib/);
}, 300_000);
it('should support non-bundle builds', () => {
const myPkg = uniq('my-pkg');
runCLI(`generate @nx/js:lib ${myPkg} --bundler=esbuild`);
updateFile(`libs/${myPkg}/src/lib/${myPkg}.ts`, `console.log('Hello');\n`);
updateFile(`libs/${myPkg}/src/index.ts`, `import './lib/${myPkg}.cjs';\n`);
runCLI(`build ${myPkg} --bundle=false`);
checkFilesExist(
`dist/libs/${myPkg}/libs/${myPkg}/src/lib/${myPkg}.cjs`,
`dist/libs/${myPkg}/index.cjs`
);
// Test files are excluded in tsconfig (e.g. tsconfig.lib.json)
checkFilesDoNotExist(
`dist/libs/${myPkg}/libs/${myPkg}/src/lib/${myPkg}.spec.cjs`
);
// Can run package (package.json fields are correctly generated)
expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/);
}, 300_000);
it('should support additional entry points', async () => {
const myPkg = uniq('my-pkg');
runCLI(`generate @nx/js:lib ${myPkg} --bundler=esbuild`);
updateFile(`libs/${myPkg}/src/index.ts`, `console.log('main');\n`);
updateFile(`libs/${myPkg}/src/extra.ts`, `console.log('extra');\n`);
updateJson(join('libs', myPkg, 'project.json'), (json) => {
json.targets.build.options.additionalEntryPoints = [
`libs/${myPkg}/src/extra.ts`,
];
return json;
});
runCLI(`build ${myPkg}`);
checkFilesExist(
`dist/libs/${myPkg}/index.cjs`,
`dist/libs/${myPkg}/extra.cjs`
);
expect(
runCommand(`node dist/libs/${myPkg}/index.cjs`, { failOnError: true })
).toMatch(/main/);
expect(
runCommand(`node dist/libs/${myPkg}/extra.cjs`, { failOnError: true })
).toMatch(/extra/);
}, 120_000);
it('should support external esbuild.config.js file', async () => {
const myPkg = uniq('my-pkg');
runCLI(`generate @nx/js:lib ${myPkg} --bundler=esbuild`);
updateFile(
`libs/${myPkg}/esbuild.config.js`,
`console.log('custom config loaded');\nmodule.exports = {};\n`
);
updateJson(join('libs', myPkg, 'project.json'), (json) => {
delete json.targets.build.options.esbuildOptions;
json.targets.build.options.esbuildConfig = `libs/${myPkg}/esbuild.config.js`;
return json;
});
const output = runCLI(`build ${myPkg}`);
expect(output).toContain('custom config loaded');
}, 120_000);
});