docs-util: extract feature flags in generated OAS (#12554)

This commit is contained in:
Shahed Nasser
2025-05-21 14:25:40 +03:00
committed by GitHub
parent bda143673d
commit 61d3bdde4a
3 changed files with 80 additions and 4 deletions

View File

@@ -605,10 +605,12 @@ class DefaultKindGenerator<T extends ts.Node = ts.Node> {
getInformationFromTags(node: ts.Node): {
deprecatedTag: ts.JSDocTag | undefined
versionTag: ts.JSDocTag | undefined
featureFlagTag: ts.JSDocTag | undefined
} {
const nodeComments = ts.getJSDocCommentsAndTags(node)
let deprecatedTag: ts.JSDocTag | undefined
let versionTag: ts.JSDocTag | undefined
let featureFlagTag: ts.JSDocTag | undefined
nodeComments.forEach((comment) => {
if (!("tags" in comment)) {
@@ -623,12 +625,17 @@ class DefaultKindGenerator<T extends ts.Node = ts.Node> {
if (tag.tagName.getText() === "version") {
versionTag = tag
}
if (tag.tagName.getText() === "featureFlag") {
featureFlagTag = tag
}
})
})
return {
deprecatedTag,
versionTag,
featureFlagTag,
}
}
}

View File

@@ -435,7 +435,8 @@ class OasKindGenerator extends FunctionKindGenerator {
}
// check deprecation and version in tags
const { deprecatedTag, versionTag } = this.getInformationFromTags(node)
const { deprecatedTag, versionTag, featureFlagTag } =
this.getInformationFromTags(node)
if (deprecatedTag) {
oas.deprecated = true
@@ -450,6 +451,12 @@ class OasKindGenerator extends FunctionKindGenerator {
: undefined
}
if (featureFlagTag) {
oas["x-featureFlag"] = featureFlagTag.comment
? (featureFlagTag.comment as string)
: undefined
}
return formatOas(oas, oasPrefix)
}
@@ -784,7 +791,8 @@ class OasKindGenerator extends FunctionKindGenerator {
}
// check deprecation and version in tags
const { deprecatedTag, versionTag } = this.getInformationFromTags(node)
const { deprecatedTag, versionTag, featureFlagTag } =
this.getInformationFromTags(node)
if (deprecatedTag) {
oas.deprecated = true
@@ -804,6 +812,14 @@ class OasKindGenerator extends FunctionKindGenerator {
delete oas["x-version"]
}
if (featureFlagTag) {
oas["x-featureFlag"] = featureFlagTag.comment
? (featureFlagTag.comment as string)
: undefined
} else {
delete oas["x-featureFlag"]
}
return formatOas(oas, oasPrefix)
}
@@ -1562,6 +1578,27 @@ class OasKindGenerator extends FunctionKindGenerator {
})
}) || undefined // avoid showing it as false in the generated OAS
let featureFlag: string | undefined
commentsAndTags.some((comment) => {
if (!("tags" in comment)) {
return false
}
comment.tags?.some((tag) => {
if (tag.tagName.getText() !== "featureFlag" || !tag.comment) {
return false
}
featureFlag =
typeof tag.comment === "string" ? tag.comment : tag.comment.join(" ")
return true
})
return featureFlag !== undefined
})
switch (true) {
case isEnum || isEnumParent:
const enumMembers: string[] = []
@@ -1584,6 +1621,7 @@ class OasKindGenerator extends FunctionKindGenerator {
description,
enum: enumMembers,
deprecated: isDeprecated,
"x-featureFlag": featureFlag,
}
case itemType.isLiteral() || typeAsString === "RegExp":
const isString =
@@ -1602,6 +1640,7 @@ class OasKindGenerator extends FunctionKindGenerator {
name: title,
}),
deprecated: isDeprecated,
"x-featureFlag": featureFlag,
}
case itemType.flags === ts.TypeFlags.String ||
itemType.flags === ts.TypeFlags.Number ||
@@ -1622,6 +1661,7 @@ class OasKindGenerator extends FunctionKindGenerator {
name: title,
}),
deprecated: isDeprecated,
"x-featureFlag": featureFlag,
}
case ("intrinsicName" in itemType &&
itemType.intrinsicName === "boolean") ||
@@ -1634,6 +1674,7 @@ class OasKindGenerator extends FunctionKindGenerator {
? this.getDefaultValue(symbol?.valueDeclaration)
: undefined,
deprecated: isDeprecated,
"x-featureFlag": featureFlag,
}
case this.checker.isArrayType(itemType):
return {
@@ -1656,6 +1697,7 @@ class OasKindGenerator extends FunctionKindGenerator {
saveSchema,
...rest,
}),
"x-featureFlag": featureFlag,
}
case itemType.isUnion():
// if it's a union of literal types,
@@ -1675,6 +1717,7 @@ class OasKindGenerator extends FunctionKindGenerator {
enum: cleanedUpTypes.map(
(unionType) => (unionType as ts.LiteralType).value
),
"x-featureFlag": featureFlag,
}
}
@@ -1690,12 +1733,17 @@ class OasKindGenerator extends FunctionKindGenerator {
)
if (oneOfItems.length === 1) {
return oneOfItems[0]
return {
...oneOfItems[0],
"x-featureFlag": oneOfItems[0]["x-featureFlag"] || featureFlag,
deprecated: oneOfItems[0].deprecated || isDeprecated,
}
}
return {
oneOf: oneOfItems,
deprecated: isDeprecated,
"x-featureFlag": featureFlag,
}
case itemType.isIntersection():
const allOfItems = this.typesHelper
@@ -1712,12 +1760,17 @@ class OasKindGenerator extends FunctionKindGenerator {
})
if (allOfItems.length === 1) {
return allOfItems[0]
return {
...allOfItems[0],
"x-featureFlag": allOfItems[0]["x-featureFlag"] || featureFlag,
deprecated: allOfItems[0].deprecated || isDeprecated,
}
}
return {
allOf: allOfItems,
deprecated: isDeprecated,
"x-featureFlag": featureFlag,
}
case typeAsString.startsWith("Pick"):
const pickTypeArgs =
@@ -1807,6 +1860,7 @@ class OasKindGenerator extends FunctionKindGenerator {
: undefined,
// this is changed later
required: undefined,
"x-featureFlag": featureFlag,
}
const properties: Record<
@@ -2472,6 +2526,19 @@ class OasKindGenerator extends FunctionKindGenerator {
}
}
if (oldSchemaObj?.["x-featureFlag"] !== newSchemaObj?.["x-featureFlag"]) {
// avoid many changes to exising OAS
if (!newSchemaObj?.["x-featureFlag"]) {
if (oldSchemaObj!["x-featureFlag"]) {
wasUpdated = true
}
delete oldSchemaObj!["x-featureFlag"]
} else {
oldSchemaObj!["x-featureFlag"] = newSchemaObj["x-featureFlag"]
wasUpdated = true
}
}
if (!wasUpdated) {
const requiredChanged =
oldSchemaObj!.required?.length !== newSchemaObj?.required?.length ||

View File

@@ -13,6 +13,7 @@ export declare type OpenApiOperation = Partial<OpenAPIV3.OperationObject> & {
"x-events"?: OasEvent[]
"x-deprecated_message"?: string
"x-version"?: string
"x-featureFlag"?: string
}
export declare type CommonCliOptions = {
@@ -23,6 +24,7 @@ export declare type CommonCliOptions = {
export declare type OpenApiSchema = OpenAPIV3.SchemaObject & {
"x-schemaName"?: string
"x-featureFlag"?: string
}
export declare interface OpenApiTagObject extends OpenAPIV3.TagObject {