Skip to content

Commit 914a287

Browse files
committed
feat(nx-plugin): add support for different clients and plugins
1 parent 1de9464 commit 914a287

File tree

11 files changed

+446
-83
lines changed

11 files changed

+446
-83
lines changed

Diff for: packages/nx-plugin/README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Run `nx g @hey-api/nx-plugin:openapi-client`
1616
- `scope`: The scope of the project. (required)
1717
- `spec`: The path to the OpenAPI spec file. (required)
1818
- `directory`: The directory to create the project in. (optional) (default: `libs`)
19-
- `client`: The type of client to generate. (optional) (default: `fetch`)
19+
- `client`: The type of client to generate. (optional) (default: `@hey-api/client-fetch`)
2020
- `tags`: The tags to add to the project. (optional) (default: `api,openapi`)
2121

2222
#### Example
@@ -37,7 +37,8 @@ Run `nx run @my-org/my-generated-package:updateApi`
3737
#### Options
3838

3939
- `spec`: The path to the OpenAPI spec file. (required)
40-
- `client`: The type of client to generate. (optional) (default: `fetch`)
41-
- `directory`: The directory to create the project in. (optional) (default: `libs`)
4240
- `name`: The name of the project. (required)
4341
- `scope`: The scope of the project. (required)
42+
- `client`: The type of client to generate. (optional) (default: `@hey-api/client-fetch`)
43+
- `directory`: The directory to create the project in. (optional) (default: `libs`)
44+
- `plugins`: Additional plugins to provide to the client api. (optional) (default:[])

Diff for: packages/nx-plugin/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"swagger"
2626
],
2727
"private": true,
28-
"type": "module",
28+
"type": "commonjs",
2929
"main": "./dist/index.js",
3030
"module": "./dist/index.js",
3131
"types": "./dist/index.d.ts",

Diff for: packages/nx-plugin/src/executors/update-api/index.spec.ts

+50-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
import type { ExecutorContext } from '@nx/devkit';
2-
import { describe, expect, it } from 'vitest';
2+
import { existsSync } from 'fs';
3+
import { mkdir, rm, writeFile } from 'fs/promises';
4+
import { join } from 'path';
5+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
36

47
import executor from '.';
58
import type { UpdateApiExecutorSchema } from './schema';
69

710
const options: UpdateApiExecutorSchema = {
811
client: 'fetch',
9-
directory: 'libs',
12+
// don't use tmp, as it is used internally in the lib code for temp files
13+
directory: 'temp',
1014
name: 'my-api',
1115
plugins: [],
1216
scope: '@my-org',
1317
spec: '',
1418
};
19+
1520
const context: ExecutorContext = {
1621
cwd: process.cwd(),
1722
isVerbose: false,
@@ -21,13 +26,55 @@ const context: ExecutorContext = {
2126
nodes: {},
2227
},
2328
projectsConfigurations: {
24-
projects: {},
29+
projects: {
30+
'my-api': {
31+
root: 'libs/my-api',
32+
targets: {},
33+
},
34+
},
2535
version: 2,
2636
},
2737
root: '',
2838
};
2939

