UNPKG

@kontent-ai/model-generator

Version:

This utility generates strongly-typed models for Delivery JS SDK, Migration toolkit or just general scripting to improve the experience when referencing Kontent.ai related objects.

171 lines (146 loc) 6.03 kB
import type { ContentTypeModels, TaxonomyModels } from "@kontent-ai/management-sdk"; import { match } from "ts-pattern"; import { deliveryConfig } from "../../../config.js"; import { wrapComment } from "../../../core/comment.utils.js"; import type { ModuleFileExtension } from "../../../core/core.models.js"; import { getImporter } from "../../../core/importer.js"; import { resolveCase } from "../../../core/resolvers.js"; import type { DeliveryEntityNames } from "../delivery-entity-name.generator.js"; import type { DeliveryEntity, DeliveryEntityType } from "../delivery-entity.generator.js"; export type TypeMappingItem = { readonly codename: string; readonly typeName: string; }; export function deliveryEntityUtils() { return { getPluralName, getTypeMapping, getTypeMappingItem, getCodeOfDeliveryEntity, getTaxonomyTermCodenames, getCodenameTypeguardFunctionCode, getCoreContentTypeEntityImports, getCoreContentTypeCode, }; } function getCoreContentTypeCode( types: readonly Readonly<ContentTypeModels.ContentType>[], entityNames: DeliveryEntityNames<"Type">, ): string { return `export type ${deliveryConfig.coreContentTypeName} = ${getCoreContentTypeValue(types, entityNames)}`; } function getCoreContentTypeEntityNames( types: Parameters<typeof getCoreContentTypeCode>[0], entityNames: Parameters<typeof getCoreContentTypeCode>[1], ): readonly string[] { return types.map((m) => entityNames.getEntityName(m)); } function getCoreContentTypeValue( types: Parameters<typeof getCoreContentTypeEntityNames>[0], entityNames: Parameters<typeof getCoreContentTypeEntityNames>[1], ): string { const typeNames = getCoreContentTypeEntityNames(types, entityNames); if (!typeNames.length) { return "never"; } return typeNames.join(" | "); } function getCoreContentTypeEntityImports( types: Parameters<typeof getCoreContentTypeEntityNames>[0], entityNames: Parameters<typeof getCoreContentTypeEntityNames>[1], moduleFileExtension: ModuleFileExtension, ): readonly string[] { const importer = getImporter(moduleFileExtension); return types.map((m) => importer.importType({ filePathOrPackage: `../${entityNames.folderName}/${entityNames.getEntityFilename(m, true)}`, importValue: entityNames.getEntityName(m), }), ); } function getTypeMappingTypeName(entityType: DeliveryEntityType): string { return `Codename${resolveCase(entityType, "pascalCase")}Mapping`; } function getTypeMapping(entityType: DeliveryEntityType, items: readonly TypeMappingItem[]): string { return `export type ${getTypeMappingTypeName(entityType)} = { ${items.map((item) => `readonly ${item.codename}: ${item.typeName},`).join("\n")} };`; } function getTypeMappingItem({ codenamesTypeName, defaultTypeName, entityType, }: { readonly entityType: DeliveryEntityType; readonly codenamesTypeName: string; readonly defaultTypeName: string; }): string { const codenameGenericName = `T${resolveCase(entityType, "pascalCase")}Codename`; const typedItemTypeName = `Codename${resolveCase(entityType, "pascalCase")}Mapper`; return `export type ${typedItemTypeName}<${codenameGenericName} extends ${codenamesTypeName}> = ${codenameGenericName} extends keyof ${getTypeMappingTypeName(entityType)} ? ${getTypeMappingTypeName(entityType)}[${codenameGenericName}] : ${defaultTypeName};`; } function getCodenameTypeguardFunctionCode({ codenameTypeName, entity, typeGuardName, }: { readonly typeGuardName: string; readonly codenameTypeName: string; readonly entity: Readonly<DeliveryEntity>; }): string { return `export function ${typeGuardName}(value: string | undefined | null): value is ${codenameTypeName} { return typeof value === 'string' && value === ('${entity.codename}' satisfies ${codenameTypeName}); }`; } function getTaxonomyTermCodenames(taxonomyTerms: readonly Readonly<TaxonomyModels.Taxonomy>[]): readonly string[] { const termCodenames = taxonomyTerms.reduce<readonly string[]>((codenames, taxonomyTerm) => { return codenames.concat(getTaxonomyTermCodenames(taxonomyTerm.terms), taxonomyTerm.codename); }, []); return [...new Set(termCodenames)]; } function getPluralName(entityType: DeliveryEntityType): string { return match(entityType) .returnType<string>() .with("Taxonomy", () => "Taxonomies") .otherwise(() => `${entityType}s`); } function getCodeOfDeliveryEntity({ extendedType, codenames, names, }: { readonly names: { readonly codenamesTypeName: string; readonly valuesPropertyName: string; readonly typeGuardFunctionName: string; }; readonly subtype?: "Term" | "Step"; readonly extendedType: DeliveryEntityType | "Workflow step" | "Taxonomy term"; readonly codenames: readonly string[]; }): string { const uniqueCodenames: readonly string[] = [...new Set(codenames)]; const getCodenameTypeguardFunctionCode = (): string => { return `export function ${names.typeGuardFunctionName}(value: string | undefined | null): value is ${names.codenamesTypeName} { return typeof value === 'string' && (${names.valuesPropertyName} as readonly string[]).includes(value); }`; }; const getValuesCode = (): string => { return `export const ${names.valuesPropertyName} = [${uniqueCodenames.map((m) => `'${m}'`).join(", ")}] as const;`; }; const getCodenamesTypeCode = (): string => { return `export type ${names.codenamesTypeName} = typeof ${names.valuesPropertyName}[number];`; }; const getComment = (title: string): string => { return wrapComment(title, { lines: [], }); }; const getEntityTypeNameForComments = (): string => `${extendedType.toLowerCase()}`; return ` ${getComment(`Array of all ${getEntityTypeNameForComments()} codenames`)} ${getValuesCode()}; ${getComment(`Type representing all ${getEntityTypeNameForComments()} codenames`)} ${getCodenamesTypeCode()}; ${getComment(`Typeguard for ${getEntityTypeNameForComments()} codename`)} ${getCodenameTypeguardFunctionCode()};`; }