Skip to content

Commit b0fcb42

Browse files
committed
refactor: fix typedocs
1 parent 75aad46 commit b0fcb42

File tree

92 files changed

+2454
-111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+2454
-111
lines changed

scripts/src/generate-type-docs.ts

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,21 @@ async function extractPropertiesOfTypeName(
5454
for (const typeStatement of typeStatements) {
5555
const properties: TypeProperties = {}
5656
const type = typeChecker.getTypeAtLocation(typeStatement)
57+
const typeName = (typeStatement as any).name.getText()
58+
5759
for (const property of type.getProperties()) {
5860
const propertyName = property.getName()
5961
const type = typeChecker.getTypeOfSymbolAtLocation(property, sourceFile)
6062
const nonNullableType = type.getNonNullableType()
61-
const typeName = typeChecker.typeToString(nonNullableType)
62-
const isRequired = nonNullableType === type && typeName !== 'any'
63+
const typeNameStr = typeChecker.typeToString(nonNullableType)
64+
const isRequired = nonNullableType === type && typeNameStr !== 'any'
6365

6466
const defaultValue = property.getJsDocTags().filter((tag) => tag.name === 'default')[0]?.text?.[0].text
6567

6668
if (shouldIgnoreProperty(property)) {
6769
continue
6870
}
69-
const prettyType = await tryPrettier(typeName)
71+
const prettyType = await tryPrettier(typeNameStr)
7072
properties[propertyName] = {
7173
type: prettyType,
7274
defaultValue,
@@ -78,8 +80,9 @@ async function extractPropertiesOfTypeName(
7880
.join('\n') || undefined,
7981
}
8082
}
83+
8184
if (Object.keys(properties).length) {
82-
results[(typeStatement as any).name.getText()] = Object.fromEntries(
85+
results[typeName] = Object.fromEntries(
8386
Object.entries(properties)
8487
.sort(([aName], [bName]) => aName.localeCompare(bName))
8588
.sort(([, a], [, b]) => (a.isRequired === b.isRequired ? 0 : a.isRequired ? -1 : 1))
@@ -98,12 +101,17 @@ async function extractPropertiesOfTypeName(
98101
return Object.keys(foo).length ? results : null
99102
}
100103

101-
async function createTypeSearch(tsConfigPath: string, typeSearchOptions: TypeSearchOptions = {}) {
104+
async function createTypeSearch(
105+
tsConfigPath: string,
106+
componentPath: string,
107+
typeSearchOptions: TypeSearchOptions = {},
108+
) {
102109
const { shouldIgnoreProperty } = typeSearchOptions
103110
const configFile = ts.readConfigFile(tsConfigPath, ts.sys.readFile)
104111
const { options } = ts.parseJsonConfigFileContent(configFile.config, ts.sys, './dist')
105112

106-
const files = globbySync('./dist/')
113+
// Scope file search to the specific component directory to avoid type name collisions
114+
const files = globbySync(`${componentPath}/**/*.d.ts`)
107115
const program = ts.createProgram(files, options)
108116
const sourceFiles = program.getSourceFiles()
109117

@@ -130,7 +138,13 @@ function getSourceFileName(symbol: ts.Symbol): string | undefined {
130138

131139
function shouldIgnoreProperty(property: ts.Symbol) {
132140
const sourceFileName = getSourceFileName(property)
133-
const isExternal = sourceFileName?.includes('node_modules') && !sourceFileName?.includes('@zag-js')
141+
142+
// Allow properties without source files (can happen with complex type resolution)
143+
if (!sourceFileName) {
144+
return false
145+
}
146+
147+
const isExternal = sourceFileName.includes('node_modules') && !sourceFileName.includes('@zag-js')
134148
const isExcludedByName = ['children'].includes(property.getName())
135149
return isExternal || isExcludedByName
136150
}
@@ -144,12 +158,15 @@ function extractTypeExports(fileContent?: string) {
144158
return sourceFile
145159
.forEachDescendantAsArray()
146160
.filter((node): node is ExportDeclaration => Node.isExportDeclaration(node))
147-
.flatMap((node) =>
148-
node
161+
.flatMap((node) => {
162+
// Check if the export declaration itself is type-only (export type {})
163+
const isTypeOnlyExport = node.isTypeOnly()
164+
165+
return node
149166
.getNamedExports()
150-
.filter((namedExport) => namedExport.isTypeOnly())
151-
.map((namedExport) => namedExport.getAliasNode()?.getText() ?? namedExport.getName()),
152-
)
167+
.filter((namedExport) => isTypeOnlyExport || namedExport.isTypeOnly())
168+
.map((namedExport) => namedExport.getAliasNode()?.getText() ?? namedExport.getName())
169+
})
153170
.sort()
154171
}
155172

@@ -180,13 +197,19 @@ const extractTypesForFramework = async (framework: string) => {
180197
),
181198
)
182199

183-
const searchType = await createTypeSearch('tsconfig.json', {
184-
shouldIgnoreProperty,
185-
})
186-
187200
const result = await Promise.all(
188201
Object.entries(componentExportMap).flatMap(async ([component, typeExports]) => {
202+
// Convert component path to dist path for type search
203+
// e.g., src/components/foo -> dist/components/foo
204+
const componentDistPath = component.replace(/^src\//, 'dist/')
205+
206+
// Create a type searcher scoped to this specific component to avoid type name collisions
207+
const searchType = await createTypeSearch('tsconfig.json', componentDistPath, {
208+
shouldIgnoreProperty,
209+
})
210+
189211
const resolvedTypeExports = await Promise.all(typeExports.map(async (type) => await searchType(type)))
212+
190213
return {
191214
component,
192215
typeExports: resolvedTypeExports
@@ -272,8 +295,11 @@ const extractTypesForFramework = async (framework: string) => {
272295
return { ...acc, ...value }
273296
}, {})
274297

298+
const filename = `${path.basename(component)}.types.json`
299+
const filePath = path.join(outDir, framework, filename)
300+
275301
fs.outputFileSync(
276-
path.join(outDir, framework, `${path.basename(component)}.types.json`),
302+
filePath,
277303
await prettier.format(JSON.stringify(typeExportsWithElement), {
278304
...prettierConfig,
279305
parser: 'json',

website/src/content/types/react/angle-slider.types.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@
5252
},
5353
"Root": {
5454
"props": {
55+
"aria-label": {
56+
"type": "string",
57+
"isRequired": false,
58+
"description": "The accessible label for the slider thumb."
59+
},
60+
"aria-labelledby": {
61+
"type": "string",
62+
"isRequired": false,
63+
"description": "The id of the element that labels the slider thumb."
64+
},
5565
"asChild": {
5666
"type": "boolean",
5767
"isRequired": false,
@@ -66,7 +76,7 @@
6676
"disabled": { "type": "boolean", "isRequired": false, "description": "Whether the slider is disabled." },
6777
"id": { "type": "string", "isRequired": false, "description": "The unique identifier of the machine." },
6878
"ids": {
69-
"type": "Partial<{ root: string; thumb: string; hiddenInput: string; control: string; valueText: string }>",
79+
"type": "Partial<{\n root: string\n thumb: string\n hiddenInput: string\n control: string\n valueText: string\n label: string\n}>",
7080
"isRequired": false,
7181
"description": "The ids of the elements in the machine.\nUseful for composition."
7282
},

0 commit comments

Comments
 (0)