Skip to content

Commit f357b4e

Browse files
authored
feat(js): update the setup-build generator to support the new ts setup (#28446)
Update the `@nx/js:setup-build` and the generators it depends on to support the new TS setup with project references. <!-- 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 #
1 parent 1fec637 commit f357b4e

File tree

33 files changed

+1031
-293
lines changed

33 files changed

+1031
-293
lines changed

docs/generated/packages/esbuild/generators/configuration.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@
6161
"description": "The build target to add.",
6262
"type": "string",
6363
"default": "build"
64+
},
65+
"format": {
66+
"description": "The format to build the library (esm or cjs).",
67+
"type": "array",
68+
"items": { "type": "string", "enum": ["esm", "cjs"] },
69+
"default": ["esm"]
6470
}
6571
},
6672
"required": [],

docs/generated/packages/js/executors/swc.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,13 @@
130130
"description": "Generate a lockfile (e.g. package-lock.json) that matches the workspace lockfile to ensure package versions match.",
131131
"default": false,
132132
"x-priority": "internal"
133+
},
134+
"stripLeadingPaths": {
135+
"type": "boolean",
136+
"description": "Remove leading directory from output (e.g. src). See: https://swc.rs/docs/usage/cli#--strip-leading-paths",
137+
"default": false
133138
}
134139
},
135-
"stripLeadingPaths": {
136-
"type": "boolean",
137-
"description": "Remove leading directory from output (e.g. src). See: https://swc.rs/docs/usage/cli#--strip-leading-paths",
138-
"default": false
139-
},
140140
"required": ["main", "outputPath", "tsConfig"],
141141
"definitions": {
142142
"assetPattern": {

docs/generated/packages/rollup/generators/configuration.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
},
2727
"main": {
2828
"type": "string",
29-
"description": "Path relative to the workspace root for the main entry file. Defaults to '<projectRoot>/src/main.ts'.",
29+
"description": "Path relative to the workspace root for the main entry file. Defaults to '<projectRoot>/src/index.ts'.",
3030
"alias": "entryFile",
3131
"x-priority": "important"
3232
},
3333
"tsConfig": {
3434
"type": "string",
35-
"description": "Path relative to the workspace root for the tsconfig file to build with. Defaults to '<projectRoot>/tsconfig.app.json'.",
35+
"description": "Path relative to the workspace root for the tsconfig file to build with. Defaults to '<projectRoot>/tsconfig.lib.json'.",
3636
"x-priority": "important"
3737
},
3838
"skipFormat": {

e2e/js/src/js-packaging.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,20 @@ describe('packaging libs', () => {
214214

215215
expect(readJson(`dist/libs/${tscLib}/package.json`).exports).toEqual({
216216
'./package.json': './package.json',
217-
'.': './src/index.js',
217+
'.': {
218+
default: './src/index.js',
219+
types: './src/index.d.ts',
220+
},
218221
'./foo/bar': './src/foo/bar.js',
219222
'./foo/faz': './src/foo/faz.js',
220223
});
221224

222225
expect(readJson(`dist/libs/${swcLib}/package.json`).exports).toEqual({
223226
'./package.json': './package.json',
224-
'.': './src/index.js',
227+
'.': {
228+
default: './src/index.js',
229+
types: './src/index.d.ts',
230+
},
225231
'./foo/bar': './src/foo/bar.js',
226232
'./foo/faz': './src/foo/faz.js',
227233
});

packages/esbuild/src/executors/esbuild/esbuild.impl.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ import {
1818
import * as esbuild from 'esbuild';
1919
import { normalizeOptions } from './lib/normalize';
2020

21-
import { EsBuildExecutorOptions } from './schema';
21+
import {
22+
EsBuildExecutorOptions,
23+
NormalizedEsBuildExecutorOptions,
24+
} from './schema';
2225
import { createAsyncIterable } from '@nx/devkit/src/utils/async-iterable';
2326
import {
2427
buildEsbuildOptions,
@@ -213,7 +216,7 @@ export async function* esbuildExecutor(
213216
}
214217

215218
function getTypeCheckOptions(
216-
options: EsBuildExecutorOptions,
219+
options: NormalizedEsBuildExecutorOptions,
217220
context: ExecutorContext
218221
) {
219222
const { watch, tsConfig, outputPath } = options;
@@ -243,7 +246,7 @@ function getTypeCheckOptions(
243246
}
244247

245248
async function runTypeCheck(
246-
options: EsBuildExecutorOptions,
249+
options: NormalizedEsBuildExecutorOptions,
247250
context: ExecutorContext
248251
) {
249252
const { errors, warnings } = await _runTypeCheck(

packages/esbuild/src/executors/esbuild/lib/build-esbuild-options.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export function buildEsbuildOptions(
125125

126126
export function getOutExtension(
127127
format: 'cjs' | 'esm',
128-
options: NormalizedEsBuildExecutorOptions
128+
options: Pick<NormalizedEsBuildExecutorOptions, 'userDefinedBuildOptions'>
129129
): '.cjs' | '.mjs' | '.js' {
130130
const userDefinedExt = options.userDefinedBuildOptions?.outExtension?.['.js'];
131131
// Allow users to change the output extensions from default CJS and ESM extensions.

packages/esbuild/src/executors/esbuild/lib/normalize.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function normalizeOptions(
1717
const isTsSolutionSetup = isUsingTsSolutionSetup();
1818
if (isTsSolutionSetup && options.generatePackageJson) {
1919
throw new Error(
20-
`Setting 'generatePackageJson: true' is not allowed with the current TypeScript setup. Please update the 'package.json' file at the project root as needed and don't set the 'generatePackageJson' option.`
20+
`Setting 'generatePackageJson: true' is not supported with the current TypeScript setup. Update the 'package.json' file at the project root as needed and unset the 'generatePackageJson' option.`
2121
);
2222
}
2323

@@ -26,7 +26,7 @@ export function normalizeOptions(
2626
// If we're not generating package.json file, then copy it as-is as an asset when not using ts solution setup.
2727
const assets =
2828
options.generatePackageJson || isTsSolutionSetup
29-
? options.assets
29+
? options.assets ?? []
3030
: [
3131
...options.assets,
3232
joinPathFragments(

packages/esbuild/src/executors/esbuild/schema.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ type Compiler = 'babel' | 'swc';
55

66
export interface EsBuildExecutorOptions {
77
additionalEntryPoints?: string[];
8-
assets: (AssetGlob | string)[];
8+
assets?: (AssetGlob | string)[];
99
bundle?: boolean;
1010
declaration?: boolean;
1111
declarationRootDir?: string;
@@ -32,7 +32,8 @@ export interface EsBuildExecutorOptions {
3232

3333
export interface NormalizedEsBuildExecutorOptions
3434
extends Omit<EsBuildExecutorOptions, 'esbuildOptions' | 'esbuildConfig'> {
35+
assets: (AssetGlob | string)[];
3536
singleEntry: boolean;
3637
external: string[];
37-
userDefinedBuildOptions: esbuild.BuildOptions;
38+
userDefinedBuildOptions: esbuild.BuildOptions | undefined;
3839
}

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

Lines changed: 129 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,38 @@
11
import {
22
formatFiles,
33
joinPathFragments,
4+
readJson,
5+
readNxJson,
46
readProjectConfiguration,
57
Tree,
68
updateProjectConfiguration,
79
writeJson,
810
} from '@nx/devkit';
9-
11+
import { addBuildTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
12+
import { getOutputDir, getUpdatedPackageJsonContent } from '@nx/js';
1013
import { getImportPath } from '@nx/js/src/utils/get-import-path';
11-
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
12-
13-
import { esbuildInitGenerator } from '../init/init';
14+
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
15+
import { basename, dirname, join } from 'node:path/posix';
16+
import { mergeTargetConfigurations } from 'nx/src/devkit-internals';
17+
import { PackageJson } from 'nx/src/utils/package-json';
18+
import { getOutExtension } from '../../executors/esbuild/lib/build-esbuild-options';
1419
import { EsBuildExecutorOptions } from '../../executors/esbuild/schema';
20+
import { esbuildInitGenerator } from '../init/init';
1521
import { EsBuildProjectSchema } from './schema';
16-
import { addBuildTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
1722

1823
export async function configurationGenerator(
1924
tree: Tree,
2025
options: EsBuildProjectSchema
2126
) {
22-
assertNotUsingTsSolutionSetup(tree, 'esbuild', 'configuration');
23-
2427
const task = await esbuildInitGenerator(tree, {
2528
...options,
2629
skipFormat: true,
2730
});
2831
options.buildTarget ??= 'build';
32+
const isTsSolutionSetup = isUsingTsSolutionSetup(tree);
2933
checkForTargetConflicts(tree, options);
30-
addBuildTarget(tree, options);
34+
addBuildTarget(tree, options, isTsSolutionSetup);
35+
updatePackageJson(tree, options, isTsSolutionSetup);
3136
await formatFiles(tree);
3237
return task;
3338
}
@@ -42,51 +47,56 @@ function checkForTargetConflicts(tree: Tree, options: EsBuildProjectSchema) {
4247
}
4348
}
4449

45-
function addBuildTarget(tree: Tree, options: EsBuildProjectSchema) {
50+
function addBuildTarget(
51+
tree: Tree,
52+
options: EsBuildProjectSchema,
53+
isTsSolutionSetup: boolean
54+
) {
4655
addBuildTargetDefaults(tree, '@nx/esbuild:esbuild', options.buildTarget);
4756
const project = readProjectConfiguration(tree, options.project);
48-
const packageJsonPath = joinPathFragments(project.root, 'package.json');
49-
50-
if (!tree.exists(packageJsonPath)) {
51-
const importPath =
52-
options.importPath || getImportPath(tree, options.project);
53-
writeJson(tree, packageJsonPath, {
54-
name: importPath,
55-
version: '0.0.1',
56-
});
57-
}
5857

5958
const prevBuildOptions = project.targets?.[options.buildTarget]?.options;
60-
6159
const tsConfig = prevBuildOptions?.tsConfig ?? getTsConfigFile(tree, options);
6260

61+
let outputPath = prevBuildOptions?.outputPath;
62+
if (!outputPath) {
63+
outputPath = isTsSolutionSetup
64+
? joinPathFragments(project.root, 'dist')
65+
: joinPathFragments(
66+
'dist',
67+
project.root === '.' ? options.project : project.root
68+
);
69+
}
70+
6371
const buildOptions: EsBuildExecutorOptions = {
6472
main: prevBuildOptions?.main ?? getMainFile(tree, options),
65-
outputPath:
66-
prevBuildOptions?.outputPath ??
67-
joinPathFragments(
68-
'dist',
69-
project.root === '.' ? options.project : project.root
70-
),
73+
outputPath,
7174
outputFileName: 'main.js',
7275
tsConfig,
73-
assets: [],
7476
platform: options.platform,
77+
format: options.format,
7578
};
7679

77-
if (options.platform === 'browser') {
78-
buildOptions.outputHashing = 'all';
79-
buildOptions.minify = true;
80-
}
80+
if (isTsSolutionSetup) {
81+
buildOptions.declarationRootDir =
82+
project.sourceRoot ?? tree.exists(`${project.root}/src`)
83+
? `${project.root}/src`
84+
: project.root;
85+
} else {
86+
buildOptions.assets = [];
8187

82-
if (tree.exists(joinPathFragments(project.root, 'README.md'))) {
83-
buildOptions.assets = [
84-
{
88+
if (tree.exists(joinPathFragments(project.root, 'README.md'))) {
89+
buildOptions.assets.push({
8590
glob: `${project.root}/README.md`,
8691
input: '.',
8792
output: '.',
88-
},
89-
];
93+
});
94+
}
95+
}
96+
97+
if (options.platform === 'browser') {
98+
buildOptions.outputHashing = 'all';
99+
buildOptions.minify = true;
90100
}
91101

92102
updateProjectConfiguration(tree, options.project, {
@@ -111,6 +121,89 @@ function addBuildTarget(tree: Tree, options: EsBuildProjectSchema) {
111121
});
112122
}
113123

124+
function updatePackageJson(
125+
tree: Tree,
126+
options: EsBuildProjectSchema,
127+
isTsSolutionSetup: boolean
128+
) {
129+
const project = readProjectConfiguration(tree, options.project);
130+
131+
const packageJsonPath = join(project.root, 'package.json');
132+
let packageJson: PackageJson;
133+
if (tree.exists(packageJsonPath)) {
134+
if (!isTsSolutionSetup) {
135+
return;
136+
}
137+
138+
packageJson = readJson(tree, packageJsonPath);
139+
} else {
140+
packageJson = {
141+
name: getImportPath(tree, options.project),
142+
version: '0.0.1',
143+
};
144+
}
145+
146+
if (isTsSolutionSetup) {
147+
const nxJson = readNxJson(tree);
148+
const projectTarget = project.targets[options.buildTarget];
149+
const mergedTarget = mergeTargetConfigurations(
150+
projectTarget,
151+
(projectTarget.executor
152+
? nxJson.targetDefaults?.[projectTarget.executor]
153+
: undefined) ?? nxJson.targetDefaults?.[options.buildTarget]
154+
);
155+
156+
const {
157+
declarationRootDir = '.',
158+
main,
159+
outputPath,
160+
outputFileName,
161+
// the executor option defaults to [esm]
162+
format = ['esm'],
163+
esbuildOptions,
164+
} = mergedTarget.options;
165+
166+
// can't use the declarationRootDir as rootDir because it only affects the typings,
167+
// not the runtime entry point
168+
packageJson = getUpdatedPackageJsonContent(packageJson, {
169+
main,
170+
outputPath,
171+
projectRoot: project.root,
172+
generateExportsField: true,
173+
packageJsonPath,
174+
format,
175+
outputFileName,
176+
outputFileExtensionForCjs: getOutExtension('cjs', {
177+
// there's very little chance that the user would have defined a custom esbuild config
178+
// since that's an Nx specific file that we're not generating here and we're setting up
179+
// the build for esbuild now
180+
userDefinedBuildOptions: esbuildOptions,
181+
}),
182+
outputFileExtensionForEsm: getOutExtension('esm', {
183+
userDefinedBuildOptions: esbuildOptions,
184+
}),
185+
});
186+
187+
if (declarationRootDir !== dirname(main)) {
188+
// the declaration file entry point will be output to a location
189+
// different than the runtime entry point, adjust accodingly
190+
const outputDir = getOutputDir({
191+
main,
192+
outputPath,
193+
projectRoot: project.root,
194+
packageJsonPath,
195+
rootDir: declarationRootDir,
196+
});
197+
const mainFile = basename(options.main).replace(/\.[tj]s$/, '');
198+
const typingsFile = `${outputDir}${mainFile}.d.ts`;
199+
packageJson.types = typingsFile;
200+
packageJson.exports['.'].types = typingsFile;
201+
}
202+
}
203+
204+
writeJson(tree, packageJsonPath, packageJson);
205+
}
206+
114207
function getMainFile(tree: Tree, options: EsBuildProjectSchema) {
115208
const project = readProjectConfiguration(tree, options.project);
116209
const candidates = [

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { SupportedFormat } from '@nx/js';
2+
13
export interface EsBuildProjectSchema {
24
project: string;
35
main?: string;
@@ -10,4 +12,5 @@ export interface EsBuildProjectSchema {
1012
esbuildConfig?: string;
1113
platform?: 'node' | 'browser' | 'neutral';
1214
buildTarget?: string;
15+
format?: SupportedFormat[];
1316
}

packages/esbuild/src/generators/configuration/schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@
6060
"description": "The build target to add.",
6161
"type": "string",
6262
"default": "build"
63+
},
64+
"format": {
65+
"description": "The format to build the library (esm or cjs).",
66+
"type": "array",
67+
"items": {
68+
"type": "string",
69+
"enum": ["esm", "cjs"]
70+
},
71+
"default": ["esm"]
6372
}
6473
},
6574
"required": [],

0 commit comments

Comments
 (0)