build(docs-infra): do not require abstract directives to declare a @ngModule (#36921)

Abstract directives cannot be part of a `@NgModule`, but the AIO dgeni
setup currently enforces this. This commit updates the logic so that
abstract directives are skipped.

PR Close #36921
This commit is contained in:
Paul Gschwendtner 2020-05-05 17:59:51 +02:00 committed by Alex Rickabaugh
parent e17fe90aaa
commit 854bd7d0c8
3 changed files with 35 additions and 7 deletions

View File

@ -18,7 +18,10 @@ module.exports = function extractDecoratedClassesProcessor(EXPORT_DOC_TYPES) {
if (decoratorTypes.indexOf(decorator.name) !== -1) {
doc.docType = decorator.name.toLowerCase();
doc[doc.docType + 'Options'] = decorator.argumentInfo[0];
// Directives do not always have an argument (i.e. abstract directives).
// We still create options for those, as an empty object literal is equal
// to just having an empty object literal as decorator argument.
doc[doc.docType + 'Options'] = decorator.argumentInfo[0] || {};
}
});
});

View File

@ -3,11 +3,20 @@ module.exports = function processNgModuleDocs(getDocFromAlias, createDocMessage,
$runAfter: ['extractDecoratedClassesProcessor', 'computeIdsProcessor'],
$runBefore: ['createSitemap'],
exportDocTypes: ['directive', 'pipe'],
skipAbstractDirectives: true,
$process(docs) {
// Match all the directives/pipes to their module
const errors = [];
docs.forEach(doc => {
if (this.exportDocTypes.indexOf(doc.docType) !== -1) {
const options = doc[`${doc.docType}Options`];
// Directives without a selector are considered abstract and do
// not need to be part of any `@NgModule`.
if (this.skipAbstractDirectives && doc.docType === 'directive' && !options.selector) {
return;
}
if (!doc.ngModules || doc.ngModules.length === 0) {
errors.push(createDocMessage(`"${doc.id}" has no @ngModule tag. Docs of type "${doc.docType}" must have this tag.`, doc));
return;

View File

@ -39,11 +39,12 @@ describe('processNgModuleDocs processor', () => {
it('should link directive/pipe docs with their NgModule docs (sorted by id)', () => {
const aliasMap = injector.get('aliasMap');
const directiveOptions = {selector: 'some-selector'};
const ngModule1 = { docType: 'ngmodule', id: 'NgModule1', aliases: ['NgModule1'], ngmoduleOptions: {}};
const ngModule2 = { docType: 'ngmodule', id: 'NgModule2', aliases: ['NgModule2'], ngmoduleOptions: {}};
const directive1 = { docType: 'directive', id: 'Directive1', ngModules: ['NgModule1']};
const directive2 = { docType: 'directive', id: 'Directive2', ngModules: ['NgModule2']};
const directive3 = { docType: 'directive', id: 'Directive3', ngModules: ['NgModule1', 'NgModule2']};
const directive1 = { docType: 'directive', id: 'Directive1', ngModules: ['NgModule1'], directiveOptions};
const directive2 = { docType: 'directive', id: 'Directive2', ngModules: ['NgModule2'], directiveOptions};
const directive3 = { docType: 'directive', id: 'Directive3', ngModules: ['NgModule1', 'NgModule2'], directiveOptions};
const pipe1 = { docType: 'pipe', id: 'Pipe1', ngModules: ['NgModule1']};
const pipe2 = { docType: 'pipe', id: 'Pipe2', ngModules: ['NgModule2']};
const pipe3 = { docType: 'pipe', id: 'Pipe3', ngModules: ['NgModule1', 'NgModule2']};
@ -66,10 +67,22 @@ describe('processNgModuleDocs processor', () => {
expect(pipe3.ngModules).toEqual([ngModule1, ngModule2]);
});
it('should not error if an abstract directove does not have a `@ngModule` tag', () => {
expect(() => {
processor.$process([{ docType: 'directive', id: 'AbstractDir', directiveOptions: {} }]);
}).not.toThrow();
expect(() => {
processor.$process([{ docType: 'directive', id: 'AbstractDir',
directiveOptions: {selector: undefined} }]);
}).not.toThrow();
});
it('should error if a pipe/directive does not have a `@ngModule` tag', () => {
const log = injector.get('log');
expect(() => {
processor.$process([{ docType: 'directive', id: 'Directive1' }]);
processor.$process([{ docType: 'directive', id: 'Directive1',
directiveOptions: {selector: 'dir1'} }]);
}).toThrowError('Failed to process NgModule relationships.');
expect(log.error).toHaveBeenCalledWith(
'"Directive1" has no @ngModule tag. Docs of type "directive" must have this tag. - doc "Directive1" (directive) ');
@ -84,7 +97,8 @@ describe('processNgModuleDocs processor', () => {
it('should error if a pipe/directive has an @ngModule tag that does not match an NgModule doc', () => {
const log = injector.get('log');
expect(() => {
processor.$process([{ docType: 'directive', id: 'Directive1', ngModules: ['MissingNgModule'] }]);
processor.$process([{ docType: 'directive', id: 'Directive1', ngModules: ['MissingNgModule'],
directiveOptions: {selector: 'dir1'} }]);
}).toThrowError('Failed to process NgModule relationships.');
expect(log.error).toHaveBeenCalledWith(
'"@ngModule MissingNgModule" does not match a public NgModule - doc "Directive1" (directive) ');
@ -105,7 +119,9 @@ describe('processNgModuleDocs processor', () => {
aliasMap.addDoc(ngModule2);
expect(() => {
processor.$process([{ docType: 'directive', id: 'Directive1', ngModules: ['NgModuleAlias'] }]);
processor.$process([{
docType: 'directive', id: 'Directive1', ngModules: ['NgModuleAlias'],
directiveOptions: {selector: 'dir1'} }]);
}).toThrowError('Failed to process NgModule relationships.');
expect(log.error).toHaveBeenCalledWith(
'"@ngModule NgModuleAlias" is ambiguous. Matches: NgModule1, NgModule2 - doc "Directive1" (directive) ');