Skip to content

Commit e5b239b

Browse files
committed
feat(nx-plugin): update to support NX 21 continuous pipelines
1 parent bee81bb commit e5b239b

File tree

16 files changed

+2411
-2596
lines changed

16 files changed

+2411
-2596
lines changed

packages/nx-plugin/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@
4848
"executors": "./executors.json",
4949
"generators": "./generators.json",
5050
"dependencies": {
51-
"@hey-api/json-schema-ref-parser": "1.0.5",
51+
"@hey-api/json-schema-ref-parser": "1.0.6",
5252
"@hey-api/openapi-ts": "workspace:*",
53-
"@nx/devkit": "20.8.1",
53+
"@nx/devkit": "21.0.3",
5454
"api-smart-diff": "^1.0.6",
5555
"latest-version": "9.0.0",
5656
"swagger2openapi": "^7.0.8",

packages/nx-plugin/src/executors/update-api/schema.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ export interface UpdateApiExecutorSchema {
1515
* Temporary folder used to store files, only change for testing
1616
*/
1717
tempFolder?: string;
18+
watch?: boolean;
1819
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@
5555
"type": "boolean",
5656
"description": "If true, the Client code will be regenerated even if the spec has not changed, also pass --skip-nx-cache to avoid caching issues",
5757
"default": false
58+
},
59+
"watch": {
60+
"type": "boolean",
61+
"description": "If true, the client will be watched for changes and regenerated when they occur",
62+
"default": false
5863
}
5964
},
6065
"required": ["spec", "scope", "name"]

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

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { existsSync, writeFileSync } from 'node:fs';
2-
import { cp, readFile, rm } from 'node:fs/promises';
2+
import {
3+
cp,
4+
readFile,
5+
rm,
6+
watch as fileWatch,
7+
writeFile,
8+
} from 'node:fs/promises';
39
import { join } from 'node:path';
410

511
import type { PromiseExecutor } from '@nx/devkit';
@@ -15,6 +21,7 @@ import {
1521
compareSpecs,
1622
formatFiles,
1723
generateClientCode,
24+
isUrl,
1825
makeDir,
1926
} from '../../utils';
2027
import { CONSTANTS } from '../../vars';
@@ -130,12 +137,41 @@ async function setup({
130137
};
131138
}
132139

