Skip to content

Commit 7587967

Browse files
LekoArtsLauraBeatris
andauthoredMar 10, 2025··
chore(shared,clerk-react,types): Improve JSDoc comments (#5296)
Co-authored-by: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com>
1 parent 7220dfd commit 7587967

37 files changed

+807
-195
lines changed
 

‎.changeset/dry-poets-cross.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/shared': patch
3+
'@clerk/clerk-react': patch
4+
'@clerk/types': patch
5+
---
6+
7+
Improve JSDoc documentation

‎.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,5 @@ scripts/.env
9797
!scripts/.env.example
9898

9999
# typedoc
100-
.typedoc
100+
.typedoc/docs
101+
.typedoc/docs.json

‎.typedoc/custom-plugin.mjs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// @ts-check
2+
import { MarkdownRendererEvent } from 'typedoc-plugin-markdown';
3+
4+
/**
5+
* @param {string} str
6+
*/
7+
function toKebabCase(str) {
8+
return str.replace(/((?<=[a-z\d])[A-Z]|(?<=[A-Z\d])[A-Z](?=[a-z]))/g, '-$1').toLowerCase();
9+
}
10+
11+
/**
12+
* @param {import('typedoc-plugin-markdown').MarkdownApplication} app
13+
*/
14+
export function load(app) {
15+
app.renderer.on(MarkdownRendererEvent.BEGIN, output => {
16+
// Do not output README.mdx files
17+
output.urls = output.urls
18+
?.filter(e => !e.url.endsWith('README.mdx'))
19+
.map(e => {
20+
// Convert URLs (by default camelCase) to kebab-case
21+
const kebabUrl = toKebabCase(e.url);
22+
23+
e.url = kebabUrl;
24+
e.model.url = kebabUrl;
25+
26+
return e;
27+
});
28+
});
29+
}

‎.typedoc/custom-theme.mjs

+261
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// @ts-check
2+
import { ReflectionKind } from 'typedoc';
3+
import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown';
4+
5+
/**
6+
* @param {import('typedoc-plugin-markdown').MarkdownApplication} app
7+
*/
8+
export function load(app) {
9+
app.renderer.defineTheme('clerkTheme', ClerkMarkdownTheme);
10+
}
11+
12+
class ClerkMarkdownTheme extends MarkdownTheme {
13+
/**
14+
* @param {import('typedoc-plugin-markdown').MarkdownPageEvent} page
15+
*/
16+
getRenderContext(page) {
17+
return new ClerkMarkdownThemeContext(this, page, this.application.options);
18+
}
19+
}
20+
21+
/**
22+
* Our custom Clerk theme
23+
* @extends MarkdownThemeContext
24+
*/
25+
class ClerkMarkdownThemeContext extends MarkdownThemeContext {
26+
/**
27+
* @param {MarkdownTheme} theme
28+
* @param {import('typedoc-plugin-markdown').MarkdownPageEvent} page
29+
* @param {MarkdownTheme["application"]["options"]} options
30+
*/
31+
constructor(theme, page, options) {
32+
super(theme, page, options);
33+
34+
const superPartials = this.partials;
35+
36+
this.partials = {
37+
...superPartials,
38+
/**
39+
* Copied from default theme / source code. This hides the return type from the output
40+
* https://github.com/typedoc2md/typedoc-plugin-markdown/blob/179a54c502b318cd4f3951e5e8b90f7f7a4752d8/packages/typedoc-plugin-markdown/src/theme/context/partials/member.signatureReturns.ts
41+
* @param {import('typedoc').SignatureReflection} model
42+
* @param {{ headingLevel: number }} options
43+
*/
44+
signatureReturns: (model, options) => {
45+
const md = [];
46+
47+
/**
48+
* @type any
49+
*/
50+
const modelType = model.type;
51+
/**
52+
* @type {import('typedoc').DeclarationReflection}
53+
*/
54+
const typeDeclaration = modelType?.declaration;
55+
56+
md.push(heading(options.headingLevel, this.i18n.theme_returns()));
57+
58+
if (model.comment?.blockTags.length) {
59+
const tags = model.comment.blockTags
60+
.filter(tag => tag.tag === '@returns')
61+
.map(tag => this.helpers.getCommentParts(tag.content));
62+
md.push(tags.join('\n\n'));
63+
}
64+
65+
if (typeDeclaration?.signatures) {
66+
typeDeclaration.signatures.forEach(signature => {
67+
md.push(
68+
this.partials.signature(signature, {
69+
headingLevel: options.headingLevel + 1,
70+
nested: true,
71+
}),
72+
);
73+
});
74+
}
75+
76+
if (typeDeclaration?.children) {
77+
md.push(
78+
this.partials.typeDeclaration(typeDeclaration, {
79+
headingLevel: options.headingLevel,
80+
}),
81+
);
82+
}
83+
84+
return md.join('\n\n');
85+
},
86+
/**
87+
* Copied from default theme / source code. This hides the "Type parameters" section and the signature title from the output
88+
* https://github.com/typedoc2md/typedoc-plugin-markdown/blob/179a54c502b318cd4f3951e5e8b90f7f7a4752d8/packages/typedoc-plugin-markdown/src/theme/context/partials/member.signature.ts
89+
* @param {import('typedoc').SignatureReflection} model
90+
* @param {{ headingLevel: number, nested?: boolean, accessor?: string, multipleSignatures?: boolean }} options
91+
*/
92+
signature: (model, options) => {
93+
const md = [];
94+
95+
if (!options.nested && model.sources && !this.options.getValue('disableSources')) {
96+
md.push(this.partials.sources(model));
97+
}
98+
99+
let modelComments = options.multipleSignatures ? model.comment : model.comment || model.parent?.comment;
100+
101+
if (modelComments && model.parent?.comment?.summary && !options.multipleSignatures) {
102+
modelComments = Object.assign(modelComments, {
103+
summary: model.parent.comment.summary,
104+
});
105+
}
106+
107+
if (modelComments && model.parent?.comment?.blockTags) {
108+
modelComments.blockTags = [...(model.parent?.comment?.blockTags || []), ...(model.comment?.blockTags || [])];
109+
}
110+
111+
if (modelComments) {
112+
md.push(
113+
this.partials.comment(modelComments, {
114+
headingLevel: options.headingLevel,
115+
showTags: false,
116+
showSummary: true,
117+
}),
118+
);
119+
}
120+
121+
if (!options.multipleSignatures && model.parent?.documents) {
122+
md.push(
123+
this.partials.documents(model?.parent, {
124+
headingLevel: options.headingLevel,
125+
}),
126+
);
127+
}
128+
129+
if (model.parameters?.length) {
130+
md.push(heading(options.headingLevel, this.internationalization.kindPluralString(ReflectionKind.Parameter)));
131+
if (this.helpers.useTableFormat('parameters')) {
132+
md.push(this.partials.parametersTable(model.parameters));
133+
} else {
134+
md.push(
135+
this.partials.parametersList(model.parameters, {
136+
headingLevel: options.headingLevel,
137+
}),
138+
);
139+
}
140+
}
141+
142+
if (model.type) {
143+
md.push(
144+
this.partials.signatureReturns(model, {
145+
headingLevel: options.headingLevel,
146+
}),
147+
);
148+
}
149+
150+
if (modelComments) {
151+
md.push(
152+
this.partials.comment(modelComments, {
153+
headingLevel: options.headingLevel,
154+
showTags: true,
155+
showSummary: false,
156+
}),
157+
);
158+
}
159+
160+
md.push(this.partials.inheritance(model, { headingLevel: options.headingLevel }));
161+
162+
return md.join('\n\n');
163+
},
164+
/**
165+
* Copied from default theme / source code. This hides the "Type parameters" section from the output
166+
* https://github.com/typedoc2md/typedoc-plugin-markdown/blob/179a54c502b318cd4f3951e5e8b90f7f7a4752d8/packages/typedoc-plugin-markdown/src/theme/context/partials/member.memberWithGroups.ts#L58
167+
* @param {import('typedoc').DeclarationReflection} model
168+
* @param {{ headingLevel: number }} options
169+
*/
170+
memberWithGroups: (model, options) => {
171+
const md = [];
172+
173+
if (
174+
![ReflectionKind.Module, ReflectionKind.Namespace].includes(model.kind) &&
175+
model.sources &&
176+
!this.options.getValue('disableSources')
177+
) {
178+
md.push(this.partials.sources(model));
179+
}
180+
181+
if (model.comment) {
182+
md.push(
183+
this.partials.comment(model.comment, {
184+
headingLevel: options.headingLevel,
185+
}),
186+
);
187+
}
188+
189+
if (model.typeHierarchy?.next) {
190+
md.push(
191+
this.partials.hierarchy(model.typeHierarchy, {
192+
headingLevel: options.headingLevel,
193+
}),
194+
);
195+
}
196+
197+
if (model.implementedTypes?.length) {
198+
md.push(heading(options.headingLevel, this.i18n.theme_implements()));
199+
md.push(
200+
unorderedList(model.implementedTypes.map(implementedType => this.partials.someType(implementedType))),
201+
);
202+
}
203+
204+
if (model.kind === ReflectionKind.Class && model.categories?.length) {
205+
model.groups
206+
?.filter(group => group.title === this.i18n.kind_plural_constructor())
207+
.forEach(group => {
208+
md.push(heading(options.headingLevel, this.i18n.kind_plural_constructor()));
209+
group.children.forEach(child => {
210+
md.push(
211+
this.partials.constructor(/** @type {import('typedoc').DeclarationReflection} */ (child), {
212+
headingLevel: options.headingLevel + 1,
213+
}),
214+
);
215+
});
216+
});
217+
}
218+
219+
if ('signatures' in model && model.signatures?.length) {
220+
model.signatures.forEach(signature => {
221+
md.push(
222+
this.partials.signature(signature, {
223+
headingLevel: options.headingLevel,
224+
}),
225+
);
226+
});
227+
}
228+
229+
if (model.indexSignatures?.length) {
230+
md.push(heading(options.headingLevel, this.i18n.theme_indexable()));
231+
model.indexSignatures.forEach(indexSignature => {
232+
md.push(this.partials.indexSignature(indexSignature));
233+
});
234+
}
235+
236+
md.push(this.partials.body(model, { headingLevel: options.headingLevel }));
237+
238+
return md.join('\n\n');
239+
},
240+
};
241+
}
242+
}
243+
244+
/**
245+
* Returns a heading in markdown format
246+
* @param {number} level The level of the heading
247+
* @param {string} text The text of the heading
248+
*/
249+
function heading(level, text) {
250+
level = level > 6 ? 6 : level;
251+
return `${[...Array(level)].map(() => '#').join('')} ${text}`;
252+
}
253+
254+
/**
255+
* Create an unordered list from an array of items
256+
* @param {string[]} items
257+
* @returns
258+
*/
259+
function unorderedList(items) {
260+
return items.map(item => `- ${item}`).join('\n');
261+
}

‎.typedoc/tsconfig.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"allowJs": true,
5+
"noEmit": true,
6+
"moduleResolution": "node16",
7+
"module": "Node16"
8+
}
9+
}

