Skip to content

Commit fa7968d

Browse files
leosvelperezFrozenPandaz
authored andcommitted
fix(js): generate js libs with exports in package.json and ensure esm output when using rollup bundler (#29565)
- Ensure libs are generated with `exports` in `package.json` - Generate `types` instead of `typings` in package.json - Update js lib with rollup to only output esm - Update `tsconfig.spec.json` for js libraries with rollup to set `module: esnext` and `moduleResolution: bundler` (they use `@swc/jest`) - Fix `@nx/js/typescript` issue with absolute paths when normalizing inputs/outputs - Fix `@nx/js/typescript` issue identifying buildable libs - Fix express app generator not installing `@types/express` <!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes # --------- Co-authored-by: Jack Hsu <[email protected]> (cherry picked from commit dd9b09f)
1 parent f6f8536 commit fa7968d

File tree

18 files changed

+235
-86
lines changed

18 files changed

+235
-86
lines changed

e2e/esbuild/src/esbuild.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('EsBuild Plugin', () => {
4848
private: true,
4949
type: 'commonjs',
5050
main: './index.cjs',
51-
typings: './index.d.ts',
51+
types: './index.d.ts',
5252
dependencies: {},
5353
});
5454

e2e/eslint/src/linter.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ describe('Linter', () => {
567567
name: `@proj/${mylib}`,
568568
private: true,
569569
type: 'commonjs',
570-
typings: './src/index.d.ts',
570+
types: './src/index.d.ts',
571571
version: '0.0.1',
572572
});
573573

e2e/node/src/node-ts-solution.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,11 @@ packages:
116116
expect(() => runCLI(`lint ${nodeapp}`)).not.toThrow();
117117
expect(() => runCLI(`test ${nodeapp}`)).not.toThrow();
118118
expect(() => runCLI(`build ${nodeapp}`)).not.toThrow();
119+
expect(() => runCLI(`typecheck ${nodeapp}`)).not.toThrow();
119120
expect(() => runCLI(`lint ${nodelib}`)).not.toThrow();
120121
expect(() => runCLI(`test ${nodelib}`)).not.toThrow();
121122
expect(() => runCLI(`build ${nodelib}`)).not.toThrow();
123+
expect(() => runCLI(`typecheck ${nodelib}`)).not.toThrow();
122124

123125
const p = await runCommandUntil(
124126
`serve ${nodeapp}`,

packages/express/src/generators/application/application.ts

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export async function applicationGeneratorInternal(tree: Tree, schema: Schema) {
8282
const applicationTask = await nodeApplicationGenerator(tree, {
8383
...options,
8484
bundler: 'webpack',
85+
framework: 'express',
8586
skipFormat: true,
8687
});
8788
tasks.push(applicationTask);

packages/jest/src/generators/configuration/lib/create-files.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ export function createFiles(
6262
? `${rootOffset}tsconfig.base.json`
6363
: './tsconfig.json',
6464
outDir: isTsSolutionSetup ? `./out-tsc/jest` : `${rootOffset}dist/out-tsc`,
65-
module: !isTsSolutionSetup ? 'commonjs' : undefined,
65+
module:
66+
!isTsSolutionSetup || transformer === 'ts-jest' ? 'commonjs' : undefined,
6667
});
6768

6869
if (options.setupFile === 'none') {

packages/js/src/generators/library/library.spec.ts

+32-2
Original file line numberDiff line numberDiff line change
@@ -1653,6 +1653,14 @@ describe('lib', () => {
16531653
expect(readJson(tree, 'my-ts-lib/package.json')).toMatchInlineSnapshot(`
16541654
{
16551655
"dependencies": {},
1656+
"exports": {
1657+
".": {
1658+
"default": "./src/index.ts",
1659+
"import": "./src/index.ts",
1660+
"types": "./src/index.ts",
1661+
},
1662+
"./package.json": "./package.json",
1663+
},
16561664
"main": "./src/index.ts",
16571665
"name": "@proj/my-ts-lib",
16581666
"private": true,
@@ -1663,6 +1671,10 @@ describe('lib', () => {
16631671
expect(readJson(tree, 'my-js-lib/package.json')).toMatchInlineSnapshot(`
16641672
{
16651673
"dependencies": {},
1674+
"exports": {
1675+
".": "./src/index.js",
1676+
"./package.json": "./package.json",
1677+
},
16661678
"main": "./src/index.js",
16671679
"name": "@proj/my-js-lib",
16681680
"private": true,
@@ -1686,11 +1698,20 @@ describe('lib', () => {
16861698
"dependencies": {
16871699
"tslib": "^2.3.0",
16881700
},
1701+
"exports": {
1702+
".": {
1703+
"default": "./dist/index.js",
1704+
"import": "./dist/index.js",
1705+
"types": "./dist/index.d.ts",
1706+
},
1707+
"./package.json": "./package.json",
1708+
},
16891709
"main": "./dist/index.js",
1710+
"module": "./dist/index.js",
16901711
"name": "@proj/my-ts-lib",
16911712
"private": true,
16921713
"type": "module",
1693-
"typings": "./dist/index.d.ts",
1714+
"types": "./dist/index.d.ts",
16941715
"version": "0.0.1",
16951716
}
16961717
`);
@@ -1710,11 +1731,20 @@ describe('lib', () => {
17101731
"dependencies": {
17111732
"@swc/helpers": "~0.5.11",
17121733
},
1734+
"exports": {
1735+
".": {
1736+
"default": "./dist/index.js",
1737+
"import": "./dist/index.js",
1738+
"types": "./dist/index.d.ts",
1739+
},
1740+
"./package.json": "./package.json",
1741+
},
17131742
"main": "./dist/index.js",
1743+
"module": "./dist/index.js",
17141744
"name": "@proj/my-ts-lib",
17151745
"private": true,
17161746
"type": "module",
1717-
"typings": "./dist/index.d.ts",
1747+
"types": "./dist/index.d.ts",
17181748
"version": "0.0.1",
17191749
}
17201750
`);

packages/js/src/generators/library/library.ts

+115-65
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {
22
addDependenciesToPackageJson,
3-
installPackagesTask,
43
addProjectConfiguration,
54
ensurePackage,
65
formatFiles,
76
generateFiles,
87
GeneratorCallback,
98
getPackageManagerCommand,
9+
installPackagesTask,
1010
joinPathFragments,
1111
names,
1212
offsetFromRoot,
@@ -35,6 +35,7 @@ import { type PackageJson } from 'nx/src/utils/package-json';
3535
import { join } from 'path';
3636
import type { CompilerOptions } from 'typescript';
3737
import { normalizeLinterOption } from '../../utils/generator-prompts';
38+
import { getUpdatedPackageJsonContent } from '../../utils/package-json/update-package-json';
3839
import {
3940
getProjectPackageManagerWorkspaceState,
4041
getProjectPackageManagerWorkspaceStateWarningTask,
@@ -43,6 +44,7 @@ import { addSwcConfig } from '../../utils/swc/add-swc-config';
4344
import { getSwcDependencies } from '../../utils/swc/add-swc-dependencies';
4445
import { getNeededCompilerOptionOverrides } from '../../utils/typescript/configuration';
4546
import { tsConfigBaseOptions } from '../../utils/typescript/create-ts-config';
47+
import { ensureTypescript } from '../../utils/typescript/ensure-typescript';
4648
import { ensureProjectIsIncludedInPluginRegistrations } from '../../utils/typescript/plugin';
4749
import {
4850
addTsConfigPath,
@@ -68,7 +70,6 @@ import type {
6870
LibraryGeneratorSchema,
6971
NormalizedLibraryGeneratorOptions,
7072
} from './schema';
71-
import { ensureTypescript } from '../../utils/typescript/ensure-typescript';
7273

7374
const defaultOutputDirectory = 'dist';
7475

@@ -118,7 +119,7 @@ export async function libraryGeneratorInternal(
118119
await configurationGenerator(tree, {
119120
project: options.name,
120121
compiler: 'swc',
121-
format: ['cjs', 'esm'],
122+
format: options.isUsingTsSolutionConfig ? ['esm'] : ['cjs', 'esm'],
122123
});
123124
}
124125

@@ -206,6 +207,12 @@ export async function libraryGeneratorInternal(
206207
// add project reference to the runtime tsconfig.lib.json file
207208
json.references ??= [];
208209
json.references.push({ path: './tsconfig.lib.json' });
210+
211+
if (options.isUsingTsSolutionConfig && options.bundler === 'rollup') {
212+
json.compilerOptions.module = 'esnext';
213+
json.compilerOptions.moduleResolution = 'bundler';
214+
}
215+
209216
return json;
210217
}
211218
);
@@ -503,8 +510,7 @@ function createFiles(tree: Tree, options: NormalizedLibraryGeneratorOptions) {
503510
let fileNameImport = options.fileName;
504511
if (
505512
options.bundler === 'vite' ||
506-
(options.isUsingTsSolutionConfig &&
507-
['esbuild', 'swc', 'tsc'].includes(options.bundler))
513+
(options.isUsingTsSolutionConfig && options.bundler !== 'none')
508514
) {
509515
const tsConfig = readTsConfigFromTree(
510516
tree,
@@ -606,17 +612,35 @@ function createFiles(tree: Tree, options: NormalizedLibraryGeneratorOptions) {
606612
// https://docs.npmjs.com/cli/v10/configuring-npm/package-json#files
607613
json.files = ['dist', '!**/*.tsbuildinfo'];
608614
}
609-
return {
615+
616+
const updatedPackageJson = {
610617
...json,
611618
dependencies: {
612619
...json.dependencies,
613620
...determineDependencies(options),
614621
},
615622
...determineEntryFields(options),
616623
};
624+
625+
if (
626+
options.isUsingTsSolutionConfig &&
627+
!['none', 'rollup', 'vite'].includes(options.bundler)
628+
) {
629+
return getUpdatedPackageJsonContent(updatedPackageJson, {
630+
main: join(options.projectRoot, 'src/index.ts'),
631+
outputPath: joinPathFragments(options.projectRoot, 'dist'),
632+
projectRoot: options.projectRoot,
633+
rootDir: join(options.projectRoot, 'src'),
634+
generateExportsField: true,
635+
packageJsonPath,
636+
format: ['esm'],
637+
});
638+
}
639+
640+
return updatedPackageJson;
617641
});
618642
} else {
619-
const packageJson: PackageJson = {
643+
let packageJson: PackageJson = {
620644
name: options.importPath,
621645
version: '0.0.1',
622646
dependencies: determineDependencies(options),
@@ -630,6 +654,22 @@ function createFiles(tree: Tree, options: NormalizedLibraryGeneratorOptions) {
630654
// https://docs.npmjs.com/cli/v10/configuring-npm/package-json#files
631655
packageJson.files = ['dist', '!**/*.tsbuildinfo'];
632656
}
657+
658+
if (
659+
options.isUsingTsSolutionConfig &&
660+
!['none', 'rollup', 'vite'].includes(options.bundler)
661+
) {
662+
packageJson = getUpdatedPackageJsonContent(packageJson, {
663+
main: join(options.projectRoot, 'src/index.ts'),
664+
outputPath: joinPathFragments(options.projectRoot, 'dist'),
665+
projectRoot: options.projectRoot,
666+
rootDir: join(options.projectRoot, 'src'),
667+
generateExportsField: true,
668+
packageJsonPath,
669+
format: ['esm'],
670+
});
671+
}
672+
633673
writeJson<PackageJson>(tree, packageJsonPath, packageJson);
634674
}
635675

@@ -1094,74 +1134,84 @@ function determineEntryFields(
10941134
): Record<string, EntryField> {
10951135
switch (options.bundler) {
10961136
case 'tsc':
1097-
return {
1098-
type: options.isUsingTsSolutionConfig ? 'module' : 'commonjs',
1099-
main: options.isUsingTsSolutionConfig
1100-
? './dist/index.js'
1101-
: './src/index.js',
1102-
typings: options.isUsingTsSolutionConfig
1103-
? './dist/index.d.ts'
1104-
: './src/index.d.ts',
1105-
};
11061137
case 'swc':
1107-
return {
1108-
type: options.isUsingTsSolutionConfig ? 'module' : 'commonjs',
1109-
main: options.isUsingTsSolutionConfig
1110-
? './dist/index.js'
1111-
: './src/index.js',
1112-
typings: options.isUsingTsSolutionConfig
1113-
? './dist/index.d.ts'
1114-
: './src/index.d.ts',
1115-
};
1138+
if (options.isUsingTsSolutionConfig) {
1139+
return {
1140+
type: 'module',
1141+
main: './dist/index.js',
1142+
types: './dist/index.d.ts',
1143+
};
1144+
} else {
1145+
return {
1146+
type: 'commonjs',
1147+
main: './src/index.js',
1148+
types: './src/index.d.ts',
1149+
};
1150+
}
11161151
case 'rollup':
1117-
return {
1118-
// Since we're publishing both formats, skip the type field.
1119-
// Bundlers or Node will determine the entry point to use.
1120-
main: options.isUsingTsSolutionConfig
1121-
? './dist/index.cjs'
1122-
: './index.cjs',
1123-
module: options.isUsingTsSolutionConfig
1124-
? './dist/index.js'
1125-
: './index.js',
1126-
};
1152+
if (options.isUsingTsSolutionConfig) {
1153+
// the rollup configuration generator already handles this
1154+
return {};
1155+
} else {
1156+
return {
1157+
// Since we're publishing both formats, skip the type field.
1158+
// Bundlers or Node will determine the entry point to use.
1159+
main: './index.cjs',
1160+
module: './index.js',
1161+
};
1162+
}
11271163
case 'vite':
1128-
return {
1129-
type: 'module',
1130-
main: options.isUsingTsSolutionConfig
1131-
? './dist/index.js'
1132-
: './index.js',
1133-
typings: options.isUsingTsSolutionConfig
1134-
? './dist/index.d.ts'
1135-
: './index.d.ts',
1136-
};
1164+
if (options.isUsingTsSolutionConfig) {
1165+
// the vite configuration generator already handle this
1166+
return {};
1167+
} else {
1168+
return {
1169+
type: 'module',
1170+
main: './index.js',
1171+
types: './index.d.ts',
1172+
};
1173+
}
11371174
case 'esbuild':
1138-
return {
1139-
type: options.isUsingTsSolutionConfig ? 'module' : 'commonjs',
1140-
main: options.isUsingTsSolutionConfig
1141-
? './dist/index.js'
1142-
: './index.cjs',
1143-
typings: options.isUsingTsSolutionConfig
1144-
? './dist/index.d.ts'
1145-
: './index.d.ts',
1146-
};
1147-
default: {
1175+
if (options.isUsingTsSolutionConfig) {
1176+
return {
1177+
type: 'module',
1178+
main: './dist/index.js',
1179+
types: './dist/index.d.ts',
1180+
};
1181+
} else {
1182+
return {
1183+
type: 'commonjs',
1184+
main: './index.cjs',
1185+
types: './index.d.ts',
1186+
};
1187+
}
1188+
case 'none': {
1189+
if (options.isUsingTsSolutionConfig) {
1190+
return {
1191+
main: options.js ? './src/index.js' : './src/index.ts',
1192+
types: options.js ? './src/index.js' : './src/index.ts',
1193+
exports: {
1194+
'.': options.js
1195+
? './src/index.js'
1196+
: {
1197+
types: './src/index.ts',
1198+
import: './src/index.ts',
1199+
default: './src/index.ts',
1200+
},
1201+
'./package.json': './package.json',
1202+
},
1203+
};
1204+
}
1205+
11481206
return {
11491207
// Safest option is to not set a type field.
11501208
// Allow the user to decide which module format their library is using
11511209
type: undefined,
1152-
// For non-buildable libraries, point to source so we can still use them in apps via bundlers like Vite.
1153-
main: options.isUsingTsSolutionConfig
1154-
? options.js
1155-
? './src/index.js'
1156-
: './src/index.ts'
1157-
: undefined,
1158-
types: options.isUsingTsSolutionConfig
1159-
? options.js
1160-
? './src/index.js'
1161-
: './src/index.ts'
1162-
: undefined,
11631210
};
11641211
}
1212+
default: {
1213+
return {};
1214+
}
11651215
}
11661216
}
11671217

0 commit comments

Comments
 (0)