Skip to content

Commit 1c8dc40

Browse files
authored
feat: split type definition (#34)
* docs: update comment * feat: update type def generator * test: add split code test
1 parent 631af46 commit 1c8dc40

File tree

8 files changed

+554
-67
lines changed

8 files changed

+554
-67
lines changed

scripts/testCodeGen.ts

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as fs from "fs";
22
import { posix as path } from "path";
33

4-
import { CodeGenerator } from "../lib";
4+
import { CodeGenerator, GeneratorTemplate } from "../lib";
55
import * as Templates from "../lib/templates";
66

77
const writeText = (filename: string, text: string): void => {
@@ -13,7 +13,7 @@ const writeText = (filename: string, text: string): void => {
1313
const generateTypedefCodeOnly = (inputFilename: string, outputFilename: string, isValidate: boolean) => {
1414
const codeGenerator = new CodeGenerator(inputFilename);
1515
if (isValidate) {
16-
codeGenerator.validate({
16+
codeGenerator.validateOpenApiSchema({
1717
logger: { displayLogLines: 1 },
1818
});
1919
}
@@ -29,14 +29,17 @@ const generateTemplateCodeOnly = (
2929
): void => {
3030
const codeGenerator = new CodeGenerator(inputFilename);
3131
if (isValidate) {
32-
codeGenerator.validate({
32+
codeGenerator.validateOpenApiSchema({
3333
logger: { displayLogLines: 1 },
3434
});
3535
}
36-
const code = codeGenerator.generateCode<Templates.ApiClient.Option>({
36+
37+
const apiClientGeneratorTemplate: GeneratorTemplate<Templates.ApiClient.Option> = {
3738
generator: Templates.ApiClient.generator,
3839
option: option,
39-
});
40+
};
41+
42+
const code = codeGenerator.generateCode([apiClientGeneratorTemplate]);
4043

4144
writeText(outputFilename, code);
4245
};
@@ -49,18 +52,53 @@ const generateTypedefWithTemplateCode = (
4952
): void => {
5053
const codeGenerator = new CodeGenerator(inputFilename);
5154
if (isValidate) {
52-
codeGenerator.validate({
55+
codeGenerator.validateOpenApiSchema({
5356
logger: { displayLogLines: 1 },
5457
});
5558
}
56-
const code = codeGenerator.generateTypeDefinition<Templates.ApiClient.Option>({
57-
generator: Templates.ApiClient.generator,
58-
option: option,
59-
});
59+
60+
const code = codeGenerator.generateTypeDefinition([
61+
{
62+
generator: () => {
63+
return codeGenerator.getAdditionalTypeStatements();
64+
},
65+
},
66+
{
67+
generator: Templates.ApiClient.generator,
68+
option: option,
69+
},
70+
]);
6071

6172
writeText(outputFilename, code);
6273
};
6374

75+
const generateSplitCode = (inputFilename: string, outputDir: string) => {
76+
const codeGenerator = new CodeGenerator(inputFilename);
77+
78+
const apiClientGeneratorTemplate: GeneratorTemplate<Templates.ApiClient.Option> = {
79+
generator: Templates.ApiClient.generator,
80+
option: { sync: false },
81+
};
82+
83+
const typeDefCode = codeGenerator.generateTypeDefinition();
84+
const apiClientCode = codeGenerator.generateCode([
85+
{
86+
generator: () => {
87+
return [`import { Schemas } from "./types";`];
88+
},
89+
},
90+
{
91+
generator: () => {
92+
return codeGenerator.getAdditionalTypeStatements();
93+
},
94+
},
95+
apiClientGeneratorTemplate,
96+
]);
97+
98+
writeText(path.join(outputDir, "types.ts"), typeDefCode);
99+
writeText(path.join(outputDir, "apiClient.ts"), apiClientCode);
100+
};
101+
64102
const main = () => {
65103
generateTypedefCodeOnly("test/api.test.domain/index.yml", "test/code/typedef-only/api.test.domain.ts", true);
66104
generateTypedefCodeOnly("test/infer.domain/index.yml", "test/code/typedef-only/infer.domain.ts", false);
@@ -69,9 +107,15 @@ const main = () => {
69107
generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/sync-api.test.domain.ts", true, { sync: true });
70108
generateTemplateCodeOnly("test/infer.domain/index.yml", "test/code/template-only/infer.domain.ts", false, { sync: true });
71109

72-
generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/api.test.domain.ts", true, { sync: false });
73-
generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/sync-api.test.domain.ts", true, { sync: true });
110+
generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/api.test.domain.ts", true, {
111+
sync: false,
112+
});
113+
generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/sync-api.test.domain.ts", true, {
114+
sync: true,
115+
});
74116
generateTypedefWithTemplateCode("test/infer.domain/index.yml", "test/code/typedef-with-template/infer.domain.ts", false, { sync: false });
117+
118+
generateSplitCode("test/api.test.domain/index.yml", "test/code/split");
75119
};
76120

77121
main();

src/index.ts

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { EOL } from "os";
22

3-
import { OpenApiTools, ResolveReference, TsGenerator, Validator, FileSystem } from "./api";
3+
import ts from "typescript";
4+
5+
import * as Api from "./api";
46
import type * as Types from "./types";
57

68
export interface GeneratorTemplate<T> {
@@ -11,43 +13,75 @@ export interface GeneratorTemplate<T> {
1113
export class CodeGenerator {
1214
private rootSchema: Types.OpenApi.Document;
1315
private resolvedReferenceDocument: Types.OpenApi.Document;
14-
private parser: OpenApiTools.Parser;
16+
private parser: Api.OpenApiTools.Parser;
1517
constructor(private readonly entryPoint: string) {
16-
this.rootSchema = FileSystem.loadJsonOrYaml(entryPoint);
17-
this.resolvedReferenceDocument = ResolveReference.resolve(entryPoint, entryPoint, JSON.parse(JSON.stringify(this.rootSchema)));
18+
this.rootSchema = Api.FileSystem.loadJsonOrYaml(entryPoint);
19+
this.resolvedReferenceDocument = Api.ResolveReference.resolve(entryPoint, entryPoint, JSON.parse(JSON.stringify(this.rootSchema)));
1820
this.parser = this.createParser();
1921
}
2022

21-
private createParser(allowOperationIds?: string[]): OpenApiTools.Parser {
22-
return new OpenApiTools.Parser(this.entryPoint, this.rootSchema, this.resolvedReferenceDocument, {
23+
private createParser(allowOperationIds?: string[]): Api.OpenApiTools.Parser {
24+
return new Api.OpenApiTools.Parser(this.entryPoint, this.rootSchema, this.resolvedReferenceDocument, {
2325
allowOperationIds: allowOperationIds,
2426
});
2527
}
2628

27-
public validate(config?: Types.Validator.Configuration) {
28-
if (!config) {
29-
Validator.validate(this.resolvedReferenceDocument);
29+
/**
30+
* Validate the OpenAPI Schema
31+
*/
32+
public validateOpenApiSchema(option?: Types.Validator.Option) {
33+
if (!option) {
34+
Api.Validator.validate(this.resolvedReferenceDocument);
3035
} else {
31-
Validator.validate(this.resolvedReferenceDocument, config.logger);
36+
Api.Validator.validate(this.resolvedReferenceDocument, option.logger);
3237
}
3338
}
3439

35-
public generateTypeDefinition<T = {}>(generatorTemplate?: GeneratorTemplate<T>): string {
40+
/**
41+
* Provides TypeScript typedefs generated from OpenAPI Schema.
42+
*
43+
* @param generatorTemplate Template for when you want to change the code following a type definition
44+
* @returns String of generated code
45+
*/
46+
public generateTypeDefinition(generatorTemplates?: GeneratorTemplate<any>[]): string {
3647
const create = () => {
37-
const statements = this.parser.getTypeDefinitionStatements();
38-
if (generatorTemplate) {
48+
const statements = this.parser.getOpenApiTypeDefinitionStatements();
49+
generatorTemplates?.forEach(generatorTemplate => {
3950
const payload = this.parser.getCodeGeneratorParamsArray();
40-
const extraStatements = TsGenerator.Utils.convertIntermediateCodes(generatorTemplate.generator(payload, generatorTemplate.option));
41-
return statements.concat(extraStatements);
42-
}
51+
const extraStatements = Api.TsGenerator.Utils.convertIntermediateCodes(generatorTemplate.generator(payload, generatorTemplate.option));
52+
statements.push(...extraStatements);
53+
});
4354
return statements;
4455
};
45-
return [OpenApiTools.Comment.generateLeading(this.resolvedReferenceDocument), TsGenerator.generate(create)].join(EOL + EOL + EOL);
56+
return [Api.OpenApiTools.Comment.generateLeading(this.resolvedReferenceDocument), Api.TsGenerator.generate(create)].join(EOL + EOL + EOL);
4657
}
4758

48-
public generateCode<T>(generatorTemplate: GeneratorTemplate<T>): string {
59+
/**
60+
* Generate code using a template
61+
*
62+
* @param generatorTemplate
63+
* @returns String of generated code
64+
*/
65+
public generateCode(generatorTemplates: GeneratorTemplate<any>[]): string {
4966
const payload = this.parser.getCodeGeneratorParamsArray();
50-
const create = () => TsGenerator.Utils.convertIntermediateCodes(generatorTemplate?.generator(payload, generatorTemplate.option));
51-
return [OpenApiTools.Comment.generateLeading(this.resolvedReferenceDocument), TsGenerator.generate(create)].join(EOL + EOL + EOL);
67+
const create = () => {
68+
return generatorTemplates
69+
.map(generatorTemplate => {
70+
return Api.TsGenerator.Utils.convertIntermediateCodes(generatorTemplate?.generator(payload, generatorTemplate.option));
71+
})
72+
.flat();
73+
};
74+
return [Api.OpenApiTools.Comment.generateLeading(this.resolvedReferenceDocument), Api.TsGenerator.generate(create)].join(EOL + EOL + EOL);
75+
}
76+
77+
/**
78+
* Provides parameters extracted from OpenApi Schema
79+
*/
80+
public getCodeGeneratorParamsArray(): Types.CodeGenerator.Params[] {
81+
return this.parser.getCodeGeneratorParamsArray();
82+
}
83+
84+
public getAdditionalTypeStatements(): ts.Statement[] {
85+
return this.parser.getAdditionalTypeStatements();
5286
}
5387
}

src/internal/OpenApiTools/Parser.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,11 @@ export class Parser {
130130
return Extractor.generateCodeGeneratorParamsArray(this.store, this.convertContext, this.option.allowOperationIds);
131131
}
132132

133-
public getTypeDefinitionStatements(): ts.Statement[] {
133+
public getOpenApiTypeDefinitionStatements(): ts.Statement[] {
134134
return this.store.getRootStatements();
135135
}
136+
137+
public getAdditionalTypeStatements(): ts.Statement[] {
138+
return this.store.getAdditionalStatements();
139+
}
136140
}

src/internal/OpenApiTools/store/Store.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import Dot from "dot-prop";
55
import ts from "typescript";
66

77
import type { OpenApi } from "../../../types";
8-
import { Factory } from "../../TsGenerator";
98
import { UnSupportError } from "../../Exception";
9+
import { Factory } from "../../TsGenerator";
1010
import * as Def from "./Definition";
1111
import * as Operation from "./Operation";
1212
import * as State from "./State";
@@ -25,6 +25,7 @@ export interface Type {
2525
hasStatement: (path: string, types: Structure.DataStructure.Kind[]) => boolean;
2626
addAdditionalStatement: (statements: ts.Statement[]) => void;
2727
getRootStatements: () => ts.Statement[];
28+
getAdditionalStatements: () => ts.Statement[];
2829
updateOperationState: (httpMethod: string, requestUri: string, operationId: string, state: Partial<State.OperationState>) => void;
2930
getNoReferenceOperationState: () => Operation.State;
3031
getPathItem: (localPath: string) => OpenApi.PathItem;
@@ -75,7 +76,11 @@ export const create = (factory: Factory.Type, rootDocument: OpenApi.Document): T
7576
}
7677
return statements;
7778
}, []);
78-
return statements.concat(state.additionalStatements);
79+
return statements;
80+
};
81+
82+
const getAdditionalStatements = (): ts.Statement[] => {
83+
return state.additionalStatements;
7984
};
8085

8186
return {
@@ -95,6 +100,7 @@ export const create = (factory: Factory.Type, rootDocument: OpenApi.Document): T
95100
return getChildByPaths(targetPath, kind);
96101
},
97102
getRootStatements,
103+
getAdditionalStatements,
98104
addComponent: (componentName: Def.ComponentName, statement: Structure.ComponentParams): void => {
99105
operator.set(`${componentName}`, Structure.createInstance(statement));
100106
},

src/typedef/Validator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ export interface Logger {
55
*/
66
displayLogLines?: number;
77
}
8-
export interface Configuration {
8+
export interface Option {
99
logger?: Logger;
1010
}

0 commit comments

Comments
 (0)