Skip to content

Commit 05a675d

Browse files
committed
feat(nx-plugin): add base tsconfig options for improved configuration management
- Introduce `baseTsConfigName` and `baseTsConfigPath` options in the OpenAPI client generator and update executor schemas. - Enhance README documentation to reflect new options and their usage. - Implement `getBaseTsConfigPath` utility to resolve tsconfig paths based on provided options. - Update tests to cover new tsconfig path handling logic.
1 parent 9b2abc4 commit 05a675d

File tree

10 files changed

+330
-18
lines changed

10 files changed

+330
-18
lines changed

packages/nx-plugin/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Run in interactive mode `nx g @hey-api/nx-plugin:openapi-client`
3232
The defaults tags will not be added to the project if you specify this option.
3333
- `plugins`: Additional plugins to provide to the client api. [ string[] ] (optional)
3434
- `test`: The type of tests to setup. [ 'none' | 'vitest' ] (optional) (default: `none`)
35+
- `baseTsConfigName`: The name of the base tsconfig file that contains the compiler paths used to resolve the imports. Use this if the base tsconfig file is in the workspace root. If provided with a baseTsConfigPath then the baseTsConfigName will be added to the path. Do not use this if the baseTsConfigPath is a file. [ string ] (optional)
36+
- `baseTsConfigPath`: The path to the base tsconfig file that contains the compiler paths used to resolve the imports. Use this if the base tsconfig file is not in the workspace root. This can be a file or a directory. If it is a directory and the baseTsConfigName is provided then the baseTsConfigName will be added to the path. If it is a file and the baseTsConfigName is provided then there will be an error. [ string ] (optional)
3537

3638
##### Example
3739

@@ -46,6 +48,8 @@ nx g @hey-api/nx-plugin:openapi-client --name=my-api --client=@hey-api/client-fe
4648
This executor updates the OpenAPI spec file and generates a new client.
4749
The options for the executor will be populated from the generator.
4850

51+
No need to add them yourself, to modify the options manually edit the `project.json` of the generated project.
52+
4953
Run `nx run @my-org/my-generated-package:updateApi`
5054

5155
##### Options

