fix(ivy): should support components without selector (#27169)

PR Close #27169
This commit is contained in:
Marc Laval
2018-11-22 15:38:28 +01:00
committed by Jason Aden
parent d767e0b2c0
commit c2f30542e7
10 changed files with 467 additions and 425 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ConstantPool, CssSelector, Expression, R3ComponentMetadata, R3DirectiveMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
import {ConstantPool, CssSelector, DomElementSchemaRegistry, ElementSchemaRegistry, Expression, R3ComponentMetadata, R3DirectiveMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
import * as path from 'path';
import * as ts from 'typescript';
@ -41,6 +41,7 @@ export class ComponentDecoratorHandler implements
private resourceLoader: ResourceLoader, private rootDirs: string[]) {}
private literalCache = new Map<Decorator, ts.ObjectLiteralExpression>();
private elementSchemaRegistry = new DomElementSchemaRegistry();
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
@ -74,8 +75,9 @@ export class ComponentDecoratorHandler implements
// @Component inherits @Directive, so begin by extracting the @Directive metadata and building
// on it.
const directiveResult =
extractDirectiveMetadata(node, decorator, this.checker, this.reflector, this.isCore);
const directiveResult = extractDirectiveMetadata(
node, decorator, this.checker, this.reflector, this.isCore,
this.elementSchemaRegistry.getDefaultComponentElementName());
if (directiveResult === undefined) {
// `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
// case, compilation of the decorator is skipped. Returning an empty object signifies

View File

@ -93,7 +93,7 @@ export class DirectiveDecoratorHandler implements
*/
export function extractDirectiveMetadata(
clazz: ts.ClassDeclaration, decorator: Decorator, checker: ts.TypeChecker,
reflector: ReflectionHost, isCore: boolean): {
reflector: ReflectionHost, isCore: boolean, defaultSelector: string | null = null): {
decorator: Map<string, ts.Expression>,
metadata: R3DirectiveMetadata,
decoratedElements: ClassMember[],
@ -154,7 +154,7 @@ export function extractDirectiveMetadata(
}
// Parse the selector.
let selector = '';
let selector = defaultSelector;
if (directive.has('selector')) {
const expr = directive.get('selector') !;
const resolved = staticallyResolve(expr, reflector, checker);

View File

@ -561,6 +561,62 @@ describe('compiler compliance', () => {
expectEmit(source, OtherDirectiveDefinition, 'Incorrect OtherDirective.ngDirectiveDef');
});
it('should support components without selector', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core';
@Directive({})
export class EmptyOutletDirective {}
@Component({template: '<router-outlet></router-outlet>'})
export class EmptyOutletComponent {}
@NgModule({declarations: [EmptyOutletComponent]})
export class MyModule{}
`
}
};
// EmptyOutletDirective definition should be:
const EmptyOutletDirectiveDefinition = `
EmptyOutletDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
type: EmptyOutletDirective,
selectors: [],
factory: function EmptyOutletDirective_Factory(t) { return new (t || EmptyOutletDirective)(); }
});
`;
// EmptyOutletComponent definition should be:
const EmptyOutletComponentDefinition = `
EmptyOutletComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: EmptyOutletComponent,
selectors: [["ng-component"]],
factory: function EmptyOutletComponent_Factory(t) { return new (t || EmptyOutletComponent)(); },
consts: 1,
vars: 0,
template: function EmptyOutletComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelement(0, "router-outlet");
}
},
encapsulation: 2
});
`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(
source, EmptyOutletDirectiveDefinition,
'Incorrect EmptyOutletDirective.ngDirectiveDefDef');
expectEmit(
source, EmptyOutletComponentDefinition, 'Incorrect EmptyOutletComponent.ngComponentDef');
});
it('should not treat ElementRef, ViewContainerRef, or ChangeDetectorRef specially when injecting',
() => {
const files = {