Factory defs are not considered public API, so the property that contains them should be prefixed with Angular's marker for "private" ('ɵ') to discourage apps from relying on def APIs directly. This commit adds the prefix and shortens the name from ngFactoryDef to fac. This is because property names cannot be minified by Uglify without turning on property mangling (which most apps have turned off) and are thus size-sensitive. Note that the other "defs" (ngPipeDef, etc) will be prefixed and shortened in follow-up PRs, in an attempt to limit how large and conflict-y this change is. PR Close #33116
419 lines
13 KiB
TypeScript
419 lines
13 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. 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.io/license
|
|
*/
|
|
import {AttributeMarker} from '@angular/compiler/src/core';
|
|
import {setup} from '@angular/compiler/test/aot/test_util';
|
|
import {compile, expectEmit} from './mock_compile';
|
|
|
|
describe('compiler compliance: directives', () => {
|
|
|
|
const angularFiles = setup({
|
|
compileAngular: false,
|
|
compileAnimations: false,
|
|
compileFakeCore: true,
|
|
});
|
|
|
|
describe('matching', () => {
|
|
|
|
it('should not match directives on i18n attribute', () => {
|
|
const files = {
|
|
app: {
|
|
'spec.ts': `
|
|
import {Component, Directive, NgModule} from '@angular/core';
|
|
|
|
@Directive({selector: '[i18n]'})
|
|
export class I18nDirective {}
|
|
|
|
@Component({selector: 'my-component', template: '<div i18n></div>'})
|
|
export class MyComponent {}
|
|
|
|
@NgModule({declarations: [I18nDirective, MyComponent]})
|
|
export class MyModule{}`
|
|
}
|
|
};
|
|
|
|
// MyComponent definition should be:
|
|
const MyComponentDefinition = `
|
|
MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|
type: MyComponent,
|
|
selectors: [["my-component"]],
|
|
decls: 1,
|
|
vars: 0,
|
|
template: function MyComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵelement(0, "div");
|
|
}
|
|
},
|
|
encapsulation: 2
|
|
});
|
|
`;
|
|
|
|
const MyComponentFactory = `
|
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
|
`;
|
|
|
|
const result = compile(files, angularFiles);
|
|
const source = result.source;
|
|
|
|
expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp');
|
|
expectEmit(source, MyComponentFactory, 'Incorrect ChildComponent.ɵfac');
|
|
});
|
|
|
|
it('should not match directives on i18n-prefixed attributes', () => {
|
|
const files = {
|
|
app: {
|
|
'spec.ts': `
|
|
import {Component, Directive, NgModule} from '@angular/core';
|
|
|
|
@Directive({selector: '[i18n]'})
|
|
export class I18nDirective {}
|
|
|
|
@Directive({selector: '[i18n-foo]'})
|
|
export class I18nFooDirective {}
|
|
|
|
@Directive({selector: '[foo]'})
|
|
export class FooDirective {}
|
|
|
|
@Component({selector: 'my-component', template: '<div i18n-foo></div>'})
|
|
export class MyComponent {}
|
|
|
|
@NgModule({declarations: [I18nDirective, I18nFooDirective, FooDirective, MyComponent]})
|
|
export class MyModule{}`
|
|
}
|
|
};
|
|
|
|
// MyComponent definition should be:
|
|
const MyComponentDefinition = `
|
|
MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|
type: MyComponent,
|
|
selectors: [["my-component"]],
|
|
decls: 1,
|
|
vars: 0,
|
|
template: function MyComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵelement(0, "div");
|
|
}
|
|
},
|
|
encapsulation: 2
|
|
});
|
|
`;
|
|
|
|
const MyComponentFactory = `
|
|
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
|
`;
|
|
|
|
const result = compile(files, angularFiles);
|
|
const source = result.source;
|
|
|
|
expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp');
|
|
expectEmit(source, MyComponentFactory, 'Incorrect ChildComponent.ɵfac');
|
|
});
|
|
|
|
it('should match directives on element bindings', () => {
|
|
|
|
const files = {
|
|
app: {
|
|
'spec.ts': `
|
|
import {Component, Directive, Input, NgModule} from '@angular/core';
|
|
|
|
@Directive({selector: '[someDirective]'})
|
|
export class SomeDirective {
|
|
@Input() someDirective;
|
|
}
|
|
|
|
@Component({selector: 'my-component', template: '<div [someDirective]="true"></div>'})
|
|
export class MyComponent {}
|
|
|
|
@NgModule({declarations: [SomeDirective, MyComponent]})
|
|
export class MyModule{}
|
|
`
|
|
}
|
|
};
|
|
|
|
|
|
// MyComponent definition should be:
|
|
const MyComponentDefinition = `
|
|
…
|
|
MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|
…
|
|
consts: [[${AttributeMarker.Bindings}, "someDirective"]],
|
|
template: function MyComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵelement(0, "div", 0);
|
|
}
|
|
if (rf & 2) {
|
|
$r3$.ɵɵproperty("someDirective", true);
|
|
}
|
|
},
|
|
…
|
|
directives: [SomeDirective],
|
|
encapsulation: 2
|
|
});
|
|
`;
|
|
|
|
const result = compile(files, angularFiles);
|
|
const source = result.source;
|
|
|
|
expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp');
|
|
});
|
|
|
|
it('should match directives on ng-templates', () => {
|
|
const files = {
|
|
app: {
|
|
'spec.ts': `
|
|
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
|
|
|
|
@Directive({
|
|
selector: 'ng-template[directiveA]'
|
|
})
|
|
export class DirectiveA {
|
|
constructor(public templateRef: TemplateRef<any>) {}
|
|
}
|
|
|
|
@Component({
|
|
selector: 'my-component',
|
|
template: \`
|
|
<ng-template directiveA>Some content</ng-template>
|
|
\`
|
|
})
|
|
export class MyComponent {}
|
|
|
|
@NgModule({declarations: [DirectiveA, MyComponent]})
|
|
export class MyModule{}
|
|
`
|
|
}
|
|
};
|
|
|
|
const MyComponentDefinition = `
|
|
…
|
|
function MyComponent_ng_template_0_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵtext(0, "Some content");
|
|
}
|
|
}
|
|
…
|
|
MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|
…
|
|
consts: [["directiveA", ""]],
|
|
template: function MyComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template", 0);
|
|
}
|
|
},
|
|
…
|
|
directives: [DirectiveA],
|
|
…
|
|
});
|
|
`;
|
|
|
|
const result = compile(files, angularFiles);
|
|
expectEmit(result.source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp');
|
|
});
|
|
|
|
it('should match directives on ng-container', () => {
|
|
const files = {
|
|
app: {
|
|
'spec.ts': `
|
|
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
|
|
|
|
@Directive({
|
|
selector: 'ng-container[directiveA]'
|
|
})
|
|
export class DirectiveA {
|
|
constructor(public templateRef: TemplateRef<any>) {}
|
|
}
|
|
|
|
@Component({
|
|
selector: 'my-component',
|
|
template: \`
|
|
<ng-container *ngIf="showing" directiveA>Some content</ng-container>
|
|
\`
|
|
})
|
|
export class MyComponent {}
|
|
|
|
@NgModule({declarations: [DirectiveA, MyComponent]})
|
|
export class MyModule{}
|
|
`
|
|
}
|
|
};
|
|
|
|
const MyComponentDefinition = `
|
|
…
|
|
function MyComponent_ng_container_0_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵelementContainerStart(0, 1);
|
|
$r3$.ɵɵtext(1, "Some content");
|
|
$r3$.ɵɵelementContainerEnd();
|
|
}
|
|
}
|
|
…
|
|
MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|
…
|
|
consts: [["directiveA", "", ${AttributeMarker.Template}, "ngIf"], ["directiveA", ""]],
|
|
template: function MyComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵtemplate(0, MyComponent_ng_container_0_Template, 2, 0, "ng-container", 0);
|
|
}
|
|
if (rf & 2) {
|
|
$r3$.ɵɵproperty("ngIf", ctx.showing);
|
|
}
|
|
},
|
|
…
|
|
directives: [DirectiveA],
|
|
…
|
|
});
|
|
`;
|
|
|
|
const result = compile(files, angularFiles);
|
|
expectEmit(result.source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp');
|
|
});
|
|
|
|
it('should match directives on ng-template bindings', () => {
|
|
|
|
const files = {
|
|
app: {
|
|
'spec.ts': `
|
|
import {Component, Directive, Input, NgModule} from '@angular/core';
|
|
|
|
@Directive({selector: '[someDirective]'})
|
|
export class SomeDirective {
|
|
@Input() someDirective;
|
|
}
|
|
|
|
@Component({selector: 'my-component', template: '<ng-template [someDirective]="true"></ng-template>'})
|
|
export class MyComponent {}
|
|
|
|
@NgModule({declarations: [SomeDirective, MyComponent]})
|
|
export class MyModule{}
|
|
`
|
|
}
|
|
};
|
|
|
|
|
|
// MyComponent definition should be:
|
|
const MyComponentDefinition = `
|
|
…
|
|
MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|
…
|
|
consts: [[${AttributeMarker.Bindings}, "someDirective"]],
|
|
template: function MyComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", 0);
|
|
}
|
|
if (rf & 2) {
|
|
$r3$.ɵɵproperty("someDirective", true);
|
|
}
|
|
},
|
|
…
|
|
directives: [SomeDirective],
|
|
encapsulation: 2
|
|
});
|
|
`;
|
|
|
|
const result = compile(files, angularFiles);
|
|
const source = result.source;
|
|
|
|
expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp');
|
|
});
|
|
|
|
it('should match structural directives', () => {
|
|
|
|
const files = {
|
|
app: {
|
|
'spec.ts': `
|
|
import {Component, Directive, Input, NgModule} from '@angular/core';
|
|
|
|
@Directive({selector: '[someDirective]'})
|
|
export class SomeDirective {
|
|
@Input() someDirective;
|
|
}
|
|
|
|
@Component({selector: 'my-component', template: '<div *someDirective></div>'})
|
|
export class MyComponent {}
|
|
|
|
@NgModule({declarations: [SomeDirective, MyComponent]})
|
|
export class MyModule{}
|
|
`
|
|
}
|
|
};
|
|
|
|
// MyComponent definition should be:
|
|
const MyComponentDefinition = `
|
|
…
|
|
MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|
…
|
|
consts: [[${AttributeMarker.Template}, "someDirective"]],
|
|
template: function MyComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 1, 0, "div", 0);
|
|
}
|
|
},
|
|
…
|
|
directives: [SomeDirective],
|
|
encapsulation: 2
|
|
});
|
|
`;
|
|
|
|
const result = compile(files, angularFiles);
|
|
const source = result.source;
|
|
|
|
expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp');
|
|
|
|
});
|
|
|
|
it('should match directives on element outputs', () => {
|
|
|
|
const files = {
|
|
app: {
|
|
'spec.ts': `
|
|
import {Component, Directive, Output, EventEmitter, NgModule} from '@angular/core';
|
|
|
|
@Directive({selector: '[someDirective]'})
|
|
export class SomeDirective {
|
|
@Output() someDirective = new EventEmitter();
|
|
}
|
|
|
|
@Component({selector: 'my-component', template: '<div (someDirective)="noop()"></div>'})
|
|
export class MyComponent {
|
|
noop() {}
|
|
}
|
|
|
|
@NgModule({declarations: [SomeDirective, MyComponent]})
|
|
export class MyModule{}
|
|
`
|
|
}
|
|
};
|
|
|
|
|
|
// MyComponent definition should be:
|
|
const MyComponentDefinition = `
|
|
…
|
|
MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|
…
|
|
consts: [[${AttributeMarker.Bindings}, "someDirective"]],
|
|
template: function MyComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
$r3$.ɵɵelementStart(0, "div", 0);
|
|
$r3$.ɵɵlistener("someDirective", function MyComponent_Template_div_someDirective_0_listener($event) { return ctx.noop(); });
|
|
$r3$.ɵɵelementEnd();
|
|
}
|
|
},
|
|
…
|
|
directives: [SomeDirective],
|
|
encapsulation: 2
|
|
});
|
|
`;
|
|
|
|
const result = compile(files, angularFiles);
|
|
const source = result.source;
|
|
|
|
expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp');
|
|
});
|
|
|
|
});
|
|
});
|