nx/e2e/js/src/js-packaging.test.ts
James Henry 68eeb2eeed
feat(linter): create new workspaces with ESLint v9 and typescript-eslint v8 (#27404)
Closes #27451

---------

Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>
Co-authored-by: Jack Hsu <jack.hsu@gmail.com>
2024-09-12 16:02:27 -04:00

302 lines
9.2 KiB
TypeScript

import {
updateJson,
cleanupProject,
newProject,
runCLI,
tmpProjPath,
runCommand,
createFile,
uniq,
getPackageManagerCommand,
readJson,
updateFile,
renameFile,
} from '@nx/e2e/utils';
import { join } from 'path';
describe('packaging libs', () => {
let scope: string;
beforeEach(() => {
scope = newProject();
});
afterEach(() => cleanupProject());
// TODO: investigate this failure
xit('should bundle libs using esbuild, vite, rollup and be used in CJS/ESM projects', () => {
const esbuildLib = uniq('esbuildlib');
const viteLib = uniq('vitelib');
const rollupLib = uniq('rolluplib');
runCLI(
`generate @nx/js:lib ${esbuildLib} --bundler=esbuild --no-interactive`
);
runCLI(`generate @nx/js:lib ${viteLib} --bundler=vite --no-interactive`);
runCLI(
`generate @nx/js:lib ${rollupLib} --bundler=rollup --no-interactive`
);
updateFile(`libs/${rollupLib}/src/index.ts`, (content) => {
// Test that default functions work in ESM (Node).
return `${content}\nexport default function f() { return 'rollup default' }`;
});
runCLI(`build ${esbuildLib}`);
runCLI(`build ${viteLib}`);
runCLI(`build ${rollupLib} --generateExportsField`);
const pmc = getPackageManagerCommand();
let output: string;
// Make sure outputs in commonjs project
createFile(
'test-cjs/package.json',
JSON.stringify(
{
name: 'test-cjs',
private: true,
type: 'commonjs',
dependencies: {
[`@proj/${esbuildLib}`]: `file:../dist/libs/${esbuildLib}`,
[`@proj/${viteLib}`]: `file:../dist/libs/${viteLib}`,
[`@proj/${rollupLib}`]: `file:../dist/libs/${rollupLib}`,
},
},
null,
2
)
);
createFile(
'test-cjs/index.js',
`
const { ${esbuildLib} } = require('@proj/${esbuildLib}');
const { ${viteLib} } = require('@proj/${viteLib}');
const { default: rollupDefault, ${rollupLib} } = require('@proj/${rollupLib}');
console.log(${esbuildLib}());
console.log(${viteLib}());
console.log(${rollupLib}());
console.log(rollupDefault());
`
);
runCommand(pmc.install, {
cwd: join(tmpProjPath(), 'test-cjs'),
});
output = runCommand('node index.js', {
cwd: join(tmpProjPath(), 'test-cjs'),
});
expect(output).toContain(esbuildLib);
expect(output).toContain(viteLib);
expect(output).toContain(rollupLib);
expect(output).toContain('rollup default');
// Make sure outputs in esm project
createFile(
'test-esm/package.json',
JSON.stringify(
{
name: 'test-esm',
private: true,
type: 'module',
dependencies: {
[`@proj/${esbuildLib}`]: `file:../dist/libs/${esbuildLib}`,
[`@proj/${viteLib}`]: `file:../dist/libs/${viteLib}`,
[`@proj/${rollupLib}`]: `file:../dist/libs/${rollupLib}`,
},
},
null,
2
)
);
createFile(
'test-esm/index.js',
`
import { ${esbuildLib} } from '@proj/${esbuildLib}';
import { ${viteLib} } from '@proj/${viteLib}';
import rollupDefault, { ${rollupLib} } from '@proj/${rollupLib}';
console.log(${esbuildLib}());
console.log(${viteLib}());
console.log(${rollupLib}());
console.log(rollupDefault());
`
);
runCommand(pmc.install, {
cwd: join(tmpProjPath(), 'test-esm'),
});
output = runCommand('node index.js', {
cwd: join(tmpProjPath(), 'test-esm'),
});
expect(output).toContain(esbuildLib);
expect(output).toContain(viteLib);
expect(output).toContain(rollupLib);
expect(output).toContain('rollup default');
}, 500_000);
it('should build with tsc, swc and be used in CJS/ESM projects', async () => {
const tscLib = uniq('tsclib');
const swcLib = uniq('swclib');
const tscEsmLib = uniq('tscesmlib');
const swcEsmLib = uniq('swcesmlib');
runCLI(`generate @nx/js:lib ${tscLib} --bundler=tsc --no-interactive`);
runCLI(`generate @nx/js:lib ${swcLib} --bundler=swc --no-interactive`);
runCLI(`generate @nx/js:lib ${tscEsmLib} --bundler=tsc --no-interactive`);
runCLI(`generate @nx/js:lib ${swcEsmLib} --bundler=swc --no-interactive`);
// Change module format to ESM
updateJson(`libs/${tscEsmLib}/tsconfig.json`, (json) => {
json.compilerOptions.module = 'esnext';
return json;
});
updateJson(`libs/${tscEsmLib}/package.json`, (json) => {
// check one lib without type, the build output should be set with type module
delete json.type;
return json;
});
updateJson(`libs/${swcEsmLib}/.swcrc`, (json) => {
json.module.type = 'es6';
return json;
});
updateJson(`libs/${swcEsmLib}/package.json`, (json) => {
// check one lib with the type set, the build output should be set with type module
json.type = 'module';
return json;
});
// Node ESM requires file extensions in imports so must add them before building
updateFile(
`libs/${tscEsmLib}/src/index.ts`,
`export * from './lib/${tscEsmLib}.js';`
);
updateFile(
`libs/${swcEsmLib}/src/index.ts`,
`export * from './lib/${swcEsmLib}.js';`
);
// We also need to update the eslint config file extensions to be explicitly commonjs
// TODO: re-evaluate this once we support ESM eslint configs
renameFile(
`libs/${tscEsmLib}/eslint.config.js`,
`libs/${tscEsmLib}/eslint.config.cjs`
);
renameFile(
`libs/${swcEsmLib}/eslint.config.js`,
`libs/${swcEsmLib}/eslint.config.cjs`
);
// Add additional entry points for `exports` field
updateJson(join('libs', tscLib, 'project.json'), (json) => {
json.targets.build.options.additionalEntryPoints = [
`libs/${tscLib}/src/foo/*.ts`,
];
return json;
});
updateFile(`libs/${tscLib}/src/foo/bar.ts`, `export const bar = 'bar';`);
updateFile(`libs/${tscLib}/src/foo/faz.ts`, `export const faz = 'faz';`);
updateJson(join('libs', swcLib, 'project.json'), (json) => {
json.targets.build.options.additionalEntryPoints = [
`libs/${swcLib}/src/foo/*.ts`,
];
return json;
});
updateFile(`libs/${swcLib}/src/foo/bar.ts`, `export const bar = 'bar';`);
updateFile(`libs/${swcLib}/src/foo/faz.ts`, `export const faz = 'faz';`);
runCLI(`build ${tscLib} --generateExportsField`);
runCLI(`build ${swcLib} --generateExportsField`);
runCLI(`build ${tscEsmLib} --generateExportsField`);
runCLI(`build ${swcEsmLib} --generateExportsField`);
expect(readJson(`dist/libs/${tscLib}/package.json`).exports).toEqual({
'./package.json': './package.json',
'.': './src/index.js',
'./foo/bar': './src/foo/bar.js',
'./foo/faz': './src/foo/faz.js',
});
expect(readJson(`dist/libs/${swcLib}/package.json`).exports).toEqual({
'./package.json': './package.json',
'.': './src/index.js',
'./foo/bar': './src/foo/bar.js',
'./foo/faz': './src/foo/faz.js',
});
const pmc = getPackageManagerCommand();
let output: string;
// Make sure CJS output is correct
createFile(
'test-cjs/package.json',
JSON.stringify(
{
name: 'test-cjs',
private: true,
type: 'commonjs',
dependencies: {
[`@proj/${tscLib}`]: `file:../dist/libs/${tscLib}`,
[`@proj/${swcLib}`]: `file:../dist/libs/${swcLib}`,
},
},
null,
2
)
);
createFile(
'test-cjs/index.js',
`
const { ${tscLib} } = require('@proj/${tscLib}');
const { ${swcLib} } = require('@proj/${swcLib}');
// additional entry-points
const { bar } = require('@proj/${tscLib}/foo/bar');
const { faz } = require('@proj/${swcLib}/foo/faz');
console.log(${tscLib}());
console.log(${swcLib}());
console.log(bar);
console.log(faz);
`
);
runCommand(pmc.install, {
cwd: join(tmpProjPath(), 'test-cjs'),
});
output = runCommand('node index.js', {
cwd: join(tmpProjPath(), 'test-cjs'),
});
expect(output).toContain(tscLib);
expect(output).toContain(swcLib);
expect(output).toContain('bar');
expect(output).toContain('faz');
// Make sure ESM output is correct
createFile(
'test-esm/package.json',
JSON.stringify(
{
name: 'test-esm',
private: true,
type: 'module',
dependencies: {
[`@proj/${tscEsmLib}`]: `file:../dist/libs/${tscEsmLib}`,
[`@proj/${swcEsmLib}`]: `file:../dist/libs/${swcEsmLib}`,
},
},
null,
2
)
);
createFile(
'test-esm/index.js',
`
import { ${tscEsmLib} } from '@proj/${tscEsmLib}';
import { ${swcEsmLib} } from '@proj/${swcEsmLib}';
console.log(${tscEsmLib}());
console.log(${swcEsmLib}());
`
);
runCommand(pmc.install, {
cwd: join(tmpProjPath(), 'test-esm'),
});
output = runCommand('node index.js', {
cwd: join(tmpProjPath(), 'test-esm'),
});
expect(output).toContain(tscEsmLib);
expect(output).toContain(swcEsmLib);
}, 500_000);
});