import * as recast from "recast"; import type { ManifestExtraInput } from "../../types/application-manifest.js"; const b = recast.types.builders; export type TSInterfaceBody = Parameters[0]; /** * Builds an import statement with named imports. * * @example * * ```ts * buildImportStatement( * ["a", "b"], * "./path", * ); * // => import { a, b } from "./path"; * ``` */ export function buildImportStatement( specifiers: string[], source: string, importKind: "type" | "value" = "value" ) { return b.importDeclaration( specifiers.map((s) => b.importSpecifier(b.identifier(s))), b.stringLiteral(source), importKind ); } type TSTypeAnnotation = Parameters[0]; const VALID_IDENTIFIER = /^[$_a-zA-Z][a-zA-Z0-9_$]*$/; /** * Builds a key/value pair for use in a type. Automatically quotes the key if it * contains invalid JavaScript identifier characters. * * @example * * ```ts * buildPropertySignature( * "key" * b.tsLiteralType(b.tsStringKeyword()) * ); * // => key: string * ``` */ export function buildPropertySignature( keyName: string, value: TSTypeAnnotation, optional = false ) { return b.tsPropertySignature( VALID_IDENTIFIER.test(keyName) ? b.identifier(keyName) : b.stringLiteral(keyName), b.tsTypeAnnotation(value), optional ); } // Type that tracks any reference to a type literal so that we can create an AST // node from it type SupportedLiteralTypes = ManifestExtraInput["type"]; /** * Returns a TypeScript literal type from a string. * * @example * ```ts * buildKeywordLiteral("string"); * // => string * * buildKeywordLiteral("number"); * // => number * ```` */ export function buildKeywordLiteral(type: SupportedLiteralTypes) { switch (type) { case "string": return b.tsStringKeyword(); case "boolean": return b.tsBooleanKeyword(); case "number": return b.tsNumberKeyword(); default: { const _: never = type; throw new Error(`Unexpected input type: ${_}`); } } } /** * Pretty prints the TypeScript AST into a code string. */ export function printRecast(ast: recast.types.ASTNode) { return recast.prettyPrint(ast, { tabWidth: 2, quote: "double" }).code; }