‎package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,9 @@
129129
"tsup": "catalog:repo",
130130
"turbo": "^2.0.14",
131131
"turbo-ignore": "^2.0.6",
132-
"typedoc": "0.27.6",
133-
"typedoc-plugin-markdown": "4.4.1",
132+
"typedoc": "0.27.9",
133+
"typedoc-plugin-markdown": "4.4.2",
134+
"typedoc-plugin-replace-text": "4.1.0",
134135
"typescript": "catalog:repo",
135136
"typescript-eslint": "8.21.0",
136137
"uuid": "8.3.2",

‎packages/react/src/hooks/useAuth.ts

+5
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ import { createGetToken, createSignOut } from './utils';
1212
/**
1313
* The `useAuth()` hook provides access to the current user's authentication state and methods to manage the active session.
1414
*
15+
* @param [initialAuthState] - An object containing the initial authentication state. If not provided, the hook will attempt to derive the state from the context.
16+
*
1517
* @example
1618
*
19+
* > [!NOTE]
20+
* > For frameworks like Next.js that support multiple ways of rendering its content, it might be preferable to use the [`auth()`](https://clerk.com/docs/references/nextjs/auth) helper instead of `useAuth()`. This depends on if you want to use React Server Components, SSR, or client-side rendering. Learn more in the [rendering modes](https://clerk.com/docs/references/nextjs/rendering-modes) guide. If you only want to access data on the client-side, `useAuth()` is sufficient.
21+
*
1722
* The following example demonstrates how to use the `useAuth()` hook to access the current auth state, like whether the user is signed in or not. It also includes a basic example for using the `getToken()` method to retrieve a session token for fetching data from an external resource.
1823
*
1924
* ```tsx {{ filename: 'src/pages/ExternalDataPage.tsx' }}

‎packages/react/src/hooks/useSignIn.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid
3333
*
3434
* The `useSignIn()` hook can also be used to build fully custom sign-in flows, if Clerk's prebuilt components don't meet your specific needs or if you require more control over the authentication flow. Different sign-in flows include email and password, email and phone codes, email links, and multifactor (MFA). To learn more about using the `useSignIn()` hook to create custom flows, see the [custom flow guides](https://clerk.com/docs/custom-flows/overview).
3535
*
36-
* ```
37-
* ```
36+
* ```empty```
3837
*/
3938
export const useSignIn = (): UseSignInReturn => {
4039
useAssertWrappedByClerkProvider('useSignIn');

‎packages/react/src/hooks/useSignUp.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid
3333
*
3434
* The `useSignUp()` hook can also be used to build fully custom sign-up flows, if Clerk's prebuilt components don't meet your specific needs or if you require more control over the authentication flow. Different sign-up flows include email and password, email and phone codes, email links, and multifactor (MFA). To learn more about using the `useSignUp()` hook to create custom flows, see the [custom flow guides](https://clerk.com/docs/custom-flows/overview).
3535
*
36-
* ```tsx
37-
* ```
36+
* ```empty```
3837
*/
3938
export const useSignUp = (): UseSignUpReturn => {
4039
useAssertWrappedByClerkProvider('useSignUp');

‎packages/react/src/types.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,27 @@ export type IsomorphicClerkOptions = Without<ClerkOptions, 'isSatellite'> & {
3636
*/
3737
clerkJSVersion?: string;
3838
/**
39-
* The Clerk publishable key for your instance
40-
* @note This can be found in your Clerk Dashboard on the [API Keys](https://dashboard.clerk.com/last-active?path=api-keys) page
39+
* The Clerk Publishable Key for your instance. This can be found on the [API keys](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard.
4140
*/
4241
publishableKey: string;
4342
/**
44-
* This nonce value will be passed through to the `@clerk/clerk-js` script tag.
45-
* @note You can use this to implement [strict-dynamic CSP](https://clerk.com/docs/security/clerk-csp#implementing-a-strict-dynamic-csp)
43+
* This nonce value will be passed through to the `@clerk/clerk-js` script tag. Use it to implement a [strict-dynamic CSP](https://clerk.com/docs/security/clerk-csp#implementing-a-strict-dynamic-csp). Requires the `dynamic` prop to also be set.
4644
*/
4745
nonce?: string;
4846
} & MultiDomainAndOrProxy;
4947

48+
/**
49+
* @interface
50+
*/
5051
export type ClerkProviderProps = IsomorphicClerkOptions & {
5152
children: React.ReactNode;
5253
/**
53-
* Provide an initial state of the Clerk client during server-side rendering (SSR)
54+
* Provide an initial state of the Clerk client during server-side rendering. You don't need to set this value yourself unless you're [developing an SDK](https://clerk.com/docs/references/sdk/overview).
5455
*/
5556
initialState?: InitialState;
5657
/**
57-
* Indicates to silently fail the initialization process when the publishable keys is not provided, instead of throwing an error.
58-
* Defaults to `false`.
58+
* Indicates to silently fail the initialization process when the publishable keys is not provided, instead of throwing an error. Defaults to `false`.
59+
* @internal
5960
*/
6061
__internal_bypassMissingPublishableKey?: boolean;
6162
};

0 commit comments

Comments
 (0)
Please sign in to comment.