Skip to content

Commit

Permalink
fix(js): output esm with esbuild in ts solution setup
Browse files Browse the repository at this point in the history
  • Loading branch information
leosvelperez committed Jan 8, 2025
1 parent 66f5df0 commit 3f97d26
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 35 deletions.
123 changes: 102 additions & 21 deletions e2e/js/src/js-ts-solution.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
cleanupProject,
getPackageManagerCommand,
getSelectedPackageManager,
newProject,
runCLI,
runCommand,
uniq,
updateFile,
updateJson,
Expand All @@ -20,7 +22,7 @@ describe('JS - TS solution setup', () => {
cleanupProject();
});

it('should generate libraries with different bundlers, link them and build them successfully', () => {
it('should generate libraries with different bundlers and link them successfully', () => {
const esbuildParentLib = uniq('esbuild-parent-lib');
const esbuildChildLib = uniq('esbuild-child-lib');
const rollupParentLib = uniq('rollup-parent-lib');
Expand All @@ -31,21 +33,37 @@ describe('JS - TS solution setup', () => {
const tscChildLib = uniq('tsc-child-lib');
const viteParentLib = uniq('vite-parent-lib');
const viteChildLib = uniq('vite-child-lib');
const noBundlerLib = uniq('no-bundler-lib');

runCLI(
`generate @nx/js:lib packages/${esbuildParentLib} --bundler=esbuild`
);
runCLI(`generate @nx/js:lib packages/${esbuildChildLib} --bundler=esbuild`);
runCLI(`generate @nx/js:lib packages/${rollupParentLib} --bundler=rollup`);
runCLI(`generate @nx/js:lib packages/${rollupChildLib} --bundler=rollup`);
runCLI(`generate @nx/js:lib packages/${swcParentLib} --bundler=swc`);
runCLI(`generate @nx/js:lib packages/${swcChildLib} --bundler=swc`);
runCLI(`generate @nx/js:lib packages/${tscParentLib} --bundler=tsc`);
runCLI(`generate @nx/js:lib packages/${tscChildLib} --bundler=tsc`);
runCLI(`generate @nx/js:lib packages/${viteParentLib} --bundler=vite`);
runCLI(`generate @nx/js:lib packages/${viteChildLib} --bundler=vite`);
runCLI(`generate @nx/js:lib packages/${noBundlerLib} --bundler=none`);
`generate @nx/js:lib packages/${esbuildParentLib} --bundler=esbuild --linter=eslint --unitTestRunner=jest`
);
runCLI(
`generate @nx/js:lib packages/${esbuildChildLib} --bundler=esbuild --linter=eslint --unitTestRunner=jest`
);
runCLI(
`generate @nx/js:lib packages/${rollupParentLib} --bundler=rollup --linter=eslint --unitTestRunner=jest`
);
runCLI(
`generate @nx/js:lib packages/${rollupChildLib} --bundler=rollup --linter=eslint --unitTestRunner=jest`
);
runCLI(
`generate @nx/js:lib packages/${swcParentLib} --bundler=swc --linter=eslint --unitTestRunner=jest`
);
runCLI(
`generate @nx/js:lib packages/${swcChildLib} --bundler=swc --linter=eslint --unitTestRunner=jest`
);
runCLI(
`generate @nx/js:lib packages/${tscParentLib} --bundler=tsc --linter=eslint --unitTestRunner=jest`
);
runCLI(
`generate @nx/js:lib packages/${tscChildLib} --bundler=tsc --linter=eslint --unitTestRunner=jest`
);
runCLI(
`generate @nx/js:lib packages/${viteParentLib} --bundler=vite --linter=eslint --unitTestRunner=jest`
);
runCLI(
`generate @nx/js:lib packages/${viteChildLib} --bundler=vite --linter=eslint --unitTestRunner=jest`
);

// add deps, each parent lib imports all child libs
const addImports = (parentLib: string) => {
Expand All @@ -56,7 +74,6 @@ export * from '@proj/${rollupChildLib}';
export * from '@proj/${swcChildLib}';
export * from '@proj/${tscChildLib}';
export * from '@proj/${viteChildLib}';
export * from '@proj/${noBundlerLib}';
${content}`
);
};
Expand All @@ -78,7 +95,6 @@ ${content}`
json.dependencies[`@proj/${swcChildLib}`] = 'workspace:*';
json.dependencies[`@proj/${tscChildLib}`] = 'workspace:*';
json.dependencies[`@proj/${viteChildLib}`] = 'workspace:*';
json.dependencies[`@proj/${noBundlerLib}`] = 'workspace:*';
return json;
});
};
Expand All @@ -88,15 +104,80 @@ ${content}`
addDeps(swcParentLib);
addDeps(tscParentLib);
addDeps(viteParentLib);

const pmc = getPackageManagerCommand({ packageManager: pm });
runCommand(pmc.install);
}

// sync to ensure the TS project references are updated
runCLI(`sync`);

expect(() => runCLI(`build ${esbuildParentLib}`)).not.toThrow();
expect(() => runCLI(`build ${rollupParentLib}`)).not.toThrow();
expect(() => runCLI(`build ${swcParentLib}`)).not.toThrow();
expect(() => runCLI(`build ${tscParentLib}`)).not.toThrow();
expect(() => runCLI(`build ${viteParentLib}`)).not.toThrow();
// check build
expect(runCLI(`build ${esbuildParentLib}`)).toContain(
`Successfully ran target build for project ${esbuildParentLib} and 5 tasks it depends on`
);
expect(runCLI(`build ${rollupParentLib}`)).toContain(
`Successfully ran target build for project ${rollupParentLib} and 5 tasks it depends on`
);
expect(runCLI(`build ${swcParentLib}`)).toContain(
`Successfully ran target build for project ${swcParentLib} and 5 tasks it depends on`
);
expect(runCLI(`build ${tscParentLib}`)).toContain(
`Successfully ran target build for project ${tscParentLib} and 5 tasks it depends on`
);
expect(runCLI(`build ${viteParentLib}`)).toContain(
`Successfully ran target build for project ${viteParentLib} and 5 tasks it depends on`
);

// check typecheck
expect(runCLI(`typecheck ${esbuildParentLib}`)).toContain(
`Successfully ran target typecheck for project ${esbuildParentLib} and 5 tasks it depends on`
);
expect(runCLI(`typecheck ${rollupParentLib}`)).toContain(
`Successfully ran target typecheck for project ${rollupParentLib} and 5 tasks it depends on`
);
expect(runCLI(`typecheck ${swcParentLib}`)).toContain(
`Successfully ran target typecheck for project ${swcParentLib} and 5 tasks it depends on`
);
expect(runCLI(`typecheck ${tscParentLib}`)).toContain(
`Successfully ran target typecheck for project ${tscParentLib} and 5 tasks it depends on`
);
expect(runCLI(`typecheck ${viteParentLib}`)).toContain(
`Successfully ran target typecheck for project ${viteParentLib} and 5 tasks it depends on`
);

// check lint
expect(runCLI(`lint ${esbuildParentLib}`)).toContain(
`Successfully ran target lint for project ${esbuildParentLib}`
);
expect(runCLI(`lint ${rollupParentLib}`)).toContain(
`Successfully ran target lint for project ${rollupParentLib}`
);
expect(runCLI(`lint ${swcParentLib}`)).toContain(
`Successfully ran target lint for project ${swcParentLib}`
);
expect(runCLI(`lint ${tscParentLib}`)).toContain(
`Successfully ran target lint for project ${tscParentLib}`
);
expect(runCLI(`lint ${viteParentLib}`)).toContain(
`Successfully ran target lint for project ${viteParentLib}`
);

// check test
expect(runCLI(`test ${esbuildParentLib}`)).toContain(
`Successfully ran target test for project ${esbuildParentLib}`
);
expect(runCLI(`test ${rollupParentLib}`)).toContain(
`Successfully ran target test for project ${rollupParentLib}`
);
expect(runCLI(`test ${swcParentLib}`)).toContain(
`Successfully ran target test for project ${swcParentLib}`
);
expect(runCLI(`test ${tscParentLib}`)).toContain(
`Successfully ran target test for project ${tscParentLib}`
);
expect(runCLI(`test ${viteParentLib}`)).toContain(
`Successfully ran target test for project ${viteParentLib}`
);
}, 300_000);
});
98 changes: 98 additions & 0 deletions e2e/vite/src/vite-ts-solution.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { names } from '@nx/devkit';
import {
cleanupProject,
getPackageManagerCommand,
getSelectedPackageManager,
newProject,
runCLI,
runCommand,
uniq,
updateFile,
updateJson,
} from '@nx/e2e/utils';

describe('Vite - TS solution setup', () => {
beforeAll(() => {
newProject({
packages: ['@nx/react', '@nx/js'],
preset: 'ts',
});
});

afterAll(() => {
cleanupProject();
});

it('should generate app and consume libraries with different bundlers', () => {
const reactApp = uniq('react-app');
const esbuildLib = uniq('esbuild-lib');
const rollupLib = uniq('rollup-lib');
const swcLib = uniq('swc-lib');
const tscLib = uniq('tsc-lib');
const viteLib = uniq('vite-lib');
const noBundlerLib = uniq('no-bundler-lib');

runCLI(`generate @nx/react:app apps/${reactApp} --bundler=vite`);
runCLI(`generate @nx/js:lib packages/${esbuildLib} --bundler=esbuild`);
runCLI(`generate @nx/js:lib packages/${rollupLib} --bundler=rollup`);
runCLI(`generate @nx/js:lib packages/${swcLib} --bundler=swc`);
runCLI(`generate @nx/js:lib packages/${tscLib} --bundler=tsc`);
runCLI(`generate @nx/js:lib packages/${viteLib} --bundler=vite`);
runCLI(`generate @nx/js:lib packages/${noBundlerLib} --bundler=none`);

// import all libs from the app
updateFile(
`apps/${reactApp}/src/app/App.tsx`,
(content) => `import { ${
names(esbuildLib).propertyName
} } from '@proj/${esbuildLib}';
import { ${names(rollupLib).propertyName} } from '@proj/${rollupLib}';
import { ${names(swcLib).propertyName} } from '@proj/${swcLib}';
import { ${names(tscLib).propertyName} } from '@proj/${tscLib}';
import { ${names(viteLib).propertyName} } from '@proj/${viteLib}';
import { ${names(noBundlerLib).propertyName} } from '@proj/${noBundlerLib}';
console.log(
${names(esbuildLib).propertyName}(),
${names(rollupLib).propertyName}(),
${names(swcLib).propertyName}(),
${names(tscLib).propertyName}(),
${names(viteLib).propertyName}(),
${names(noBundlerLib).propertyName}()
);
${content}`
);

const pm = getSelectedPackageManager();
if (pm === 'pnpm') {
// for pnpm we need to add the local packages as dependencies to each consumer package.json
updateJson(`apps/${reactApp}/package.json`, (json) => {
json.dependencies ??= {};
json.dependencies[`@proj/${esbuildLib}`] = 'workspace:*';
json.dependencies[`@proj/${rollupLib}`] = 'workspace:*';
json.dependencies[`@proj/${swcLib}`] = 'workspace:*';
json.dependencies[`@proj/${tscLib}`] = 'workspace:*';
json.dependencies[`@proj/${viteLib}`] = 'workspace:*';
json.dependencies[`@proj/${noBundlerLib}`] = 'workspace:*';
return json;
});

const pmc = getPackageManagerCommand({ packageManager: pm });
runCommand(pmc.install);
}

// sync to ensure the TS project references are updated
runCLI(`sync`);

// check build
expect(runCLI(`build ${reactApp}`)).toContain(
`Successfully ran target build for project ${reactApp} and 5 tasks it depends on`
);

// check typecheck
expect(runCLI(`typecheck ${reactApp}`)).toContain(
`Successfully ran target typecheck for project ${reactApp} and 6 tasks it depends on`
);
}, 300_000);
});
10 changes: 7 additions & 3 deletions packages/esbuild/src/executors/esbuild/esbuild.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ export async function* esbuildExecutor(
setup(build: esbuild.PluginBuild) {
build.onEnd(async (result: esbuild.BuildResult) => {
if (
!options.skipTypeCheck &&
!options.isTsSolutionSetup
!options.skipTypeCheck ||
options.isTsSolutionSetup
) {
const { errors } = await runTypeCheck(
options,
Expand Down Expand Up @@ -183,7 +183,7 @@ export async function* esbuildExecutor(
);
} else {
// Run type-checks first and bail if they don't pass.
if (!options.skipTypeCheck && !options.isTsSolutionSetup) {
if (!options.skipTypeCheck || options.isTsSolutionSetup) {
const { errors } = await runTypeCheck(options, context);
if (errors.length > 0) {
yield { success: false };
Expand Down Expand Up @@ -245,6 +245,10 @@ function getTypeCheckOptions(
typeCheckOptions.cacheDir = cacheDir;
}

if (options.isTsSolutionSetup && options.skipTypeCheck) {
typeCheckOptions.ignoreDiagnostics = true;
}

return typeCheckOptions;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/js/src/generators/library/library.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1439,7 +1439,7 @@ describe('lib', () => {
{
ignoredFiles: [
'{projectRoot}/eslint.config.{js,cjs,mjs}',
'{projectRoot}/rollup.config.{js,ts,mjs,mts}',
'{projectRoot}/rollup.config.{js,ts,mjs,mts,cjs,cts}',
],
},
],
Expand Down
15 changes: 6 additions & 9 deletions packages/js/src/generators/library/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,6 @@ async function configureProject(
},
};

if (options.bundler === 'esbuild') {
projectConfiguration.targets.build.options.format = ['cjs'];
}

if (
options.bundler === 'swc' &&
(options.skipTypeCheck || options.isUsingTsSolutionConfig)
Expand All @@ -298,6 +294,7 @@ async function configureProject(

if (options.isUsingTsSolutionConfig) {
if (options.bundler === 'esbuild') {
projectConfiguration.targets.build.options.format = ['esm'];
projectConfiguration.targets.build.options.declarationRootDir = `${options.projectRoot}/src`;
} else if (options.bundler === 'swc') {
projectConfiguration.targets.build.options.stripLeadingPaths = true;
Expand All @@ -306,6 +303,7 @@ async function configureProject(
projectConfiguration.targets.build.options.assets = [];

if (options.bundler === 'esbuild') {
projectConfiguration.targets.build.options.format = ['cjs'];
projectConfiguration.targets.build.options.generatePackageJson = true;
}

Expand Down Expand Up @@ -468,7 +466,7 @@ export async function addLint(
} else if (options.bundler === 'rollup') {
ruleOptions.ignoredFiles ??= [];
ruleOptions.ignoredFiles.push(
'{projectRoot}/rollup.config.{js,ts,mjs,mts}'
'{projectRoot}/rollup.config.{js,ts,mjs,mts,cjs,cts}'
);
o.rules['@nx/dependency-checks'] = [ruleSeverity, ruleOptions];
} else if (options.bundler === 'esbuild') {
Expand Down Expand Up @@ -506,7 +504,7 @@ function createFiles(tree: Tree, options: NormalizedLibraryGeneratorOptions) {
if (
options.bundler === 'vite' ||
(options.isUsingTsSolutionConfig &&
(options.bundler === 'tsc' || options.bundler === 'swc'))
['esbuild', 'swc', 'tsc'].includes(options.bundler))
) {
const tsConfig = readTsConfigFromTree(
tree,
Expand Down Expand Up @@ -1137,11 +1135,10 @@ function determineEntryFields(
: './index.d.ts',
};
case 'esbuild':
// For libraries intended for Node, use CJS.
return {
type: 'commonjs',
type: options.isUsingTsSolutionConfig ? 'module' : 'commonjs',
main: options.isUsingTsSolutionConfig
? './dist/index.cjs'
? './dist/index.js'
: './index.cjs',
typings: options.isUsingTsSolutionConfig
? './dist/index.d.ts'
Expand Down
2 changes: 1 addition & 1 deletion packages/js/src/generators/setup-build/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export async function setupBuildGenerator(
project: options.project,
skipFormat: true,
skipValidation: true,
format: ['cjs'],
format: isTsSolutionSetup ? ['esm'] : ['cjs'],
});
tasks.push(task);
break;
Expand Down

0 comments on commit 3f97d26

Please sign in to comment.