Skip to content

Instantly share code, notes, and snippets.

@ASafaeirad
Last active May 15, 2022 17:04
Show Gist options
  • Select an option

  • Save ASafaeirad/e8c3fe38a1a61445b681b4f446df5060 to your computer and use it in GitHub Desktop.

Select an option

Save ASafaeirad/e8c3fe38a1a61445b681b4f446df5060 to your computer and use it in GitHub Desktop.
Orval Tuple Morph

A morph to add tuples to orval schema.

import { toCamelCase } from '@fullstacksjs/toolbox';
import path from 'path';
import pluralize from 'pluralize';
import type {
Node,
SourceFile,
ts,
TypeAliasDeclaration,
TypeNode,
UnionTypeNode,
} from 'ts-morph';
import { Project, SyntaxKind, VariableDeclarationKind } from 'ts-morph';
const project = new Project({
tsConfigFilePath: path.resolve(__dirname, '..', 'tsconfig.web.json'),
});
const isAllLiteral = (t: TypeNode<ts.TypeNode>) =>
t?.getKindName() === 'LiteralType';
const isLiteralUnion = (t: UnionTypeNode) =>
t?.getTypeNodes().every(isAllLiteral);
const getIdentifierName = (n: Node<ts.Node>) =>
n?.getChildrenOfKind(SyntaxKind.Identifier)[0]?.getText();
const findIdentifier =
(name: string) =>
(n: Node<ts.Node>): boolean =>
n
.getChildrenOfKind(SyntaxKind.Identifier)
.some((i) => i.getText() === name);
const getAllUnionTypeDeclarations = (file: SourceFile) =>
file
.getChildrenOfKind(SyntaxKind.TypeAliasDeclaration)
.filter((t) =>
isLiteralUnion(t.getChildrenOfKind(SyntaxKind.UnionType)[0]),
);
const getAllUnions = (t: TypeAliasDeclaration) => {
return t
.getChildrenOfKind(SyntaxKind.UnionType)[0]
.getChildrenOfKind(SyntaxKind.LiteralType)
.map((n) => n.getText());
};
const toTuple = (unions: string[], name: string) =>
`[${unions.map((literal) => `${name}[${literal}]`).join(', ')}] as const`;
const isIdentifierExists = (file: SourceFile, name: string) =>
file
.getDescendantsOfKind(SyntaxKind.VariableDeclaration)
.some(findIdentifier(name));
function transform(filePath: string) {
const file = project.getSourceFileOrThrow(filePath);
const unionTypeAliasDeclarations = getAllUnionTypeDeclarations(file);
unionTypeAliasDeclarations?.forEach((typeAlias) => {
const unions = getAllUnions(typeAlias);
const name = getIdentifierName(typeAlias);
const pluralName = pluralize(name);
const tuple = toTuple(unions, name);
const variableName = toCamelCase(pluralName);
const isDeclarationExists = isIdentifierExists(file, variableName);
if (!isDeclarationExists)
file.addVariableStatement({
isExported: true,
declarations: [{ name: variableName, initializer: tuple }],
declarationKind: VariableDeclarationKind.Const,
});
const tupleName = pluralName;
const isTupleExists = isIdentifierExists(file, tupleName);
if (!isTupleExists)
file.addTypeAlias({
name: tupleName,
isExported: true,
type: `typeof ${variableName}[number]`,
});
});
file.save();
}
transform('web/src/generated/api.schemas.ts');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment