Skip to content
Snippets Groups Projects
Unverified Commit 48439d28 authored by Sebastian Poxhofer's avatar Sebastian Poxhofer Committed by GitHub
Browse files

chore(util/yaml): allow to provide zod schemas to YAML parser (#26647)

parent 717ba628
No related branches found
No related tags found
No related merge requests found
import { codeBlock } from 'common-tags'; import { codeBlock } from 'common-tags';
import { z } from 'zod';
import { parseSingleYaml, parseYaml } from './yaml'; import { parseSingleYaml, parseYaml } from './yaml';
describe('util/yaml', () => { describe('util/yaml', () => {
...@@ -22,6 +23,31 @@ describe('util/yaml', () => { ...@@ -22,6 +23,31 @@ describe('util/yaml', () => {
]); ]);
}); });
it('should parse content with single document with schema', () => {
expect(
parseYaml(
codeBlock`
myObject:
aString: value
`,
null,
{
customSchema: z.object({
myObject: z.object({
aString: z.string(),
}),
}),
},
),
).toEqual([
{
myObject: {
aString: 'value',
},
},
]);
});
it('should parse content with multiple documents', () => { it('should parse content with multiple documents', () => {
expect( expect(
parseYaml(codeBlock` parseYaml(codeBlock`
...@@ -42,6 +68,60 @@ describe('util/yaml', () => { ...@@ -42,6 +68,60 @@ describe('util/yaml', () => {
]); ]);
}); });
it('should parse content with multiple documents with schema', () => {
expect(
parseYaml(
codeBlock`
myObject:
aString: foo
---
myObject:
aString: bar
`,
null,
{
customSchema: z.object({
myObject: z.object({
aString: z.string(),
}),
}),
},
),
).toEqual([
{
myObject: {
aString: 'foo',
},
},
{
myObject: {
aString: 'bar',
},
},
]);
});
it('should throw if schema does not match', () => {
expect(() =>
parseYaml(
codeBlock`
myObject:
aString: foo
---
aString: bar
`,
null,
{
customSchema: z.object({
myObject: z.object({
aString: z.string(),
}),
}),
},
),
).toThrow();
});
it('should parse content with templates', () => { it('should parse content with templates', () => {
expect( expect(
parseYaml( parseYaml(
...@@ -85,6 +165,45 @@ describe('util/yaml', () => { ...@@ -85,6 +165,45 @@ describe('util/yaml', () => {
}); });
}); });
it('should parse content with single document with schema', () => {
expect(
parseSingleYaml(
codeBlock`
myObject:
aString: value
`,
{
customSchema: z.object({
myObject: z.object({
aString: z.string(),
}),
}),
},
),
).toEqual({
myObject: {
aString: 'value',
},
});
});
it('should throw with single document with schema if parsing fails', () => {
expect(() =>
parseSingleYaml(
codeBlock`
myObject: foo
`,
{
customSchema: z.object({
myObject: z.object({
aString: z.string(),
}),
}),
},
),
).toThrow();
});
it('should parse content with multiple documents', () => { it('should parse content with multiple documents', () => {
expect(() => expect(() =>
parseSingleYaml(codeBlock` parseSingleYaml(codeBlock`
......
...@@ -5,28 +5,52 @@ import { ...@@ -5,28 +5,52 @@ import {
load as single, load as single,
dump as upstreamDump, dump as upstreamDump,
} from 'js-yaml'; } from 'js-yaml';
import type { ZodType } from 'zod';
import { regEx } from './regex'; import { regEx } from './regex';
interface YamlOptions extends LoadOptions { type YamlOptions<
ResT = unknown,
Schema extends ZodType<ResT> = ZodType<ResT>,
> = {
customSchema?: Schema;
removeTemplates?: boolean; removeTemplates?: boolean;
} } & LoadOptions;
export function parseYaml( export function parseYaml<ResT = unknown>(
content: string, content: string,
iterator?: null | undefined, iterator?: null | undefined,
options?: YamlOptions, options?: YamlOptions<ResT>,
): unknown[] { ): ResT[] {
const massagedContent = massageContent(content, options); const massagedContent = massageContent(content, options);
return multiple(massagedContent, iterator, 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 singleParsed = schema.parse(element);
parsed.push(singleParsed);
}
return parsed;
} }
export function parseSingleYaml( export function parseSingleYaml<ResT = unknown>(
content: string, content: string,
options?: YamlOptions, options?: YamlOptions<ResT>,
): unknown { ): ResT {
const massagedContent = massageContent(content, options); const massagedContent = massageContent(content, options);
return single(massagedContent, 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 { export function dump(obj: any, opts?: DumpOptions | undefined): string {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment