Skip to content

Commit ee1c7b1

Browse files
authored
feat(core): support nx-plugin-openapi for more advanced openapi generation (#589)
1 parent 09c3b4e commit ee1c7b1

File tree

11 files changed

+142
-36
lines changed

11 files changed

+142
-36
lines changed

docs/core/generators/add-swagger-target.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,7 @@ Generates a swagger setup for a given project
2929
### target
3030

3131
- (string): What should the project be called?
32+
33+
### useNxPluginOpenAPI
34+
35+
- (boolean): Should the codegen target use nx-plugin-openapi instead?

docs/core/generators/application.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ Generate a dotnet project under the application directory.
4343
### pathScheme
4444

4545
- (string): Determines if the project should follow NX or dotnet path naming conventions
46+
47+
### useNxPluginOpenAPI
48+
49+
- (boolean): If using a codgen project, use openapi-generator

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@docusaurus/theme-search-algolia": "^2.1.0",
2424
"@mdx-js/react": "^1.6.21",
2525
"@swc/helpers": "~0.3.3",
26+
"@trumbitta/nx-plugin-openapi": "^1.12.1",
2627
"@types/xmldoc": "^1.1.6",
2728
"chokidar": "^3.5.2",
2829
"clsx": "^1.1.1",
Lines changed: 113 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import {
22
addProjectConfiguration,
3+
ensurePackage,
4+
GeneratorCallback,
35
getWorkspaceLayout,
46
joinPathFragments,
57
ProjectConfiguration,
68
readProjectConfiguration,
9+
readWorkspaceConfiguration,
710
Tree,
811
updateProjectConfiguration,
12+
updateWorkspaceConfiguration,
913
} from '@nrwl/devkit';
1014
import { libraryGenerator } from '@nrwl/js/src/generators/library/library';
1115

@@ -16,6 +20,7 @@ export default async function generateSwaggerSetup(
1620
host: Tree,
1721
options: AddSwaggerJsonExecutorSchema,
1822
) {
23+
const tasks: GeneratorCallback[] = [];
1924
const project = readProjectConfiguration(host, options.project);
2025
project.targets ??= {};
2126
if (!options.output) {
@@ -25,6 +30,7 @@ export default async function generateSwaggerSetup(
2530
'swagger.json',
2631
);
2732
generateShellProject(host, {
33+
...options,
2834
swaggerProject: options.swaggerProject,
2935
project: options.project,
3036
codegenProject: options.codegenProject,
@@ -33,19 +39,14 @@ export default async function generateSwaggerSetup(
3339
throw new Error('Either specify --output or --swagger-project');
3440
}
3541
} else {
36-
if (options.codegenProject) {
42+
if (options.codegenProject && !options.useNxPluginOpenAPI) {
3743
project.targets.codegen = {
3844
executor: '@nx-dotnet/core:openapi-codegen',
3945
options: {
4046
openapiJsonPath: options.output,
4147
outputProject: options.codegenProject,
4248
},
43-
dependsOn: [
44-
{
45-
target: options.target || 'swagger',
46-
projects: 'self',
47-
},
48-
],
49+
dependsOn: ['swagger'],
4950
};
5051
}
5152
}
@@ -54,22 +55,16 @@ export default async function generateSwaggerSetup(
5455
};
5556

5657
if (options.codegenProject) {
57-
await libraryGenerator(host, {
58-
name: options.codegenProject,
59-
directory: 'generated',
60-
buildable: true,
61-
});
62-
const codegenProjectConfiguration = readProjectConfiguration(
63-
host,
64-
`generated-${options.codegenProject}`,
65-
);
66-
codegenProjectConfiguration.implicitDependencies ??= [];
67-
codegenProjectConfiguration.implicitDependencies.push(
68-
options.swaggerProject ? options.swaggerProject : options.project,
69-
);
58+
tasks.push(...(await generateCodegenProject(host, options)));
7059
}
7160

7261
updateProjectConfiguration(host, options.project, project);
62+
63+
return async () => {
64+
for (const task of tasks) {
65+
await task();
66+
}
67+
};
7368
}
7469

7570
function swaggerProjectRoot(host: Tree, swaggerProject: string) {
@@ -82,7 +77,7 @@ function swaggerProjectRoot(host: Tree, swaggerProject: string) {
8277

8378
function generateShellProject(
8479
host: Tree,
85-
options: { project: string; swaggerProject: string; codegenProject?: string },
80+
options: AddSwaggerJsonExecutorSchema & { swaggerProject: string },
8681
) {
8782
const root = swaggerProjectRoot(host, options.swaggerProject);
8883
const targets: ProjectConfiguration['targets'] = {};
@@ -94,26 +89,108 @@ function generateShellProject(
9489
executor: 'nx:noop',
9590
outputs: [root],
9691
};
97-
targets.codegen = {
98-
executor: '@nx-dotnet/core:openapi-codegen',
99-
options: {
100-
openapiJsonPath: `${swaggerProjectRoot(
101-
host,
102-
options.swaggerProject,
103-
)}/swagger.json`,
104-
outputProject: `generated-${options.codegenProject}`,
105-
},
106-
dependsOn: [
107-
{
108-
projects: 'dependencies',
109-
target: 'swagger',
92+
if (!options.useNxPluginOpenAPI) {
93+
targets.codegen = {
94+
executor: '@nx-dotnet/core:openapi-codegen',
95+
options: {
96+
openapiJsonPath: `${swaggerProjectRoot(
97+
host,
98+
options.swaggerProject,
99+
)}/swagger.json`,
100+
outputProject: `generated-${options.codegenProject}`,
110101
},
111-
],
112-
};
102+
dependsOn: ['^swagger'],
103+
};
104+
}
113105
}
114106
addProjectConfiguration(host, options.swaggerProject, {
115107
root,
116108
targets,
117109
implicitDependencies: [options.project],
118110
});
119111
}
112+
113+
async function generateCodegenProject(
114+
host: Tree,
115+
options: AddSwaggerJsonExecutorSchema,
116+
): Promise<GeneratorCallback[]> {
117+
const tasks: GeneratorCallback[] = [];
118+
const nameWithDirectory = `generated-${options.codegenProject}`;
119+
if (options.useNxPluginOpenAPI) {
120+
ensurePackage(host, '@trumbitta/nx-plugin-openapi', '^1.12.1');
121+
const {
122+
default: nxPluginOpenAPIGenerator,
123+
}: // eslint-disable-next-line @typescript-eslint/no-var-requires
124+
typeof import('@trumbitta/nx-plugin-openapi/src/generators/api-lib/generator') = require('@trumbitta/nx-plugin-openapi/src/generators/api-lib/generator');
125+
const {
126+
default: nxPluginOpenAPIInitGenerator,
127+
}: // eslint-disable-next-line @typescript-eslint/no-var-requires
128+
typeof import('@trumbitta/nx-plugin-openapi/src/generators/init/generator') = require('@trumbitta/nx-plugin-openapi/src/generators/init/generator');
129+
130+
tasks.push(await nxPluginOpenAPIInitGenerator(host));
131+
132+
tasks.push(
133+
await nxPluginOpenAPIGenerator(host, {
134+
isRemoteSpec: false,
135+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
136+
name: options.codegenProject!,
137+
directory: 'generated',
138+
generator: 'typescript-fetch',
139+
sourceSpecLib: options.swaggerProject,
140+
}),
141+
);
142+
143+
const configuration = readProjectConfiguration(host, nameWithDirectory);
144+
configuration.targets ??= {};
145+
const targetConfiguration = configuration.targets?.['generate-sources'];
146+
targetConfiguration.options['sourceSpecPathOrUrl'] = joinPathFragments(
147+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
148+
readProjectConfiguration(host, options.swaggerProject!).root,
149+
'swagger.json',
150+
);
151+
targetConfiguration.dependsOn = ['^swagger'];
152+
configuration.targets['codegen'] = targetConfiguration;
153+
delete configuration.targets['generate-sources'];
154+
updateProjectConfiguration(host, nameWithDirectory, configuration);
155+
} else {
156+
tasks.push(
157+
await libraryGenerator(host, {
158+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
159+
name: options.codegenProject!,
160+
directory: 'generated',
161+
buildable: true,
162+
}),
163+
);
164+
const codegenProjectConfiguration = readProjectConfiguration(
165+
host,
166+
nameWithDirectory,
167+
);
168+
codegenProjectConfiguration.implicitDependencies ??= [];
169+
codegenProjectConfiguration.implicitDependencies.push(
170+
options.swaggerProject ? options.swaggerProject : options.project,
171+
);
172+
updateProjectConfiguration(
173+
host,
174+
nameWithDirectory,
175+
codegenProjectConfiguration,
176+
);
177+
}
178+
179+
const wc = readWorkspaceConfiguration(host);
180+
181+
const cacheableOperations: string[] | null =
182+
wc.tasksRunnerOptions?.default?.options?.cacheableOperations;
183+
if (cacheableOperations) {
184+
cacheableOperations.push('codegen', options.target ?? 'swagger');
185+
}
186+
187+
const newBuildDeps = ['codegen', '^codegen'];
188+
wc.targetDefaults ??= {};
189+
wc.targetDefaults['build'] ??= {};
190+
wc.targetDefaults['build'].dependsOn ??= [];
191+
wc.targetDefaults['build'].dependsOn.push(...newBuildDeps);
192+
193+
updateWorkspaceConfiguration(host, wc);
194+
195+
return tasks;
196+
}

packages/core/src/generators/add-swagger-target/schema.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export type AddSwaggerJsonExecutorSchema = {
66
target?: string;
77
swaggerProject?: string;
88
codegenProject?: string;
9+
useNxPluginOpenAPI?: boolean;
910
};

packages/core/src/generators/add-swagger-target/schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
"type": "string",
3232
"description": "What should the project be called?",
3333
"default": "swagger"
34+
},
35+
"useNxPluginOpenAPI": {
36+
"type": "boolean",
37+
"description": "Should the codegen target use nx-plugin-openapi instead?",
38+
"default": "false"
3439
}
3540
},
3641
"required": ["project"]

packages/core/src/generators/app/schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@
8686
{ "value": "dotnet", "label": "Dotnet naming conventions" }
8787
]
8888
}
89+
},
90+
"useNxPluginOpenAPI": {
91+
"type": "boolean",
92+
"description": "If using a codgen project, use openapi-generator"
8993
}
9094
},
9195
"required": ["name", "language"]

packages/core/src/generators/utils/generate-project.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ export async function GenerateProject(
228228
project: normalizedOptions.projectName,
229229
swaggerProject: `${normalizedOptions.projectName}-swagger`,
230230
codegenProject: `${normalizedOptions.projectName}-types`,
231+
useNxPluginOpenAPI: normalizedOptions.useNxPluginOpenAPI,
231232
});
232233
}
233234

packages/core/src/models/project-generator-schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ export interface NxDotnetProjectGeneratorSchema {
1616
solutionFile?: string | boolean;
1717
skipSwaggerLib: boolean;
1818
pathScheme: 'nx' | 'dotnet';
19+
useNxPluginOpenAPI?: boolean;
1920
}

packages/core/src/models/swagger-executor-configuration.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export function getSwaggerExecutorConfiguration(
1414
options: {
1515
output: outputDirectory,
1616
},
17+
dependsOn: ['build'],
1718
};
1819
}
1920

0 commit comments

Comments
 (0)