diff --git a/.dependency-cruiser.js b/.dependency-cruiser.js index b40abf12..4ac156df 100644 --- a/.dependency-cruiser.js +++ b/.dependency-cruiser.js @@ -20,6 +20,16 @@ module.exports = { path: "^src/internal", }, }, + { + name: "Don't dependent on typescript from Walker", + severity: "error", + from: { + path: "^src/internal/OpenApiTools", + }, + to: { + path: "typescript", + }, + }, { name: "not-to-test", comment: diff --git a/.prettierrc.cjs b/.prettierrc.cjs deleted file mode 100644 index d15a357b..00000000 --- a/.prettierrc.cjs +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - printWidth: 144, - trailingComma: "all", - tabWidth: 2, - useTabs: false, - arrowParens: "avoid", - bracketSpacing: true, - singleQuote: false, - semi: true, -}; diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..5adcb5be --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "printWidth": 144, + "trailingComma": "all", + "tabWidth": 2, + "useTabs": false, + "arrowParens": "avoid", + "bracketSpacing": true, + "singleQuote": false, + "semi": true +} diff --git a/package.json b/package.json index c354c829..8d344bb9 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "scripts": { "build": "yarn ts ./scripts/build.ts", "clean": "yarn ts ./scripts/clean.ts", + "generate:test:code": "yarn build && yarn test:code:gen", "format:code": "prettier \"**/*.{js,jsx,ts,tsx,json,yml,yaml,md,html}\" --write", "format:code:eslint": "eslint \"**/*.{ts,tsx}\" --fix", "format:yarn:lock": "yarn-deduplicate yarn.lock --strategy highest", @@ -73,7 +74,7 @@ "node-fetch": "2.6.1" }, "dependencies": { - "@himenon/path-oriented-data-structure": "0.1.3", + "@himenon/path-oriented-data-structure": "0.2.1", "@types/json-schema": "7.0.7", "ajv": "8.0.5", "dot-prop": "6.0.1", diff --git a/scripts/testCodeGen.ts b/scripts/testCodeGen.ts index bd6088d3..a527d3ca 100644 --- a/scripts/testCodeGen.ts +++ b/scripts/testCodeGen.ts @@ -1,13 +1,29 @@ import * as fs from "fs"; import { posix as path } from "path"; +import prettier from "prettier"; + import { CodeGenerator } from "../lib"; import * as Templates from "../lib/templates"; import type * as Types from "../lib/types"; +const prettierConfig = require("../.prettierrc.json"); + +const codeFormat = (code: string): string => { + try { + return prettier.format(code, { + ...prettierConfig, + parser: "babel", + }); + } catch (error) { + console.log("Failed code format"); + return code; + } +}; + const writeText = (filename: string, text: string): void => { fs.mkdirSync(path.dirname(filename), { recursive: true }); - fs.writeFileSync(filename, text, { encoding: "utf-8" }); + fs.writeFileSync(filename, codeFormat(text), { encoding: "utf-8" }); console.log(`Generate Code : ${filename}`); }; @@ -97,26 +113,44 @@ const generateParameter = (inputFilename: string, outputFilename: string) => { writeText(outputFilename, JSON.stringify(codeGenerator.getCodeGeneratorParamsArray(), null, 2)); }; -const main = () => { +const generateAdvancedTestCode = async (inputFilename: string, outputDir: string): Promise => { + const codeGenerator = new CodeGenerator(inputFilename); + + await codeGenerator.init(); + + const typeDefCode = codeGenerator.generateCode([ + { + kind: "advanced", + generator: Templates.TypeDef.generator, + }, + ]); + writeText(path.join(outputDir, "types.ts"), typeDefCode); +}; + +const main = async () => { generateTypedefCodeOnly("test/api.test.domain/index.yml", "test/code/typedef-only/api.test.domain.ts", true); - generateTypedefCodeOnly("test/infer.domain/index.yml", "test/code/typedef-only/infer.domain.ts", false); + await generateAdvancedTestCode("test/api.test.domain/index.yml", "test/code/advanced/api.test.domain.ts"); + // generateTypedefCodeOnly("test/infer.domain/index.yml", "test/code/typedef-only/infer.domain.ts", false); - generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/api.test.domain.ts", true, { sync: false }); - generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/sync-api.test.domain.ts", true, { sync: true }); - generateTemplateCodeOnly("test/infer.domain/index.yml", "test/code/template-only/infer.domain.ts", false, { sync: true }); + // generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/api.test.domain.ts", true, { sync: false }); + // generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/sync-api.test.domain.ts", true, { sync: true }); + // generateTemplateCodeOnly("test/infer.domain/index.yml", "test/code/template-only/infer.domain.ts", false, { sync: true }); - generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/api.test.domain.ts", true, { - sync: false, - }); - generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/sync-api.test.domain.ts", true, { - sync: true, - }); - generateTypedefWithTemplateCode("test/infer.domain/index.yml", "test/code/typedef-with-template/infer.domain.ts", false, { sync: false }); + // generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/api.test.domain.ts", true, { + // sync: false, + // }); + // generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/sync-api.test.domain.ts", true, { + // sync: true, + // }); + // generateTypedefWithTemplateCode("test/infer.domain/index.yml", "test/code/typedef-with-template/infer.domain.ts", false, { sync: false }); - generateSplitCode("test/api.test.domain/index.yml", "test/code/split"); + // generateSplitCode("test/api.test.domain/index.yml", "test/code/split"); - generateParameter("test/api.test.domain/index.yml", "test/code/parameter/api.test.domain.json"); - generateParameter("test/infer.domain/index.yml", "test/code/parameter/infer.domain.json"); + // generateParameter("test/api.test.domain/index.yml", "test/code/parameter/api.test.domain.json"); + // generateParameter("test/infer.domain/index.yml", "test/code/parameter/infer.domain.json"); }; -main(); +main().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/src/api.ts b/src/api.ts index 8f4108cf..23463385 100644 --- a/src/api.ts +++ b/src/api.ts @@ -3,3 +3,4 @@ export * as OpenApiTools from "./internal/OpenApiTools"; export { FileSystem } from "./internal/FileSystem"; export * as ResolveReference from "./internal/ResolveReference"; export * as Validator from "./internal/Validator"; +export * as Reference from "./reference-resolver"; diff --git a/src/code-templates/typedef/StructToTypeNode.ts b/src/code-templates/typedef/StructToTypeNode.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/code-templates/typedef/components/ConvertToolKit.ts b/src/code-templates/typedef/components/ConvertToolKit.ts new file mode 100644 index 00000000..31b8b84b --- /dev/null +++ b/src/code-templates/typedef/components/ConvertToolKit.ts @@ -0,0 +1,14 @@ +import ts from "typescript"; + +import type { OpenApi } from "../../../types"; +import { JsonSchemaToTypeDefinition } from "../../../utils"; + +export interface ConvertToolkit { + generateTypeNode: (schema: OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition) => ts.TypeNode; +} + +const converter = new JsonSchemaToTypeDefinition.Converter(); + +export const ConvertToolkit: ConvertToolkit = { + generateTypeNode: converter.generateTypeNode.bind(converter), +}; diff --git a/src/code-templates/typedef/components/Reference.ts b/src/code-templates/typedef/components/Reference.ts new file mode 100644 index 00000000..717132f8 --- /dev/null +++ b/src/code-templates/typedef/components/Reference.ts @@ -0,0 +1,19 @@ +import ts from "typescript"; + +import { OpenApi } from "../../../types"; +import type { InitializeParams } from "./types"; + +export interface Payload { + entryPoint: string; + currentPoint: string; + schemas: Record; +} + +export class Convert { + constructor(private readonly params: InitializeParams) {} + public generateTypeNode(schema: OpenApi.Reference): ts.TypeNode { + + this.params.accessor.getChildByPaths("", "OpenApiSchema"); + return this.params.toolkit.generateTypeNode(schema); + } +} diff --git a/src/code-templates/typedef/components/Schema.ts b/src/code-templates/typedef/components/Schema.ts new file mode 100644 index 00000000..60360ed1 --- /dev/null +++ b/src/code-templates/typedef/components/Schema.ts @@ -0,0 +1,16 @@ +import ts from "typescript"; + +import { TsGenerator } from "../../../api"; +import { OpenApi } from "../../../types"; +import type { InitializeParams } from "./types"; + +export class Convert { + constructor(private readonly params: InitializeParams) {} + public generateStatement(name: string, schema: OpenApi.Schema | OpenApi.Reference | boolean): ts.Statement { + return TsGenerator.factory.TypeAliasDeclaration.create({ + export: true, + name: name, + type: this.params.toolkit.generateTypeNode(schema), + }); + } +} diff --git a/src/code-templates/typedef/components/Schemas.ts b/src/code-templates/typedef/components/Schemas.ts new file mode 100644 index 00000000..12b52e7e --- /dev/null +++ b/src/code-templates/typedef/components/Schemas.ts @@ -0,0 +1,36 @@ +import type ts from "typescript"; + +import { TsGenerator } from "../../../api"; +import { AbstractStruct, OpenApi } from "../../../types"; +import * as Schema from "./Schema"; +import type { InitializeParams } from "./types"; + +export interface Payload { + entryPoint: string; + currentPoint: string; + schemas: Record; +} + +export class Convert { + private readonly schema = new Schema.Convert(this.params); + constructor(private readonly params: InitializeParams) {} + public generateNamespace(schemaLocations: { [currentPoint: string]: AbstractStruct.SchemaLocation }): ts.Statement { + const statements: ts.Statement[] = Object.values(schemaLocations).map((schemaLocation) => { + if (schemaLocation.kind === "common") { + return this.schema.generateStatement(schemaLocation.name, schemaLocation.schema); + } else { + const ref = schemaLocation.schema.$ref; + const resolvedSchema = this.params.resolver.getSchema(schemaLocation.currentPoint, ref); + if (!resolvedSchema) { + throw new Error(resolvedSchema); + } + return this.schema.generateStatement("名前を決める関数を入れる", resolvedSchema); + } + }); + return TsGenerator.factory.Namespace.create({ + export: true, + name: "Schemas", + statements: statements, + }); + } +} diff --git a/src/code-templates/typedef/components/index.ts b/src/code-templates/typedef/components/index.ts new file mode 100644 index 00000000..862d55cf --- /dev/null +++ b/src/code-templates/typedef/components/index.ts @@ -0,0 +1 @@ +export * as Schemas from "./Schemas"; diff --git a/src/code-templates/typedef/components/types.ts b/src/code-templates/typedef/components/types.ts new file mode 100644 index 00000000..2cf575bf --- /dev/null +++ b/src/code-templates/typedef/components/types.ts @@ -0,0 +1,10 @@ +import type { ParsedSchema } from "../../../types"; +import type { ConvertToolkit } from "./ConvertToolKit"; +import type { Resolver } from "../../../reference-resolver"; + +export interface InitializeParams { + readonly entryPoint: string; + readonly accessor: ParsedSchema.Accessor; + readonly toolkit: ConvertToolkit; + readonly resolver: Resolver; +} diff --git a/src/code-templates/typedef/index.ts b/src/code-templates/typedef/index.ts new file mode 100644 index 00000000..43e587a5 --- /dev/null +++ b/src/code-templates/typedef/index.ts @@ -0,0 +1,36 @@ +import type { AbstractStruct, CodeGenerator } from "../../types"; +import * as Components from "./components"; +import { ConvertToolkit } from "./components/ConvertToolKit"; +import type { InitializeParams } from "./components/types"; + +export interface Option {} + +export const generator: CodeGenerator.AdvancedGenerateFunction