Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: switch to openapi3-ts which requires no patches #9

Merged
merged 1 commit into from
Oct 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { writeFile } from 'node:fs/promises';
import prettierConfig from '@block65/eslint-config/prettier';
import type { OpenAPIV3_1 } from 'openapi-types';
import type { oas31 } from 'openapi3-ts';
import { format as prettier } from 'prettier';
import { processOpenApiDocument } from './process-document.js';

Expand All @@ -11,7 +11,7 @@ export async function build(
) {
const apischema = (await import(inputFile, {
with: { type: 'json' },
})) as { default: OpenAPIV3_1.Document };
})) as { default: oas31.OpenAPIObject };

const banner = `
/**
Expand Down
33 changes: 17 additions & 16 deletions lib/process-document.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-restricted-syntax */
import { join, relative } from 'node:path';
import { $RefParser } from '@apidevtools/json-schema-ref-parser';
import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
import type { oas30, oas31 } from 'openapi3-ts';
import toposort from 'toposort';
import {
InterfaceDeclaration,
Expand All @@ -14,6 +14,7 @@ import {
VariableDeclarationKind,
Writers,
} from 'ts-morph';
import type { Simplify } from 'type-fest';
import { registerTypesFromSchema, schemaToType } from './process-schema.js';
import {
castToValidJsIdentifier,
Expand Down Expand Up @@ -60,7 +61,7 @@ function createUnion(...types: (string | undefined)[]) {

export async function processOpenApiDocument(
outputDir: string,
schema: OpenAPIV3_1.Document,
schema: Simplify<oas31.OpenAPIObject>,
tags?: string[] | undefined,
) {
const project = new Project();
Expand Down Expand Up @@ -191,22 +192,22 @@ export async function processOpenApiDocument(
);
}

for (const [path, pathItemObject] of Object.entries(schema.paths || {})) {
for (const [path, pathItemObject] of Object.entries<oas31.PathItemObject>(
schema.paths || {},
)) {
if (pathItemObject) {
for (const [method, operationObject] of Object.entries(
pathItemObject,
).filter(
([, o]) =>
!tags ||
(typeof o === 'object' && 'tags' in o
? o.tags.some((t) => tags.includes(t))
: false),
)) {
for (const [method, operationObject] of Object.entries(pathItemObject)
// ensure op is an object
.filter(
(e): e is [string, oas31.OperationObject] => typeof e[1] === 'object',
)
// tags
.filter(([, o]) => !tags || o.tags?.some((t) => tags?.includes(t)))) {
if (
typeof operationObject === 'object' &&
'operationId' in operationObject
) {
const pathParameters: OpenAPIV3.ParameterObject[] = [];
const pathParameters: oas30.ParameterObject[] = [];

const commandName = pascalCase(
operationObject.operationId.replace(/command$/i, ''),
Expand Down Expand Up @@ -258,13 +259,13 @@ export async function processOpenApiDocument(
? operationObject.requestBody
: undefined;

const queryParameters: OpenAPIV3.ParameterObject[] = [];
const queryParameters: oas30.ParameterObject[] = [];

for (const parameter of [
...(operationObject.parameters || []),
...(pathItemObject.parameters || []),
]) {
const resolvedParameter: OpenAPIV3.ParameterObject =
const resolvedParameter: oas30.ParameterObject =
'$ref' in parameter ? refs.get(parameter.$ref) : parameter;

if (resolvedParameter.in === 'path') {
Expand Down Expand Up @@ -481,7 +482,7 @@ export async function processOpenApiDocument(
}

for (const [statusCode, response] of Object.entries(
operationObject.responses,
operationObject.responses || {},
).filter(([s]) => s.startsWith('2'))) {
// early out if response is 204
if (statusCode === '204') {
Expand Down
41 changes: 22 additions & 19 deletions lib/process-schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-console */
import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
import type { oas31, oas30 } from 'openapi3-ts';
import {
Writers,
type CodeBlockWriter,
Expand All @@ -24,9 +24,7 @@ function maybeWithNullUnion(type: string | WriterFunction, withNull = false) {
return withNull && type !== 'null' ? Writers.unionType(type, 'null') : type;
}

function schemaTypeIsNull(
schema: OpenAPIV3.SchemaObject | OpenAPIV3_1.SchemaObject,
) {
function schemaTypeIsNull(schema: oas30.SchemaObject | oas31.SchemaObject) {
return (
schema.type === 'null' ||
('nullable' in schema && schema.nullable) ||
Expand Down Expand Up @@ -63,12 +61,9 @@ export function schemaToType(
string,
InterfaceDeclaration | TypeAliasDeclaration | EnumDeclaration
>,
parentSchema: OpenAPIV3_1.SchemaObject | OpenAPIV3.SchemaObject,
parentSchema: oas31.SchemaObject | oas30.SchemaObject,
propertyName: string,
schemaObject:
| OpenAPIV3_1.SchemaObject
| OpenAPIV3.SchemaObject
| OpenAPIV3_1.ReferenceObject,
schemaObject: oas31.SchemaObject | oas30.SchemaObject | oas31.ReferenceObject,
options: {
exactOptionalPropertyTypes?: boolean;
booleanAsStringish?: boolean;
Expand Down Expand Up @@ -127,9 +122,19 @@ export function schemaToType(
},
]
: []),
...(schemaObject.example

// 3
...('example' in schemaObject
? [{ tagName: 'example', text: String(schemaObject.example) }]
: []),

// 3.1
...('examples' in schemaObject
? schemaObject.examples.map((example) => ({
tagName: 'example',
text: JSON.stringify(example),
}))
: []),
...(schemaObject.deprecated ? [{ tagName: 'deprecated' }] : []),
];

Expand Down Expand Up @@ -168,13 +173,11 @@ export function schemaToType(
items: {},
...schemaObject,
type: 'array',
} satisfies
| OpenAPIV3.ArraySchemaObject
| OpenAPIV3_1.ArraySchemaObject)
: {
} satisfies typeof schemaObject)
: ({
...schemaObject,
type,
};
} satisfies typeof schemaObject);

return (
schemaToType(typesAndInterfaces, schemaObject, name, schema).type ||
Expand Down Expand Up @@ -420,10 +423,10 @@ export function registerTypesFromSchema(
typesFile: SourceFile,
schemaName: string,
schemaObject:
| OpenAPIV3.SchemaObject
| OpenAPIV3.ReferenceObject
| OpenAPIV3_1.SchemaObject
| OpenAPIV3_1.ReferenceObject,
| oas30.SchemaObject
| oas30.ReferenceObject
| oas31.SchemaObject
| oas31.ReferenceObject,
) {
// deal with refs
if ('$ref' in schemaObject) {
Expand Down
14 changes: 6 additions & 8 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import camelcase from 'camelcase';
import type { OpenAPIV3_1 } from 'openapi-types';
import type { oas31 } from 'openapi3-ts';
import wrap from 'word-wrap';

export function maybeJsDocDescription(
Expand All @@ -8,19 +8,17 @@ export function maybeJsDocDescription(
return str.length > 0 ? ['', ...str].filter(Boolean).join(' - ').trim() : '';
}

export function isReferenceObject(
obj: unknown,
): obj is OpenAPIV3_1.ReferenceObject {
export function isReferenceObject(obj: unknown): obj is oas31.ReferenceObject {
return typeof obj === 'object' && obj !== null && '$ref' in obj;
}

export function getDependency(obj: unknown): string | undefined {
return isReferenceObject(obj) ? obj.$ref : undefined;
}

export function isNotReferenceObject<
T extends OpenAPIV3_1.ReferenceObject | unknown,
>(obj: T): obj is Exclude<T, OpenAPIV3_1.ReferenceObject> {
export function isNotReferenceObject<T extends oas31.ReferenceObject | unknown>(
obj: T,
): obj is Exclude<T, oas31.ReferenceObject> {
return !isReferenceObject(obj);
}

Expand All @@ -29,7 +27,7 @@ export function isNotNullOrUndefined<T>(obj: T | null | undefined): obj is T {
}

export function getDependents(
obj: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject,
obj: oas31.ReferenceObject | oas31.SchemaObject,
): string[] {
const strOnly = (x: string | undefined): x is string => typeof x === 'string';

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"eslint-plugin-react": "^7.37.1",
"eslint-plugin-react-hooks": "^4.6.2",
"js-yaml": "^4.1.0",
"openapi-types": "^12.1.3",
"openapi3-ts": "^4.4.0",
"prettier": "^2.8.8",
"typescript": "^5.6.2",
"undici": "^6.19.8",
Expand Down
21 changes: 15 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.