133-
const runExecutor: PromiseExecutor<UpdateApiExecutorSchema> = async (
140+
const handleWatch: PromiseExecutor<UpdateApiExecutorSchema> = async (
134141
options,
135-
// this is added to stop the CI from complaining about not using the context and to stop the linter from complaining
142+
context,
143+
) => {
144+
// Do not watch spec files if they are URLs
145+
const isSpecFileUrl = isUrl(options.spec);
146+
if (isSpecFileUrl) {
147+
logger.error('Spec file is a url.');
148+
throw new Error('Spec file is a url, not watching.');
149+
}
150+
151+
const { watch, ...rest } = options;
152+
if (!watch) {
153+
return { success: false };
154+
}
155+
logger.info(`Watching spec file ${options.spec} for changes...`);
156+
const watcher = fileWatch(rest.spec);
136157
// eslint-disable-next-line @typescript-eslint/no-unused-vars
137-
_context,
158+
for await (const _ of watcher) {
159+
logger.info(`Spec file ${options.spec} has changed, updating...`);
160+
// do not pass the watch flag to the runExecutor as it will cause an infinite loop
161+
await runExecutor(rest, context);
162+
logger.info(`Spec file ${options.spec} updated successfully.`);
163+
}
164+
return { success: true };
165+
};
166+
167+
const runExecutor: PromiseExecutor<UpdateApiExecutorSchema> = async (
168+
options,
169+
context,
138170
) => {
171+
if (options.watch) {
172+
return handleWatch(options, context);
173+
}
174+
139175
const tempFolder =
140176
// use the provided temp folder or use the default temp folder and append the project name to it
141177
// we append the project name to the temp folder to avoid conflicts between different projects using the same temp folder
@@ -224,7 +260,7 @@ const runExecutor: PromiseExecutor<UpdateApiExecutorSchema> = async (
224260
filepath: absoluteTempSpecPath,
225261
});
226262

227-
writeFileSync(absoluteExistingSpecPath, formattedSpec);
263+
await writeFile(absoluteExistingSpecPath, formattedSpec);
228264
logger.debug(`Spec file updated successfully`);
229265
} else {
230266
logger.error(

packages/nx-plugin/src/generators/openapi-client/README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,22 @@ The generator creates a new library project with the following structure:
5050
libs/<name>/
5151
├── api/
5252
│ └── spec.yaml # Bundled and dereferenced OpenAPI spec file
53-
├── src/
54-
│ ├── generated/ # Generated API client code (not committed to git)
55-
| ├── client.spec.ts # Unit test for the client code
56-
│ ├── index.ts # Exports everything from generated/
57-
| └── rq.ts # Exports tanstack query client code (if @tanstack/react-query is in the plugins array)
5853
├── package.json
5954
├── vitest.config.ts # Vitest configuration (if test is set to 'vitest')
6055
├── README.md
6156
├── project.json # NX project configuration
6257
├── tsconfig.json # root config
6358
├── tsconfig.lib.json # library config
6459
├── tsconfig.spec.json # test config (if test is set to a value other than none)
65-
└── openapi-ts.config.ts # Configuration for @hey-api/openapi-ts
60+
├── openapi-ts.config.ts # Configuration for @hey-api/openapi-ts
61+
└── src/
62+
├── generated/ # Generated API client code (not committed to git)
63+
├── client.spec.ts # Unit test for the client code
64+
├── index.ts # Exports everything from generated/
65+
│ # conditional
66+
├── rq.ts # Exports tanstack query client code (if @tanstack/react-query is in the plugins array)
67+
├── schemas.ts # Exports json schemas (if @hey-api/schemas is in the plugins array)
68+
└── zod.ts # Exports zod schemas (if zod is in the plugins array)
6669
```
6770

6871
## Generating the API Client
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { defineConfig } from '@hey-api/openapi-ts';
22

33
export default defineConfig({
4-
input: '<%= SPEC_DIR_NAME %>/<%= SPEC_FILE_NAME %>',
4+
input: '<%- SPEC_DIR_NAME %>/<%= SPEC_FILE_NAME %>',
55
output: 'src/<%= GENERATED_DIR_NAME %>',
66
plugins: [
7-
'<%= clientType %>',
7+
'<%- clientType %>',
88
<% for(let x = 0; x < plugins.length; x++) { %>
9-
'<%= plugins[x] %>',
9+
<%- plugins[x] %>,
1010
<% } %>
1111
],
1212
});
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"name": "<%= projectScope %>/<%= projectName %>",
2+
"name": "<%= projectScope %>/<%- projectName %>",
33
"version": "0.1.0",
44
"type": "commonjs",
55
"scripts": {
6-
"update": "nx run <%= projectScope %>/<%= projectName %>:updateApi"
6+
"update": "nx run <%- projectScope %>/<%- projectName %>:updateApi"
77
}
88
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends": "<%= pathToTsConfig %>/<%= tsConfigName %>",
2+
"extends": "<%- pathToTsConfig %>/<%- tsConfigName %>",
33
"compilerOptions": {
44
},
55
"files": [],

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@
7474
"baseTsConfigPath": {
7575
"type": "string",
7676
"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."
77+
},
78+
"serveCmdName": {
79+
"type": "string",
80+
"description": "The command name to use to serve the implicit dependencies, defaults to `serve`. This is used to watch the implicit dependencies for changes."
7781
}
7882
},
7983
"required": ["name", "spec", "scope"]

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,12 @@ describe('openapi-client generator', () => {
8181
clientType: '@hey-api/client-fetch',
8282
isPrivate: true,
8383
plugins: ['@hey-api/typescript', '@hey-api/sdk'],
84+
preformInstall: false,
8485
projectDirectory: `${tempDirectory}/test-api-${uuid}`,
8586
projectName: 'test-api',
8687
projectRoot: `${tempDirectory}/test-api-${uuid}/test-api`,
8788
projectScope: '@test-api',
89+
serveCmdName: 'serve',
8890
specFile: specPath,
8991
tagArray: ['api', 'openapi'],
9092
tempFolder: options.tempFolderDir,
@@ -112,10 +114,12 @@ describe('openapi-client generator', () => {
112114
clientType: '@hey-api/client-fetch',
113115
isPrivate: true,
114116
plugins: ['@hey-api/typescript', '@hey-api/sdk'],
117+
preformInstall: false,
115118
projectDirectory: 'custom-dir',
116119
projectName: 'test-api',
117120
projectRoot: 'custom-dir/test-api',
118121
projectScope: '@test-api',
122+
serveCmdName: 'serve',
119123
specFile: specPath,
120124
tagArray: ['custom', 'tags'],
121125
tempFolder: options.tempFolderDir,

0 commit comments

Comments
 (0)