mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-16 03:23:29 +01:00
refactor: minify api.json by shortening keys (#9971)
* refactor: minify api.json by shortening keys * fix: links to other packages * refactor: get doclink from canonicalReference, not model * fix: types * fix: again * fix: @link tags with alt texts
This commit is contained in:
@@ -1,26 +1,22 @@
|
|||||||
import type { ApiModel, Excerpt } from '@discordjs/api-extractor-model';
|
import type { Excerpt } from '@discordjs/api-extractor-model';
|
||||||
import { ExcerptTokenKind } from '@discordjs/api-extractor-model';
|
import { ExcerptTokenKind } from '@discordjs/api-extractor-model';
|
||||||
import { BuiltinDocumentationLinks } from '~/util/builtinDocumentationLinks';
|
import { BuiltinDocumentationLinks } from '~/util/builtinDocumentationLinks';
|
||||||
import { DISCORD_API_TYPES_DOCS_URL } from '~/util/constants';
|
import { DISCORD_API_TYPES_DOCS_URL } from '~/util/constants';
|
||||||
import { DocumentationLink } from './DocumentationLink';
|
import { DocumentationLink } from './DocumentationLink';
|
||||||
import { ItemLink } from './ItemLink';
|
import { ItemLink } from './ItemLink';
|
||||||
import { resolveItemURI } from './documentation/util';
|
import { resolveCanonicalReference, resolveItemURI } from './documentation/util';
|
||||||
|
|
||||||
export interface ExcerptTextProps {
|
export interface ExcerptTextProps {
|
||||||
/**
|
/**
|
||||||
* The tokens to render.
|
* The tokens to render.
|
||||||
*/
|
*/
|
||||||
readonly excerpt: Excerpt;
|
readonly excerpt: Excerpt;
|
||||||
/**
|
|
||||||
* The model to resolve item references from.
|
|
||||||
*/
|
|
||||||
readonly model: ApiModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component that renders excerpt tokens from an api item.
|
* A component that renders excerpt tokens from an api item.
|
||||||
*/
|
*/
|
||||||
export function ExcerptText({ model, excerpt }: ExcerptTextProps) {
|
export function ExcerptText({ excerpt }: ExcerptTextProps) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{excerpt.spannedTokens.map((token, idx) => {
|
{excerpt.spannedTokens.map((token, idx) => {
|
||||||
@@ -53,20 +49,18 @@ export function ExcerptText({ model, excerpt }: ExcerptTextProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = token.canonicalReference
|
const resolved = token.canonicalReference ? resolveCanonicalReference(token.canonicalReference) : null;
|
||||||
? model.resolveDeclarationReference(token.canonicalReference!, model).resolvedApiItem
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (!item) {
|
if (!resolved) {
|
||||||
return token.text;
|
return token.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemLink
|
<ItemLink
|
||||||
className="text-blurple"
|
className="text-blurple"
|
||||||
itemURI={resolveItemURI(item)}
|
itemURI={resolveItemURI(resolved.item)}
|
||||||
key={`${item.displayName}-${item.containerKey}-${idx}`}
|
key={`${resolved.item.displayName}-${resolved.item.containerKey}-${idx}`}
|
||||||
packageName={item.getAssociatedPackage()?.displayName.replace('@discordjs/', '')}
|
packageName={resolved.package}
|
||||||
>
|
>
|
||||||
{token.text}
|
{token.text}
|
||||||
</ItemLink>
|
</ItemLink>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export function ParameterTable({ item }: { readonly item: ApiDocumentedItem & Ap
|
|||||||
() =>
|
() =>
|
||||||
params.map((param) => ({
|
params.map((param) => ({
|
||||||
Name: param.isRest ? `...${param.name}` : param.name,
|
Name: param.isRest ? `...${param.name}` : param.name,
|
||||||
Type: <ExcerptText excerpt={param.parameterTypeExcerpt} model={item.getAssociatedModel()!} />,
|
Type: <ExcerptText excerpt={param.parameterTypeExcerpt} />,
|
||||||
Optional: param.isOptional ? 'Yes' : 'No',
|
Optional: param.isOptional ? 'Yes' : 'No',
|
||||||
Description: param.description ? <TSDoc item={item} tsdoc={param.description} /> : 'None',
|
Description: param.description ? <TSDoc item={item} tsdoc={param.description} /> : 'None',
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -32,9 +32,7 @@ export function Property({
|
|||||||
>
|
>
|
||||||
{`${item.displayName}${item.isOptional ? '?' : ''}`}
|
{`${item.displayName}${item.isOptional ? '?' : ''}`}
|
||||||
<span>:</span>
|
<span>:</span>
|
||||||
{item.propertyTypeExcerpt.text ? (
|
{item.propertyTypeExcerpt.text ? <ExcerptText excerpt={item.propertyTypeExcerpt} /> : null}
|
||||||
<ExcerptText excerpt={item.propertyTypeExcerpt} model={item.getAssociatedModel()!} />
|
|
||||||
) : null}
|
|
||||||
</CodeHeading>
|
</CodeHeading>
|
||||||
</div>
|
</div>
|
||||||
{hasSummary || inheritedFrom ? (
|
{hasSummary || inheritedFrom ? (
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { ApiModel, Excerpt } from '@discordjs/api-extractor-model';
|
import type { Excerpt } from '@discordjs/api-extractor-model';
|
||||||
import { ExcerptText } from './ExcerptText';
|
import { ExcerptText } from './ExcerptText';
|
||||||
|
|
||||||
export function SignatureText({ excerpt, model }: { readonly excerpt: Excerpt; readonly model: ApiModel }) {
|
export function SignatureText({ excerpt }: { readonly excerpt: Excerpt }) {
|
||||||
return (
|
return (
|
||||||
<h4 className="break-all text-lg font-bold font-mono">
|
<h4 className="break-all text-lg font-bold font-mono">
|
||||||
<ExcerptText excerpt={excerpt} model={model} />
|
<ExcerptText excerpt={excerpt} />
|
||||||
</h4>
|
</h4>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,21 +11,20 @@ const rowElements = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function TypeParamTable({ item }: { readonly item: ApiTypeParameterListMixin }) {
|
export function TypeParamTable({ item }: { readonly item: ApiTypeParameterListMixin }) {
|
||||||
const model = item.getAssociatedModel()!;
|
|
||||||
const rows = useMemo(
|
const rows = useMemo(
|
||||||
() =>
|
() =>
|
||||||
item.typeParameters.map((typeParam) => ({
|
item.typeParameters.map((typeParam) => ({
|
||||||
Name: typeParam.name,
|
Name: typeParam.name,
|
||||||
Constraints: <ExcerptText excerpt={typeParam.constraintExcerpt} model={model} />,
|
Constraints: <ExcerptText excerpt={typeParam.constraintExcerpt} />,
|
||||||
Optional: typeParam.isOptional ? 'Yes' : 'No',
|
Optional: typeParam.isOptional ? 'Yes' : 'No',
|
||||||
Default: <ExcerptText excerpt={typeParam.defaultTypeExcerpt} model={model} />,
|
Default: <ExcerptText excerpt={typeParam.defaultTypeExcerpt} />,
|
||||||
Description: typeParam.tsdocTypeParamBlock ? (
|
Description: typeParam.tsdocTypeParamBlock ? (
|
||||||
<TSDoc item={item} tsdoc={typeParam.tsdocTypeParamBlock.content} />
|
<TSDoc item={item} tsdoc={typeParam.tsdocTypeParamBlock.content} />
|
||||||
) : (
|
) : (
|
||||||
'None'
|
'None'
|
||||||
),
|
),
|
||||||
})),
|
})),
|
||||||
[item, model],
|
[item],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ export function HierarchyText({
|
|||||||
readonly item: ApiClass | ApiInterface;
|
readonly item: ApiClass | ApiInterface;
|
||||||
readonly type: 'Extends' | 'Implements';
|
readonly type: 'Extends' | 'Implements';
|
||||||
}) {
|
}) {
|
||||||
const model = item.getAssociatedModel()!;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(item.kind === ApiItemKind.Class &&
|
(item.kind === ApiItemKind.Class &&
|
||||||
(item as ApiClass).extendsType === undefined &&
|
(item as ApiClass).extendsType === undefined &&
|
||||||
@@ -50,7 +48,7 @@ export function HierarchyText({
|
|||||||
<div className="flex flex-row place-items-center gap-4" key={`${type}-${idx}`}>
|
<div className="flex flex-row place-items-center gap-4" key={`${type}-${idx}`}>
|
||||||
<h3 className="text-xl font-bold">{type}</h3>
|
<h3 className="text-xl font-bold">{type}</h3>
|
||||||
<span className="break-all font-mono space-y-2">
|
<span className="break-all font-mono space-y-2">
|
||||||
<ExcerptText excerpt={excerpt} model={model} />
|
<ExcerptText excerpt={excerpt} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApiItemKind } from '@discordjs/api-extractor-model';
|
import { ApiItemKind, Meaning } from '@discordjs/api-extractor-model';
|
||||||
import type {
|
import type {
|
||||||
ApiItem,
|
ApiItem,
|
||||||
ApiItemContainerMixin,
|
ApiItemContainerMixin,
|
||||||
@@ -10,11 +10,25 @@ import type {
|
|||||||
ApiParameterListMixin,
|
ApiParameterListMixin,
|
||||||
ApiEvent,
|
ApiEvent,
|
||||||
} from '@discordjs/api-extractor-model';
|
} from '@discordjs/api-extractor-model';
|
||||||
|
import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference';
|
||||||
import { METHOD_SEPARATOR, OVERLOAD_SEPARATOR } from '~/util/constants';
|
import { METHOD_SEPARATOR, OVERLOAD_SEPARATOR } from '~/util/constants';
|
||||||
import { resolveMembers } from '~/util/members';
|
import { resolveMembers } from '~/util/members';
|
||||||
import { resolveParameters } from '~/util/model';
|
import { resolveParameters } from '~/util/model';
|
||||||
import type { TableOfContentsSerialized } from '../TableOfContentItems';
|
import type { TableOfContentsSerialized } from '../TableOfContentItems';
|
||||||
|
|
||||||
|
export type ApiItemLike = {
|
||||||
|
[K in keyof ApiItem]?: K extends 'displayName' | 'kind'
|
||||||
|
? ApiItem[K]
|
||||||
|
: K extends 'parent'
|
||||||
|
? ApiItemLike | undefined
|
||||||
|
: ApiItem[K] | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ResolvedCanonicalReference {
|
||||||
|
item: ApiItemLike;
|
||||||
|
package: string;
|
||||||
|
}
|
||||||
|
|
||||||
export function hasProperties(item: ApiItemContainerMixin) {
|
export function hasProperties(item: ApiItemContainerMixin) {
|
||||||
return resolveMembers(item, memberPredicate).some(
|
return resolveMembers(item, memberPredicate).some(
|
||||||
({ item: member }) => member.kind === ApiItemKind.Property || member.kind === ApiItemKind.PropertySignature,
|
({ item: member }) => member.kind === ApiItemKind.Property || member.kind === ApiItemKind.PropertySignature,
|
||||||
@@ -31,12 +45,65 @@ export function hasEvents(item: ApiItemContainerMixin) {
|
|||||||
return resolveMembers(item, memberPredicate).some(({ item: member }) => member.kind === ApiItemKind.Event);
|
return resolveMembers(item, memberPredicate).some(({ item: member }) => member.kind === ApiItemKind.Event);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveItemURI(item: ApiItem): string {
|
export function resolveItemURI(item: ApiItemLike): string {
|
||||||
return !item.parent || item.parent.kind === ApiItemKind.EntryPoint
|
return !item.parent || item.parent.kind === ApiItemKind.EntryPoint
|
||||||
? `${item.displayName}${OVERLOAD_SEPARATOR}${item.kind}`
|
? `${item.displayName}${OVERLOAD_SEPARATOR}${item.kind}`
|
||||||
: `${item.parent.displayName}${OVERLOAD_SEPARATOR}${item.parent.kind}${METHOD_SEPARATOR}${item.displayName}`;
|
: `${item.parent.displayName}${OVERLOAD_SEPARATOR}${item.parent.kind}${METHOD_SEPARATOR}${item.displayName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveCanonicalReference(canonicalReference: DeclarationReference): ResolvedCanonicalReference | null {
|
||||||
|
if (
|
||||||
|
canonicalReference.source &&
|
||||||
|
'packageName' in canonicalReference.source &&
|
||||||
|
canonicalReference.symbol?.componentPath &&
|
||||||
|
canonicalReference.symbol.meaning
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
package: canonicalReference.source.unscopedPackageName,
|
||||||
|
item: {
|
||||||
|
kind: mapMeaningToKind(canonicalReference.symbol.meaning as unknown as Meaning),
|
||||||
|
displayName: canonicalReference.symbol.componentPath.component.toString(),
|
||||||
|
containerKey: `|${
|
||||||
|
canonicalReference.symbol.meaning
|
||||||
|
}|${canonicalReference.symbol.componentPath.component.toString()}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapMeaningToKind(meaning: Meaning): ApiItemKind {
|
||||||
|
switch (meaning) {
|
||||||
|
case Meaning.CallSignature:
|
||||||
|
return ApiItemKind.CallSignature;
|
||||||
|
case Meaning.Class:
|
||||||
|
return ApiItemKind.Class;
|
||||||
|
case Meaning.ComplexType:
|
||||||
|
throw new Error('Not a valid canonicalReference: Meaning.ComplexType');
|
||||||
|
case Meaning.ConstructSignature:
|
||||||
|
return ApiItemKind.ConstructSignature;
|
||||||
|
case Meaning.Constructor:
|
||||||
|
return ApiItemKind.Constructor;
|
||||||
|
case Meaning.Enum:
|
||||||
|
return ApiItemKind.Enum;
|
||||||
|
case Meaning.Event:
|
||||||
|
return ApiItemKind.Event;
|
||||||
|
case Meaning.Function:
|
||||||
|
return ApiItemKind.Function;
|
||||||
|
case Meaning.IndexSignature:
|
||||||
|
return ApiItemKind.IndexSignature;
|
||||||
|
case Meaning.Interface:
|
||||||
|
return ApiItemKind.Interface;
|
||||||
|
case Meaning.Member:
|
||||||
|
return ApiItemKind.Property;
|
||||||
|
case Meaning.Namespace:
|
||||||
|
return ApiItemKind.Namespace;
|
||||||
|
case Meaning.TypeAlias:
|
||||||
|
return ApiItemKind.TypeAlias;
|
||||||
|
case Meaning.Variable:
|
||||||
|
return ApiItemKind.Variable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function memberPredicate(
|
export function memberPredicate(
|
||||||
item: ApiItem,
|
item: ApiItem,
|
||||||
): item is ApiEvent | ApiMethod | ApiMethodSignature | ApiProperty | ApiPropertySignature {
|
): item is ApiEvent | ApiMethod | ApiMethodSignature | ApiProperty | ApiPropertySignature {
|
||||||
|
|||||||
@@ -14,9 +14,7 @@ export function EnumMember({ member }: { readonly member: ApiEnumMember }) {
|
|||||||
>
|
>
|
||||||
{member.name}
|
{member.name}
|
||||||
<span>=</span>
|
<span>=</span>
|
||||||
{member.initializerExcerpt ? (
|
{member.initializerExcerpt ? <SignatureText excerpt={member.initializerExcerpt} /> : null}
|
||||||
<SignatureText excerpt={member.initializerExcerpt} model={member.getAssociatedModel()!} />
|
|
||||||
) : null}
|
|
||||||
</CodeHeading>
|
</CodeHeading>
|
||||||
{member.tsdocComment ? <TSDoc item={member} tsdoc={member.tsdocComment.summarySection} /> : null}
|
{member.tsdocComment ? <TSDoc item={member} tsdoc={member.tsdocComment.summarySection} /> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export function MethodHeader({ method }: { readonly method: ApiMethod | ApiMetho
|
|||||||
>
|
>
|
||||||
{`${method.name}(${parametersString(method)})`}
|
{`${method.name}(${parametersString(method)})`}
|
||||||
<span>:</span>
|
<span>:</span>
|
||||||
<ExcerptText excerpt={method.returnTypeExcerpt} model={method.getAssociatedModel()!} />
|
<ExcerptText excerpt={method.returnTypeExcerpt} />
|
||||||
</CodeHeading>
|
</CodeHeading>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,25 +1,8 @@
|
|||||||
import type { ApiModel, ApiPackage } from '@discordjs/api-extractor-model';
|
import { ApiPackage } from '@discordjs/api-extractor-model';
|
||||||
import { ApiItem } from '@discordjs/api-extractor-model';
|
import type { ApiModel } from '@discordjs/api-extractor-model';
|
||||||
import { TSDocConfiguration } from '@microsoft/tsdoc';
|
|
||||||
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
|
|
||||||
|
|
||||||
export const addPackageToModel = (model: ApiModel, data: any) => {
|
export const addPackageToModel = (model: ApiModel, data: any) => {
|
||||||
let apiPackage: ApiPackage;
|
const apiPackage = ApiPackage.loadFromJson(data);
|
||||||
if (data.metadata) {
|
|
||||||
const tsdocConfiguration = new TSDocConfiguration();
|
|
||||||
const tsdocConfigFile = TSDocConfigFile.loadFromObject(data.metadata.tsdocConfig);
|
|
||||||
tsdocConfigFile.configureParser(tsdocConfiguration);
|
|
||||||
|
|
||||||
apiPackage = ApiItem.deserialize(data, {
|
|
||||||
apiJsonFilename: '',
|
|
||||||
toolPackage: data.metadata.toolPackage,
|
|
||||||
toolVersion: data.metadata.toolVersion,
|
|
||||||
versionToDeserialize: data.metadata.schemaVersion,
|
|
||||||
tsdocConfiguration,
|
|
||||||
}) as ApiPackage;
|
|
||||||
} else {
|
|
||||||
apiPackage = ApiItem.deserializeDocgen(data, 'discord.js') as ApiPackage;
|
|
||||||
}
|
|
||||||
|
|
||||||
model.addMember(apiPackage);
|
model.addMember(apiPackage);
|
||||||
return model;
|
return model;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// See LICENSE in the project root for license information.
|
// See LICENSE in the project root for license information.
|
||||||
|
|
||||||
import { Buffer } from 'node:buffer';
|
import { Buffer } from 'node:buffer';
|
||||||
|
import path from 'node:path';
|
||||||
|
import util from 'node:util';
|
||||||
import { TSDocConfiguration } from '@microsoft/tsdoc';
|
import { TSDocConfiguration } from '@microsoft/tsdoc';
|
||||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||||
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
|
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
|
||||||
@@ -29,10 +31,58 @@ export interface IApiPackageOptions
|
|||||||
extends IApiItemContainerMixinOptions,
|
extends IApiItemContainerMixinOptions,
|
||||||
IApiNameMixinOptions,
|
IApiNameMixinOptions,
|
||||||
IApiDocumentedItemOptions {
|
IApiDocumentedItemOptions {
|
||||||
|
dependencies?: Record<string, string> | undefined;
|
||||||
projectFolderUrl?: string | undefined;
|
projectFolderUrl?: string | undefined;
|
||||||
tsdocConfiguration: TSDocConfiguration;
|
tsdocConfiguration: TSDocConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MinifyJSONMapping = {
|
||||||
|
canonicalReference: 'c',
|
||||||
|
constraintTokenRange: 'ctr',
|
||||||
|
dependencies: 'dp',
|
||||||
|
defaultTypeTokenRange: 'dtr',
|
||||||
|
docComment: 'd',
|
||||||
|
endIndex: 'en',
|
||||||
|
excerptTokens: 'ex',
|
||||||
|
extendsTokenRange: 'etr',
|
||||||
|
extendsTokenRanges: 'etrs',
|
||||||
|
fileColumn: 'co',
|
||||||
|
fileLine: 'l',
|
||||||
|
fileUrlPath: 'pat',
|
||||||
|
implementsTokenRanges: 'itrs',
|
||||||
|
initializerTokenRange: 'itr',
|
||||||
|
isAbstract: 'ab',
|
||||||
|
isOptional: 'op',
|
||||||
|
isProtected: 'pr',
|
||||||
|
isReadonly: 'ro',
|
||||||
|
isRest: 'rs',
|
||||||
|
isStatic: 'sta',
|
||||||
|
kind: 'k',
|
||||||
|
members: 'ms',
|
||||||
|
metadata: 'meta',
|
||||||
|
name: 'n',
|
||||||
|
oldestForwardsCompatibleVersion: 'ov',
|
||||||
|
overloadIndex: 'oi',
|
||||||
|
parameterName: 'pn',
|
||||||
|
parameterTypeTokenRange: 'ptr',
|
||||||
|
parameters: 'ps',
|
||||||
|
preserveMemberOrder: 'pmo',
|
||||||
|
projectFolderUrl: 'pdir',
|
||||||
|
propertyTypeTokenRange: 'prtr',
|
||||||
|
releaseTag: 'r',
|
||||||
|
returnTypeTokenRange: 'rtr',
|
||||||
|
schemaVersion: 'v',
|
||||||
|
startIndex: 'st',
|
||||||
|
text: 't',
|
||||||
|
toolPackage: 'tpk',
|
||||||
|
toolVersion: 'tv',
|
||||||
|
tsdocConfig: 'ts',
|
||||||
|
typeParameterName: 'tp',
|
||||||
|
typeParameters: 'tps',
|
||||||
|
typeTokenRange: 'ttr',
|
||||||
|
variableTypeTokenRange: 'vtr',
|
||||||
|
};
|
||||||
|
|
||||||
export interface IApiPackageMetadataJson {
|
export interface IApiPackageMetadataJson {
|
||||||
/**
|
/**
|
||||||
* To support forwards compatibility, the `oldestForwardsCompatibleVersion` field tracks the oldest schema version
|
* To support forwards compatibility, the `oldestForwardsCompatibleVersion` field tracks the oldest schema version
|
||||||
@@ -77,10 +127,15 @@ export interface IApiPackageMetadataJson {
|
|||||||
* Normally this configuration is loaded from the project's tsdoc.json file. It is stored
|
* Normally this configuration is loaded from the project's tsdoc.json file. It is stored
|
||||||
* in the .api.json file so that doc comments can be parsed accurately when loading the file.
|
* in the .api.json file so that doc comments can be parsed accurately when loading the file.
|
||||||
*/
|
*/
|
||||||
tsdocConfig: JsonObject;
|
tsdocConfig?: JsonObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IApiPackageJson extends IApiItemJson {
|
export interface IApiPackageJson extends IApiItemJson {
|
||||||
|
/**
|
||||||
|
* Names of packages in the same monorepo this one uses mapped to the version of said package.
|
||||||
|
*/
|
||||||
|
dependencies?: Record<string, string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A file header that stores metadata about the tool that wrote the *.api.json file.
|
* A file header that stores metadata about the tool that wrote the *.api.json file.
|
||||||
*/
|
*/
|
||||||
@@ -141,11 +196,31 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented
|
|||||||
|
|
||||||
private readonly _projectFolderUrl?: string | undefined;
|
private readonly _projectFolderUrl?: string | undefined;
|
||||||
|
|
||||||
|
private readonly _dependencies?: Record<string, string> | undefined;
|
||||||
|
|
||||||
public constructor(options: IApiPackageOptions) {
|
public constructor(options: IApiPackageOptions) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
this._tsdocConfiguration = options.tsdocConfiguration;
|
this._tsdocConfiguration = options.tsdocConfiguration;
|
||||||
this._projectFolderUrl = options.projectFolderUrl;
|
this._projectFolderUrl = options.projectFolderUrl;
|
||||||
|
|
||||||
|
if (options.dependencies) {
|
||||||
|
this._dependencies = options.dependencies;
|
||||||
|
} else {
|
||||||
|
const packageJson = PackageJsonLookup.instance.tryLoadPackageJsonFor('.');
|
||||||
|
if (packageJson?.dependencies) {
|
||||||
|
this._dependencies = {};
|
||||||
|
for (const [pack, semVer] of Object.entries(packageJson.dependencies)) {
|
||||||
|
const pathToPackage = path.join('..', pack.includes('/') ? pack.slice(pack.lastIndexOf('/')) : pack);
|
||||||
|
if (semVer === 'workspace:^') {
|
||||||
|
this._dependencies[pack] =
|
||||||
|
PackageJsonLookup.instance.tryLoadPackageJsonFor(pathToPackage)?.version ?? 'unknown';
|
||||||
|
} else if (FileSystem.exists(pathToPackage)) {
|
||||||
|
this._dependencies[pack] = semVer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,11 +234,17 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented
|
|||||||
super.onDeserializeInto(options, context, jsonObject);
|
super.onDeserializeInto(options, context, jsonObject);
|
||||||
|
|
||||||
options.projectFolderUrl = jsonObject.projectFolderUrl;
|
options.projectFolderUrl = jsonObject.projectFolderUrl;
|
||||||
|
options.dependencies = jsonObject.dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static loadFromJsonFile(apiJsonFilename: string): ApiPackage {
|
public static loadFromJsonFile(apiJsonFilename: string): ApiPackage {
|
||||||
const jsonObject: IApiPackageJson = JsonFile.load(apiJsonFilename);
|
return this.loadFromJson(JsonFile.load(apiJsonFilename), apiJsonFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static loadFromJson(rawJson: any, apiJsonFilename: string = ''): ApiPackage {
|
||||||
|
const jsonObject =
|
||||||
|
MinifyJSONMapping.metadata in rawJson ? this._mapFromMinified(rawJson) : (rawJson as IApiPackageJson);
|
||||||
|
if (!jsonObject?.metadata) throw new Error(util.inspect(rawJson, { depth: 2 }));
|
||||||
if (!jsonObject?.metadata || typeof jsonObject.metadata.schemaVersion !== 'number') {
|
if (!jsonObject?.metadata || typeof jsonObject.metadata.schemaVersion !== 'number') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error loading ${apiJsonFilename}:` +
|
`Error loading ${apiJsonFilename}:` +
|
||||||
@@ -212,7 +293,7 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented
|
|||||||
|
|
||||||
const tsdocConfiguration: TSDocConfiguration = new TSDocConfiguration();
|
const tsdocConfiguration: TSDocConfiguration = new TSDocConfiguration();
|
||||||
|
|
||||||
if (versionToDeserialize >= ApiJsonSchemaVersion.V_1004) {
|
if (versionToDeserialize >= ApiJsonSchemaVersion.V_1004 && 'tsdocConfiguration' in jsonObject) {
|
||||||
const tsdocConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromObject(jsonObject.metadata.tsdocConfig);
|
const tsdocConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromObject(jsonObject.metadata.tsdocConfig);
|
||||||
if (tsdocConfigFile.hasErrors) {
|
if (tsdocConfigFile.hasErrors) {
|
||||||
throw new Error(`Error loading ${apiJsonFilename}:\n` + tsdocConfigFile.getErrorSummary());
|
throw new Error(`Error loading ${apiJsonFilename}:\n` + tsdocConfigFile.getErrorSummary());
|
||||||
@@ -251,6 +332,10 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented
|
|||||||
return this.members as readonly ApiEntryPoint[];
|
return this.members as readonly ApiEntryPoint[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get dependencies(): Record<string, string> | undefined {
|
||||||
|
return this._dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TSDoc configuration that was used when analyzing the API for this package.
|
* The TSDoc configuration that was used when analyzing the API for this package.
|
||||||
*
|
*
|
||||||
@@ -306,9 +391,13 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented
|
|||||||
jsonObject.projectFolderUrl = this.projectFolderUrl;
|
jsonObject.projectFolderUrl = this.projectFolderUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._dependencies) {
|
||||||
|
jsonObject.dependencies = this._dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
this.serializeInto(jsonObject);
|
this.serializeInto(jsonObject);
|
||||||
if (ioptions.minify) {
|
if (ioptions.minify) {
|
||||||
FileSystem.writeFile(apiJsonFilename, Buffer.from(JSON.stringify(jsonObject), 'utf8'), {
|
FileSystem.writeFile(apiJsonFilename, Buffer.from(JSON.stringify(this._mapToMinified(jsonObject)), 'utf8'), {
|
||||||
ensureFolderExists: ioptions.ensureFolderExists ?? true,
|
ensureFolderExists: ioptions.ensureFolderExists ?? true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -322,4 +411,47 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented
|
|||||||
public override buildCanonicalReference(): DeclarationReference {
|
public override buildCanonicalReference(): DeclarationReference {
|
||||||
return DeclarationReference.package(this.name);
|
return DeclarationReference.package(this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _mapToMinified(jsonObject: IApiPackageJson) {
|
||||||
|
const mapper = (item: any): any => {
|
||||||
|
if (Array.isArray(item)) return item.map(mapper);
|
||||||
|
else {
|
||||||
|
const result: any = {};
|
||||||
|
for (const key of Object.keys(item)) {
|
||||||
|
if (key === 'dependencies') {
|
||||||
|
result[MinifyJSONMapping.dependencies] = item.dependencies;
|
||||||
|
} else
|
||||||
|
result[MinifyJSONMapping[key as keyof typeof MinifyJSONMapping]] =
|
||||||
|
typeof item[key] === 'object' ? mapper(item[key]) : item[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return mapper(jsonObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _mapFromMinified(jsonObject: any): IApiPackageJson {
|
||||||
|
const mapper = (item: any): any => {
|
||||||
|
if (Array.isArray(item)) return item.map(mapper);
|
||||||
|
else {
|
||||||
|
const result: any = {};
|
||||||
|
for (const key of Object.keys(item)) {
|
||||||
|
if (key === MinifyJSONMapping.dependencies) {
|
||||||
|
result.dependencies = item[MinifyJSONMapping.dependencies];
|
||||||
|
} else
|
||||||
|
result[
|
||||||
|
Object.keys(MinifyJSONMapping).find(
|
||||||
|
(look) => MinifyJSONMapping[look as keyof typeof MinifyJSONMapping] === key,
|
||||||
|
)!
|
||||||
|
] = typeof item[key] === 'object' ? mapper(item[key]) : item[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return mapper(jsonObject) as IApiPackageJson;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,13 +96,18 @@ export enum ApiJsonSchemaVersion {
|
|||||||
*/
|
*/
|
||||||
V_1012 = 1_012,
|
V_1012 = 1_012,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make tsdocConfiguration optional
|
||||||
|
*/
|
||||||
|
V_1013 = 1_013,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current latest .api.json schema version.
|
* The current latest .api.json schema version.
|
||||||
*
|
*
|
||||||
* IMPORTANT: When incrementing this number, consider whether `OLDEST_SUPPORTED` or `OLDEST_FORWARDS_COMPATIBLE`
|
* IMPORTANT: When incrementing this number, consider whether `OLDEST_SUPPORTED` or `OLDEST_FORWARDS_COMPATIBLE`
|
||||||
* should be updated.
|
* should be updated.
|
||||||
*/
|
*/
|
||||||
LATEST = V_1012,
|
LATEST = V_1013,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The oldest .api.json schema version that is still supported for backwards compatibility.
|
* The oldest .api.json schema version that is still supported for backwards compatibility.
|
||||||
@@ -119,7 +124,7 @@ export enum ApiJsonSchemaVersion {
|
|||||||
* if the older library would not be able to deserialize your new file format. Adding a nonessential field
|
* if the older library would not be able to deserialize your new file format. Adding a nonessential field
|
||||||
* is generally okay. Removing, modifying, or reinterpreting existing fields is NOT safe.
|
* is generally okay. Removing, modifying, or reinterpreting existing fields is NOT safe.
|
||||||
*/
|
*/
|
||||||
OLDEST_FORWARDS_COMPATIBLE = V_1001,
|
OLDEST_FORWARDS_COMPATIBLE = V_1013,
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DeserializerContext {
|
export class DeserializerContext {
|
||||||
|
|||||||
@@ -212,7 +212,11 @@ interface IProcessAstEntityContext {
|
|||||||
|
|
||||||
const linkRegEx = /{@link\s(?<class>\w+)#(?<event>event:)?(?<prop>[\w()]+)(?<name>\s[^}]*)?}/g;
|
const linkRegEx = /{@link\s(?<class>\w+)#(?<event>event:)?(?<prop>[\w()]+)(?<name>\s[^}]*)?}/g;
|
||||||
function fixLinkTags(input?: string): string | undefined {
|
function fixLinkTags(input?: string): string | undefined {
|
||||||
return input?.replaceAll(linkRegEx, '{@link $<class>.$<prop>$<name>}');
|
return input?.replaceAll(
|
||||||
|
linkRegEx,
|
||||||
|
(_match, _p1, _p2, _p3, _p4, _offset, _string, groups) =>
|
||||||
|
`{@link ${groups.class}.${groups.prop}${groups.name ? ` |${groups.name}` : ''}}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filePathFromJson(meta: DocgenMetaJson): string {
|
function filePathFromJson(meta: DocgenMetaJson): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user