Skip to content

Commit 0f047dd

Browse files
committed
fix: broken schema generation
1 parent d0d509a commit 0f047dd

File tree

5 files changed

+2183
-1152
lines changed

5 files changed

+2183
-1152
lines changed

scripts/generate-registry-types.ts

Lines changed: 155 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,163 @@ function getKind(node: any): ExtractedDeclaration['kind'] {
3636
return 'const'
3737
}
3838

39+
// --- Schema field extraction (static AST-based) ---
40+
41+
interface SchemaFieldMeta {
42+
name: string
43+
type: string
44+
required: boolean
45+
description?: string
46+
defaultValue?: string
47+
}
48+
49+
const JSDOC_START_RE = /^\s*\/\*\*/
50+
const JSDOC_END_RE = /^\s*\*\//
51+
const DOC_LINE_RE = /^\s*\*\s?(.*)/
52+
const DEFAULT_TAG_RE = /^@default\s*/
53+
const FIELD_MATCH_RE = /^\s*(\w+)\s*:/
54+
55+
function resolveAstType(node: any, source: string): string {
56+
if (!node)
57+
return 'unknown'
58+
if (node.type === 'CallExpression') {
59+
const callee = node.callee?.name
60+
if (callee === 'string')
61+
return 'string'
62+
if (callee === 'number')
63+
return 'number'
64+
if (callee === 'boolean')
65+
return 'boolean'
66+
if (callee === 'any')
67+
return 'any'
68+
if (callee === 'object')
69+
return 'object'
70+
if (callee === 'optional')
71+
return resolveAstType(node.arguments?.[0], source)
72+
if (callee === 'pipe')
73+
return resolveAstType(node.arguments?.[0], source)
74+
if (callee === 'array')
75+
return `${resolveAstType(node.arguments?.[0], source)}[]`
76+
if (callee === 'record')
77+
return `Record<${resolveAstType(node.arguments?.[0], source)}, ${resolveAstType(node.arguments?.[1], source)}>`
78+
if (callee === 'literal') {
79+
const arg = node.arguments?.[0]
80+
if (arg?.type === 'StringLiteral')
81+
return `'${arg.value}'`
82+
if (arg?.type === 'NumericLiteral')
83+
return String(arg.value)
84+
if (arg?.type === 'BooleanLiteral')
85+
return String(arg.value)
86+
return source.slice(arg?.start, arg?.end)
87+
}
88+
if (callee === 'union') {
89+
const arrArg = node.arguments?.[0]
90+
if (arrArg?.type === 'ArrayExpression') {
91+
return arrArg.elements.map((e: any) => resolveAstType(e, source)).join(' | ')
92+
}
93+
return 'unknown'
94+
}
95+
if (callee === 'custom')
96+
return 'Function'
97+
}
98+
return 'unknown'
99+
}
100+
101+
function isOptionalCall(node: any): boolean {
102+
return node?.type === 'CallExpression' && node.callee?.name === 'optional'
103+
}
104+
105+
function parseSchemaComments(code: string): Record<string, { description?: string, defaultValue?: string }> {
106+
const result: Record<string, { description?: string, defaultValue?: string }> = {}
107+
const lines = code.split('\n')
108+
let desc = ''
109+
let def = ''
110+
111+
for (const line of lines) {
112+
if (JSDOC_START_RE.test(line)) {
113+
desc = ''
114+
def = ''
115+
continue
116+
}
117+
if (JSDOC_END_RE.test(line))
118+
continue
119+
120+
const docLine = line.match(DOC_LINE_RE)
121+
if (docLine) {
122+
const content = docLine[1]!.trim()
123+
if (content.startsWith('@default'))
124+
def = content.replace(DEFAULT_TAG_RE, '')
125+
else if (!content.startsWith('@') && content)
126+
desc += (desc ? ' ' : '') + content
127+
continue
128+
}
129+
130+
const fieldMatch = line.match(FIELD_MATCH_RE)
131+
if (fieldMatch) {
132+
if (desc || def)
133+
result[fieldMatch[1]!] = { description: desc || undefined, defaultValue: def || undefined }
134+
desc = ''
135+
def = ''
136+
}
137+
}
138+
139+
return result
140+
}
141+
142+
function extractSchemaFields(node: any, source: string, code: string): SchemaFieldMeta[] | null {
143+
// node is the init of the VariableDeclarator, should be CallExpression with callee "object"
144+
if (node?.type !== 'CallExpression' || node.callee?.name !== 'object')
145+
return null
146+
const objArg = node.arguments?.[0]
147+
if (objArg?.type !== 'ObjectExpression')
148+
return null
149+
150+
const comments = parseSchemaComments(code)
151+
const fields: SchemaFieldMeta[] = []
152+
153+
for (const prop of objArg.properties || []) {
154+
if (prop.type === 'SpreadElement')
155+
continue
156+
const key = prop.key?.name || prop.key?.value
157+
if (!key)
158+
continue
159+
160+
const isOpt = isOptionalCall(prop.value)
161+
const typeNode = isOpt ? prop.value.arguments?.[0] : prop.value
162+
163+
fields.push({
164+
name: key,
165+
type: resolveAstType(typeNode, source),
166+
required: !isOpt,
167+
description: comments[key]?.description,
168+
defaultValue: comments[key]?.defaultValue,
169+
})
170+
}
171+
172+
return fields.length ? fields : null
173+
}
174+
39175
// --- Registry type extraction ---
40176

