import { DumpOptions, LoadOptions, loadAll as multiple, load as single, dump as upstreamDump, } from 'js-yaml'; import type { ZodType } from 'zod'; import { logger } from '../logger'; import { regEx } from './regex'; interface YamlOptions< ResT = unknown, Schema extends ZodType = ZodType, > extends LoadOptions { customSchema?: Schema; removeTemplates?: boolean; } interface YamlOptionsMultiple< ResT = unknown, Schema extends ZodType = ZodType, > extends YamlOptions { failureBehaviour?: 'throw' | 'filter'; } export function parseYaml( content: string, iterator?: null | undefined, options?: YamlOptionsMultiple, ): ResT[] { const massagedContent = massageContent(content, options); const rawDocuments = multiple(massagedContent, iterator, options); const schema = options?.customSchema; if (!schema) { return rawDocuments as ResT[]; } const parsed: ResT[] = []; for (const element of rawDocuments) { const result = schema.safeParse(element); if (result.success) { parsed.push(result.data); continue; } if (options?.failureBehaviour !== 'filter') { throw new Error('Failed to parse YAML file', { cause: result.error }); } logger.trace( { error: result.error, document: element }, 'Failed to parse schema for YAML', ); } return parsed; } export function parseSingleYaml( content: string, options?: YamlOptions, ): ResT { const massagedContent = massageContent(content, options); const rawDocument = single(massagedContent, options); const schema = options?.customSchema; if (!schema) { return rawDocument as ResT; } return schema.parse(rawDocument); } export function dump(obj: any, opts?: DumpOptions | undefined): string { return upstreamDump(obj, opts); } function massageContent(content: string, options?: YamlOptions): string { if (options?.removeTemplates) { return content .replace(regEx(/{{`.+?`}}/gs), '') .replace(regEx(/{{.+?}}/g), '') .replace(regEx(/{%`.+?`%}/gs), '') .replace(regEx(/{%.+?%}/g), ''); } return content; }