-
-
Notifications
You must be signed in to change notification settings - Fork 532
feat: root types #1599
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
feat: root types #1599
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,13 +17,18 @@ type SchemaTransforms = keyof Pick< | |
|
||
const transformers: Record< | ||
SchemaTransforms, | ||
(node: any, options: GlobalContext) => ts.TypeNode // eslint-disable-line @typescript-eslint/no-explicit-any | ||
( | ||
node: any, // eslint-disable-line @typescript-eslint/no-explicit-any | ||
options: GlobalContext, | ||
) => [ts.TypeNode, ts.TypeAliasDeclaration[]] | ||
> = { | ||
paths: transformPathsObject, | ||
webhooks: transformWebhooksObject, | ||
components: transformComponentsObject, | ||
$defs: (node, options) => | ||
$defs: (node, options) => [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another tuple type to remove: let’s change this to an |
||
transformSchemaObject(node, { path: createRef(["$defs"]), ctx: options }), | ||
[], | ||
], | ||
}; | ||
|
||
export default function transformSchema(schema: OpenAPI3, ctx: GlobalContext) { | ||
|
@@ -39,7 +44,7 @@ export default function transformSchema(schema: OpenAPI3, ctx: GlobalContext) { | |
|
||
if (schema[root] && typeof schema[root] === "object") { | ||
const rootT = performance.now(); | ||
const subType = transformers[root](schema[root], ctx); | ||
const [subType, aliasTypes] = transformers[root](schema[root], ctx); | ||
if ((subType as ts.TypeLiteralNode).members?.length) { | ||
type.push( | ||
ctx.exportType | ||
|
@@ -62,6 +67,12 @@ export default function transformSchema(schema: OpenAPI3, ctx: GlobalContext) { | |
type.push(emptyObj); | ||
debug(`${root} done (skipped)`, "ts", 0); | ||
} | ||
|
||
if (ctx.rootTypes) { | ||
for (const alias of aliasTypes) { | ||
type.push(alias); | ||
} | ||
} | ||
} else { | ||
type.push(emptyObj); | ||
debug(`${root} done (skipped)`, "ts", 0); | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,3 +1,4 @@ | ||||||
import { pascalCase } from "scule"; | ||||||
import ts from "typescript"; | ||||||
import { | ||||||
addJSDocComment, | ||||||
|
@@ -26,8 +27,9 @@ const PATH_PARAM_RE = /\{[^}]+\}/g; | |||||
export default function transformPathsObject( | ||||||
pathsObject: PathsObject, | ||||||
ctx: GlobalContext, | ||||||
): ts.TypeNode { | ||||||
): [ts.TypeNode, ts.TypeAliasDeclaration[]] { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This is a personal opinion, but let’s not return tuple types for any functions; those don’t scale well. If a function needs to return multiple things, a named object is much better (same for parameters—named objects are more maintainable than ordered params or tuples) |
||||||
const type: ts.TypeElement[] = []; | ||||||
const refs: ts.TypeAliasDeclaration[] = []; | ||||||
for (const [url, pathItemObject] of getEntries(pathsObject, ctx)) { | ||||||
if (!pathItemObject || typeof pathItemObject !== "object") { | ||||||
continue; | ||||||
|
@@ -52,7 +54,10 @@ export default function transformPathsObject( | |||||
|
||||||
// pathParamsAsTypes | ||||||
if (ctx.pathParamsAsTypes && url.includes("{")) { | ||||||
const pathParams = extractPathParams(pathItemObject, ctx); | ||||||
const { parameters: pathParams = {} } = extractParams( | ||||||
pathItemObject, | ||||||
ctx, | ||||||
); | ||||||
const matches = url.match(PATH_PARAM_RE); | ||||||
let rawPath = `\`${url}\``; | ||||||
if (matches) { | ||||||
|
@@ -106,20 +111,45 @@ export default function transformPathsObject( | |||||
|
||||||
debug(`Transformed path "${url}"`, "ts", performance.now() - pathT); | ||||||
} | ||||||
|
||||||
if (ctx.rootTypes) { | ||||||
const { operations } = extractParams(pathItemObject, ctx); | ||||||
for (const name in operations) { | ||||||
refs.push( | ||||||
ts.factory.createTypeAliasDeclaration( | ||||||
/* modifiers */ tsModifiers({ export: true }), | ||||||
/* name */ pascalCase(`request-${operations[name]}`), | ||||||
/* typeParameters */ undefined, | ||||||
/* type */ oapiRef( | ||||||
createRef(["paths", url, name, "parameters"]), | ||||||
), | ||||||
), | ||||||
); | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
return ts.factory.createTypeLiteralNode(type); | ||||||
return [ts.factory.createTypeLiteralNode(type), refs]; | ||||||
} | ||||||
|
||||||
function extractPathParams(pathItemObject: PathItemObject, ctx: GlobalContext) { | ||||||
const params: Record<string, ParameterObject> = {}; | ||||||
function extractParams(pathItemObject: PathItemObject, ctx: GlobalContext) { | ||||||
const params: { | ||||||
parameters: Record<string, ParameterObject>; | ||||||
operations: Record<string, string>; | ||||||
} = { | ||||||
parameters: {}, | ||||||
operations: {}, | ||||||
}; | ||||||
for (const p of pathItemObject.parameters ?? []) { | ||||||
const resolved = | ||||||
"$ref" in p && p.$ref | ||||||
? ctx.resolve<ParameterObject>(p.$ref) | ||||||
: (p as ParameterObject); | ||||||
if (resolved && resolved.in === "path") { | ||||||
params[resolved.name] = resolved; | ||||||
if (resolved) { | ||||||
params.parameters = { | ||||||
...params.parameters, | ||||||
[resolved.name]: resolved, | ||||||
}; | ||||||
} | ||||||
} | ||||||
for (const method of [ | ||||||
|
@@ -146,8 +176,17 @@ function extractPathParams(pathItemObject: PathItemObject, ctx: GlobalContext) { | |||||
"$ref" in p && p.$ref | ||||||
? ctx.resolve<ParameterObject>(p.$ref) | ||||||
: (p as ParameterObject); | ||||||
if (resolvedParam && resolvedParam.in === "path") { | ||||||
params[resolvedParam.name] = resolvedParam; | ||||||
if (resolvedParam) { | ||||||
params.parameters = { | ||||||
...params.parameters, | ||||||
[resolvedParam.name]: resolvedParam, | ||||||
}; | ||||||
if (resolvedMethod.operationId) { | ||||||
params.operations = { | ||||||
...params.operations, | ||||||
[method]: resolvedMethod.operationId, | ||||||
}; | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m fine with adding this since it’s tiny, well-documented, and well-written. But this will need to be in
dependencies
if it’s required for runtime! Otherwise it won’t install, and users will get a “module not found” error