@usecross/docs 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +158 -5
- package/dist/index.js +1260 -31
- package/dist/index.js.map +1 -1
- package/dist/ssr.d.ts +1 -1
- package/dist/types-_anC1UJu.d.ts +320 -0
- package/package.json +1 -1
- package/src/components/Sidebar.tsx +171 -28
- package/src/components/TableOfContents.tsx +1 -1
- package/src/components/api/APILayout.tsx +231 -0
- package/src/components/api/APIPage.tsx +216 -0
- package/src/components/api/Breadcrumb.tsx +98 -0
- package/src/components/api/ClassDoc.tsx +257 -0
- package/src/components/api/CodeSpan.tsx +53 -0
- package/src/components/api/Docstring.tsx +269 -0
- package/src/components/api/FunctionDoc.tsx +130 -0
- package/src/components/api/ModuleDoc.tsx +298 -0
- package/src/components/api/ParameterTable.tsx +183 -0
- package/src/components/api/Signature.tsx +317 -0
- package/src/components/api/TableOfContents.tsx +50 -0
- package/src/components/api/index.ts +17 -0
- package/src/components/index.ts +12 -0
- package/src/index.ts +31 -0
- package/src/types.ts +218 -2
- package/dist/types-Qbhh2Ihz.d.ts +0 -136
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { GriffeFunction, GriffeDocstringElement } from '../../types'
|
|
2
|
+
import { Signature } from './Signature'
|
|
3
|
+
import { Docstring } from './Docstring'
|
|
4
|
+
import { ParameterTable } from './ParameterTable'
|
|
5
|
+
import { CodeSpan } from './CodeSpan'
|
|
6
|
+
import { Markdown } from '../Markdown'
|
|
7
|
+
|
|
8
|
+
interface FunctionDocProps {
|
|
9
|
+
fn: GriffeFunction
|
|
10
|
+
/** Whether this is a method (inside a class) */
|
|
11
|
+
isMethod?: boolean
|
|
12
|
+
/** Show function name as title */
|
|
13
|
+
showName?: boolean
|
|
14
|
+
/** GitHub repo URL for source links */
|
|
15
|
+
githubUrl?: string
|
|
16
|
+
/** Additional CSS class */
|
|
17
|
+
className?: string
|
|
18
|
+
/** Override display path (e.g., for aliases to show alias name instead of target path) */
|
|
19
|
+
displayPath?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Renders documentation for a function or method matching strawberry.rocks design.
|
|
24
|
+
*/
|
|
25
|
+
export function FunctionDoc({ fn, isMethod = false, showName = true, githubUrl, className = '', displayPath }: FunctionDocProps) {
|
|
26
|
+
const hasParams = fn.parameters && fn.parameters.filter(p => p.name !== 'self').length > 0
|
|
27
|
+
|
|
28
|
+
// Get returns description from docstring
|
|
29
|
+
const returnsSection = fn.docstring?.parsed?.find(s => s.kind === 'returns')
|
|
30
|
+
const returnsValue = returnsSection?.value
|
|
31
|
+
const returnsDescription = Array.isArray(returnsValue)
|
|
32
|
+
? (returnsValue[0] as GriffeDocstringElement)?.description
|
|
33
|
+
: undefined
|
|
34
|
+
|
|
35
|
+
// Get additional text sections (examples, notes, etc.) - all text sections after the first
|
|
36
|
+
const textSections = fn.docstring?.parsed?.filter(s => s.kind === 'text') || []
|
|
37
|
+
const additionalTextSections = textSections.slice(1) // Skip first (description)
|
|
38
|
+
|
|
39
|
+
// Get relative filepath for display (prefer package-relative path)
|
|
40
|
+
const relativeFilepath = fn.relative_package_filepath || fn.relative_filepath || fn.filepath
|
|
41
|
+
|
|
42
|
+
// Build GitHub URL for source link
|
|
43
|
+
const githubSourceUrl = githubUrl && relativeFilepath && fn.lineno
|
|
44
|
+
? `${githubUrl}/blob/main/${relativeFilepath}#L${fn.lineno}-L${fn.endlineno || fn.lineno}`
|
|
45
|
+
: undefined
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<article id={fn.name} className={`scroll-mt-20 ${className}`}>
|
|
49
|
+
{/* Function name/title */}
|
|
50
|
+
{showName && (
|
|
51
|
+
<h1 className="font-mono text-2xl font-normal text-gray-900 dark:text-white mb-8">
|
|
52
|
+
{displayPath || fn.path || fn.name}
|
|
53
|
+
</h1>
|
|
54
|
+
)}
|
|
55
|
+
|
|
56
|
+
{/* Docstring - description text only */}
|
|
57
|
+
{fn.docstring && (
|
|
58
|
+
<div className="mb-6">
|
|
59
|
+
<Docstring docstring={fn.docstring} showOnlyText />
|
|
60
|
+
</div>
|
|
61
|
+
)}
|
|
62
|
+
|
|
63
|
+
{/* Returns section */}
|
|
64
|
+
{returnsDescription && (
|
|
65
|
+
<section className="mb-6">
|
|
66
|
+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
|
67
|
+
Returns:
|
|
68
|
+
</h2>
|
|
69
|
+
<p className="text-gray-700 dark:text-gray-300">
|
|
70
|
+
{returnsDescription}
|
|
71
|
+
</p>
|
|
72
|
+
</section>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
{/* Signature section */}
|
|
76
|
+
<section className="mb-6">
|
|
77
|
+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
|
78
|
+
Signature:
|
|
79
|
+
</h2>
|
|
80
|
+
<Signature fn={fn} />
|
|
81
|
+
</section>
|
|
82
|
+
|
|
83
|
+
{/* Parameters section */}
|
|
84
|
+
{hasParams && (
|
|
85
|
+
<section id="parameters" className="mb-6">
|
|
86
|
+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
|
87
|
+
Parameters:
|
|
88
|
+
</h2>
|
|
89
|
+
<ParameterTable
|
|
90
|
+
parameters={fn.parameters!}
|
|
91
|
+
docstringSections={fn.docstring?.parsed}
|
|
92
|
+
/>
|
|
93
|
+
</section>
|
|
94
|
+
)}
|
|
95
|
+
|
|
96
|
+
{/* Additional text sections (examples, notes, etc.) */}
|
|
97
|
+
{additionalTextSections.length > 0 && (
|
|
98
|
+
<section className="mb-6 prose prose-sm dark:prose-invert max-w-none">
|
|
99
|
+
{additionalTextSections.map((section, i) => (
|
|
100
|
+
<Markdown key={i} content={section.value as string} />
|
|
101
|
+
))}
|
|
102
|
+
</section>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
{/* Footer with file path and GitHub link (only for top-level functions) */}
|
|
106
|
+
{!isMethod && relativeFilepath && (
|
|
107
|
+
<footer className="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700 space-y-4">
|
|
108
|
+
<p className="flex items-center gap-2">
|
|
109
|
+
<span className="text-sm font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
|
110
|
+
File path:
|
|
111
|
+
</span>
|
|
112
|
+
<CodeSpan allowCopy>{relativeFilepath}</CodeSpan>
|
|
113
|
+
</p>
|
|
114
|
+
{githubSourceUrl && (
|
|
115
|
+
<p>
|
|
116
|
+
<a
|
|
117
|
+
href={githubSourceUrl}
|
|
118
|
+
target="_blank"
|
|
119
|
+
rel="noopener noreferrer"
|
|
120
|
+
className="text-sm font-semibold text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 uppercase tracking-wide"
|
|
121
|
+
>
|
|
122
|
+
Open in GitHub
|
|
123
|
+
</a>
|
|
124
|
+
</p>
|
|
125
|
+
)}
|
|
126
|
+
</footer>
|
|
127
|
+
)}
|
|
128
|
+
</article>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { Link } from '@inertiajs/react'
|
|
2
|
+
import type { GriffeModule, GriffeClass, GriffeFunction, GriffeAttribute, GriffeMember, GriffeExpression } from '../../types'
|
|
3
|
+
import { Docstring } from './Docstring'
|
|
4
|
+
import { ClassDoc } from './ClassDoc'
|
|
5
|
+
import { FunctionDoc } from './FunctionDoc'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Safely render a Griffe expression or value to a string
|
|
9
|
+
*/
|
|
10
|
+
function renderValue(value: string | GriffeExpression | undefined): string {
|
|
11
|
+
if (!value) return ''
|
|
12
|
+
if (typeof value === 'string') return value
|
|
13
|
+
// Handle Griffe expression objects
|
|
14
|
+
if (value.str) return value.str
|
|
15
|
+
if (value.canonical) return value.canonical
|
|
16
|
+
|
|
17
|
+
const exprAny = value as any
|
|
18
|
+
|
|
19
|
+
// Handle ExprName with member reference
|
|
20
|
+
if (value.name && typeof value.name === 'string') return value.name
|
|
21
|
+
|
|
22
|
+
// Handle ExprBoolOp (like `config or StrawberryConfig()`)
|
|
23
|
+
if (exprAny.cls === 'ExprBoolOp' && exprAny.operator && Array.isArray(exprAny.values)) {
|
|
24
|
+
return exprAny.values.map((v: any) => renderValue(v)).join(` ${exprAny.operator} `)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Handle ExprBinOp (like `type | None`)
|
|
28
|
+
if (exprAny.cls === 'ExprBinOp' && exprAny.left && exprAny.right) {
|
|
29
|
+
const left = renderValue(exprAny.left)
|
|
30
|
+
const right = renderValue(exprAny.right)
|
|
31
|
+
const op = exprAny.operator || '|'
|
|
32
|
+
return `${left} ${op} ${right}`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Handle ExprCall (like `StrawberryConfig()`)
|
|
36
|
+
if (exprAny.cls === 'ExprCall' && exprAny.function) {
|
|
37
|
+
const funcName = renderValue(exprAny.function)
|
|
38
|
+
const args = Array.isArray(exprAny.arguments)
|
|
39
|
+
? exprAny.arguments.map((a: any) => renderValue(a)).join(', ')
|
|
40
|
+
: ''
|
|
41
|
+
return `${funcName}(${args})`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Handle ExprAttribute (like contextlib.asynccontextmanager)
|
|
45
|
+
if (exprAny.cls === 'ExprAttribute' && Array.isArray(exprAny.values)) {
|
|
46
|
+
return exprAny.values.map((v: any) => renderValue(v)).join('.')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Handle ExprList and ExprTuple
|
|
50
|
+
if ('elements' in exprAny && Array.isArray(exprAny.elements)) {
|
|
51
|
+
const inner = exprAny.elements.map((el: any) => renderValue(el)).join(', ')
|
|
52
|
+
return exprAny.cls === 'ExprTuple' ? `(${inner})` : `[${inner}]`
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Handle ExprDict
|
|
56
|
+
if (exprAny.cls === 'ExprDict' && Array.isArray(exprAny.keys) && Array.isArray(exprAny.values)) {
|
|
57
|
+
const pairs = exprAny.keys.map((k: any, i: number) =>
|
|
58
|
+
`${renderValue(k)}: ${renderValue(exprAny.values[i])}`
|
|
59
|
+
).join(', ')
|
|
60
|
+
return `{${pairs}}`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Handle ExprSubscript (like Dict[str, int])
|
|
64
|
+
if (exprAny.left && exprAny.slice) {
|
|
65
|
+
const left = renderValue(exprAny.left)
|
|
66
|
+
const slice = renderValue(exprAny.slice)
|
|
67
|
+
return `${left}[${slice}]`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Handle slice expressions
|
|
71
|
+
if ('slice' in exprAny && exprAny.slice && !exprAny.left) {
|
|
72
|
+
return renderValue(exprAny.slice)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fallback for unknown expressions
|
|
76
|
+
if (typeof value === 'object') {
|
|
77
|
+
return JSON.stringify(value)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return String(value)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface ModuleDocProps {
|
|
84
|
+
module: GriffeModule
|
|
85
|
+
/** URL prefix for links */
|
|
86
|
+
prefix?: string
|
|
87
|
+
/** Show full module content or just summary */
|
|
88
|
+
showFull?: boolean
|
|
89
|
+
/** Additional CSS class */
|
|
90
|
+
className?: string
|
|
91
|
+
/** Override display path (e.g., for aliases to show alias name instead of target path) */
|
|
92
|
+
displayPath?: string
|
|
93
|
+
/** GitHub repository URL for "Open in GitHub" links */
|
|
94
|
+
githubUrl?: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Renders documentation for a module including its classes, functions, and submodules.
|
|
99
|
+
*/
|
|
100
|
+
export function ModuleDoc({ module, prefix = '/api', showFull = true, className = '', displayPath, githubUrl }: ModuleDocProps) {
|
|
101
|
+
const members = module.members ?? {}
|
|
102
|
+
|
|
103
|
+
// Separate members by type
|
|
104
|
+
const submodules: GriffeModule[] = []
|
|
105
|
+
const classes: GriffeClass[] = []
|
|
106
|
+
const functions: GriffeFunction[] = []
|
|
107
|
+
const attributes: GriffeAttribute[] = []
|
|
108
|
+
|
|
109
|
+
for (const member of Object.values(members)) {
|
|
110
|
+
switch (member.kind) {
|
|
111
|
+
case 'module':
|
|
112
|
+
submodules.push(member as GriffeModule)
|
|
113
|
+
break
|
|
114
|
+
case 'class':
|
|
115
|
+
classes.push(member as GriffeClass)
|
|
116
|
+
break
|
|
117
|
+
case 'function':
|
|
118
|
+
functions.push(member as GriffeFunction)
|
|
119
|
+
break
|
|
120
|
+
case 'attribute':
|
|
121
|
+
attributes.push(member as GriffeAttribute)
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Sort alphabetically
|
|
127
|
+
submodules.sort((a, b) => a.name.localeCompare(b.name))
|
|
128
|
+
classes.sort((a, b) => a.name.localeCompare(b.name))
|
|
129
|
+
functions.sort((a, b) => a.name.localeCompare(b.name))
|
|
130
|
+
attributes.sort((a, b) => a.name.localeCompare(b.name))
|
|
131
|
+
|
|
132
|
+
// Generate href for a member using dotted path
|
|
133
|
+
const memberHref = (member: GriffeMember) => {
|
|
134
|
+
const modulePath = module.path || module.name
|
|
135
|
+
return `${prefix}/${modulePath}.${member.name}`
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div className={className}>
|
|
140
|
+
{/* Module header */}
|
|
141
|
+
<h1 id={module.name} className="text-3xl font-bold text-gray-900 dark:text-white mb-2">
|
|
142
|
+
<span className="text-gray-500 dark:text-gray-400 font-normal">module </span>
|
|
143
|
+
{displayPath || module.path || module.name}
|
|
144
|
+
</h1>
|
|
145
|
+
|
|
146
|
+
{/* Docstring */}
|
|
147
|
+
{module.docstring && (
|
|
148
|
+
<div className="mb-8">
|
|
149
|
+
<Docstring docstring={module.docstring} />
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
|
|
153
|
+
{/* Submodules */}
|
|
154
|
+
{submodules.length > 0 && (
|
|
155
|
+
<div className="mb-8">
|
|
156
|
+
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 pb-2 border-b border-gray-200 dark:border-gray-700">
|
|
157
|
+
Submodules
|
|
158
|
+
</h2>
|
|
159
|
+
<ul className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
160
|
+
{submodules.map((submodule) => (
|
|
161
|
+
<li key={submodule.name}>
|
|
162
|
+
<Link
|
|
163
|
+
href={`${prefix}/${submodule.path || submodule.name}`}
|
|
164
|
+
className="block p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:border-primary-300 dark:hover:border-primary-600 transition-colors"
|
|
165
|
+
>
|
|
166
|
+
<div className="font-mono text-sm text-primary-600 dark:text-primary-400">
|
|
167
|
+
{submodule.path || submodule.name}
|
|
168
|
+
</div>
|
|
169
|
+
{submodule.docstring && (
|
|
170
|
+
<div className="text-sm text-gray-600 dark:text-gray-300 mt-1 line-clamp-2">
|
|
171
|
+
{submodule.docstring.value.split('\n')[0]}
|
|
172
|
+
</div>
|
|
173
|
+
)}
|
|
174
|
+
</Link>
|
|
175
|
+
</li>
|
|
176
|
+
))}
|
|
177
|
+
</ul>
|
|
178
|
+
</div>
|
|
179
|
+
)}
|
|
180
|
+
|
|
181
|
+
{/* Classes - summary or full */}
|
|
182
|
+
{classes.length > 0 && (
|
|
183
|
+
<div id="classes" className="mb-8">
|
|
184
|
+
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 pb-2 border-b border-gray-200 dark:border-gray-700">
|
|
185
|
+
Classes
|
|
186
|
+
</h2>
|
|
187
|
+
{showFull ? (
|
|
188
|
+
<div className="space-y-12">
|
|
189
|
+
{classes.map((cls) => (
|
|
190
|
+
<ClassDoc key={cls.name} cls={cls} prefix={prefix} githubUrl={githubUrl} />
|
|
191
|
+
))}
|
|
192
|
+
</div>
|
|
193
|
+
) : (
|
|
194
|
+
<ul className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
195
|
+
{classes.map((cls) => (
|
|
196
|
+
<li key={cls.name}>
|
|
197
|
+
<Link
|
|
198
|
+
href={memberHref(cls)}
|
|
199
|
+
className="block p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:border-primary-300 dark:hover:border-primary-600 transition-colors"
|
|
200
|
+
>
|
|
201
|
+
<div className="font-mono text-sm text-primary-600 dark:text-primary-400">
|
|
202
|
+
{cls.name}
|
|
203
|
+
</div>
|
|
204
|
+
{cls.docstring && (
|
|
205
|
+
<div className="text-sm text-gray-600 dark:text-gray-300 mt-1 line-clamp-2">
|
|
206
|
+
{cls.docstring.value.split('\n')[0]}
|
|
207
|
+
</div>
|
|
208
|
+
)}
|
|
209
|
+
</Link>
|
|
210
|
+
</li>
|
|
211
|
+
))}
|
|
212
|
+
</ul>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
216
|
+
|
|
217
|
+
{/* Functions - summary or full */}
|
|
218
|
+
{functions.length > 0 && (
|
|
219
|
+
<div id="functions" className="mb-8">
|
|
220
|
+
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 pb-2 border-b border-gray-200 dark:border-gray-700">
|
|
221
|
+
Functions
|
|
222
|
+
</h2>
|
|
223
|
+
{showFull ? (
|
|
224
|
+
<div className="space-y-8">
|
|
225
|
+
{functions.map((fn) => (
|
|
226
|
+
<FunctionDoc key={fn.name} fn={fn} githubUrl={githubUrl} />
|
|
227
|
+
))}
|
|
228
|
+
</div>
|
|
229
|
+
) : (
|
|
230
|
+
<ul className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
231
|
+
{functions.map((fn) => (
|
|
232
|
+
<li key={fn.name}>
|
|
233
|
+
<Link
|
|
234
|
+
href={memberHref(fn)}
|
|
235
|
+
className="block p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:border-primary-300 dark:hover:border-primary-600 transition-colors"
|
|
236
|
+
>
|
|
237
|
+
<div className="font-mono text-sm text-primary-600 dark:text-primary-400">
|
|
238
|
+
{fn.name}()
|
|
239
|
+
</div>
|
|
240
|
+
{fn.docstring && (
|
|
241
|
+
<div className="text-sm text-gray-600 dark:text-gray-300 mt-1 line-clamp-2">
|
|
242
|
+
{fn.docstring.value.split('\n')[0]}
|
|
243
|
+
</div>
|
|
244
|
+
)}
|
|
245
|
+
</Link>
|
|
246
|
+
</li>
|
|
247
|
+
))}
|
|
248
|
+
</ul>
|
|
249
|
+
)}
|
|
250
|
+
</div>
|
|
251
|
+
)}
|
|
252
|
+
|
|
253
|
+
{/* Module-level attributes */}
|
|
254
|
+
{attributes.length > 0 && (
|
|
255
|
+
<div className="mb-8">
|
|
256
|
+
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 pb-2 border-b border-gray-200 dark:border-gray-700">
|
|
257
|
+
Attributes
|
|
258
|
+
</h2>
|
|
259
|
+
<dl className="space-y-3">
|
|
260
|
+
{attributes.map((attr) => (
|
|
261
|
+
<div key={attr.name} id={attr.name} className="scroll-mt-20">
|
|
262
|
+
<dt className="font-mono text-sm">
|
|
263
|
+
<span className="text-orange-600 dark:text-orange-400 font-semibold">{attr.name}</span>
|
|
264
|
+
{attr.annotation && (
|
|
265
|
+
<>
|
|
266
|
+
<span className="text-gray-600 dark:text-gray-400">: </span>
|
|
267
|
+
<span className="text-green-600 dark:text-green-400">
|
|
268
|
+
{typeof attr.annotation === 'string' ? attr.annotation : attr.annotation.str || attr.annotation.name}
|
|
269
|
+
</span>
|
|
270
|
+
</>
|
|
271
|
+
)}
|
|
272
|
+
{attr.value && (
|
|
273
|
+
<>
|
|
274
|
+
<span className="text-gray-600 dark:text-gray-400"> = </span>
|
|
275
|
+
<span className="text-cyan-600 dark:text-cyan-400">{renderValue(attr.value)}</span>
|
|
276
|
+
</>
|
|
277
|
+
)}
|
|
278
|
+
</dt>
|
|
279
|
+
{attr.docstring && (
|
|
280
|
+
<dd className="mt-1 text-sm text-gray-600 dark:text-gray-300 ml-4">
|
|
281
|
+
{attr.docstring.value}
|
|
282
|
+
</dd>
|
|
283
|
+
)}
|
|
284
|
+
</div>
|
|
285
|
+
))}
|
|
286
|
+
</dl>
|
|
287
|
+
</div>
|
|
288
|
+
)}
|
|
289
|
+
|
|
290
|
+
{/* Source location */}
|
|
291
|
+
{(module.relative_package_filepath || module.filepath) && (
|
|
292
|
+
<div className="mt-4 text-xs text-gray-500 dark:text-gray-400">
|
|
293
|
+
<span className="font-mono">{module.relative_package_filepath || module.filepath}</span>
|
|
294
|
+
</div>
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
)
|
|
298
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { GriffeParameter, GriffeDocstringSection, GriffeDocstringElement, GriffeExpression } from '../../types'
|
|
2
|
+
import { CodeSpan } from './CodeSpan'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Render a type annotation expression to string
|
|
6
|
+
*/
|
|
7
|
+
function renderExpression(expr: GriffeExpression | string | undefined): string {
|
|
8
|
+
if (!expr) return ''
|
|
9
|
+
if (typeof expr === 'string') return expr
|
|
10
|
+
if (expr.str) return expr.str
|
|
11
|
+
if (expr.canonical) return expr.canonical
|
|
12
|
+
|
|
13
|
+
const exprAny = expr as any
|
|
14
|
+
|
|
15
|
+
// Handle ExprName with member reference
|
|
16
|
+
if (expr.name && typeof expr.name === 'string') return expr.name
|
|
17
|
+
|
|
18
|
+
// Handle ExprBoolOp (like `config or StrawberryConfig()`)
|
|
19
|
+
if (exprAny.cls === 'ExprBoolOp' && exprAny.operator && Array.isArray(exprAny.values)) {
|
|
20
|
+
return exprAny.values.map((v: any) => renderExpression(v)).join(` ${exprAny.operator} `)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Handle ExprBinOp (like `type | None`)
|
|
24
|
+
if (exprAny.cls === 'ExprBinOp' && exprAny.left && exprAny.right) {
|
|
25
|
+
const left = renderExpression(exprAny.left)
|
|
26
|
+
const right = renderExpression(exprAny.right)
|
|
27
|
+
const op = exprAny.operator || '|'
|
|
28
|
+
return `${left} ${op} ${right}`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Handle ExprCall (like `StrawberryConfig()`)
|
|
32
|
+
if (exprAny.cls === 'ExprCall' && exprAny.function) {
|
|
33
|
+
const funcName = renderExpression(exprAny.function)
|
|
34
|
+
const args = Array.isArray(exprAny.arguments)
|
|
35
|
+
? exprAny.arguments.map((a: any) => renderExpression(a)).join(', ')
|
|
36
|
+
: ''
|
|
37
|
+
return `${funcName}(${args})`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Handle ExprAttribute (like contextlib.asynccontextmanager)
|
|
41
|
+
if (exprAny.cls === 'ExprAttribute' && Array.isArray(exprAny.values)) {
|
|
42
|
+
return exprAny.values.map((v: any) => renderExpression(v)).join('.')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Handle ExprList and ExprTuple
|
|
46
|
+
if ('elements' in exprAny && Array.isArray(exprAny.elements)) {
|
|
47
|
+
const inner = exprAny.elements.map((el: any) => renderExpression(el)).join(', ')
|
|
48
|
+
return exprAny.cls === 'ExprTuple' ? `(${inner})` : `[${inner}]`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle ExprDict
|
|
52
|
+
if (exprAny.cls === 'ExprDict' && Array.isArray(exprAny.keys) && Array.isArray(exprAny.values)) {
|
|
53
|
+
const pairs = exprAny.keys.map((k: any, i: number) =>
|
|
54
|
+
`${renderExpression(k)}: ${renderExpression(exprAny.values[i])}`
|
|
55
|
+
).join(', ')
|
|
56
|
+
return `{${pairs}}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle ExprSubscript (like Dict[str, int])
|
|
60
|
+
if (exprAny.left && exprAny.slice) {
|
|
61
|
+
const left = renderExpression(exprAny.left)
|
|
62
|
+
const slice = renderExpression(exprAny.slice)
|
|
63
|
+
return `${left}[${slice}]`
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle slice expressions
|
|
67
|
+
if ('slice' in exprAny && exprAny.slice && !exprAny.left) {
|
|
68
|
+
return renderExpression(exprAny.slice)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Fallback for unknown expressions
|
|
72
|
+
if (typeof expr === 'object') {
|
|
73
|
+
return JSON.stringify(expr)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return String(expr)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get parameter description from docstring sections
|
|
81
|
+
*/
|
|
82
|
+
function getParamDescription(
|
|
83
|
+
paramName: string,
|
|
84
|
+
docstringSections?: GriffeDocstringSection[]
|
|
85
|
+
): string | undefined {
|
|
86
|
+
if (!docstringSections) return undefined
|
|
87
|
+
|
|
88
|
+
for (const section of docstringSections) {
|
|
89
|
+
if (section.kind === 'parameters' && Array.isArray(section.value)) {
|
|
90
|
+
const param = (section.value as GriffeDocstringElement[]).find(
|
|
91
|
+
(p) => p.name === paramName
|
|
92
|
+
)
|
|
93
|
+
if (param) return param.description
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return undefined
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface ParameterTableProps {
|
|
100
|
+
parameters: GriffeParameter[]
|
|
101
|
+
/** Docstring sections for parameter descriptions */
|
|
102
|
+
docstringSections?: GriffeDocstringSection[]
|
|
103
|
+
/** Additional CSS class */
|
|
104
|
+
className?: string
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Renders a two-column parameter list matching strawberry.rocks design.
|
|
109
|
+
* Left column: parameter name badge
|
|
110
|
+
* Right column: description, type, and default value
|
|
111
|
+
*/
|
|
112
|
+
export function ParameterTable({ parameters, docstringSections, className = '' }: ParameterTableProps) {
|
|
113
|
+
if (!parameters || parameters.length === 0) return null
|
|
114
|
+
|
|
115
|
+
// Filter out 'self' parameter for cleaner display
|
|
116
|
+
const displayParams = parameters.filter(p => p.name !== 'self')
|
|
117
|
+
|
|
118
|
+
if (displayParams.length === 0) return null
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<ol className={`list-none p-0 ${className}`}>
|
|
122
|
+
{displayParams.map((param, index) => {
|
|
123
|
+
const description = getParamDescription(param.name, docstringSections)
|
|
124
|
+
const annotation = renderExpression(param.annotation)
|
|
125
|
+
const defaultValue = renderExpression(param.default)
|
|
126
|
+
const isLast = index === displayParams.length - 1
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<li
|
|
130
|
+
key={param.name}
|
|
131
|
+
className="contents"
|
|
132
|
+
>
|
|
133
|
+
<div className={`grid grid-cols-[max-content_1fr] items-baseline gap-x-8 gap-y-2 py-6 ${!isLast ? 'border-b border-gray-200 dark:border-gray-700' : ''}`}>
|
|
134
|
+
{/* Parameter name badge - left column */}
|
|
135
|
+
<div className="flex-shrink-0">
|
|
136
|
+
<CodeSpan>
|
|
137
|
+
{param.kind === 'var-positional' && '*'}
|
|
138
|
+
{param.kind === 'var-keyword' && '**'}
|
|
139
|
+
{param.name}:
|
|
140
|
+
</CodeSpan>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{/* Description and metadata - right column */}
|
|
144
|
+
<div className="space-y-3">
|
|
145
|
+
{/* Description */}
|
|
146
|
+
{description && (
|
|
147
|
+
<p className="text-gray-700 dark:text-gray-300 leading-relaxed">
|
|
148
|
+
{description}
|
|
149
|
+
</p>
|
|
150
|
+
)}
|
|
151
|
+
|
|
152
|
+
{/* Type and Default in definition list style */}
|
|
153
|
+
<dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-2">
|
|
154
|
+
{annotation && (
|
|
155
|
+
<>
|
|
156
|
+
<dt className="font-semibold text-gray-500 dark:text-gray-400">
|
|
157
|
+
Type
|
|
158
|
+
</dt>
|
|
159
|
+
<dd className="m-0">
|
|
160
|
+
<CodeSpan>{annotation}</CodeSpan>
|
|
161
|
+
</dd>
|
|
162
|
+
</>
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{defaultValue && (
|
|
166
|
+
<>
|
|
167
|
+
<dt className="font-semibold text-gray-500 dark:text-gray-400">
|
|
168
|
+
Default
|
|
169
|
+
</dt>
|
|
170
|
+
<dd className="m-0">
|
|
171
|
+
<CodeSpan>{defaultValue}</CodeSpan>
|
|
172
|
+
</dd>
|
|
173
|
+
</>
|
|
174
|
+
)}
|
|
175
|
+
</dl>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</li>
|
|
179
|
+
)
|
|
180
|
+
})}
|
|
181
|
+
</ol>
|
|
182
|
+
)
|
|
183
|
+
}
|