Skip to content

Commit 4db6583

Browse files
committed
docs: resolve API hover to correct directory name
The Shiki transformer was mapping composable names to incorrect API cache keys. For example, createStackPlugin mapped to createStack, but the API cache key is useStack (the directory name). Added V0_COMPOSABLE_TO_DIR mapping that maps each exported function to its containing directory, which is used as the API cache key.
1 parent ae6d09b commit 4db6583

File tree

2 files changed

+47
-40
lines changed

2 files changed

+47
-40
lines changed

apps/docs/build/generate-api-whitelist.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,16 @@ async function getComponentNames (): Promise<string[]> {
4646
*
4747
* This automatically picks up any exported function matching composable patterns
4848
* (useX, createX, toX, provideX) without needing manual maintenance.
49+
*
50+
* Returns both a set of all names and a mapping from function name to directory name
51+
* (the directory name is the API cache key).
4952
*/
50-
async function getComposableNames (): Promise<string[]> {
53+
async function getComposableData (): Promise<{
54+
names: string[]
55+
toDir: Record<string, string>
56+
}> {
5157
const names = new Set<string>()
58+
const toDir: Record<string, string> = {}
5259
const dirs = await readdir(COMPOSABLES_DIR)
5360

5461
// Pattern for composable function names we care about
@@ -69,29 +76,41 @@ async function getComposableNames (): Promise<string[]> {
6976
const fnName = match[1]
7077
if (COMPOSABLE_PATTERN.test(fnName)) {
7178
names.add(fnName)
79+
toDir[fnName] = dir
7280
}
7381
}
7482

7583
// Also add the directory name itself (the primary export)
7684
names.add(dir)
85+
toDir[dir] = dir
7786
}
7887

7988
// Add trinity return values that are commonly used
8089
names.add('useContext')
8190
names.add('provideContext')
91+
toDir['useContext'] = 'createContext'
92+
toDir['provideContext'] = 'createContext'
8293

83-
return Array.from(names).toSorted()
94+
return {
95+
names: Array.from(names).toSorted(),
96+
toDir,
97+
}
8498
}
8599

86100
/**
87101
* Generate the whitelist TypeScript file
88102
*/
89103
export async function generateApiWhitelist (): Promise<void> {
90-
const [components, composables] = await Promise.all([
104+
const [components, composableData] = await Promise.all([
91105
getComponentNames(),
92-
getComposableNames(),
106+
getComposableData(),
93107
])
94108

109+
const { names: composables, toDir } = composableData
110+
111+
// Sort toDir entries for stable output
112+
const toDirEntries = Object.entries(toDir).toSorted((a, b) => a[0].localeCompare(b[0]))
113+
95114
const content = `/**
96115
* AUTO-GENERATED - DO NOT EDIT
97116
*
@@ -120,6 +139,14 @@ ${components.map(c => ` '${c}',`).join('\n')}
120139
export const V0_COMPOSABLES = new Set([
121140
${composables.map(c => ` '${c}',`).join('\n')}
122141
])
142+
143+
/**
144+
* Maps composable function names to their directory name (API cache key).
145+
* Used by the shiki-api-transformer to resolve the correct API page.
146+
*/
147+
export const V0_COMPOSABLE_TO_DIR: Record<string, string> = {
148+
${toDirEntries.map(([fn, dir]) => ` '${fn}': '${dir}',`).join('\n')}
149+
}
123150
`
124151

125152
// Only write if content changed to avoid triggering Vite's file watcher loop

apps/docs/build/shiki-api-transformer.ts

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import type { ShikiTransformer } from 'shiki'
1414

1515
// Auto-generated whitelists from packages/0/src/
16-
import { V0_COMPONENTS, V0_COMPOSABLES } from './generated/api-whitelist'
16+
import { V0_COMPONENTS, V0_COMPOSABLES, V0_COMPOSABLE_TO_DIR } from './generated/api-whitelist'
1717
// Vue API content - import only keys for build-time detection
1818
import { VUE_API_CONTENT } from './vue-api-content'
1919

@@ -34,15 +34,18 @@ const TRINITY_RETURNS: Record<string, string> = {
3434
const VUE_API_NAMES = new Set(Object.keys(VUE_API_CONTENT))
3535

3636
/**
37-
* Maps composable names to their canonical API page.
37+
* Maps composable names to their canonical API page (directory name).
3838
* Returns the API name if valid, null otherwise.
3939
*
40-
* Patterns:
41-
* - createX -> createX (the canonical API page)
42-
* - useX -> createX (trinity return maps to factory)
43-
* - createXContext -> createX (variant maps to base)
44-
* - createXPlugin -> createX or useX (createStackPlugin -> createStack, createStoragePlugin -> useStorage)
45-
* - useContext/provideContext -> createContext (special trinity returns)
40+
* Uses the V0_COMPOSABLE_TO_DIR mapping generated from source directories.
41+
* Each function name maps to its containing directory, which is the API cache key.
42+
*
43+
* Examples:
44+
* - createStackPlugin -> useStack (the directory containing it)
45+
* - createStackContext -> useStack
46+
* - useStack -> useStack
47+
* - createSelection -> createSelection
48+
* - useContext -> createContext (trinity return value)
4649
*/
4750
function resolveComposable (name: string): { apiName: string } | null {
4851
// Check special trinity return values first (e.g., useContext -> createContext)
@@ -56,36 +59,13 @@ function resolveComposable (name: string): { apiName: string } | null {
5659
return null
5760
}
5861

59-
// For useX, check if createX exists (trinity pattern)
60-
// useStack -> createStack, useGroup -> createGroup
61-
if (name.startsWith('use')) {
62-
const base = name.slice(3) // 'useStack' -> 'Stack'
63-
const createVersion = `create${base}`
64-
if (V0_COMPOSABLES.has(createVersion)) {
65-
return { apiName: createVersion }
66-
}
67-
}
68-
69-
// For createXContext/createXPlugin, map to createX or useX
70-
// createStackPlugin -> createStack, createStoragePlugin -> useStorage
71-
if (name.startsWith('create')) {
72-
const withoutPrefix = name.slice(6) // 'createStackPlugin' -> 'StackPlugin'
73-
const base = withoutPrefix.replace(/(Plugin|Context)$/, '')
74-
if (base && base !== withoutPrefix) {
75-
// Try createX first (trinity factories)
76-
const createVersion = `create${base}`
77-
if (V0_COMPOSABLES.has(createVersion)) {
78-
return { apiName: createVersion }
79-
}
80-
// Fall back to useX (plugin composables like useStorage, useTheme)
81-
const useVersion = `use${base}`
82-
if (V0_COMPOSABLES.has(useVersion)) {
83-
return { apiName: useVersion }
84-
}
85-
}
62+
// Use the mapping to get the directory name (API cache key)
63+
const dirName = V0_COMPOSABLE_TO_DIR[name]
64+
if (dirName) {
65+
return { apiName: dirName }
8666
}
8767

88-
// Direct match - use as-is (createX, toX, etc.)
68+
// Fallback: use the name as-is (shouldn't happen if whitelist is in sync)
8969
return { apiName: name }
9070
}
9171

0 commit comments

Comments
 (0)