enhance: 生成物からnever型を除去するように
This commit is contained in:
parent
6a042e3f9e
commit
547d20ec50
|
|
@ -0,0 +1,149 @@
|
|||
import * as ts from 'typescript';
|
||||
|
||||
/**
|
||||
* TypeScript AST ノードから 'never' 型のプロパティを削除します。
|
||||
* この関数は、特に 'paths' という名前の TypeAliasDeclaration 内や
|
||||
* 'operations' という名前の InterfaceDeclaration 内を再帰的に探索し、
|
||||
* そこに含まれるすべての TypeLiteralNode/Interfaceから 'PropertySignature' で型が 'never' であるものを削除します。
|
||||
* さらに、すべてのプロパティが 'never' で除去された場合は、そのオブジェクト自体も削除します。
|
||||
*
|
||||
* @param astNodes 処理対象の ts.Node 配列 (例: `openapi-typescript` から出力されたもの)。
|
||||
* @returns 'never' 型プロパティが削除された新しい ts.Node 配列。
|
||||
*/
|
||||
export function removeNeverPropertiesFromAST(astNodes: readonly ts.Node[]): ts.Node[] {
|
||||
const factory = ts.factory;
|
||||
|
||||
const typeNodeRecursiveVisitor = (node: ts.Node): ts.Node | undefined => {
|
||||
if (ts.isTypeLiteralNode(node)) {
|
||||
const newMembers: ts.TypeElement[] = [];
|
||||
let hasTypeLiteralChanged = false;
|
||||
|
||||
for (const member of node.members) {
|
||||
if (ts.isPropertySignature(member)) {
|
||||
if (member.type && member.type.kind === ts.SyntaxKind.NeverKeyword) {
|
||||
hasTypeLiteralChanged = true;
|
||||
continue;
|
||||
}
|
||||
let updatedPropertySignature = member;
|
||||
if (member.type) {
|
||||
const visitedMemberType = ts.visitNode(member.type, typeNodeRecursiveVisitor);
|
||||
if (visitedMemberType && visitedMemberType !== member.type) {
|
||||
updatedPropertySignature = factory.updatePropertySignature(
|
||||
member,
|
||||
member.modifiers,
|
||||
member.name,
|
||||
member.questionToken,
|
||||
visitedMemberType as ts.TypeNode
|
||||
);
|
||||
hasTypeLiteralChanged = true;
|
||||
} else if (visitedMemberType === undefined) {
|
||||
// 子の型が消された場合、このプロパティも消す
|
||||
hasTypeLiteralChanged = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
newMembers.push(updatedPropertySignature);
|
||||
} else {
|
||||
newMembers.push(member);
|
||||
}
|
||||
}
|
||||
|
||||
if (newMembers.length === 0) {
|
||||
// すべてのプロパティがneverで消された場合、このTypeLiteralNode自体も消す
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (hasTypeLiteralChanged) {
|
||||
return factory.updateTypeLiteralNode(node, factory.createNodeArray(newMembers));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
return ts.visitEachChild(node, typeNodeRecursiveVisitor, undefined);
|
||||
};
|
||||
|
||||
const interfaceRecursiveVisitor = (node: ts.Node): ts.Node | undefined => {
|
||||
if (ts.isInterfaceDeclaration(node)) {
|
||||
const newMembers: ts.TypeElement[] = [];
|
||||
let hasChanged = false;
|
||||
|
||||
for (const member of node.members) {
|
||||
if (ts.isPropertySignature(member)) {
|
||||
if (member.type && member.type.kind === ts.SyntaxKind.NeverKeyword) {
|
||||
hasChanged = true;
|
||||
continue;
|
||||
}
|
||||
let updatedPropertySignature = member;
|
||||
if (member.type) {
|
||||
const visitedMemberType = ts.visitNode(member.type, typeNodeRecursiveVisitor);
|
||||
if (visitedMemberType && visitedMemberType !== member.type) {
|
||||
updatedPropertySignature = factory.updatePropertySignature(
|
||||
member,
|
||||
member.modifiers,
|
||||
member.name,
|
||||
member.questionToken,
|
||||
visitedMemberType as ts.TypeNode
|
||||
);
|
||||
hasChanged = true;
|
||||
} else if (visitedMemberType === undefined) {
|
||||
hasChanged = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
newMembers.push(updatedPropertySignature);
|
||||
} else {
|
||||
newMembers.push(member);
|
||||
}
|
||||
}
|
||||
|
||||
if (newMembers.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (hasChanged) {
|
||||
return factory.updateInterfaceDeclaration(
|
||||
node,
|
||||
node.modifiers,
|
||||
node.name,
|
||||
node.typeParameters,
|
||||
node.heritageClauses,
|
||||
newMembers
|
||||
);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
return ts.visitEachChild(node, interfaceRecursiveVisitor, undefined);
|
||||
};
|
||||
|
||||
const topLevelVisitor = (node: ts.Node): ts.Node | undefined => {
|
||||
if (ts.isTypeAliasDeclaration(node) && node.name.escapedText === 'paths') {
|
||||
const newType = ts.visitNode(node.type, typeNodeRecursiveVisitor);
|
||||
if (newType && newType !== node.type) {
|
||||
return factory.updateTypeAliasDeclaration(
|
||||
node,
|
||||
node.modifiers,
|
||||
node.name,
|
||||
node.typeParameters,
|
||||
newType as ts.TypeNode
|
||||
);
|
||||
} else if (newType === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (ts.isInterfaceDeclaration(node) && node.name.escapedText === 'operations') {
|
||||
const result = interfaceRecursiveVisitor(node);
|
||||
return result;
|
||||
}
|
||||
return ts.visitEachChild(node, topLevelVisitor, undefined);
|
||||
};
|
||||
|
||||
const transformedNodes: ts.Node[] = [];
|
||||
for (const astNode of astNodes) {
|
||||
const resultNode = ts.visitNode(astNode, topLevelVisitor);
|
||||
if (resultNode) {
|
||||
transformedNodes.push(resultNode);
|
||||
}
|
||||
}
|
||||
|
||||
return transformedNodes;
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import OpenAPIParser from '@readme/openapi-parser';
|
|||
import openapiTS, { astToString } from 'openapi-typescript';
|
||||
import type { OpenAPI3, OperationObject, PathItemObject } from 'openapi-typescript';
|
||||
import ts from 'typescript';
|
||||
import { removeNeverPropertiesFromAST } from './ast-transformer.js';
|
||||
|
||||
async function generateBaseTypes(
|
||||
openApiDocs: OpenAPIV3_1.Document,
|
||||
|
|
@ -53,7 +54,11 @@ async function generateBaseTypes(
|
|||
}
|
||||
},
|
||||
});
|
||||
lines.push(astToString(generatedTypesAst));
|
||||
|
||||
const filteredAst = removeNeverPropertiesFromAST(generatedTypesAst);
|
||||
|
||||
lines.push(astToString(filteredAst));
|
||||
|
||||
lines.push('');
|
||||
|
||||
await writeFile(typeFileName, lines.join('\n'));
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue