Skip to content

Commit 60d570b

Browse files
devversionjelbourn
authored andcommitted
build: api-docs can display incorrect module for entry-point (#16470)
In some cases, an entry-point can export a deprecated NgModule. Currently if that module is exported after all previous modules, it will show up in the docs as recommended import. e.g. `import {ScrollDispatchModule} from '@angular/cdk/scrolling';` This is causing confusion as the non-deprecated module should be used for rendering the recommended import. This commit ensures that non-deprecated modules are prioritized over deprecated modules and that specific modules can be explicitly selected as primary module for an entry-point. Fixes #16353.
1 parent fa9da31 commit 60d570b

File tree

3 files changed

+38
-3
lines changed

3 files changed

+38
-3
lines changed

tools/dgeni/common/decorators.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ export function isDeprecatedDoc(doc: any) {
4242
return (doc.tags && doc.tags.tags || []).some((tag: any) => tag.tagName === 'deprecated');
4343
}
4444

45+
/** Whether the given document is annotated with the "@docs-primary-module" jsdoc tag. */
46+
export function isPrimaryModuleDoc(doc: any) {
47+
return (doc.tags && doc.tags.tags || [])
48+
.some((tag: any) => tag.tagName === 'docs-primary-module');
49+
}
50+
4551
export function getDirectiveSelectors(classDoc: CategorizedClassDoc) {
4652
if (!classDoc.directiveMetadata) {
4753
return;

tools/dgeni/docs-package.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ apiDocsPackage.config(function(parseTagsProcessor: any) {
7979
parseTagsProcessor.tagDefinitions = parseTagsProcessor.tagDefinitions.concat([
8080
{name: 'docs-private'},
8181
{name: 'docs-public'},
82+
{name: 'docs-primary-module'},
8283
{name: 'breaking-change'},
8384
]);
8485
});

tools/dgeni/processors/entry-point-grouper.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {InterfaceExportDoc} from 'dgeni-packages/typescript/api-doc-types/Interf
55
import {TypeAliasExportDoc} from 'dgeni-packages/typescript/api-doc-types/TypeAliasExportDoc';
66
import * as path from 'path';
77
import {computeApiDocumentUrl} from '../common/compute-api-url';
8+
import {isDeprecatedDoc, isPrimaryModuleDoc} from '../common/decorators';
89
import {CategorizedClassDoc} from '../common/dgeni-definitions';
910

1011
export interface ModuleInfo {
@@ -64,8 +65,11 @@ export class EntryPointDoc {
6465
/** Constants that belong to the entry-point. */
6566
constants: ConstExportDoc[] = [];
6667

67-
/** NgModule that defines the current entry-point. */
68-
ngModule: CategorizedClassDoc | null = null;
68+
/** List of NgModules which are exported in the current entry-point. */
69+
exportedNgModules: CategorizedClassDoc[] = [];
70+
71+
/** NgModule that defines the current entry-point. Null if no module could be found. */
72+
ngModule: CategorizedClassDoc|null = null;
6973

7074
constructor(name: string) {
7175
this.name = name;
@@ -117,7 +121,12 @@ export class EntryPointGrouper implements Processor {
117121
} else if (doc.isService) {
118122
entryPoint.services.push(doc);
119123
} else if (doc.isNgModule) {
120-
entryPoint.ngModule = doc;
124+
entryPoint.exportedNgModules.push(doc);
125+
// If the module is explicitly marked as primary module using the "@docs-primary-module"
126+
// annotation, we set is as primary entry-point module.
127+
if (isPrimaryModuleDoc(doc)) {
128+
entryPoint.ngModule = doc;
129+
}
121130
} else if (doc.docType === 'class') {
122131
entryPoint.classes.push(doc);
123132
} else if (doc.docType === 'interface') {
@@ -131,6 +140,25 @@ export class EntryPointGrouper implements Processor {
131140
}
132141
});
133142

143+
// For each entry-point we determine a primary NgModule that defines the entry-point
144+
// if no primary module has been explicitly declared (using "@docs-primary-module").
145+
entryPoints.forEach(entryPoint => {
146+
if (entryPoint.ngModule !== null) {
147+
return;
148+
}
149+
150+
// Usually the first module that is not deprecated is used, but in case there are
151+
// only deprecated modules, the last deprecated module is used. We don't want to
152+
// always skip deprecated modules as they could be still needed for documentation
153+
// of a deprecated entry-point.
154+
for (let ngModule of entryPoint.exportedNgModules) {
155+
entryPoint.ngModule = ngModule;
156+
if (!isDeprecatedDoc(ngModule)) {
157+
break;
158+
}
159+
}
160+
});
161+
134162
return Array.from(entryPoints.values());
135163
}
136164
}

0 commit comments

Comments
 (0)