docs: update rendering to use generated repo link (#61130)

PR Close #61130
This commit is contained in:
Miles Malerba
2025-05-05 22:10:49 +00:00
committed by Andrew Kushnir
parent 67adcd8c9b
commit 2d9eaeefa6
24 changed files with 140 additions and 63 deletions
@@ -16,6 +16,7 @@ extract_api_to_json(
module_name = "@angular/core",
output_name = "api.json",
private_modules = [""],
repo = "angular/angular",
)
extract_api_to_json(
@@ -34,6 +35,7 @@ extract_api_to_json(
module_name = "@angular/core",
output_name = "extra_api.json",
private_modules = [""],
repo = "angular/angular",
)
filegroup(
@@ -1,5 +1,5 @@
load("//adev/shared-docs/pipeline/api-gen/manifest:generate_api_manifest.bzl", "generate_api_manifest")
load("//adev/shared-docs/pipeline/api-gen/extraction:extract_api_to_json.bzl", "extract_api_to_json")
load("//adev/shared-docs/pipeline/api-gen/manifest:generate_api_manifest.bzl", "generate_api_manifest")
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
generate_api_manifest(
@@ -17,6 +17,7 @@ extract_api_to_json(
module_name = "@angular/router",
output_name = "api.json",
private_modules = [""],
repo = "angular/angular",
)
ts_library(
@@ -33,6 +33,7 @@ export interface JsDocTagRenderable extends JsDocTagEntry {
/** A documentation entry augmented with transformed content for rendering. */
export interface DocEntryRenderable extends DocEntry {
repo: string;
moduleName: string;
htmlDescription: string;
shortHtmlDescription: string;
@@ -71,6 +71,11 @@ export interface HasModuleName {
moduleName: string;
}
/** A doc entry that has an associated github repo. */
export interface HasRepo {
repo: string;
}
/** A doc entry that has ToC transformed for rendering. */
export interface HasRenderableToc {
beforeCodeGroups: string;
@@ -19,6 +19,7 @@ import {setCurrentSymbol, setSymbols} from './symbol-context';
/** The JSON data file format for extracted API reference info. */
interface EntryCollection {
repo: string;
moduleName: string;
moduleLabel?: string;
normalizedModuleName: string;
@@ -44,6 +45,7 @@ function parseEntryData(srcs: string[]): EntryCollection[] {
const command = fileContentJson as CliCommand;
return [
{
repo: 'anglar/cli',
moduleName: 'unknown',
normalizedModuleName: 'unknown',
entries: [fileContentJson as DocEntry],
@@ -51,6 +53,7 @@ function parseEntryData(srcs: string[]): EntryCollection[] {
},
...command.subcommands!.map((subCommand) => {
return {
repo: 'angular/cli',
moduleName: 'unknown',
normalizedModuleName: 'unknown',
entries: [{...subCommand, parentCommand: command} as any],
@@ -61,6 +64,7 @@ function parseEntryData(srcs: string[]): EntryCollection[] {
}
return {
repo: 'unknown',
moduleName: 'unknown',
normalizedModuleName: 'unknown',
entries: [fileContentJson as DocEntry], // TODO: fix the typing cli entries aren't DocEntry
@@ -122,7 +126,7 @@ async function main() {
const renderableEntries = extractedEntries.map((entry) => {
setCurrentSymbol(entry.name);
return getRenderable(entry, collection.moduleName);
return getRenderable(entry, collection.moduleName, collection.repo);
});
const htmlOutputs = renderableEntries.map(renderEntry);
@@ -8,6 +8,7 @@
import {DocEntry} from './entities';
import {CliCommand} from './cli-entities';
import {
isClassEntry,
isCliEntry,
@@ -21,9 +22,9 @@ import {
} from './entities/categorization';
import {CliCommandRenderable, DocEntryRenderable} from './entities/renderables';
import {getClassRenderable} from './transforms/class-transforms';
import {getDecoratorRenderable} from './transforms/decorator-transforms';
import {getCliRenderable} from './transforms/cli-transforms';
import {getConstantRenderable} from './transforms/constant-transforms';
import {getDecoratorRenderable} from './transforms/decorator-transforms';
import {getEnumRenderable} from './transforms/enum-transforms';
import {getFunctionRenderable} from './transforms/function-transforms';
import {getInitializerApiFunctionRenderable} from './transforms/initializer-api-functions-transform';
@@ -36,47 +37,48 @@ import {
setEntryFlags,
} from './transforms/jsdoc-transforms';
import {addModuleName} from './transforms/module-name';
import {addRepo} from './transforms/repo';
import {getTypeAliasRenderable} from './transforms/type-alias-transforms';
import {CliCommand} from './cli-entities';
export function getRenderable(
entry: DocEntry | CliCommand,
moduleName: string,
repo: string,
): DocEntryRenderable | CliCommandRenderable {
if (isCliEntry(entry)) {
return getCliRenderable(entry);
}
if (isClassEntry(entry)) {
return getClassRenderable(entry, moduleName);
return getClassRenderable(entry, moduleName, repo);
}
if (isDecoratorEntry(entry)) {
return getDecoratorRenderable(entry, moduleName);
return getDecoratorRenderable(entry, moduleName, repo);
}
if (isConstantEntry(entry)) {
return getConstantRenderable(entry, moduleName);
return getConstantRenderable(entry, moduleName, repo);
}
if (isEnumEntry(entry)) {
return getEnumRenderable(entry, moduleName);
return getEnumRenderable(entry, moduleName, repo);
}
if (isInterfaceEntry(entry)) {
return getInterfaceRenderable(entry, moduleName);
return getInterfaceRenderable(entry, moduleName, repo);
}
if (isFunctionEntry(entry)) {
return getFunctionRenderable(entry, moduleName);
return getFunctionRenderable(entry, moduleName, repo);
}
if (isTypeAliasEntry(entry)) {
return getTypeAliasRenderable(entry, moduleName);
return getTypeAliasRenderable(entry, moduleName, repo);
}
if (isInitializerApiFunctionEntry(entry)) {
return getInitializerApiFunctionRenderable(entry, moduleName);
return getInitializerApiFunctionRenderable(entry, moduleName, repo);
}
// Fallback to an uncategorized renderable.
return setEntryFlags(
addHtmlAdditionalLinks(
addHtmlDescription(
addHtmlUsageNotes(addHtmlJsDocTagComments(addModuleName(entry, moduleName))),
addHtmlUsageNotes(addHtmlJsDocTagComments(addRepo(addModuleName(entry, moduleName), repo))),
),
),
);
@@ -20,11 +20,11 @@ import {
REFERENCE_MEMBER_CARD_HEADER,
REFERENCE_MEMBER_CARD_ITEM,
} from '../styling/css-classes';
import {getFunctionMetadataRenderable} from '../transforms/function-transforms';
import {ClassMethodInfo} from './class-method-info';
import {CodeSymbol} from './code-symbols';
import {DeprecatedLabel} from './deprecated-label';
import {RawHtml} from './raw-html';
import {getFunctionMetadataRenderable} from '../transforms/function-transforms';
import {CodeSymbol} from './code-symbols';
export function ClassMember(props: {member: MemberEntryRenderable}) {
const member = props.member;
@@ -32,7 +32,7 @@ export function ClassMember(props: {member: MemberEntryRenderable}) {
const renderMethod = (method: MethodEntryRenderable) => {
const signature = method.signatures.length ? method.signatures : [method.implementation];
return signature.map((sig) => {
const renderableMember = getFunctionMetadataRenderable(sig);
const renderableMember = getFunctionMetadataRenderable(sig, method.moduleName, method.repo);
return <ClassMethodInfo entry={renderableMember} options={{showUsageNotes: true}} />;
});
};
@@ -7,25 +7,25 @@
*/
import {Fragment, h} from 'preact';
import {PipeEntry} from '../entities';
import {
ClassEntryRenderable,
DecoratorEntryRenderable,
PipeEntryRenderable,
} from '../entities/renderables';
import {ClassMemberList} from './class-member-list';
import {HeaderApi} from './header-api';
import {codeToHtml} from '../shiki/shiki';
import {
API_REFERENCE_CONTAINER,
REFERENCE_MEMBERS,
SECTION_CONTAINER,
} from '../styling/css-classes';
import {SectionDescription} from './section-description';
import {SectionUsageNotes} from './section-usage-notes';
import {SectionApi} from './section-api';
import {SectionHeading} from './section-heading';
import {PipeEntry} from '../entities';
import {codeToHtml} from '../shiki/shiki';
import {ClassMemberList} from './class-member-list';
import {HeaderApi} from './header-api';
import {RawHtml} from './raw-html';
import {SectionApi} from './section-api';
import {SectionDescription} from './section-description';
import {SectionHeading} from './section-heading';
import {SectionUsageNotes} from './section-usage-notes';
/** Component to render a class API reference document. */
export function ClassReference(
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {h, Fragment} from 'preact';
import {Fragment, h} from 'preact';
import {
FunctionEntryRenderable,
FunctionSignatureMetadataRenderable,
@@ -18,15 +18,15 @@ import {
REFERENCE_MEMBER_CARD_BODY,
REFERENCE_MEMBER_CARD_HEADER,
} from '../styling/css-classes';
import {printInitializerFunctionSignatureLine} from '../transforms/code-transforms';
import {getFunctionMetadataRenderable} from '../transforms/function-transforms';
import {ClassMethodInfo} from './class-method-info';
import {CodeSymbol} from './code-symbols';
import {HeaderApi} from './header-api';
import {HighlightTypeScript} from './highlight-ts';
import {SectionApi} from './section-api';
import {SectionDescription} from './section-description';
import {SectionUsageNotes} from './section-usage-notes';
import {HighlightTypeScript} from './highlight-ts';
import {printInitializerFunctionSignatureLine} from '../transforms/code-transforms';
import {getFunctionMetadataRenderable} from '../transforms/function-transforms';
import {CodeSymbol} from './code-symbols';
export const signatureCard = (
name: string,
@@ -75,7 +75,7 @@ export function FunctionReference(entry: FunctionEntryRenderable) {
{entry.signatures.map((s, i) =>
signatureCard(
s.name,
getFunctionMetadataRenderable(s, entry.moduleName),
getFunctionMetadataRenderable(s, entry.moduleName, entry.repo),
{
id: `${s.name}_${i}`,
},
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {h, Fragment} from 'preact';
import {Fragment, h} from 'preact';
import {EntryType, isDocEntryWithSourceInfo, PipeEntry} from '../entities';
import {DocEntryRenderable, PipeEntryRenderable} from '../entities/renderables';
import {
@@ -153,6 +153,7 @@ function sourceUrlForEntry(entry: DocEntryRenderable): string | null {
// We don't know the source path in external repos link the CLI
return null;
} else {
return `https://github.com/angular/angular/blob/main${entry.source.filePath}#L${entry.source.startLine}-L${entry.source.endLine}`;
const filePath = entry.source.filePath.replace(/^\//, '');
return `https://github.com/${entry.repo}/blob/main/${filePath}#L${entry.source.startLine}-L${entry.source.endLine}`;
}
}
@@ -8,12 +8,12 @@
import {h, JSX} from 'preact';
import {InitializerApiFunctionRenderable} from '../entities/renderables';
import {HeaderApi} from './header-api';
import {SectionApi} from './section-api';
import {SectionUsageNotes} from './section-usage-notes';
import {API_REFERENCE_CONTAINER, REFERENCE_MEMBERS} from '../styling/css-classes';
import {getFunctionMetadataRenderable} from '../transforms/function-transforms';
import {signatureCard} from './function-reference';
import {HeaderApi} from './header-api';
import {SectionApi} from './section-api';
import {SectionUsageNotes} from './section-usage-notes';
/** Component to render a constant API reference document. */
export function InitializerApiFunction(entry: InitializerApiFunctionRenderable) {
@@ -42,7 +42,7 @@ export function InitializerApiFunction(entry: InitializerApiFunctionRenderable)
{entry.callFunction.signatures.map((s, i) =>
signatureCard(
s.name,
getFunctionMetadataRenderable(s, entry.moduleName),
getFunctionMetadataRenderable(s, entry.moduleName, entry.repo),
{
id: `${s.name}_${i}`,
},
@@ -56,7 +56,7 @@ export function InitializerApiFunction(entry: InitializerApiFunctionRenderable)
...subFunction.signatures.map((s, i) =>
signatureCard(
`${entry.name}.${s.name}`,
getFunctionMetadataRenderable(s, entry.moduleName),
getFunctionMetadataRenderable(s, entry.moduleName, entry.repo),
{
id: `${entry.name}_${s.name}_${i}`,
},
@@ -9,10 +9,10 @@
import {runfiles} from '@bazel/runfiles';
import {readFile} from 'fs/promises';
import {JSDOM} from 'jsdom';
import {renderEntry} from '../rendering';
import {getRenderable} from '../processing';
import {initHighlighter} from '../shiki/shiki';
import {configureMarkedGlobally} from '../marked/configuration';
import {getRenderable} from '../processing';
import {renderEntry} from '../rendering';
import {initHighlighter} from '../shiki/shiki';
describe('CLI docs to html', () => {
let fragment: DocumentFragment;
@@ -27,7 +27,7 @@ describe('CLI docs to html', () => {
});
entryJson = JSON.parse(entryContent) as any;
const renderableJson = getRenderable(entryJson, '');
const renderableJson = getRenderable(entryJson, '', 'angular/cli');
fragment = JSDOM.fragment(await renderEntry(renderableJson));
});
@@ -35,7 +35,7 @@ describe('CLI docs to html', () => {
const generateComponentSubcommand = entryJson.subcommands.find(
(subcommand: any) => subcommand.name === 'component',
);
const renderableJson = getRenderable(generateComponentSubcommand, '');
const renderableJson = getRenderable(generateComponentSubcommand, '', 'angular/cli');
fragment = JSDOM.fragment(await renderEntry(renderableJson));
const cliTocs = fragment.querySelectorAll('.docs-reference-cli-toc')!;
@@ -9,10 +9,10 @@
import {runfiles} from '@bazel/runfiles';
import {readFile} from 'fs/promises';
import {JSDOM} from 'jsdom';
import {renderEntry} from '../rendering';
import {getRenderable} from '../processing';
import {initHighlighter} from '../shiki/shiki';
import {configureMarkedGlobally} from '../marked/configuration';
import {getRenderable} from '../processing';
import {renderEntry} from '../rendering';
import {initHighlighter} from '../shiki/shiki';
import {setSymbols} from '../symbol-context';
// Note: The tests will probably break if the schema of the api extraction changes.
@@ -44,7 +44,7 @@ describe('markdown to html', () => {
]);
setSymbols(symbols);
for (const entry of entryJson.entries) {
const renderableJson = getRenderable(entry, '@angular/fakeentry');
const renderableJson = getRenderable(entry, '@angular/fakeentry', 'angular/angular');
const fragment = JSDOM.fragment(await renderEntry(renderableJson));
entries.set(entry['name'], fragment);
entries2.set(entry['name'], await renderEntry(renderableJson));
@@ -8,10 +8,10 @@
import {runfiles} from '@bazel/runfiles';
import {readFile} from 'fs/promises';
import {getRenderable} from '../processing';
import {DocEntryRenderable} from '../entities/renderables';
import {initHighlighter} from '../shiki/shiki';
import {configureMarkedGlobally} from '../marked/configuration';
import {getRenderable} from '../processing';
import {initHighlighter} from '../shiki/shiki';
import {setSymbols} from '../symbol-context';
// Note: The tests will probably break if the schema of the api extraction changes.
@@ -43,7 +43,11 @@ describe('renderable', () => {
setSymbols(symbols);
for (const entry of entryJson.entries) {
const renderableJson = getRenderable(entry, '@angular/fakeentry') as DocEntryRenderable;
const renderableJson = getRenderable(
entry,
'@angular/fakeentry',
'angular/angular',
) as DocEntryRenderable;
entries.set(entry['name'], renderableJson);
}
});
@@ -18,18 +18,22 @@ import {
} from './jsdoc-transforms';
import {addRenderableMembers} from './member-transforms';
import {addModuleName} from './module-name';
import {addRepo} from './repo';
/** Given an unprocessed class entry, get the fully renderable class entry. */
export function getClassRenderable(
classEntry: ClassEntry,
moduleName: string,
repo: string,
): ClassEntryRenderable {
return setEntryFlags(
addRenderableCodeToc(
addRenderableMembers(
addHtmlAdditionalLinks(
addHtmlUsageNotes(
addHtmlJsDocTagComments(addHtmlDescription(addModuleName(classEntry, moduleName))),
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(classEntry, moduleName), repo)),
),
),
),
),
@@ -17,17 +17,21 @@ import {
setEntryFlags,
} from './jsdoc-transforms';
import {addModuleName} from './module-name';
import {addRepo} from './repo';
/** Given an unprocessed constant entry, get the fully renderable constant entry. */
export function getConstantRenderable(
classEntry: ConstantEntry,
moduleName: string,
repo: string,
): ConstantEntryRenderable {
return setEntryFlags(
addRenderableCodeToc(
addHtmlAdditionalLinks(
addHtmlUsageNotes(
addHtmlJsDocTagComments(addHtmlDescription(addModuleName(classEntry, moduleName))),
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(classEntry, moduleName), repo)),
),
),
),
),
@@ -18,18 +18,22 @@ import {
} from './jsdoc-transforms';
import {addRenderableMembers} from './member-transforms';
import {addModuleName} from './module-name';
import {addRepo} from './repo';
/** Given an unprocessed class entry, get the fully renderable class entry. */
export function getDecoratorRenderable(
classEntry: DecoratorEntry,
moduleName: string,
repo: string,
): DecoratorEntryRenderable {
return setEntryFlags(
addRenderableCodeToc(
addRenderableMembers(
addHtmlAdditionalLinks(
addHtmlUsageNotes(
addHtmlJsDocTagComments(addHtmlDescription(addModuleName(classEntry, moduleName))),
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(classEntry, moduleName), repo)),
),
),
),
),
@@ -18,15 +18,22 @@ import {
} from './jsdoc-transforms';
import {addRenderableMembers} from './member-transforms';
import {addModuleName} from './module-name';
import {addRepo} from './repo';
/** Given an unprocessed enum entry, get the fully renderable enum entry. */
export function getEnumRenderable(classEntry: EnumEntry, moduleName: string): EnumEntryRenderable {
export function getEnumRenderable(
classEntry: EnumEntry,
moduleName: string,
repo: string,
): EnumEntryRenderable {
return setEntryFlags(
addRenderableCodeToc(
addRenderableMembers(
addHtmlAdditionalLinks(
addHtmlUsageNotes(
addHtmlJsDocTagComments(addHtmlDescription(addModuleName(classEntry, moduleName))),
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(classEntry, moduleName), repo)),
),
),
),
),
@@ -21,18 +21,22 @@ import {
} from './jsdoc-transforms';
import {addModuleName} from './module-name';
import {addRenderableFunctionParams} from './params-transforms';
import {addRepo} from './repo';
/** Given an unprocessed function entry, get the fully renderable function entry. */
export function getFunctionRenderable(
entry: FunctionEntry,
moduleName: string,
repo: string,
): FunctionEntryRenderable {
return setEntryFlags(
addRenderableCodeToc(
addHtmlAdditionalLinks(
addHtmlUsageNotes(
setEntryFlags(
addHtmlJsDocTagComments(addHtmlDescription(addModuleName(entry, moduleName))),
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(entry, moduleName), repo)),
),
),
),
),
@@ -42,13 +46,16 @@ export function getFunctionRenderable(
export function getFunctionMetadataRenderable(
entry: FunctionSignatureMetadata,
moduleName: string = '',
moduleName: string,
repo: string,
): FunctionSignatureMetadataRenderable {
return addHtmlAdditionalLinks(
addRenderableFunctionParams(
addHtmlUsageNotes(
setEntryFlags(
addHtmlJsDocTagComments(addHtmlDescription(addModuleName(entry, moduleName))),
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(entry, moduleName), repo)),
),
),
),
),
@@ -19,16 +19,20 @@ import {
setEntryFlags,
} from './jsdoc-transforms';
import {addModuleName} from './module-name';
import {addRepo} from './repo';
export function getInitializerApiFunctionRenderable(
entry: InitializerApiFunctionEntry,
moduleName: string,
repo: string,
): InitializerApiFunctionRenderable {
return setEntryFlags(
addRenderableCodeToc(
addHtmlJsDocTagComments(
addHtmlUsageNotes(
addHtmlDescription(addHtmlAdditionalLinks(addModuleName(entry, moduleName))),
addHtmlDescription(
addHtmlAdditionalLinks(addRepo(addModuleName(entry, moduleName), repo)),
),
),
),
),
@@ -18,18 +18,22 @@ import {
} from './jsdoc-transforms';
import {addRenderableMembers} from './member-transforms';
import {addModuleName} from './module-name';
import {addRepo} from './repo';
/** Given an unprocessed interface entry, get the fully renderable interface entry. */
export function getInterfaceRenderable(
entry: InterfaceEntry,
moduleName: string,
repo: string,
): InterfaceEntryRenderable {
return setEntryFlags(
addRenderableCodeToc(
addRenderableMembers(
addHtmlAdditionalLinks(
addHtmlUsageNotes(
addHtmlJsDocTagComments(addHtmlDescription(addModuleName(entry, moduleName))),
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(entry, moduleName), repo)),
),
),
),
),
@@ -9,7 +9,7 @@
import {MemberEntry, MemberTags, MemberType} from '../entities';
import {isHiddenEntry} from '../entities/categorization';
import {HasMembers, HasModuleName, HasRenderableMembers} from '../entities/traits';
import {HasMembers, HasModuleName, HasRenderableMembers, HasRepo} from '../entities/traits';
import {
addHtmlDescription,
@@ -18,6 +18,7 @@ import {
setEntryFlags,
} from './jsdoc-transforms';
import {addModuleName} from './module-name';
import {addRepo} from './repo';
const lifecycleMethods = [
'ngAfterContentChecked',
@@ -63,7 +64,7 @@ export function mergeGettersAndSetters(members: MemberEntry[]): MemberEntry[] {
);
}
export function addRenderableMembers<T extends HasMembers & HasModuleName>(
export function addRenderableMembers<T extends HasMembers & HasModuleName & HasRepo>(
entry: T,
): T & HasRenderableMembers {
const members = entry.members
@@ -71,7 +72,9 @@ export function addRenderableMembers<T extends HasMembers & HasModuleName>(
.map((member) =>
setEntryFlags(
addHtmlDescription(
addHtmlUsageNotes(addHtmlJsDocTagComments(addModuleName(member, entry.moduleName))),
addHtmlUsageNotes(
addHtmlJsDocTagComments(addRepo(addModuleName(member, entry.moduleName), entry.repo)),
),
),
),
);
@@ -0,0 +1,16 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {HasRepo} from '../entities/traits';
export function addRepo<T>(entry: T, repo: string): T & HasRepo {
return {
...entry,
repo,
};
}
@@ -17,17 +17,21 @@ import {
setEntryFlags,
} from './jsdoc-transforms';
import {addModuleName} from './module-name';
import {addRepo} from './repo';
/** Given an unprocessed type alias entry, get the fully renderable type alias entry. */
export function getTypeAliasRenderable(
typeAliasEntry: TypeAliasEntry,
moduleName: string,
repo: string,
): TypeAliasEntryRenderable {
return setEntryFlags(
addRenderableCodeToc(
addHtmlAdditionalLinks(
addHtmlUsageNotes(
addHtmlJsDocTagComments(addHtmlDescription(addModuleName(typeAliasEntry, moduleName))),
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(typeAliasEntry, moduleName), repo)),
),
),
),
),