Skip to content

Commit c85de5d

Browse files
leosvelperezFrozenPandaz
authored andcommitted
feat(misc): set a development conditional export for buildable libraries when using the ts solution setup (#30451)
Update library generators to set a `development` conditional export for buildable libraries' `package.json` files and set the `customConditions` compiler options in `tsconfig.base.json`. This will only be done for workspaces using the TS solution setup. ## Current Behavior ## Expected Behavior ## Related Issue(s) Fixes # (cherry picked from commit 176c792)
1 parent 25a6ae3 commit c85de5d

File tree

25 files changed

+867
-39
lines changed

25 files changed

+867
-39
lines changed

packages/esbuild/src/generators/configuration/configuration.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ function updatePackageJson(
167167
esbuildOptions,
168168
} = mergedTarget.options;
169169

170+
// the file must exist in the TS solution setup
171+
const tsconfigBase = readJson(tree, 'tsconfig.base.json');
172+
170173
// can't use the declarationRootDir as rootDir because it only affects the typings,
171174
// not the runtime entry point
172175
packageJson = getUpdatedPackageJsonContent(packageJson, {
@@ -186,6 +189,10 @@ function updatePackageJson(
186189
outputFileExtensionForEsm: getOutExtension('esm', {
187190
userDefinedBuildOptions: esbuildOptions,
188191
}),
192+
skipDevelopmentExports:
193+
!tsconfigBase.compilerOptions?.customConditions?.includes(
194+
'development'
195+
),
189196
});
190197

191198
if (declarationRootDir !== dirname(main)) {

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ describe('lib', () => {
467467
compilerOptions: {
468468
composite: true,
469469
declaration: true,
470+
customConditions: ['development'],
470471
},
471472
});
472473
writeJson(appTree, 'tsconfig.json', {
@@ -630,13 +631,14 @@ describe('lib', () => {
630631
{
631632
"exports": {
632633
".": {
633-
"default": "./src/index.ts",
634-
"import": "./src/index.ts",
634+
"default": "./dist/index.cjs.js",
635+
"development": "./src/index.ts",
636+
"import": "./dist/index.esm.js",
635637
"types": "./dist/index.esm.d.ts",
636638
},
637639
"./package.json": "./package.json",
638640
},
639-
"main": "./src/index.ts",
641+
"main": "./dist/index.cjs.js",
640642
"module": "./dist/index.esm.js",
641643
"name": "@proj/my-lib",
642644
"peerDependencies": {
@@ -649,6 +651,25 @@ describe('lib', () => {
649651
`);
650652
});
651653

654+
it('should not set the "development" condition in exports when it does not exist in tsconfig.base.json', async () => {
655+
updateJson(appTree, 'tsconfig.base.json', (json) => {
656+
delete json.compilerOptions.customConditions;
657+
return json;
658+
});
659+
660+
await expoLibraryGenerator(appTree, {
661+
...defaultSchema,
662+
buildable: true,
663+
strict: false,
664+
useProjectJson: false,
665+
skipFormat: true,
666+
});
667+
668+
expect(
669+
readJson(appTree, 'my-lib/package.json').exports['.']
670+
).not.toHaveProperty('development');
671+
});
672+
652673
it('should set "nx.name" in package.json when the user provides a name that is different than the package name', async () => {
653674
await expoLibraryGenerator(appTree, {
654675
...defaultSchema,

packages/expo/src/generators/library/library.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,15 @@ function createFiles(host: Tree, options: NormalizedSchema) {
295295
function determineEntryFields(
296296
options: NormalizedSchema
297297
): Pick<PackageJson, 'main' | 'types' | 'exports'> {
298+
if (
299+
options.buildable ||
300+
options.publishable ||
301+
!options.isUsingTsSolutionConfig
302+
) {
303+
// For buildable libraries, the entries are configured by the bundler (i.e. Rollup).
304+
return undefined;
305+
}
306+
298307
return {
299308
main: options.js ? './src/index.js' : './src/index.ts',
300309
types: options.js ? './src/index.js' : './src/index.ts',

packages/js/src/generators/init/files/ts-solution/tsconfig.base.json__tmpl__

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"noUnusedLocals": true,
1818
"skipLibCheck": true,
1919
"strict": true,
20-
"target": "es2022"
20+
"target": "es2022",
21+
"customConditions": ["development"]
2122
}
2223
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ describe('js init generator', () => {
208208
"noUnusedLocals": true,
209209
"skipLibCheck": true,
210210
"strict": true,
211-
"target": "es2022"
211+
"target": "es2022",
212+
"customConditions": ["development"]
212213
}
213214
}
214215
"

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,7 @@ describe('lib', () => {
18171817
compilerOptions: {
18181818
composite: true,
18191819
declaration: true,
1820+
customConditions: ['development'],
18201821
},
18211822
});
18221823
writeJson(tree, 'tsconfig.json', {
@@ -1954,6 +1955,7 @@ describe('lib', () => {
19541955
"exports": {
19551956
".": {
19561957
"default": "./dist/index.js",
1958+
"development": "./src/index.ts",
19571959
"import": "./dist/index.js",
19581960
"types": "./dist/index.d.ts",
19591961
},
@@ -1987,6 +1989,7 @@ describe('lib', () => {
19871989
"exports": {
19881990
".": {
19891991
"default": "./dist/index.js",
1992+
"development": "./src/index.ts",
19901993
"import": "./dist/index.js",
19911994
"types": "./dist/index.d.ts",
19921995
},
@@ -2428,5 +2431,26 @@ describe('lib', () => {
24282431
expect(readJson(tree, 'my-lib/project.json').name).toBe('my-lib');
24292432
expect(readJson(tree, 'my-lib/package.json').nx).toBeUndefined();
24302433
});
2434+
2435+
it('should not set the "development" condition in exports when it does not exist in tsconfig.base.json', async () => {
2436+
updateJson(tree, 'tsconfig.base.json', (json) => {
2437+
delete json.compilerOptions.customConditions;
2438+
return json;
2439+
});
2440+
2441+
await libraryGenerator(tree, {
2442+
...defaultOptions,
2443+
directory: 'my-lib',
2444+
name: 'my-lib',
2445+
useProjectJson: true,
2446+
bundler: 'tsc',
2447+
addPlugin: true,
2448+
skipFormat: true,
2449+
});
2450+
2451+
expect(
2452+
readJson(tree, 'my-lib/package.json').exports['.']
2453+
).not.toHaveProperty('development');
2454+
});
24312455
});
24322456
});

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
names,
1111
offsetFromRoot,
1212
ProjectConfiguration,
13+
readJson,
1314
readNxJson,
1415
readProjectConfiguration,
1516
runTasksInSerial,
@@ -631,6 +632,9 @@ function createFiles(tree: Tree, options: NormalizedLibraryGeneratorOptions) {
631632
options.isUsingTsSolutionConfig &&
632633
!['none', 'rollup', 'vite'].includes(options.bundler)
633634
) {
635+
// the file must exist in the TS solution setup
636+
const tsconfigBase = readJson(tree, 'tsconfig.base.json');
637+
634638
return getUpdatedPackageJsonContent(updatedPackageJson, {
635639
main: join(options.projectRoot, 'src/index.ts'),
636640
outputPath: joinPathFragments(options.projectRoot, 'dist'),
@@ -639,6 +643,10 @@ function createFiles(tree: Tree, options: NormalizedLibraryGeneratorOptions) {
639643
generateExportsField: true,
640644
packageJsonPath,
641645
format: ['esm'],
646+
skipDevelopmentExports:
647+
!tsconfigBase.compilerOptions?.customConditions?.includes(
648+
'development'
649+
),
642650
});
643651
}
644652

@@ -664,6 +672,8 @@ function createFiles(tree: Tree, options: NormalizedLibraryGeneratorOptions) {
664672
options.isUsingTsSolutionConfig &&
665673
!['none', 'rollup', 'vite'].includes(options.bundler)
666674
) {
675+
const tsconfigBase = readJson(tree, 'tsconfig.base.json');
676+
667677
packageJson = getUpdatedPackageJsonContent(packageJson, {
668678
main: join(options.projectRoot, 'src/index.ts'),
669679
outputPath: joinPathFragments(options.projectRoot, 'dist'),
@@ -672,6 +682,10 @@ function createFiles(tree: Tree, options: NormalizedLibraryGeneratorOptions) {
672682
generateExportsField: true,
673683
packageJsonPath,
674684
format: ['esm'],
685+
skipDevelopmentExports:
686+
!tsconfigBase.compilerOptions?.customConditions?.includes(
687+
'development'
688+
),
675689
});
676690
}
677691

packages/js/src/generators/setup-build/generator.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,11 @@ function updatePackageJson(
325325
version: '0.0.1',
326326
};
327327
}
328+
329+
// the file must exist in the TS solution setup, which is the only case this
330+
// function is called
331+
const tsconfigBase = readJson(tree, 'tsconfig.base.json');
332+
328333
packageJson = getUpdatedPackageJsonContent(packageJson, {
329334
main,
330335
outputPath,
@@ -333,6 +338,8 @@ function updatePackageJson(
333338
packageJsonPath,
334339
rootDir,
335340
format,
341+
skipDevelopmentExports:
342+
!tsconfigBase.compilerOptions?.customConditions?.includes('development'),
336343
});
337344
writeJson(tree, packageJsonPath, packageJson);
338345
}

packages/js/src/plugins/typescript/plugin.spec.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2206,6 +2206,111 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
22062206
`);
22072207
});
22082208

2209+
it('should not create build target when the entry points point to source files', async () => {
2210+
// Sibling package.json
2211+
await applyFilesToTempFsAndContext(tempFs, context, {
2212+
'libs/my-lib/tsconfig.json': `{}`,
2213+
'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"outDir": "dist"}}`,
2214+
'libs/my-lib/package.json': JSON.stringify({
2215+
exports: {
2216+
'.': {
2217+
default: './src/index.ts',
2218+
},
2219+
},
2220+
}),
2221+
});
2222+
expect(
2223+
await invokeCreateNodesOnMatchingFiles(context, {
2224+
// Reduce noise in build snapshots by disabling default typecheck target
2225+
typecheck: false,
2226+
build: true,
2227+
})
2228+
).toMatchInlineSnapshot(`
2229+
{
2230+
"projects": {
2231+
"libs/my-lib": {
2232+
"projectType": "library",
2233+
"targets": {},
2234+
},
2235+
},
2236+
}
2237+
`);
2238+
});
2239+
2240+
it('should create build target when the entry points point to dist files', async () => {
2241+
// Sibling package.json
2242+
await applyFilesToTempFsAndContext(tempFs, context, {
2243+
'libs/my-lib/tsconfig.json': `{}`,
2244+
'libs/my-lib/tsconfig.lib.json': `{"compilerOptions": {"outDir": "dist"}}`,
2245+
'libs/my-lib/package.json': JSON.stringify({
2246+
exports: {
2247+
'.': {
2248+
// should ignore the fact that the development condition points to source
2249+
development: './src/index.ts',
2250+
types: './dist/index.d.ts',
2251+
default: './dist/index.js',
2252+
},
2253+
},
2254+
}),
2255+
});
2256+
expect(
2257+
await invokeCreateNodesOnMatchingFiles(context, {
2258+
// Reduce noise in build snapshots by disabling default typecheck target
2259+
typecheck: false,
2260+
build: true,
2261+
})
2262+
).toMatchInlineSnapshot(`
2263+
{
2264+
"projects": {
2265+
"libs/my-lib": {
2266+
"projectType": "library",
2267+
"targets": {
2268+
"build": {
2269+
"cache": true,
2270+
"command": "tsc --build tsconfig.lib.json",
2271+
"dependsOn": [
2272+
"^build",
2273+
],
2274+
"inputs": [
2275+
"production",
2276+
"^production",
2277+
{
2278+
"externalDependencies": [
2279+
"typescript",
2280+
],
2281+
},
2282+
],
2283+
"metadata": {
2284+
"description": "Builds the project with \`tsc\`.",
2285+
"help": {
2286+
"command": "npx tsc --build --help",
2287+
"example": {
2288+
"args": [
2289+
"--force",
2290+
],
2291+
},
2292+
},
2293+
"technologies": [
2294+
"typescript",
2295+
],
2296+
},
2297+
"options": {
2298+
"cwd": "libs/my-lib",
2299+
},
2300+
"outputs": [
2301+
"{projectRoot}/dist",
2302+
],
2303+
"syncGenerators": [
2304+
"@nx/js:typescript-sync",
2305+
],
2306+
},
2307+
},
2308+
},
2309+
},
2310+
}
2311+
`);
2312+
});
2313+
22092314
it('should create a node with a build target when enabled, for a project level tsconfig.lib.json build file by default', async () => {
22102315
// Sibling package.json
22112316
await applyFilesToTempFsAndContext(tempFs, context, {

packages/js/src/plugins/typescript/util.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ export function isValidPackageJsonBuildConfig(
9595
return isPathSourceFile(value);
9696
} else if (typeof value === 'object') {
9797
return Object.entries(value).some(([currentKey, subValue]) => {
98-
// Skip types field
99-
if (currentKey === 'types') {
98+
// Skip types and development conditions
99+
if (currentKey === 'types' || currentKey === 'development') {
100100
return false;
101101
}
102102
if (typeof subValue === 'string') {

0 commit comments

Comments
 (0)