fix(compiler_cli): allow to use builtin directives like `NgIf`, …
Related to #8448 Closes #8454
This commit is contained in:
parent
0297398f5e
commit
edec158dd8
|
@ -1,4 +1,4 @@
|
||||||
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
|
import {ChangeDetectionStrategy, ViewEncapsulation, reflector} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
CHANGE_DETECTION_STRATEGY_VALUES,
|
CHANGE_DETECTION_STRATEGY_VALUES,
|
||||||
VIEW_ENCAPSULATION_VALUES,
|
VIEW_ENCAPSULATION_VALUES,
|
||||||
|
@ -259,10 +259,13 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var UNDEFINED = new Object();
|
||||||
|
|
||||||
export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
|
export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
|
||||||
value: any;
|
value: any;
|
||||||
identifier: CompileIdentifierMetadata;
|
identifier: CompileIdentifierMetadata;
|
||||||
identifierIsInstance: boolean;
|
identifierIsInstance: boolean;
|
||||||
|
private _assetCacheKey = UNDEFINED;
|
||||||
|
|
||||||
constructor({value, identifier, identifierIsInstance}: {
|
constructor({value, identifier, identifierIsInstance}: {
|
||||||
value?: any,
|
value?: any,
|
||||||
|
@ -299,14 +302,23 @@ export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
get assetCacheKey(): any {
|
get assetCacheKey(): any {
|
||||||
if (isPresent(this.identifier)) {
|
if (this._assetCacheKey === UNDEFINED) {
|
||||||
return isPresent(this.identifier.moduleUrl) &&
|
if (isPresent(this.identifier)) {
|
||||||
isPresent(getUrlScheme(this.identifier.moduleUrl)) ?
|
if (isPresent(this.identifier.moduleUrl) &&
|
||||||
`${this.identifier.name}|${this.identifier.moduleUrl}|${this.identifierIsInstance}` :
|
isPresent(getUrlScheme(this.identifier.moduleUrl))) {
|
||||||
null;
|
var uri = reflector.importUri({
|
||||||
} else {
|
'filePath': this.identifier.moduleUrl,
|
||||||
return this.value;
|
'name': this.identifier.name
|
||||||
|
});
|
||||||
|
this._assetCacheKey = `${this.identifier.name}|${uri}|${this.identifierIsInstance}`;
|
||||||
|
} else {
|
||||||
|
this._assetCacheKey = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._assetCacheKey = this.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return this._assetCacheKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
equalsTo(token2: CompileTokenMetadata): boolean {
|
equalsTo(token2: CompileTokenMetadata): boolean {
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<div>{{ctxProp}}</div>
|
<div>{{ctxProp}}</div>
|
||||||
<form><input type="button" [(ngModel)]="ctxProp"/></form>
|
<form><input type="button" [(ngModel)]="ctxProp"/></form>
|
||||||
<my-comp></my-comp>
|
<my-comp *ngIf="ctxBool"></my-comp>
|
|
@ -1,5 +1,5 @@
|
||||||
import {Component, Inject} from '@angular/core';
|
import {Component, Inject} from '@angular/core';
|
||||||
import {FORM_DIRECTIVES} from '@angular/common';
|
import {FORM_DIRECTIVES, NgIf} from '@angular/common';
|
||||||
import {MyComp} from './a/multiple_components';
|
import {MyComp} from './a/multiple_components';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -7,9 +7,10 @@ import {MyComp} from './a/multiple_components';
|
||||||
templateUrl: './basic.html',
|
templateUrl: './basic.html',
|
||||||
styles: ['.red { color: red }'],
|
styles: ['.red { color: red }'],
|
||||||
styleUrls: ['./basic.css'],
|
styleUrls: ['./basic.css'],
|
||||||
directives: [MyComp, FORM_DIRECTIVES]
|
directives: [MyComp, FORM_DIRECTIVES, NgIf]
|
||||||
})
|
})
|
||||||
export class Basic {
|
export class Basic {
|
||||||
ctxProp: string;
|
ctxProp: string;
|
||||||
constructor() { this.ctxProp = 'initiaValue'; }
|
ctxBool: boolean;
|
||||||
|
constructor() { this.ctxProp = 'initialValue'; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import {BasicNgFactory} from '../src/basic.ngfactory';
|
||||||
|
import {MyComp} from '../src/a/multiple_components';
|
||||||
|
import {ReflectiveInjector, DebugElement, getDebugNode} from '@angular/core';
|
||||||
|
import {browserPlatform, BROWSER_APP_STATIC_PROVIDERS} from '@angular/platform-browser';
|
||||||
|
|
||||||
describe("template codegen output", () => {
|
describe("template codegen output", () => {
|
||||||
const outDir = path.join('dist', 'all', '@angular', 'compiler_cli', 'integrationtest', 'src');
|
const outDir = path.join('dist', 'all', '@angular', 'compiler_cli', 'integrationtest', 'src');
|
||||||
|
@ -23,4 +27,17 @@ describe("template codegen output", () => {
|
||||||
expect(fs.existsSync(dtsOutput)).toBeTruthy();
|
expect(fs.existsSync(dtsOutput)).toBeTruthy();
|
||||||
expect(fs.readFileSync(dtsOutput, {encoding: 'utf-8'})).toContain('Basic');
|
expect(fs.readFileSync(dtsOutput, {encoding: 'utf-8'})).toContain('Basic');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should be able to create the basic component and trigger an ngIf", () => {
|
||||||
|
const appInjector = ReflectiveInjector.resolveAndCreate(BROWSER_APP_STATIC_PROVIDERS,
|
||||||
|
browserPlatform().injector);
|
||||||
|
var comp = BasicNgFactory.create(appInjector);
|
||||||
|
var debugElement = <DebugElement>getDebugNode(comp.location.nativeElement);
|
||||||
|
expect(debugElement.children.length).toBe(2);
|
||||||
|
|
||||||
|
comp.instance.ctxBool = true;
|
||||||
|
comp.changeDetectorRef.detectChanges();
|
||||||
|
expect(debugElement.children.length).toBe(3);
|
||||||
|
expect(debugElement.children[2].injector.get(MyComp)).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -25,6 +25,7 @@ import {Parse5DomAdapter} from '@angular/platform-server';
|
||||||
|
|
||||||
import {MetadataCollector} from 'ts-metadata-collector';
|
import {MetadataCollector} from 'ts-metadata-collector';
|
||||||
import {NodeReflectorHost} from './reflector_host';
|
import {NodeReflectorHost} from './reflector_host';
|
||||||
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||||
|
|
||||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||||
|
|
||||||
|
@ -164,6 +165,7 @@ export class CodeGenerator {
|
||||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||||
const reflectorHost = new NodeReflectorHost(program, compilerHost, options, ngOptions);
|
const reflectorHost = new NodeReflectorHost(program, compilerHost, options, ngOptions);
|
||||||
const staticReflector = new StaticReflector(reflectorHost);
|
const staticReflector = new StaticReflector(reflectorHost);
|
||||||
|
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||||
const htmlParser = new HtmlParser();
|
const htmlParser = new HtmlParser();
|
||||||
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser);
|
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser);
|
||||||
const parser = new Parser(new Lexer());
|
const parser = new Parser(new Lexer());
|
||||||
|
|
|
@ -2,3 +2,6 @@ import {__core_private__ as r, __core_private_types__ as t} from '@angular/core'
|
||||||
|
|
||||||
export type ReflectorReader = t.ReflectorReader;
|
export type ReflectorReader = t.ReflectorReader;
|
||||||
export var ReflectorReader: typeof t.ReflectorReader = r.ReflectorReader;
|
export var ReflectorReader: typeof t.ReflectorReader = r.ReflectorReader;
|
||||||
|
|
||||||
|
export type ReflectionCapabilities = t.ReflectionCapabilities;
|
||||||
|
export var ReflectionCapabilities: typeof t.ReflectionCapabilities = r.ReflectionCapabilities;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin / env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
// Must be imported first, because angular2 decorators throws on load.
|
// Must be imported first, because angular2 decorators throws on load.
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
|
@ -37,11 +37,15 @@ export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||||
return resolved ? resolved.resolvedFileName : null;
|
return resolved ? resolved.resolvedFileName : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private normalizeAssetUrl(url: string): string {
|
||||||
|
let assetUrl = AssetUrl.parse(url);
|
||||||
|
return assetUrl ? `${assetUrl.packageName}/${assetUrl.modulePath}` : null;
|
||||||
|
}
|
||||||
|
|
||||||
private resolveAssetUrl(url: string, containingFile: string): string {
|
private resolveAssetUrl(url: string, containingFile: string): string {
|
||||||
let assetUrl = AssetUrl.parse(url);
|
let assetUrl = this.normalizeAssetUrl(url);
|
||||||
if (assetUrl) {
|
if (assetUrl) {
|
||||||
return this.resolve(`${assetUrl.packageName}/${assetUrl.modulePath}`, containingFile);
|
return this.resolve(assetUrl, containingFile);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
@ -92,6 +96,10 @@ export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
let assetUrl = this.normalizeAssetUrl(module);
|
||||||
|
if (assetUrl) {
|
||||||
|
module = assetUrl;
|
||||||
|
}
|
||||||
const filePath = this.resolve(module, containingFile);
|
const filePath = this.resolve(module, containingFile);
|
||||||
|
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import {reflector} from '@angular/core';
|
||||||
|
import {ReflectionCapabilities} from './core_private';
|
||||||
|
import {StaticReflector} from './static_reflector';
|
||||||
|
|
||||||
|
export class StaticAndDynamicReflectionCapabilities {
|
||||||
|
static install(staticDelegate: StaticReflector) {
|
||||||
|
reflector.updateCapabilities(new StaticAndDynamicReflectionCapabilities(staticDelegate));
|
||||||
|
}
|
||||||
|
|
||||||
|
private dynamicDelegate = new ReflectionCapabilities();
|
||||||
|
|
||||||
|
constructor(private staticDelegate: StaticReflector) {}
|
||||||
|
|
||||||
|
isReflectionEnabled(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
factory(type: any): Function {
|
||||||
|
return this.dynamicDelegate.factory(type);
|
||||||
|
}
|
||||||
|
interfaces(type: any): any[] {
|
||||||
|
return this.dynamicDelegate.interfaces(type);
|
||||||
|
}
|
||||||
|
parameters(type: any): any[][] {
|
||||||
|
return isStaticType(type) ? this.staticDelegate.parameters(type) : this.dynamicDelegate.parameters(type);
|
||||||
|
}
|
||||||
|
annotations(type: any): any[] {
|
||||||
|
return isStaticType(type) ? this.staticDelegate.annotations(type) : this.dynamicDelegate.annotations(type);
|
||||||
|
}
|
||||||
|
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
||||||
|
return isStaticType(typeOrFunc) ? this.staticDelegate.propMetadata(typeOrFunc) : this.dynamicDelegate.propMetadata(typeOrFunc);
|
||||||
|
}
|
||||||
|
getter(name: string) {
|
||||||
|
return this.dynamicDelegate.getter(name);
|
||||||
|
}
|
||||||
|
setter(name: string) {
|
||||||
|
return this.dynamicDelegate.setter(name);
|
||||||
|
}
|
||||||
|
method(name: string) {
|
||||||
|
return this.dynamicDelegate.method(name);
|
||||||
|
}
|
||||||
|
importUri(type: any): string {
|
||||||
|
return this.staticDelegate.importUri(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStaticType(type: any): boolean {
|
||||||
|
return typeof type === 'object' && type.name && type.filePath;
|
||||||
|
}
|
|
@ -75,7 +75,10 @@ export class StaticReflector implements ReflectorReader {
|
||||||
|
|
||||||
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
||||||
|
|
||||||
importUri(typeOrFunc: any): string { return (<StaticSymbol>typeOrFunc).filePath; }
|
importUri(typeOrFunc: StaticSymbol): string {
|
||||||
|
var staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
||||||
|
return staticSymbol ? staticSymbol.filePath : null;
|
||||||
|
}
|
||||||
|
|
||||||
public annotations(type: StaticSymbol): any[] {
|
public annotations(type: StaticSymbol): any[] {
|
||||||
let annotations = this.annotationCache.get(type);
|
let annotations = this.annotationCache.get(type);
|
||||||
|
|
|
@ -11,5 +11,5 @@ export interface PlatformReflectionCapabilities {
|
||||||
getter(name: string): GetterFn;
|
getter(name: string): GetterFn;
|
||||||
setter(name: string): SetterFn;
|
setter(name: string): SetterFn;
|
||||||
method(name: string): MethodFn;
|
method(name: string): MethodFn;
|
||||||
importUri(type: Type): string;
|
importUri(type: any): string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -342,7 +342,12 @@ class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||||
return classMirror.metadata;
|
return classMirror.metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
String importUri(Type type) {
|
String importUri(dynamic type) {
|
||||||
|
// StaticSymbol
|
||||||
|
if (type is Map && type['filePath'] != null) {
|
||||||
|
return type['filePath'];
|
||||||
|
}
|
||||||
|
// Runtime type
|
||||||
return '${(reflectClass(type).owner as LibraryMirror).uri}';
|
return '${(reflectClass(type).owner as LibraryMirror).uri}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,7 +214,14 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is not a concept of import uri in Js, but this is useful in developing Dart applications.
|
// There is not a concept of import uri in Js, but this is useful in developing Dart applications.
|
||||||
importUri(type: Type): string { return `./${stringify(type)}`; }
|
importUri(type: any): string {
|
||||||
|
// StaticSymbol
|
||||||
|
if (typeof type === 'object' && type['filePath']) {
|
||||||
|
return type['filePath'];
|
||||||
|
}
|
||||||
|
// Runtime type
|
||||||
|
return `./${stringify(type)}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertTsickleDecoratorIntoMetadata(decoratorInvocations: any[]): any[] {
|
function convertTsickleDecoratorIntoMetadata(decoratorInvocations: any[]): any[] {
|
||||||
|
|
|
@ -45,6 +45,10 @@ export class Reflector extends ReflectorReader {
|
||||||
this.reflectionCapabilities = reflectionCapabilities;
|
this.reflectionCapabilities = reflectionCapabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateCapabilities(caps: PlatformReflectionCapabilities) {
|
||||||
|
this.reflectionCapabilities = caps;
|
||||||
|
}
|
||||||
|
|
||||||
isReflectionEnabled(): boolean { return this.reflectionCapabilities.isReflectionEnabled(); }
|
isReflectionEnabled(): boolean { return this.reflectionCapabilities.isReflectionEnabled(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,7 +164,7 @@ export class Reflector extends ReflectorReader {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_containsReflectionInfo(typeOrFunc: any) { return this._injectableInfo.has(typeOrFunc); }
|
_containsReflectionInfo(typeOrFunc: any) { return this._injectableInfo.has(typeOrFunc); }
|
||||||
|
|
||||||
importUri(type: Type): string { return this.reflectionCapabilities.importUri(type); }
|
importUri(type: any): string { return this.reflectionCapabilities.importUri(type); }
|
||||||
}
|
}
|
||||||
|
|
||||||
function _mergeMaps(target: Map<string, Function>, config: {[key: string]: Function}): void {
|
function _mergeMaps(target: Map<string, Function>, config: {[key: string]: Function}): void {
|
||||||
|
|
Loading…
Reference in New Issue