UNPKG

@matatbread/typia

Version:

Superfast runtime validators with only one line

209 lines (191 loc) 5.91 kB
import ts from "typescript"; import { Metadata } from "../../../schemas/metadata/Metadata"; import { MetadataObjectType } from "../../../schemas/metadata/MetadataObjectType"; import { MetadataProperty } from "../../../schemas/metadata/MetadataProperty"; import { Writable } from "../../../typings/Writable"; import { ArrayUtil } from "../../../utils/ArrayUtil"; import { CommentFactory } from "../../CommentFactory"; import { IMetadataIteratorProps } from "./IMetadataIteratorProps"; import { MetadataHelper } from "./MetadataHelper"; import { explore_metadata } from "./explore_metadata"; import { iterate_metadata_coalesce } from "./iterate_metadata_coalesce"; export const emplace_metadata_object = ( props: IMetadataIteratorProps, ): MetadataObjectType => { // EMPLACE OBJECT const [obj, newbie] = props.collection.emplace(props.checker, props.type); ArrayUtil.add( obj.nullables, props.metadata.nullable, (elem) => elem === props.metadata.nullable, ); if (newbie === false) return obj; // PREPARE ASSETS const isClass: boolean = props.type.isClass(); const isProperty = significant(!!props.options.functional); const pred: (node: ts.Declaration) => boolean = isClass ? (node) => { const kind: ts.SyntaxKind | undefined = node .getChildren()[0] ?.getChildren()[0]?.kind; return ( kind !== ts.SyntaxKind.PrivateKeyword && kind !== ts.SyntaxKind.ProtectedKeyword && isProperty(node) ); } : (node) => isProperty(node); const insert = (props: { key: Metadata; value: Metadata; symbol: ts.Symbol | undefined; filter?: (doc: ts.JSDocTagInfo) => boolean; }): MetadataProperty => { // COMMENTS AND TAGS const description: string | null = props.symbol ? (CommentFactory.description(props.symbol) ?? null) : null; const jsDocTags: ts.JSDocTagInfo[] = ( props.symbol?.getJsDocTags() ?? [] ).filter(props.filter ?? (() => true)); // THE PROPERTY const property: MetadataProperty = MetadataProperty.create({ key: props.key, value: props.value, description, jsDocTags, }); obj.properties.push(property); return property; }; //---- // REGULAR PROPERTIES //---- for (const symbol of props.type.getApparentProperties()) { // CHECK INTERNAL TAG if ( (symbol.getJsDocTags(props.checker) ?? []).find( (tag) => tag.name === "internal", ) !== undefined ) continue; // CHECK NODE IS A FORMAL PROPERTY const [node, type] = (() => { const node = symbol.getDeclarations()?.[0] as | ts.PropertyDeclaration | undefined; const type: ts.Type | undefined = node ? props.checker.getTypeOfSymbolAtLocation(symbol, node) : props.checker.getTypeOfPropertyOfType(props.type, symbol.name); return [node, type]; })(); if ((node && pred(node) === false) || type === undefined) continue; // GET EXACT TYPE const key: Metadata = MetadataHelper.literal_to_metadata(symbol.name); const value: Metadata = explore_metadata({ ...props, type, explore: { top: false, object: obj, property: symbol.name, parameter: null, nested: null, aliased: false, escaped: false, output: false, }, intersected: false, }); Writable(value).optional = (symbol.flags & ts.SymbolFlags.Optional) !== 0; insert({ key, value, symbol, }); } //---- // DYNAMIC PROPERTIES //---- for (const index of props.checker.getIndexInfosOfType(props.type)) { // GET EXACT TYPE const analyzer = (type: ts.Type) => (property: {} | null) => explore_metadata({ ...props, type, explore: { top: false, object: obj, property, parameter: null, nested: null, aliased: false, escaped: false, output: false, }, intersected: false, }); const key: Metadata = analyzer(index.keyType)(null); const value: Metadata = analyzer(index.type)({}); if ( key.atomics.length + key.constants.map((c) => c.values.length).reduce((a, b) => a + b, 0) + key.templates.length + key.natives.filter( (native) => native.name === "Boolean" || native.name === "BigInt" || native.name === "Number" || native.name === "String", ).length !== key.size() ) props.errors.push({ name: key.getName(), explore: { top: false, object: obj, property: "[key]", parameter: null, nested: null, aliased: false, escaped: false, output: false, }, messages: [], }); // INSERT WITH REQUIRED CONFIGURATION insert({ key, value, symbol: index.declaration?.parent ? props.checker.getSymbolAtLocation(index.declaration.parent) : undefined, filter: (doc) => doc.name !== "default", }); } return obj; }; const significant = (functional: boolean) => functional ? (node: ts.Declaration) => !ts.isAccessor(node) : (node: ts.Declaration) => ts.isParameter(node) || ts.isPropertyDeclaration(node) || ts.isPropertyAssignment(node) || ts.isPropertySignature(node) || ts.isTypeLiteralNode(node) || ts.isShorthandPropertyAssignment(node); const iterate_optional_coalesce = (props: { metadata: Metadata; type: ts.Type; }): void => { if (props.type.isUnionOrIntersection()) props.type.types.forEach((child) => iterate_optional_coalesce({ metadata: props.metadata, type: child, }), ); else iterate_metadata_coalesce(props); };