41177
// Pre-parse schemas.ts to look up re-exported schema declarations
42178
const schemasSource = readFileSync(join(registryDir, 'schemas.ts'), 'utf-8')
43179
const schemaDeclarations = new Map<string, string>()
180+
const schemaFields: Record<string, SchemaFieldMeta[]> = {}
44181
{
45182
const { program } = parseSync('schemas.ts', schemasSource)
46183
for (const node of program.body) {
47184
if (node.type === 'ExportNamedDeclaration' && node.declaration?.type === 'VariableDeclaration') {
48-
const name = node.declaration.declarations[0]?.id?.name
49-
if (name)
185+
const declarator = node.declaration.declarations[0]
186+
const name = declarator?.id?.name
187+
if (name) {
50188
schemaDeclarations.set(name, schemasSource.slice(node.start, node.end))
189+
// Extract field metadata for pre-computed schema fields
190+
if (!name.endsWith('Defaults')) {
191+
const fields = extractSchemaFields(declarator.init, schemasSource, schemasSource.slice(node.start, node.end))
192+
if (fields)
193+
schemaFields[name] = fields
194+
}
195+
}
51196
}
52197
}
53198
}
@@ -233,6 +378,7 @@ const componentToSlug: Record<string, string> = {
233378
ScriptCrisp: 'crisp',
234379
ScriptIntercom: 'intercom',
235380
ScriptGoogleAdsense: 'google-adsense',
381+
ScriptBlueskyEmbed: 'bluesky-embed',
236382
ScriptInstagramEmbed: 'instagram-embed',
237383
ScriptXEmbed: 'x-embed',
238384
ScriptLemonSqueezy: 'lemon-squeezy',
@@ -268,5 +414,10 @@ for (const [componentName, props] of Object.entries(componentProps)) {
268414
}
269415
}
270416

271-
writeFileSync(outputPath, JSON.stringify(types, null, 2))
272-
console.log(`Generated registry types for ${Object.keys(types).length} scripts (${Object.keys(componentProps).length} with component props)`)
417+
const output = {
418+
types,
419+
schemaFields,
420+
}
421+
422+
writeFileSync(outputPath, `${JSON.stringify(output, null, 2)}\n`)
423+
console.log(`Generated registry types for ${Object.keys(types).length} scripts (${Object.keys(componentProps).length} with component props, ${Object.keys(schemaFields).length} schema fields)`)

src/registry-schemas.ts

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)