Skip to content

Commit 6c2beba

Browse files
JeanMecheAndrewKushnir
authored andcommitted
docs(docs-infra): Add dev-mode only mention for core/global (angular#57365)
PR Close angular#57365
1 parent 8bddab6 commit 6c2beba

File tree

15 files changed

+137
-83
lines changed

15 files changed

+137
-83
lines changed

adev/shared-docs/pipeline/api-gen/extraction/extract_api_to_json.bzl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ def _extract_api_to_json(ctx):
1313
# Pass the module_name for the extracted APIs. This will be something like "@angular/core".
1414
args.add(ctx.attr.module_name)
1515

16+
# Pass the module_label for the extracted APIs, This is something like core for "@angular/core".
17+
args.add(ctx.attr.module_label)
18+
1619
# Pass the entry_point for from which to extract public symbols.
1720
args.add(ctx.file.entry_point)
1821

@@ -82,6 +85,9 @@ extract_api_to_json = rule(
8285
doc = """JS Module name to be used for the extracted symbols""",
8386
mandatory = True,
8487
),
88+
"module_label": attr.string(
89+
doc = """Module label to be used for the extracted symbols. To be used as display name, for example in API docs""",
90+
),
8591
"extra_entries": attr.label_list(
8692
doc = """JSON files that contain extra entries to append to the final collection.""",
8793
allow_files = True,

adev/shared-docs/pipeline/api-gen/extraction/index.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import {readFileSync, writeFileSync} from 'fs';
22
import path from 'path';
33
// @ts-ignore This compiles fine, but Webstorm doesn't like the ESM import in a CJS context.
4-
import {NgtscProgram, CompilerOptions, createCompilerHost, DocEntry} from '@angular/compiler-cli';
4+
import {
5+
NgtscProgram,
6+
CompilerOptions,
7+
createCompilerHost,
8+
DocEntry,
9+
EntryCollection,
10+
} from '@angular/compiler-cli';
511
import ts from 'typescript';
612

713
function main() {
@@ -10,6 +16,7 @@ function main() {
1016

1117
const [
1218
moduleName,
19+
moduleLabel,
1320
entryPointExecRootRelativePath,
1421
srcs,
1522
outputFilenameExecRootRelativePath,
@@ -57,10 +64,14 @@ function main() {
5764
const extractedEntries = program.getApiDocumentation(entryPointExecRootRelativePath);
5865
const combinedEntries = extractedEntries.concat(extraEntries);
5966

67+
const normalized = moduleName.replace('@', '').replace(/[\/]/g, '_');
68+
6069
const output = JSON.stringify({
70+
moduleLabel: moduleLabel || moduleName,
6171
moduleName: moduleName,
72+
normalizedModuleName: normalized,
6273
entries: combinedEntries,
63-
});
74+
} satisfies EntryCollection);
6475

6576
writeFileSync(outputFilenameExecRootRelativePath, output, {encoding: 'utf8'});
6677
}

adev/shared-docs/pipeline/api-gen/generate_api_docs.bzl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
load("//adev/shared-docs/pipeline/api-gen/extraction:extract_api_to_json.bzl", "extract_api_to_json")
22
load("//adev/shared-docs/pipeline/api-gen/rendering:render_api_to_html.bzl", "render_api_to_html")
33

4-
def generate_api_docs(name, module_name, entry_point, srcs, import_map = {}, extra_entries = []):
4+
def generate_api_docs(name, module_name, entry_point, srcs, module_label = None, import_map = {}, extra_entries = []):
55
"""Generates API documentation reference pages for the given sources."""
66
json_outfile = name + "_api.json"
77

88
extract_api_to_json(
99
name = name + "_extraction",
1010
module_name = module_name,
11+
module_label = module_label,
1112
entry_point = entry_point,
1213
srcs = srcs,
1314
output_name = json_outfile,

adev/shared-docs/pipeline/api-gen/manifest/generate_manifest.ts

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
// @ts-ignore This compiles fine, but Webstorm doesn't like the ESM import in a CJS context.
2-
import type {DocEntry, JsDocTagEntry} from '@angular/compiler-cli';
3-
4-
/** The JSON data file format for extracted API reference info. */
5-
export interface EntryCollection {
6-
moduleName: string;
7-
entries: DocEntry[];
8-
}
2+
import type {DocEntry, EntryCollection, JsDocTagEntry} from '@angular/compiler-cli';
93

104
export interface ManifestEntry {
115
name: string;
@@ -16,7 +10,12 @@ export interface ManifestEntry {
1610
}
1711

1812
/** Manifest that maps each module name to a list of API symbols. */
19-
export type Manifest = Record<string, ManifestEntry[]>;
13+
export type Manifest = {
14+
moduleName: string;
15+
normalizedModuleName: string;
16+
moduleLabel: string;
17+
entries: ManifestEntry[];
18+
}[];
2019

2120
/** Gets a unique lookup key for an API, e.g. "@angular/core/ElementRef". */
2221
function getApiLookupKey(moduleName: string, name: string) {
@@ -114,22 +113,39 @@ export function generateManifest(apiCollections: EntryCollection[]): Manifest {
114113
});
115114
}
116115

117-
const manifest: Manifest = {};
116+
const manifest: Manifest = [];
118117
for (const collection of apiCollections) {
119-
if (!manifest[collection.moduleName]) {
120-
manifest[collection.moduleName] = [];
118+
const entries = collection.entries.map((entry) => ({
119+
name: entry.name,
120+
type: entry.entryType,
121+
isDeprecated: isDeprecated(entryLookup, collection.moduleName, entry),
122+
isDeveloperPreview: isDeveloperPreview(entryLookup, collection.moduleName, entry),
123+
isExperimental: isExperimental(entryLookup, collection.moduleName, entry),
124+
}));
125+
126+
const existingEntry = manifest.find((entry) => entry.moduleName === collection.moduleName);
127+
if (existingEntry) {
128+
existingEntry.entries.push(...entries);
129+
} else {
130+
manifest.push({
131+
moduleName: collection.moduleName,
132+
normalizedModuleName: collection.normalizedModuleName,
133+
moduleLabel: collection.moduleLabel ?? collection.moduleName,
134+
entries,
135+
});
121136
}
122-
123-
manifest[collection.moduleName].push(
124-
...collection.entries.map((entry) => ({
125-
name: entry.name,
126-
type: entry.entryType,
127-
isDeprecated: isDeprecated(entryLookup, collection.moduleName, entry),
128-
isDeveloperPreview: isDeveloperPreview(entryLookup, collection.moduleName, entry),
129-
isExperimental: isExperimental(entryLookup, collection.moduleName, entry),
130-
})),
131-
);
132137
}
133138

139+
manifest.sort((entry1, entry2) => {
140+
// Ensure that labels that start with a `code` tag like `window.ng` are last
141+
if (entry1.moduleLabel.startsWith('<')) {
142+
return 1;
143+
} else if (entry2.moduleLabel.startsWith('<')) {
144+
return -1;
145+
}
146+
147+
return entry1.moduleLabel.localeCompare(entry2.moduleLabel);
148+
});
149+
134150
return manifest;
135151
}

adev/shared-docs/pipeline/api-gen/manifest/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {readFileSync, writeFileSync} from 'fs';
2-
import {EntryCollection, generateManifest} from './generate_manifest';
2+
import {generateManifest} from './generate_manifest';
3+
import type {EntryCollection} from '@angular/compiler-cli';
34

45
function main() {
56
const [paramFilePath] = process.argv.slice(2);

adev/shared-docs/pipeline/api-gen/rendering/index.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {initHighlighter} from './shiki/shiki';
1111
/** The JSON data file format for extracted API reference info. */
1212
interface EntryCollection {
1313
moduleName: string;
14+
moduleLabel?: string;
15+
normalizedModuleName: string;
1416
entries: DocEntry[];
1517
}
1618

@@ -30,11 +32,13 @@ function parseEntryData(srcs: string[]): EntryCollection[] {
3032
return [
3133
{
3234
moduleName: 'unknown',
35+
normalizedModuleName: 'unknown',
3336
entries: [fileContentJson as DocEntry],
3437
},
3538
...command.subcommands!.map((subCommand) => {
3639
return {
3740
moduleName: 'unknown',
41+
normalizedModuleName: 'unknown',
3842
entries: [{...subCommand, parentCommand: command} as any],
3943
};
4044
}),
@@ -43,23 +47,21 @@ function parseEntryData(srcs: string[]): EntryCollection[] {
4347

4448
return {
4549
moduleName: 'unknown',
50+
normalizedModuleName: 'unknown',
4651
entries: [fileContentJson as DocEntry], // TODO: fix the typing cli entries aren't DocEntry
4752
};
4853
});
4954
}
5055

5156
/** Gets a normalized filename for a doc entry. */
52-
function getNormalizedFilename(moduleName: string, entry: DocEntry | CliCommand): string {
57+
function getNormalizedFilename(normalizedModuleName: string, entry: DocEntry | CliCommand): string {
5358
if (isCliEntry(entry)) {
5459
return entry.parentCommand
5560
? `${entry.parentCommand.name}/${entry.name}.html`
5661
: `${entry.name}.html`;
5762
}
5863

5964
entry = entry as DocEntry;
60-
// Angular entry points all contain an "@" character, which we want to remove
61-
// from the filename. We also swap `/` with an underscore.
62-
const normalizedModuleName = moduleName.replace('@', '').replace(/\//g, '_');
6365

6466
// Append entry type as suffix to prevent writing to file that only differs in casing or query string from already written file.
6567
// This will lead to a race-condition and corrupted files on case-insensitive file systems.
@@ -103,7 +105,10 @@ async function main() {
103105
const htmlOutputs = renderableEntries.map(renderEntry);
104106

105107
for (let i = 0; i < htmlOutputs.length; i++) {
106-
const filename = getNormalizedFilename(collection.moduleName, collection.entries[i]);
108+
const filename = getNormalizedFilename(
109+
collection.normalizedModuleName,
110+
collection.entries[i],
111+
);
107112
const outputPath = path.join(outputFilenameExecRootRelativePath, filename);
108113

109114
// in case the output path is nested, ensure the directory exists

adev/src/app/core/services/content-loader.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import {HttpClient} from '@angular/common/http';
1010
import {Injectable, inject} from '@angular/core';
1111
import {DocContent, DocsContentLoader} from '@angular/docs';
1212
import {Router} from '@angular/router';
13-
import {firstValueFrom} from 'rxjs';
14-
import {map} from 'rxjs/operators';
13+
import {firstValueFrom, of} from 'rxjs';
14+
import {catchError, map} from 'rxjs/operators';
1515

1616
@Injectable()
1717
export class ContentLoader implements DocsContentLoader {

adev/src/app/features/references/api-items-section/api-items-section.component.html

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,26 @@ <h3 [attr.id]="group.id">
33
@if (group.isFeatured) {
44
<docs-icon aria-hidden>star</docs-icon>
55
}
6-
<a routerLink="/api" [fragment]="group.id" queryParamsHandling="preserve" class="adev-api-anchor" tabindex="-1">{{ group.title }}</a>
6+
<!-- we use innerHtml because the title can be an html string-->
7+
<a
8+
routerLink="/api"
9+
[fragment]="group.id"
10+
queryParamsHandling="preserve"
11+
class="adev-api-anchor"
12+
tabindex="-1"
13+
[innerHtml]="group.title"
14+
></a>
715
</h3>
816
</header>
917

1018
<ul class="adev-api-items-section-grid">
1119
@for (apiItem of group.items; track apiItem.url) {
1220
<li [class.adev-api-items-section-item-deprecated]="apiItem.isDeprecated">
13-
<a [routerLink]="'/' + apiItem.url" class="adev-api-items-section-item" [attr.aria-describedby]="apiItem.isDeprecated ? 'deprecated-description' : null">
21+
<a
22+
[routerLink]="'/' + apiItem.url"
23+
class="adev-api-items-section-item"
24+
[attr.aria-describedby]="apiItem.isDeprecated ? 'deprecated-description' : null"
25+
>
1426
<docs-api-item-label
1527
[type]="apiItem.itemType"
1628
mode="short"
@@ -20,9 +32,7 @@ <h3 [attr.id]="group.id">
2032
<span class="adev-item-title">{{ apiItem.title }}</span>
2133
</a>
2234
@if (apiItem.isDeprecated) {
23-
<span class="docs-deprecated">
24-
&lt;!&gt;
25-
</span>
35+
<span class="docs-deprecated"> &lt;!&gt; </span>
2636
}
2737
@if (apiItem.isFeatured) {
2838
<docs-icon

adev/src/app/features/references/api-reference-list/api-reference-manager.service.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {Injectable, signal} from '@angular/core';
1010
// This file is generated at build-time, error is expected here.
1111
import API_MANIFEST_JSON from '../../../../../src/assets/api/manifest.json';
12-
import {ANGULAR_PACKAGE_PREFIX, getApiUrl} from '../helpers/manifest.helper';
12+
import {getApiUrl} from '../helpers/manifest.helper';
1313
import {ApiItem} from '../interfaces/api-item';
1414
import {ApiItemsGroup} from '../interfaces/api-items-group';
1515
import {ApiManifest} from '../interfaces/api-manifest';
@@ -34,6 +34,8 @@ export const FEATURED_ITEMS_URLS = [
3434
'api/router/CanActivate',
3535
];
3636

37+
const manifest = API_MANIFEST_JSON as ApiManifest;
38+
3739
@Injectable({
3840
providedIn: 'root',
3941
})
@@ -50,20 +52,14 @@ export class ApiReferenceManager {
5052

5153
private mapManifestToApiGroups(): ApiItemsGroup[] {
5254
const groups: ApiItemsGroup[] = [];
53-
const manifest = API_MANIFEST_JSON as ApiManifest;
54-
55-
const packageNames = Object.keys(API_MANIFEST_JSON);
56-
57-
for (const packageName of packageNames) {
58-
const packageNameWithoutPrefix = packageName.replace(ANGULAR_PACKAGE_PREFIX, '');
59-
const packageApis = manifest[packageName];
6055

56+
for (const module of manifest) {
6157
groups.push({
62-
title: packageNameWithoutPrefix,
63-
id: packageNameWithoutPrefix.replace(/\//g, '-'),
64-
items: packageApis
58+
title: module.moduleLabel.replace('@angular/', ''),
59+
id: module.normalizedModuleName,
60+
items: module.entries
6561
.map((api) => {
66-
const url = getApiUrl(packageNameWithoutPrefix, api.name);
62+
const url = getApiUrl(module, api.name);
6763
const isFeatured = FEATURED_ITEMS_URLS.some((featuredUrl) => featuredUrl === url);
6864
const apiItem = {
6965
itemType: api.type,

0 commit comments

Comments
 (0)