Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/large-rabbits-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@traversable/valibot": patch
---

feat(valibot): adds `vx.toType` JSDoc support for `v.title`, `v.metadata` and defaults
46 changes: 36 additions & 10 deletions packages/valibot/src/to-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,17 +190,30 @@ const fold = F.fold<string>((x, ix, input) => {
const REST = 'rest' in option && typeof option.rest === 'string' ? `{ [x: string]: ${option.rest} }` : ''
const OPT = Object.entries(original.entries).filter(([, v]) => hasOptional(v)).map(([k]) => k)
const xs = Object.entries(option.entries).map(
([k, v]) => {
const description = getDescription(original[k])
([k, value]) => {
const originalValue = original.entries[k]
const description = v.getDescription(originalValue)
const title = v.getTitle(originalValue)
const default_ = v.getDefault(originalValue)
const metadata = v.getMetadata(originalValue)
const $description = description === undefined ? null : ` * @description ${escapeJsDoc(description)}`
const $title = title === undefined ? null : ` * @title ${escapeJsDoc(title)}`
const $default = default_ === undefined ? null : ` * @default ${escapeJsDoc(JSON.stringify(default_, null, 2).split('\n').map((line) => ` * ${line}`).join('\n'))}`
const $metadata = Object.keys(metadata).length === 0 ? null : ` * @metadata \n${JSON.stringify(metadata, null, 2).split('\n').map((line) => ` * ${line}`).join('\n')}`
const hasJsDoc = preserveJsDocsEnabled(ix) && ($default !== null || $description !== null || $title !== null || $metadata !== null)

const READONLY = isReadonly(original.entries[k]) ? 'readonly ' : ''
const JSDOCS = description == null || !preserveJsDocsEnabled(ix) ? null : [
const JSDOCS = !hasJsDoc ? null : [
'\n/**',
` * ${escapeJsDoc(description)}`,
$description,
$default,
$title,
$metadata,
' */',
].filter((_) => _ !== null)
return [
JSDOCS === null ? null : JSDOCS.join('\n'),
READONLY + parseKey(k) + (OPT.includes(k) ? '?: ' : ': ') + v,
READONLY + parseKey(k) + (OPT.includes(k) ? '?: ' : ': ') + value,
].filter((_) => _ !== null).join('\n')
}
)
Expand Down Expand Up @@ -244,17 +257,30 @@ const fold = F.fold<string>((x, ix, input) => {
const READONLY_OPEN = isReadonly(input) ? 'Readonly<' : ''
const READONLY_CLOSE = isReadonly(input) ? '>' : ''
const xs = Object.entries(x.entries).map(
([k, v]) => {
const description = getDescription(input.entries[k])
([k, value]) => {
const originalValue = input.entries[k]
const description = v.getDescription(originalValue)
const title = v.getTitle(originalValue)
const default_ = v.getDefault(originalValue)
const metadata = v.getMetadata(originalValue)
const $description = description === undefined ? null : ` * @description ${escapeJsDoc(description)}`
const $title = title === undefined ? null : ` * @title ${escapeJsDoc(title)}`
const $default = default_ === undefined ? null : ` * @default ${escapeJsDoc(JSON.stringify(default_, null, 2).split('\n').map((line) => ` * ${line}`).join('\n'))}`
const $metadata = Object.keys(metadata).length === 0 ? null : ` * @metadata \n${JSON.stringify(metadata, null, 2).split('\n').map((line) => ` * ${line}`).join('\n')}`
const hasJsDoc = preserveJsDocsEnabled(ix) && ($default !== null || $description !== null || $title !== null || $metadata !== null)

const READONLY = isReadonly(input.entries[k]) ? 'readonly ' : ''
const JSDOCS = description == null || !preserveJsDocsEnabled(ix) ? null : [
const JSDOCS = !hasJsDoc ? null : [
'\n/**',
` * ${escapeJsDoc(description)}`,
$description,
$default,
$title,
$metadata,
' */',
].filter((_) => _ !== null)
return [
JSDOCS === null ? null : JSDOCS.join('\n'),
READONLY + parseKey(k) + (OPT.includes(k) ? '?: ' : ': ') + v,
READONLY + parseKey(k) + (OPT.includes(k) ? '?: ' : ': ') + value,
].filter((_) => _ !== null).join('\n')
}
)
Expand Down
54 changes: 51 additions & 3 deletions packages/valibot/test/to-type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { vx } from '@traversable/valibot'
const format = (src: string) => prettier.format(src, { parser: 'typescript', semi: false })

vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/valibot❳: vx.toType', () => {
vi.test('〖️⛳️〗› ❲vx.toType❳: jsdocs', () => {
vi.test('〖️⛳️〗› ❲vx.toType❳: jsdoc descriptions', () => {
vi.expect.soft(format(
vx.toType(
v.object({}),
Expand All @@ -31,17 +31,65 @@ vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/valibot❳: vx.toType', ()
(`
"type Type = {
/**
* abc description
* @description abc description
*/
abc: number
/**
* def description
* @description def description
*/
def: number
}
"
`)
})

vi.test('〖️⛳️〗› ❲vx.toType❳: jsdoc metadata', () => {
vi.expect.soft(format(
vx.toType(
v.object({
abc: v.pipe(
v.string(),
v.metadata({ a: 1, b: [2, 3, 4, 5] })
)
}),
{ preserveJsDocs: true }
)
)).toMatchInlineSnapshot
(`
"{
/**
* @metadata
* {
* "a": 1,
* "b": [
* 2,
* 3,
* 4,
* 5
* ]
* }
*/
abc: string
}
"
`)
})

vi.test('〖️⛳️〗› ❲vx.toType❳: setting `preserveJsDocs` to false ignores jsdocs', () => {
vi.expect.soft(format(
vx.toType(
v.object({
abc: v.pipe(v.number(), v.description('abc description')),
def: v.pipe(v.number(), v.description('def description')),
}),
{ typeName: 'Type', preserveJsDocs: false }
)
)).toMatchInlineSnapshot
(`
"type Type = { abc: number; def: number }
"
`)
})
})

vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/valibot❳: vx.toType', () => {
Expand Down
Loading