3040
describe('UpdateApi Executor', () => {
41+
beforeEach(async () => {
42+
// Create the API directory and spec file
43+
const apiDir = join(process.cwd(), options.directory, options.name, 'api');
44+
if (!existsSync(apiDir)) {
45+
await mkdir(apiDir, { recursive: true });
46+
}
47+
const specPath = join(apiDir, 'spec.yaml');
48+
const validSpec = `
49+
openapi: 3.0.0
50+
info:
51+
title: Test API
52+
version: 1.0.0
53+
paths:
54+
/test:
55+
get:
56+
responses:
57+
'200':
58+
description: OK
59+
content:
60+
application/json:
61+
schema:
62+
type: object
63+
properties:
64+
message:
65+
type: string
66+
`;
67+
await writeFile(specPath, validSpec);
68+
options.spec = specPath;
69+
});
70+
71+
afterEach(async () => {
72+
const apiDir = join(process.cwd(), options.directory);
73+
if (existsSync(apiDir)) {
74+
await rm(apiDir, { force: true, recursive: true });
75+
}
76+
});
77+
3178
it('can run', async () => {
3279
const output = await executor(options, context);
3380
expect(output.success).toBe(true);

Diff for: packages/nx-plugin/src/executors/update-api/schema.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222
"client": {
2323
"type": "string",
2424
"description": "The type of client to generate",
25-
"x-prompt": "What is the type of client to generate? (fetch, axios)",
26-
"default": "fetch",
27-
"enum": ["fetch", "axios"]
25+
"x-prompt": "What is the type of client to generate? (@hey-api/client-fetch, @hey-api/client-axios)",
26+
"default": "@hey-api/client-fetch"
2827
},
2928
"scope": {
3029
"type": "string",

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,6 @@ The generator also uses `@redocly/cli` for bundling the spec file.
9797
- [x] Generate the client code
9898
- [x] Setup tests
9999
- [x] Add generator to update the API spec file and regenerate the client code
100-
- [ ] Make plugins configurable and ability to add additional plugins
100+
- [x] Make plugins configurable and ability to add additional plugins
101+
- [x] Support additional client types
101102
- [ ] Support different test frameworks
102-
- [ ] Support additional client types

Diff for: packages/nx-plugin/src/generators/openapi-client/files/package.json.template

+1-7
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,7 @@
99
"types": "./dist/index.d.ts",
1010
"import": "./dist/index.js",
1111
"default": "./dist/index.js"
12-
},
13-
"./rq": {
14-
"development": "./src/rq.ts",
15-
"types": "./dist/rq.d.ts",
16-
"import": "./dist/rq.js",
17-
"default": "./dist/rq.js"
18-
},
12+
}
1913
},
2014
"scripts": {
2115
"update": "nx run <%= projectScope %>/<%= projectName %>:updateApi"

Diff for: packages/nx-plugin/src/generators/openapi-client/index.spec.ts

+103-32
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ describe('openapi-client generator', () => {
6767
let tempSpecPath: string;
6868

6969
const options = {
70-
client: 'fetch' as const,
70+
client: '@hey-api/client-fetch',
7171
directory: './libs',
7272
name: 'test-api',
7373
plugins: [],
@@ -122,10 +122,11 @@ paths:
122122
const normalized = normalizeOptions(options);
123123

124124
expect(normalized).toEqual({
125-
clientType: 'fetch',
126-
projectDirectory: 'test-api',
125+
clientType: '@hey-api/client-fetch',
126+
plugins: [],
127+
projectDirectory: 'libs',
127128
projectName: 'test-api',
128-
projectRoot: 'test-api',
129+
projectRoot: 'libs/test-api',
129130
projectScope: '@test-api',
130131
specFile: tempSpecPath,
131132
tagArray: ['api', 'openapi'],
@@ -142,8 +143,9 @@ paths:
142143
const normalized = normalizeOptions(customOptions);
143144

144145
expect(normalized).toEqual({
145-
clientType: 'fetch',
146-
projectDirectory: 'custom-dir/test-api',
146+
clientType: '@hey-api/client-fetch',
147+
plugins: [],
148+
projectDirectory: 'custom-dir',
147149
projectName: 'test-api',
148150
projectRoot: 'custom-dir/test-api',
149151
projectScope: '@test-api',
@@ -157,7 +159,7 @@ paths:
157159
it('should generate project configuration', () => {
158160
const normalizedOptions = normalizeOptions(options);
159161

160-
generateNxProject({ normalizedOptions, tree });
162+
generateNxProject({ clientPlugins: {}, normalizedOptions, tree });
161163

162164
const config = readJson(
163165
tree,
@@ -166,14 +168,13 @@ paths:
166168
expect(config).toBeDefined();
167169
expect(config.projectType).toBe('library');
168170
expect(config.targets.build).toBeDefined();
169-
expect(config.targets.lint).toBeDefined();
170171
expect(config.targets.generateApi).toBeDefined();
171172
});
172173

173174
it('should generate project files', () => {
174175
const normalizedOptions = normalizeOptions(options);
175176

176-
generateNxProject({ normalizedOptions, tree });
177+
generateNxProject({ clientPlugins: {}, normalizedOptions, tree });
177178

178179
expect(
179180
tree.exists(`${normalizedOptions.projectRoot}/tsconfig.json`),
@@ -223,67 +224,137 @@ paths:
223224
});
224225

225226
describe('updatePackageJson', () => {
226-
it('should update package.json with correct dependencies', () => {
227+
it('should update package.json with correct dependencies', async () => {
227228
const normalizedOptions = normalizeOptions(options);
228-
const { clientType, projectName, projectRoot, projectScope } =
229-
normalizedOptions;
229+
const { projectName, projectRoot, projectScope } = normalizedOptions;
230230

231231
// Create initial package.json
232232
tree.write(
233233
`${projectRoot}/package.json`,
234234
JSON.stringify({
235235
dependencies: {},
236236
devDependencies: {},
237-
name: projectName,
237+
name: `${projectScope}/${projectName}`,
238238
}),
239239
);
240240

241-
updatePackageJson({
242-
clientType,
241+
// Create tsconfig.base.json
242+
tree.write(
243+
'tsconfig.base.json',
244+
JSON.stringify({
245+
compilerOptions: {
246+
paths: {},
247+
},
248+
}),
249+
);
250+
251+
await updatePackageJson({
252+
clientPlugins: {},
253+
clientType: '@hey-api/client-fetch',
243254
projectName,
244255
projectRoot,
245256
projectScope,
246257
tree,
247258
});
248259

260+
// Verify tsconfig.base.json was updated
261+
const tsconfig = readJson(tree, 'tsconfig.base.json');
262+
expect(
263+
tsconfig.compilerOptions.paths[`${projectScope}/${projectName}`],
264+
).toBeDefined();
265+
249266
const packageJson = readJson(tree, `${projectRoot}/package.json`);
250-
expect(packageJson.dependencies['@hey-api/client-fetch']).toBe('^0.9.0');
251-
expect(packageJson.devDependencies['@hey-api/openapi-ts']).toBe(
252-
'^0.66.0',
267+
expect(packageJson.dependencies['@hey-api/client-fetch']).toBeDefined();
268+
});
269+
270+
it('should update package.json with correct dependencies', async () => {
271+
const normalizedOptions = normalizeOptions(options);
272+
const { projectName, projectRoot, projectScope } = normalizedOptions;
273+
274+
// Create initial package.json
275+
tree.write(
276+
`${projectRoot}/package.json`,
277+
JSON.stringify({
278+
dependencies: {},
279+
devDependencies: {},
280+
name: `${projectScope}/${projectName}`,
281+
}),
282+
);
283+
284+
// Create tsconfig.base.json
285+
tree.write(
286+
'tsconfig.base.json',
287+
JSON.stringify({
288+
compilerOptions: {
289+
paths: {},
290+
},
291+
}),
253292
);
293+
294+
await updatePackageJson({
295+
clientPlugins: {
296+
'@tanstack/react-query': {
297+
tsConfigCompilerPaths: {
298+
'my-test-path': './src/index.ts',
299+
},
300+
},
301+
},
302+
clientType: '@hey-api/client-fetch',
303+
projectName,
304+
projectRoot,
305+
projectScope,
306+
tree,
307+
});
308+
309+
// Verify tsconfig.base.json was updated
310+
const tsconfig = readJson(tree, 'tsconfig.base.json');
311+
expect(
312+
tsconfig.compilerOptions.paths[`${projectScope}/${projectName}`],
313+
).toBeDefined();
314+
expect(tsconfig.compilerOptions.paths['my-test-path']).toBeDefined();
254315
});
255316

256-
it('should update package.json with axios dependencies when clientType is axios', () => {
257-
const axiosOptions = {
258-
...options,
259-
client: 'axios' as const,
260-
} satisfies OpenApiClientGeneratorSchema;
261-
const normalizedOptions = normalizeOptions(axiosOptions);
262-
const { clientType, projectName, projectRoot, projectScope } =
263-
normalizedOptions;
317+
it('should update package.json with axios dependencies when clientType is axios', async () => {
318+
const normalizedOptions = normalizeOptions(options);
319+
const { projectName, projectRoot, projectScope } = normalizedOptions;
264320

265321
// Create initial package.json
266322
tree.write(
267323
`${projectRoot}/package.json`,
268324
JSON.stringify({
269325
dependencies: {},
270326
devDependencies: {},
271-
name: projectName,
327+
name: `${projectScope}/${projectName}`,
328+
}),
329+
);
330+
331+
// Create tsconfig.base.json
332+
tree.write(
333+
'tsconfig.base.json',
334+
JSON.stringify({
335+
compilerOptions: {
336+
paths: {},
337+
},
272338
}),
273339
);
274340

275-
updatePackageJson({
276-
clientType,
341+
await updatePackageJson({
342+
clientPlugins: {},
343+
clientType: '@hey-api/client-axios',
277344
projectName,
278345
projectRoot,
279346
projectScope,
280347
tree,
281348
});
282349

350+
// Verify tsconfig.base.json was updated
351+
const tsconfig = readJson(tree, 'tsconfig.base.json');
352+
expect(
353+
tsconfig.compilerOptions.paths[`${projectScope}/${projectName}`],
354+
).toBeDefined();
355+
283356
const packageJson = readJson(tree, `${projectRoot}/package.json`);
284-
expect(packageJson.dependencies['@hey-api/client-axios']).toBeTruthy();
285-
expect(packageJson.dependencies['axios']).toBeTruthy();
286-
expect(packageJson.devDependencies['@hey-api/openapi-ts']).toBeTruthy();
357+
expect(packageJson.dependencies.axios).toBeDefined();
287358
});
288359
});
289360

0 commit comments

Comments
 (0)