Skip to content

Commit

Permalink
refactor(api-gen): transform @link in JSDoc body (#2069)
Browse files Browse the repository at this point in the history
PR Close #2069
  • Loading branch information
JeanMeche authored and josephperrott committed May 17, 2024
1 parent a5418b0 commit 7d12344
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 26 deletions.
36 changes: 24 additions & 12 deletions bazel/api-gen/rendering/transforms/jsdoc-transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@ export const JS_DOC_USAGE_NOTES_TAG = 'usageNotes';
export const JS_DOC_SEE_TAG = 'see';
export const JS_DOC_DESCRIPTION_TAG = 'description';

// Some links are written in the following format: {@link Route}
const jsDoclinkRegex = /\{\s*@link\s+([^}]+)\s*\}/;

/** Given an entity with a description, gets the entity augmented with an `htmlDescription`. */
export function addHtmlDescription<T extends HasDescription>(entry: T): T & HasHtmlDescription {
export function addHtmlDescription<T extends HasDescription & HasModuleName>(
entry: T,
): T & HasHtmlDescription {
const firstParagraphRule = /(.*?)(?:\n\n|$)/s;

let jsDocDescription = '';
Expand All @@ -49,8 +54,11 @@ export function addHtmlDescription<T extends HasDescription>(entry: T): T & HasH

const description = !!entry.description ? entry.description : jsDocDescription;
const shortTextMatch = description.match(firstParagraphRule);
const htmlDescription = getHtmlForJsDocText(description).trim();
const shortHtmlDescription = getHtmlForJsDocText(shortTextMatch ? shortTextMatch[0] : '').trim();
const htmlDescription = getHtmlForJsDocText(description, entry).trim();
const shortHtmlDescription = getHtmlForJsDocText(
shortTextMatch ? shortTextMatch[0] : '',
entry,
).trim();
return {
...entry,
htmlDescription,
Expand All @@ -62,14 +70,14 @@ export function addHtmlDescription<T extends HasDescription>(entry: T): T & HasH
* Given an entity with JsDoc tags, gets the entity with JsDocTagRenderable entries that
* have been augmented with an `htmlComment`.
*/
export function addHtmlJsDocTagComments<T extends HasJsDocTags>(
export function addHtmlJsDocTagComments<T extends HasJsDocTags & HasModuleName>(
entry: T,
): T & HasRenderableJsDocTags {
return {
...entry,
jsdocTags: entry.jsdocTags.map((tag) => ({
...tag,
htmlComment: getHtmlForJsDocText(tag.comment),
htmlComment: getHtmlForJsDocText(tag.comment, entry),
})),
};
}
Expand Down Expand Up @@ -99,19 +107,19 @@ export function addHtmlUsageNotes<T extends HasJsDocTags>(entry: T): T & HasHtml
}

/** Given a markdown JsDoc text, gets the rendered HTML. */
export function getHtmlForJsDocText(text: string): string {
return marked.parse(wrapExampleHtmlElementsWithCode(text)) as string;
function getHtmlForJsDocText<T extends HasModuleName>(text: string, entry: T): string {
return marked.parse(convertLinks(wrapExampleHtmlElementsWithCode(text), entry)) as string;
}

export function setEntryFlags<T extends HasJsDocTags>(
export function setEntryFlags<T extends HasJsDocTags & HasModuleName>(
entry: T,
): T & HasDeprecatedFlag & HasDeveloperPreviewFlag & hasExperimentalFlag {
const deprecationMessage = getDeprecatedEntry(entry);
return {
...entry,
isDeprecated: isDeprecatedEntry(entry),
deprecationMessage: deprecationMessage
? getHtmlForJsDocText(deprecationMessage)
? getHtmlForJsDocText(deprecationMessage, entry)
: deprecationMessage,
isDeveloperPreview: isDeveloperPreview(entry),
isExperimental: isExperimental(entry),
Expand All @@ -122,8 +130,6 @@ function getHtmlAdditionalLinks<T extends HasJsDocTags & HasModuleName>(
entry: T,
): LinkEntryRenderable[] {
const markdownLinkRule = /\[([^\]]+)\]\(([^)]+)\)/;
// Some links are written in the following format: {@link Route }
const apiLinkRule = /\{\s*@link\s+([^}]+)\s*\}/;

const seeAlsoLinks = entry.jsdocTags
.filter((tag) => tag.name === JS_DOC_SEE_TAG)
Expand All @@ -138,7 +144,7 @@ function getHtmlAdditionalLinks<T extends HasJsDocTags & HasModuleName>(
};
}

const linkMatch = comment.match(apiLinkRule);
const linkMatch = comment.match(jsDoclinkRegex);

if (linkMatch) {
const link = linkMatch[1];
Expand Down Expand Up @@ -184,3 +190,9 @@ function convertJsDocExampleToHtmlExample(text: string): string {
return `<code-example path="${path}" region="${region}" />`;
});
}

function convertLinks(text: string, entry: HasModuleName) {
return text.replace(jsDoclinkRegex, (_, symbol) => {
return `<a href="${getLinkToModule(entry.moduleName)}/${symbol}"><code>${symbol}</code></a>`;
});
}
30 changes: 22 additions & 8 deletions bazel/api-gen/rendering/transforms/member-transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ import {MemberEntry, MemberTags, MemberType} from '../entities';

import {isClassMethodEntry} from '../entities/categorization';
import {MemberEntryRenderable} from '../entities/renderables';
import {HasMembers, HasRenderableMembers, HasRenderableMembersGroups} from '../entities/traits';
import {
HasMembers,
HasModuleName,
HasRenderableMembers,
HasRenderableMembersGroups,
} from '../entities/traits';

import {
addHtmlDescription,
addHtmlJsDocTagComments,
addHtmlUsageNotes,
setEntryFlags,
} from './jsdoc-transforms';
import {addModuleName} from './module-name';

const lifecycleMethods = [
'ngAfterContentChecked',
Expand Down Expand Up @@ -64,15 +70,17 @@ export function mergeGettersAndSetters(members: MemberEntry[]): MemberEntry[] {
}

/** Given an entity with members, gets the entity augmented with renderable members. */
export function addRenderableGroupMembers<T extends HasMembers>(
export function addRenderableGroupMembers<T extends HasMembers & HasModuleName>(
entry: T,
): T & HasRenderableMembersGroups {
const members = filterLifecycleMethods(entry.members);

const membersGroups = members.reduce((groups, item) => {
const member = setEntryFlags(
addMethodParamsDescription(
addHtmlDescription(addHtmlUsageNotes(addHtmlJsDocTagComments(item))),
addHtmlDescription(
addHtmlUsageNotes(addHtmlJsDocTagComments(addModuleName(item, entry.moduleName))),
),
),
);
if (groups.has(member.name)) {
Expand All @@ -90,11 +98,15 @@ export function addRenderableGroupMembers<T extends HasMembers>(
};
}

export function addRenderableMembers<T extends HasMembers>(entry: T): T & HasRenderableMembers {
const members = entry.members.map((entry) =>
export function addRenderableMembers<T extends HasMembers & HasModuleName>(
entry: T,
): T & HasRenderableMembers {
const members = entry.members.map((member) =>
setEntryFlags(
addMethodParamsDescription(
addHtmlDescription(addHtmlUsageNotes(addHtmlJsDocTagComments(entry))),
addHtmlDescription(
addHtmlUsageNotes(addHtmlJsDocTagComments(addModuleName(member, entry.moduleName))),
),
),
),
);
Expand All @@ -105,11 +117,13 @@ export function addRenderableMembers<T extends HasMembers>(entry: T): T & HasRen
};
}

function addMethodParamsDescription<T extends MemberEntry>(entry: T): T {
function addMethodParamsDescription<T extends MemberEntry & HasModuleName>(entry: T): T {
if (isClassMethodEntry(entry)) {
return {
...entry,
params: entry.params.map((param) => addHtmlDescription(param)),
params: entry.params.map((param) =>
addHtmlDescription(addModuleName(param, entry.moduleName)),
),
};
}
return entry;
Expand Down
4 changes: 1 addition & 3 deletions bazel/api-gen/rendering/transforms/module-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {DocEntry} from '../entities';

import {HasModuleName} from '../entities/traits';

export function addModuleName<T extends DocEntry>(entry: T, moduleName: string): T & HasModuleName {
export function addModuleName<T>(entry: T, moduleName: string): T & HasModuleName {
return {
...entry,
moduleName,
Expand Down
9 changes: 6 additions & 3 deletions bazel/api-gen/rendering/transforms/params-transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {HasParams, HasRenderableParams} from '../entities/traits';
import {HasModuleName, HasParams, HasRenderableParams} from '../entities/traits';
import {addHtmlDescription} from './jsdoc-transforms';
import {addModuleName} from './module-name';

export function addRenderableFunctionParams<T extends HasParams>(
export function addRenderableFunctionParams<T extends HasParams & HasModuleName>(
entry: T,
): T & HasRenderableParams {
const params = entry.params.map((entry) => addHtmlDescription(entry));
const params = entry.params.map((param) =>
addHtmlDescription(addModuleName(param, entry.moduleName)),
);

return {
...entry,
Expand Down

0 comments on commit 7d12344

Please sign in to comment.