Skip to content

Commit

Permalink
feat: return parse results unless fatal
Browse files Browse the repository at this point in the history
  • Loading branch information
zce committed May 8, 2024
1 parent dcd1c58 commit 4599972
Show file tree
Hide file tree
Showing 17 changed files with 60 additions and 37 deletions.
2 changes: 1 addition & 1 deletion docs/guide/custom-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const remoteImage = () =>
return { src: value, ...metadata }
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
addIssue({ code: 'custom', message })
addIssue({ fatal: true, code: 'custom', message })
return null as never
}
})
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/using-mdx.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ export const mdxBundle = (options: MdxOptions = {}) =>
custom<string>().transform<string>(async (value, { meta: { path, content, config }, addIssue }) => {
value = value ?? content
if (value == null) {
addIssue({ code: 'custom', message: 'No content found' })
addIssue({ fatal: true, code: 'custom', message: 'No content found' })
return null as never
}

Expand All @@ -341,7 +341,7 @@ export const mdxBundle = (options: MdxOptions = {}) =>
try {
return await compileMdx(value, path, compilerOptions)
} catch (err: any) {
addIssue({ code: 'custom', message: err.message })
addIssue({ fatal: true, code: 'custom', message: err.message })
return null as never
}
})
Expand Down
4 changes: 2 additions & 2 deletions docs/other/snippets.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const remoteImage = () =>
return { src: value, ...metadata }
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
addIssue({ code: 'custom', message })
addIssue({ fatal: true, code: 'custom', message })
return null as never
}
})
Expand Down Expand Up @@ -223,7 +223,7 @@ export const excerpt = ({ separator = 'more', length = 300 }: ExcerptOptions = {
await rehypeCopyLinkedFiles(config.output)(output, { path })
return toHtml(output)
} catch (err: any) {
ctx.addIssue({ code: 'custom', message: err.message })
ctx.addIssue({ fatal: true, code: 'custom', message: err.message })
return value
}
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
title: Style Guide
slug: style-guide
date: 1970-01-01 00:00:00
cover: cover.jpg
---
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
title: Hello world
slug: hello-world
date: 2024-05-08 13:00:00
cover: cover.jpg
video: video.mp4
Expand Down
45 changes: 35 additions & 10 deletions src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ import { resolveConfig } from './config'
import { VeliteFile } from './file'
import { logger } from './logger'
import { outputAssets, outputData, outputEntry } from './output'
import { getParsedType, ParseContext } from './schemas/zod'

import type { LogLevel } from './logger'
import type { Schema } from './schemas'
import type { Schema, ZodMeta } from './schemas'
import type { Config } from './types'

declare module './schemas' {
interface ZodMeta extends VeliteFile {}
}

// cache resolved result for rebuild
const resolved = new Map<string, VeliteFile[]>()

Expand All @@ -33,28 +38,48 @@ const load = async (config: Config, path: string, schema: Schema, changed?: stri
if (exists) return exists
}

const meta = await VeliteFile.create({ path, config })
const file = await VeliteFile.create({ path, config })

// may be one or more records in one file, such as yaml array or json array
const isArr = Array.isArray(meta.records)
const list = isArr ? meta.records : [meta.records]
const isArr = Array.isArray(file.records)
const list = isArr ? file.records : [file.records]

const parsed = await Promise.all(
list.map(async (item, index) => {
list.map(async (data, index) => {
// push index in path if file is array
const path = isArr ? [index] : []

const ctx: ParseContext = {
common: { issues: [], async: true },
path,
meta: file as ZodMeta,
data,
parent: null,
parsedType: getParsedType(data),
schemaErrorMap: schema._def.errorMap
}

// parse data with given schema
const result = await schema.safeParseAsync(item, { path, meta } as any)
if (result.success) return result.data
const ret = schema._parse({ data, path, meta: ctx.meta, parent: ctx })

const result = await (ret instanceof Promise ? ret : Promise.resolve(ret))

if (result.status === 'valid') return result.value

// report error if parsing failed
result.error.issues.forEach(issue => meta.message(issue.message, { source: issue.path.join('.') }))
ctx.common.issues.forEach(issue => {
const message = file.message(issue.message, { source: issue.path.join('.') })
message.fatal = issue.fatal
})

return result.status === 'dirty' && result.value
})
)

// logger.log(`loaded '${path}' with ${parsed.length} records`)
meta.result = isArr ? parsed : parsed[0]
file.result = isArr ? parsed : parsed[0]

return meta
return file
}

/**
Expand Down
4 changes: 0 additions & 4 deletions src/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,3 @@ export class VeliteFile extends VFile {
return meta
}
}

declare module './schemas' {
interface ZodMeta extends VeliteFile {}
}
5 changes: 2 additions & 3 deletions src/schemas/excerpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ export interface ExcerptOptions {
}

export const excerpt = ({ length = 260 }: ExcerptOptions = {}) =>
custom<string>().transform<string>(async (value, { meta, addIssue }) => {
custom<string>().transform<string>(async (value, { meta: { plain }, addIssue }) => {
if (value != null) return value.slice(0, length)
const { plain } = meta
if (plain == null) {
addIssue({ code: 'custom', message: 'No excerpt found' })
return null as never
return ''
}
return plain.slice(0, length)
})
2 changes: 1 addition & 1 deletion src/schemas/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const file = ({ allowNonRelativePath = true }: FileOptions = {}) =>
return await processAsset(value, path, config.output.name, config.output.base)
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
addIssue({ code: 'custom', message })
addIssue({ fatal: true, code: 'custom', message })
return null as never
}
})
2 changes: 1 addition & 1 deletion src/schemas/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const image = ({ absoluteRoot }: ImageOptions = {}) =>
return await processAsset(value, path, config.output.name, config.output.base, true)
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
addIssue({ code: 'custom', message })
addIssue({ fatal: true, code: 'custom', message })
return null as never
}
})
6 changes: 3 additions & 3 deletions src/schemas/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ const rehypeMetaString = () => (tree: Hast) => {
export const markdown = (options: MarkdownOptions = {}) =>
custom<string>().transform<string>(async (value, { meta: { path, content, config }, addIssue }) => {
value = value ?? content
if (value == null) {
if (value == null || value.length === 0) {
addIssue({ code: 'custom', message: 'No content found' })
return null as never
return ''
}

const enableGfm = options.gfm ?? config.markdown?.gfm ?? true
Expand Down Expand Up @@ -73,7 +73,7 @@ export const markdown = (options: MarkdownOptions = {}) =>
.process({ value, path })
return html.toString()
} catch (err: any) {
addIssue({ code: 'custom', message: err.message })
addIssue({ fatal: true, code: 'custom', message: err.message })
return null as never
}
})
6 changes: 3 additions & 3 deletions src/schemas/mdx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ const remarkRemoveComments = () => (tree: Root) => {
export const mdx = (options: MdxOptions = {}) =>
custom<string>().transform<string>(async (value, { meta: { path, content, config }, addIssue }) => {
value = value ?? content
if (value == null) {
if (value == null || value.length === 0) {
addIssue({ code: 'custom', message: 'No content found' })
return null as never
return ''
}

const enableGfm = options.gfm ?? config.mdx?.gfm ?? true
Expand Down Expand Up @@ -61,7 +61,7 @@ export const mdx = (options: MdxOptions = {}) =>
})
return minified.code ?? code.toString()
} catch (err: any) {
addIssue({ code: 'custom', message: err.message })
addIssue({ fatal: true, code: 'custom', message: err.message })
return null as never
}
})
2 changes: 1 addition & 1 deletion src/schemas/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export interface Metadata {
export const metadata = () =>
custom<string>().transform<Metadata>(async (value, { meta, addIssue }) => {
value = value ?? meta.plain
if (value == null) {
if (value == null || value.length === 0) {
addIssue({ code: 'custom', message: 'No content found' })
return { readingTime: 0, wordCount: 0 }
}
Expand Down
2 changes: 1 addition & 1 deletion src/schemas/slug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const slug = (by: string = 'global', reserved: string[] = []) =>
.superRefine((value, { path, meta: { path: filepath, config }, addIssue }) => {
const key = `schemas:slug:${by}:${value}`
if (config.cache.has(key)) {
addIssue({ code: 'custom', message: `duplicate slug '${value}' in '${filepath}:${path.join('.')}'` })
addIssue({ fatal: true, code: 'custom', message: `duplicate slug '${value}' in '${filepath}:${path.join('.')}'` })
} else {
config.cache.set(key, filepath)
}
Expand Down
5 changes: 4 additions & 1 deletion src/schemas/toc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ const parse = (tree?: List): TocEntry[] => {

export const toc = <T extends TocOptions>(options?: T) =>
custom<string>().transform<T extends { original: true } ? TocTree : TocEntry[]>(async (value, { meta, addIssue }) => {
if (value == null || value.length === 0) {
return (options?.original ? {} : []) as T extends { original: true } ? TocTree : TocEntry[]
}
try {
// extract ast tree from markdown/mdx content
const tree = value != null ? fromMarkdown(value) : meta.mdast
Expand All @@ -108,7 +111,7 @@ export const toc = <T extends TocOptions>(options?: T) =>
if (options?.original) return tocTree as T extends { original: true } ? TocTree : TocEntry[]
return parse(tocTree.map) as T extends { original: true } ? TocTree : TocEntry[]
} catch (err: any) {
addIssue({ code: 'custom', message: err.message })
addIssue({ fatal: true, code: 'custom', message: err.message })
return null as never
}
})
2 changes: 1 addition & 1 deletion src/schemas/unique.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const unique = (by: string = 'global') =>
string().superRefine((value, { path, meta: { path: filepath, config }, addIssue }) => {
const key = `schemas:unique:${by}:${value}`
if (config.cache.has(key)) {
addIssue({ code: 'custom', message: `duplicate value '${value}' in '${filepath}:${path.join('.')}'` })
addIssue({ fatal: true, code: 'custom', message: `duplicate value '${value}' in '${filepath}:${path.join('.')}'` })
} else {
config.cache.set(key, filepath)
}
Expand Down
4 changes: 3 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ export interface Collections {
/**
* Collection Type
*/
export type CollectionType<T extends Collections, P extends keyof T> = T[P]['single'] extends true ? T[P]['schema']['_output'] : Array<T[P]['schema']['_output']>
export type CollectionType<T extends Collections, P extends keyof T> = T[P]['single'] extends true
? T[P]['schema']['_output']
: Array<T[P]['schema']['_output']>

/**
* All collections result
Expand Down

0 comments on commit 4599972

Please sign in to comment.