Skip to content

Commit

Permalink
feat(website): type parameters links, builtin doc links, default valu…
Browse files Browse the repository at this point in the history
…es (#10515)

* feat(website): links to type parameters, builtin doc links in api.json

* feat(website): show default values for params and props in excerpt

* fix: link in jsdoc

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Jiralite <[email protected]>
  • Loading branch information
3 people authored Oct 19, 2024
1 parent 93b84ae commit 3540c31
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 21 deletions.
1 change: 1 addition & 0 deletions apps/website/src/components/ParameterNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export async function ParameterNode({
{description ? <Badges node={parameter} /> : null}
{parameter.name}
{parameter.isOptional ? '?' : ''}: <ExcerptNode node={parameter.typeExcerpt} version={version} />
{parameter.defaultValue ? ` = ${parameter.defaultValue}` : ''}
</span>
{description && parameter.description?.length ? (
<div className="mt-4 pl-4">
Expand Down
8 changes: 7 additions & 1 deletion apps/website/src/components/PropertyNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ export async function PropertyNode({
<LinkIcon aria-hidden size={16} />
</Link>
{property.displayName}
{property.isOptional ? '?' : ''} : <ExcerptNode node={property.typeExcerpt} version={version} />
{property.isOptional ? '?' : ''} : <ExcerptNode node={property.typeExcerpt} version={version} />{' '}
{property.summary?.defaultValueBlock.length
? `= ${property.summary.defaultValueBlock.reduce(
(acc: string, def: { kind: string; text: string }) => `${acc}${def.text}`,
'',
)}`
: ''}
</span>
</h3>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { IExcerptTokenRange } from './Excerpt.js';
* @public
*/
export interface IApiParameterOptions {
defaultValue: string | undefined;
isOptional: boolean;
isRest: boolean;
parameterName: string;
Expand Down Expand Up @@ -124,6 +125,7 @@ export function ApiParameterListMixin<TBaseClass extends IApiItemConstructor>(
isOptional: Boolean(parameterOptions.isOptional),
isRest: Boolean(parameterOptions.isRest),
parent: this,
defaultValue: parameterOptions.defaultValue,
});

this[_parameters].push(parameter);
Expand Down Expand Up @@ -171,6 +173,7 @@ export function ApiParameterListMixin<TBaseClass extends IApiItemConstructor>(
parameterTypeTokenRange: parameter.parameterTypeExcerpt.tokenRange,
isOptional: parameter.isOptional,
isRest: parameter.isRest,
defaultValue: parameter.defaultValue,
});
}

Expand Down
1 change: 1 addition & 0 deletions packages/api-extractor-model/src/model/ApiPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const MinifyJSONMapping = {
constraintTokenRange: 'ctr',
dependencies: 'dp',
defaultTypeTokenRange: 'dtr',
defaultValue: 'dv',
docComment: 'd',
endIndex: 'en',
excerptTokens: 'ex',
Expand Down
1 change: 1 addition & 0 deletions packages/api-extractor-model/src/model/Deserializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ function mapParam(
startIndex: 1 + index + paramTokens.slice(0, index).reduce((akk, num) => akk + num, 0),
endIndex: 1 + index + paramTokens.slice(0, index + 1).reduce((akk, num) => akk + num, 0),
},
defaultValue: param.default,
};
}

Expand Down
7 changes: 7 additions & 0 deletions packages/api-extractor-model/src/model/Parameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { Excerpt } from '../mixins/Excerpt.js';
* @public
*/
export interface IParameterOptions {
defaultValue: string | undefined;
isOptional: boolean;
isRest: boolean;
name: string;
Expand Down Expand Up @@ -56,6 +57,11 @@ export class Parameter {
*/
public isRest: boolean;

/**
* The default value for this parameter if optional
*/
public defaultValue: string | undefined;

private readonly _parent: ApiParameterListMixin;

public constructor(options: IParameterOptions) {
Expand All @@ -64,6 +70,7 @@ export class Parameter {
this.isOptional = options.isOptional;
this.isRest = options.isRest;
this._parent = options.parent;
this.defaultValue = options.defaultValue;
}

/**
Expand Down
30 changes: 23 additions & 7 deletions packages/api-extractor/src/generators/ApiModelGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(
nodesToCapture,
functionDeclaration.parameters,
jsDoc?.params,
);

const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
Expand Down Expand Up @@ -1043,6 +1044,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(
nodesToCapture,
methodDeclaration.parameters,
jsDoc?.params,
);

const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
Expand Down Expand Up @@ -1137,7 +1139,11 @@ export class ApiModelGenerator {
methodSignature.typeParameters,
);

const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, methodSignature.parameters);
const parameters: IApiParameterOptions[] = this._captureParameters(
nodesToCapture,
methodSignature.parameters,
jsDoc?.params,
);

const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
Expand Down Expand Up @@ -1342,7 +1348,7 @@ export class ApiModelGenerator {
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString(
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}${jsDoc.default ? ` (default: ${this._escapeSpecialChars(jsDoc.default)})` : ''}\n${
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}${jsDoc.default ? `\n * @defaultValue ${this._escapeSpecialChars(jsDoc.default)}` : ''}\n${
'see' in jsDoc ? jsDoc.see.map((see) => ` * @see ${see}\n`).join('') : ''
}${'readonly' in jsDoc && jsDoc.readonly ? ' * @readonly\n' : ''}${
'deprecated' in jsDoc && jsDoc.deprecated
Expand Down Expand Up @@ -1529,6 +1535,7 @@ export class ApiModelGenerator {
},
isOptional: Boolean(parameter.optional),
isRest: parameter.name.startsWith('...'),
defaultValue: parameter.default?.toString(),
});
excerptTokens.push(...newTokens);
excerptTokens.push({
Expand All @@ -1548,6 +1555,7 @@ export class ApiModelGenerator {
},
isOptional: Boolean(parameter.optional),
isRest: parameter.name.startsWith('...'),
defaultValue: parameter.default?.toString(),
});
excerptTokens.push(...newTokens);
excerptTokens.push({
Expand Down Expand Up @@ -1640,6 +1648,7 @@ export class ApiModelGenerator {
private _captureParameters(
nodesToCapture: IExcerptBuilderNodeToCapture[],
parameterNodes: ts.NodeArray<ts.ParameterDeclaration>,
jsDoc?: DocgenParamJson[] | undefined,
): IApiParameterOptions[] {
const parameters: IApiParameterOptions[] = [];
for (const parameter of parameterNodes) {
Expand All @@ -1650,6 +1659,9 @@ export class ApiModelGenerator {
parameterTypeTokenRange,
isOptional: this._collector.typeChecker.isOptionalParameter(parameter),
isRest: Boolean(parameter.dotDotDotToken),
defaultValue:
parameter.initializer?.getText() ??
jsDoc?.find((param) => param.name === parameter.name.getText().trim())?.default?.toString(),
});
}

Expand Down Expand Up @@ -1753,7 +1765,7 @@ export class ApiModelGenerator {
return input;
}

return input.replaceAll(/(?<char>[{}])/g, '\\$<char>');
return input.replaceAll(/(?<char>[@{}])/g, '\\$<char>');
}

private _fixLinkTags(input?: string): string | undefined {
Expand Down Expand Up @@ -1848,7 +1860,7 @@ export class ApiModelGenerator {
isOptional: Boolean(prop.nullable),
isReadonly: Boolean(prop.readonly),
docComment: this._tsDocParser.parseString(
`/**\n * ${this._fixLinkTags(prop.description) ?? ''}${prop.default ? ` (default: ${this._escapeSpecialChars(prop.default)})` : ''}\n${
`/**\n * ${this._fixLinkTags(prop.description) ?? ''}\n${prop.default ? ` * @defaultValue ${this._escapeSpecialChars(prop.default)}\n` : ''}${
prop.see?.map((see) => ` * @see ${see}\n`).join('') ?? ''
}${prop.readonly ? ' * @readonly\n' : ''} */`,
).docComment,
Expand All @@ -1860,7 +1872,7 @@ export class ApiModelGenerator {
}${prop.name} :`,
},
...mappedVarType,
{ kind: ExcerptTokenKind.Content, text: ';' },
{ kind: ExcerptTokenKind.Content, text: `${prop.default ? ` = ${prop.default}` : ''};` },
],
propertyTypeTokenRange: { startIndex: 1, endIndex: 1 + mappedVarType.length },
releaseTag: prop.access === 'private' ? ReleaseTag.Internal : ReleaseTag.Public,
Expand All @@ -1883,6 +1895,7 @@ export class ApiModelGenerator {
startIndex: 1 + index + paramTokens.slice(0, index).reduce((akk, num) => akk + num, 0),
endIndex: 1 + index + paramTokens.slice(0, index + 1).reduce((akk, num) => akk + num, 0),
},
defaultValue: param.default?.toString(),
};
}

Expand All @@ -1907,7 +1920,7 @@ export class ApiModelGenerator {
excerptTokens.push(...newTokens);
excerptTokens.push({
kind: ExcerptTokenKind.Content,
text: `, ${method.params![index + 1]!.name}${
text: `${method.params![index]!.default ? ` = ${method.params![index]!.default}` : ''}, ${method.params![index + 1]!.name}${
method.params![index + 1]!.nullable || method.params![index + 1]!.optional ? '?' : ''
}: `,
});
Expand All @@ -1917,7 +1930,10 @@ export class ApiModelGenerator {
const newTokens = this._mapVarType(method.params[method.params.length - 1]!.type);
paramTokens.push(newTokens.length);
excerptTokens.push(...newTokens);
excerptTokens.push({ kind: ExcerptTokenKind.Content, text: `): ` });
excerptTokens.push({
kind: ExcerptTokenKind.Content,
text: `${method.params![method.params.length - 1]!.default ? ` = ${method.params![method.params.length - 1]!.default}` : ''}): `,
});
}

const returnTokens = this._mapVarType(method.returns?.[0] ?? []);
Expand Down
4 changes: 2 additions & 2 deletions packages/discord.js/src/util/Options.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const { version } = require('../../package.json');
* @property {MessageMentionOptions} [allowedMentions] The default value for {@link BaseMessageOptions#allowedMentions}
* @property {Partials[]} [partials] Structures allowed to be partial. This means events can be emitted even when
* they're missing all the data for a particular structure. See the "Partial Structures" topic on the
* [guide](https://discordjs.guide/popular-topics/partials.html) for some
* {@link https://discordjs.guide/popular-topics/partials.html guide} for some
* important usage information, as partials require you to put checks in place when handling data.
* @property {boolean} [failIfNotExists=true] The default value for {@link MessageReplyOptions#failIfNotExists}
* @property {PresenceData} [presence] Presence data to use upon login
Expand All @@ -37,7 +37,7 @@ const { version } = require('../../package.json');
* @property {WebSocketManagerOptions} [ws] Options for the WebSocketManager
* @property {RESTOptions} [rest] Options for the REST manager
* @property {Function} [jsonTransformer] A function used to transform outgoing json data
* @property {boolean} [enforceNonce=false] The default value for {@link MessageReplyOptions#enforceNonce}
* @property {boolean} [enforceNonce=false] The default value for {@link MessageCreateOptions#enforceNonce}
*/

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5731,7 +5731,7 @@ export type GuildAuditLogsResolvable = AuditLogEvent | null;
export type GuildAuditLogsTargetType = GuildAuditLogsTypes[keyof GuildAuditLogsTypes][0] | 'All' | 'Unknown';

export type GuildAuditLogsTargets = {
[key in GuildAuditLogsTargetType]: GuildAuditLogsTargetType;
[Key in GuildAuditLogsTargetType]: GuildAuditLogsTargetType;
};

export type GuildBanResolvable = GuildBan | UserResolvable;
Expand Down
109 changes: 109 additions & 0 deletions packages/scripts/src/builtinDocumentationLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
export const BuiltinDocumentationLinks = {
// Built-in types
bigint: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/BigInt',
boolean: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean',
null: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/null',
number: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number',
string: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String',
symbol: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol',
undefined: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined',

// Built-in classes
AbortSignal: 'https://developer.mozilla.org/docs/Web/API/AbortSignal',
Agent: 'https://undici.nodejs.org/#/docs/api/Agent',
Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array',
ArrayBuffer: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer',
AsyncGenerator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator',
AsyncIterable: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols',
AsyncIterableIterator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols',
Buffer: 'https://nodejs.org/api/buffer.html#class-buffer',
ChildProcess: 'https://nodejs.org/api/child_process.html#class-childprocess',
Date: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date',
Dispatcher: 'https://undici.nodejs.org/#/docs/api/Dispatcher',
Error: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error',
Function: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function',
Generator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Generator',
IncomingMessage: 'https://nodejs.org/api/http.html#class-httpincomingmessage',
Iterable: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols',
IterableIterator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols',
Iterator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Iterator',
Map: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map',
MessagePort: 'https://nodejs.org/api/worker_threads.html#class-messageport',
Promise: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise',
RangeError: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RangeError',
Readable: 'https://nodejs.org/api/stream.html#class-streamreadable',
ReadableStream: 'https://developer.mozilla.org/docs/Web/API/ReadableStream',
RegExp: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp',
Response: 'https://developer.mozilla.org/docs/Web/API/Response',
ServerResponse: 'https://nodejs.org/api/http.html#class-httpserverresponse',
Set: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set',
Stream: 'https://nodejs.org/api/stream.html#stream',
SymbolConstructor: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol',
TypeError: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypeError',
URL: 'https://developer.mozilla.org/docs/Web/API/URL',
URLSearchParams: 'https://developer.mozilla.org/docs/Web/API/URLSearchParams',
WeakMap: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakMap',
WeakRef: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakRef',
WeakSet: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakSet',
WebSocket: 'https://developer.mozilla.org/docs/Web/API/WebSocket',
Worker: 'https://nodejs.org/api/worker_threads.html#class-worker',
'NodeJS.Timeout': 'https://nodejs.org/api/timers.html#class-timeout',

// Typed arrays
BigInt64Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array',
BigUint64Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array',
Float32Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float32Array',
Float64Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float64Array',
Int16Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int16Array',
Int32Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int32Array',
Int8Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int8Array',
Uint16Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array',
Uint32Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array',
Uint8Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array',
Uint8ClampedArray: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray',

// TypeScript types
any: 'https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any',
keyof: 'https://www.typescriptlang.org/docs/handbook/2/keyof-types.html',
never: 'https://www.typescriptlang.org/docs/handbook/2/functions.html#never',
object: 'https://www.typescriptlang.org/docs/handbook/2/functions.html#object',
ReadonlyArray: 'https://www.typescriptlang.org/docs/handbook/2/objects.html#the-readonlyarray-type',
ReadonlyMap:
'https://github.com/microsoft/TypeScript/blob/1416053b9e85ca2344a7a6aa10456d633ea1cd65/src/lib/es2015.collection.d.ts#L38-L43',
ReadonlySet:
'https://github.com/microsoft/TypeScript/blob/1416053b9e85ca2344a7a6aa10456d633ea1cd65/src/lib/es2015.collection.d.ts#L104-L108',
unknown: 'https://www.typescriptlang.org/docs/handbook/2/functions.html#unknown',
this: 'https://www.typescriptlang.org/docs/handbook/2/classes.html#this-types',
typeof: 'https://www.typescriptlang.org/docs/handbook/2/typeof-types.html',
void: 'https://www.typescriptlang.org/docs/handbook/2/functions.html#void',

// TypeScript utility types
Awaited: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#awaitedtype',
Partial: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype',
Required: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype',
Readonly: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype',
Record: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type',
Pick: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys',
Omit: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys',
Exclude: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers',
Extract: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union',
NonNullable: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#nonnullabletype',
Parameters: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#parameterstype',
ConstructorParameters: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#constructorparameterstype',
ReturnType: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype',
InstanceType: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#instancetypetype',
ThisParameterType: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#thisparametertypetype',
OmitThisParameter: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#omitthisparametertype',
ThisType: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#thistypetype',
Uppercase: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#uppercasestringtype',
Lowercase: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#lowercasestringtype',
Capitalize: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#capitalizestringtype',
Uncapitalize: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#uncapitalizestringtype',

// External Libraries
AsyncEventEmitter: 'https://github.com/vladfrangu/async_event_emitter',
AsyncQueue: 'https://www.sapphirejs.dev/docs/Documentation/api-utilities/classes/sapphire_async_queue.AsyncQueue',
Redis: 'https://redis.github.io/ioredis/classes/Redis.html',
'prism.opus.Encoder': 'https://amishshah.github.io/prism-media/opus.Encoder.html',
'prism.VolumeTransformer': 'https://amishshah.github.io/prism-media/core.VolumeTransformer.html',
} as const;
Loading

0 comments on commit 3540c31

Please sign in to comment.