packages/nx-plugin/src/executors/update-api/updateApi.schema.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,16 @@
3939
"description": "The plugins to be provided to @hey-api/openapi-ts",
4040
"default": [],
4141
"items": {
42-
"type": "string"
42+
"oneOf": [
43+
{ "type": "string" },
44+
{
45+
"type": "object",
46+
"properties": {
47+
"asClass": { "type": "boolean" },
48+
"name": { "type": "string" }
49+
}
50+
}
51+
]
4352
}
4453
},
4554
"force": {

packages/nx-plugin/src/executors/update-api/updateApi.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ import { cp, readFile, rm } from 'node:fs/promises';
33
import { join } from 'node:path';
44

55
import type { PromiseExecutor } from '@nx/devkit';
6-
import { logger, names } from '@nx/devkit';
7-
import { format } from 'prettier';
6+
import { logger, names, workspaceRoot } from '@nx/devkit';
7+
import {
8+
format,
9+
type Options as PrettierOptions,
10+
resolveConfig,
11+
} from 'prettier';
812

913
import {
1014
bundleAndDereferenceSpecFile,
@@ -201,6 +205,13 @@ const runExecutor: PromiseExecutor<UpdateApiExecutorSchema> = async (
201205
const apiDirectoryExists = existsSync(absoluteApiDirectory);
202206
const existingSpecFileExists = existsSync(absoluteExistingSpecPath);
203207

208+
const prettierConfig = await resolveConfig(workspaceRoot, {
209+
editorconfig: true,
210+
});
211+
const prettierOptions: PrettierOptions = {
212+
...prettierConfig,
213+
};
214+
204215
// Copy new spec to project
205216
if (apiDirectoryExists) {
206217
if (existingSpecFileExists) {
@@ -209,8 +220,10 @@ const runExecutor: PromiseExecutor<UpdateApiExecutorSchema> = async (
209220
logger.debug('No existing spec file found. Creating...');
210221
}
211222
const formattedSpec = await format(newSpecString, {
212-
parser: 'yaml',
223+
...prettierOptions,
224+
filepath: absoluteTempSpecPath,
213225
});
226+
214227
writeFileSync(absoluteExistingSpecPath, formattedSpec);
215228
logger.debug(`Spec file updated successfully`);
216229
} else {
@@ -253,7 +266,7 @@ const runExecutor: PromiseExecutor<UpdateApiExecutorSchema> = async (
253266
});
254267

255268
logger.debug('Formatting generated directory...');
256-
await formatFiles(absoluteProjectGeneratedDir);
269+
await formatFiles(absoluteProjectGeneratedDir, prettierOptions);
257270

258271
logger.info('Successfully updated API client and spec files.');
259272
await cleanup(absoluteTempFolder);

packages/nx-plugin/src/generators/openapi-client/files/tsconfig.json.template

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends": "../../tsconfig.base.json",
2+
"extends": "<%= pathToTsConfig %>/<%= tsConfigName %>",
33
"compilerOptions": {
44
},
55
"files": [],

packages/nx-plugin/src/generators/openapi-client/openapiClient.schema.json

+8
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@
6666
"type": "boolean",
6767
"description": "** not working ** Whether to use the class style for the generated code, defaults to `false`",
6868
"default": false
69+
},
70+
"baseTsConfigName": {
71+
"type": "string",
72+
"description": "The name of the base tsconfig file that contains the compiler paths used to resolve the imports, use this if the base tsconfig file is in the workspace root, if provided with a baseTsConfigPath then the baseTsConfigName will be added to the path. DO not use this if the baseTsConfigPath is a file."
73+
},
74+
"baseTsConfigPath": {
75+
"type": "string",
76+
"description": "The path to the base tsconfig file that contains the compiler paths used to resolve the imports, use this if the base tsconfig file is not in the workspace root. This can be a file or a directory. If it is a directory and the baseTsConfigName is provided then the baseTsConfigName will be added to the path. If it is a file and the baseTsConfigName is provided then there will be an error."
6977
}
7078
},
7179
"required": ["name", "spec", "scope"]

packages/nx-plugin/src/generators/openapi-client/openapiClient.spec.ts

+4
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ describe('openapi-client generator', () => {
7777

7878
expect(normalized).toEqual({
7979
clientType: '@hey-api/client-fetch',
80+
baseTsConfigName: undefined,
8081
isPrivate: true,
82+
baseTsConfigPath: undefined,
8183
plugins: ['@hey-api/typescript', '@hey-api/sdk'],
8284
projectDirectory: `${tempDirectory}/test-api-${uuid}`,
8385
projectName: 'test-api',
@@ -106,7 +108,9 @@ describe('openapi-client generator', () => {
106108

107109
expect(normalized).toEqual({
108110
clientType: '@hey-api/client-fetch',
111+
baseTsConfigName: undefined,
109112
isPrivate: true,
113+
baseTsConfigPath: undefined,
110114
plugins: ['@hey-api/typescript', '@hey-api/sdk'],
111115
projectDirectory: 'custom-dir',
112116
projectName: 'test-api',

packages/nx-plugin/src/generators/openapi-client/openapiClient.ts

+36-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
bundleAndDereferenceSpecFile,
2929
generateClientCode,
3030
generateClientCommand,
31+
getBaseTsConfigPath,
3132
getPackageName,
3233
getPluginName,
3334
getVersionOfPackage,
@@ -128,6 +129,18 @@ export interface OpenApiClientGeneratorSchema {
128129
* Whether to use the class style for the generated code, defaults to `false`
129130
*/
130131
asClass?: boolean;
132+
/**
133+
* The name of the base tsconfig file that contains the compiler paths used to resolve the imports, use this if the base tsconfig file is in the workspace root,
134+
* if provided with a baseTsConfigPath then the baseTsConfigName will be added to the path.
135+
* DO not use this if the baseTsConfigPath is a file.
136+
*/
137+
baseTsConfigName?: string;
138+
/**
139+
* The path to the base tsconfig file that contains the compiler paths used to resolve the imports, use this if the base tsconfig file is not in the workspace root.
140+
* This can be a file or a directory. If it is a directory and the baseTsConfigName is provided then the baseTsConfigName will be added to the path.
141+
* If it is a file and the baseTsConfigName is provided then there will be an error.
142+
*/
143+
baseTsConfigPath?: string;
131144
/**
132145
* The client to use for the OpenAPI client
133146
*/
@@ -286,6 +299,8 @@ export default async function (
286299
}
287300

288301
export interface NormalizedOptions {
302+
baseTsConfigName: string | undefined;
303+
baseTsConfigPath: string | undefined;
289304
clientType: string;
290305
isPrivate: boolean;
291306
plugins: Plugin[];
@@ -294,12 +309,16 @@ export interface NormalizedOptions {
294309
projectRoot: string;
295310
projectScope: string;
296311
specFile: string;
312+
test: TestRunner | 'none';
297313
tagArray: string[];
298314
tempFolder: string;
299-
test: TestRunner | 'none';
300315
}
301316

302-
export type GeneratedOptions = NormalizedOptions & typeof CONSTANTS;
317+
export type GeneratedOptions = NormalizedOptions &
318+
typeof CONSTANTS & {
319+
pathToTsConfig: string;
320+
tsConfigName: string;
321+
};
303322

304323
type ProjectConfigurationTargets = NonNullable<ProjectConfiguration['targets']>;
305324
type ValueType<T extends Record<string, any>> = T[keyof T];
@@ -335,7 +354,6 @@ export function normalizeOptions(
335354
const tempFolder =
336355
options.tempFolderDir ?? join(defaultTempFolder, projectName);
337356
const [default1, default2, ...rest] = defaultPlugins;
338-
logger.debug('As Class', options.asClass);
339357
const plugins = [
340358
default1,
341359
options.asClass
@@ -350,7 +368,9 @@ export function normalizeOptions(
350368

351369
return {
352370
clientType: options.client,
371+
baseTsConfigName: options.baseTsConfigName,
353372
isPrivate: options.private ?? true,
373+
baseTsConfigPath: options.baseTsConfigPath,
354374
plugins,
355375
projectDirectory,
356376
projectName,
@@ -413,6 +433,8 @@ export async function generateNxProject({
413433
}) {
414434
logger.debug(`Generating Nx project...`);
415435
const {
436+
baseTsConfigName,
437+
baseTsConfigPath,
416438
clientType,
417439
plugins,
418440
projectName,
@@ -476,6 +498,12 @@ export async function generateNxProject({
476498
);
477499
}
478500

501+
const { tsConfigDirectory, tsConfigName } = await getBaseTsConfigPath({
502+
baseTsConfigName,
503+
baseTsConfigPath,
504+
projectRoot,
505+
});
506+
479507
// Create basic project structure
480508
addProjectConfiguration(tree, `${projectScope}/${projectName}`, {
481509
implicitDependencies: dependsOnProject ? [dependsOnProject] : [],
@@ -536,10 +564,15 @@ export async function generateNxProject({
536564
},
537565
});
538566

567+
/**
568+
* The variables that are passed to the template files
569+
*/
539570
const generatedOptions: GeneratedOptions = {
540571
...normalizedOptions,
541572
...CONSTANTS,
573+
pathToTsConfig: tsConfigDirectory,
542574
plugins: plugins.map(getPluginName),
575+
tsConfigName,
543576
};
544577

545578
// Create directory structure

packages/nx-plugin/src/generators/openapi-client/tests/vitest/tsconfig.spec.json.template

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends": "../../tsconfig.base.json",
2+
"extends": "<%= pathToTsConfig %>/<%= tsConfigName %>",
33
"compilerOptions": {
44
"outDir": "./out-tsc/vitest",
55
"types": [

0 commit comments

Comments
 (0)