Revert "feat(compiler-cli): lower metadata `useValue` and `data` literal fields (#18905)"
This reverts commit c685cc2f0a
.
This commit is contained in:
parent
3a6d270bb8
commit
c7e1bda32f
|
@ -166,13 +166,9 @@ function diagnosticMessageToString(message: ts.DiagnosticMessageChain | string):
|
||||||
return ts.flattenDiagnosticMessageText(message, '\n');
|
return ts.flattenDiagnosticMessageText(message, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
const REWRITE_PREFIX = /^\u0275[0-9]+$/;
|
|
||||||
|
|
||||||
function createFactoryInfo(emitter: TypeScriptEmitter, file: GeneratedFile): FactoryInfo {
|
function createFactoryInfo(emitter: TypeScriptEmitter, file: GeneratedFile): FactoryInfo {
|
||||||
const {sourceText, context} = emitter.emitStatementsAndContext(
|
const {sourceText, context} =
|
||||||
file.srcFileUrl, file.genFileUrl, file.stmts !,
|
emitter.emitStatementsAndContext(file.srcFileUrl, file.genFileUrl, file.stmts !);
|
||||||
/* preamble */ undefined, /* emitSourceMaps */ undefined,
|
|
||||||
/* referenceFilter */ reference => !!(reference.name && REWRITE_PREFIX.test(reference.name)));
|
|
||||||
const source = ts.createSourceFile(
|
const source = ts.createSourceFile(
|
||||||
file.genFileUrl, sourceText, ts.ScriptTarget.Latest, /* setParentNodes */ true);
|
file.genFileUrl, sourceText, ts.ScriptTarget.Latest, /* setParentNodes */ true);
|
||||||
return {source, context};
|
return {source, context};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CollectorOptions, MetadataCollector, MetadataValue, ModuleMetadata, isMetadataGlobalReferenceExpression} from '@angular/tsc-wrapped';
|
import {CollectorOptions, MetadataCollector, MetadataValue, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
export interface LoweringRequest {
|
export interface LoweringRequest {
|
||||||
|
@ -181,30 +181,6 @@ function shouldLower(node: ts.Node | undefined): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const REWRITE_PREFIX = '\u0275';
|
|
||||||
|
|
||||||
function isPrimitive(value: any): boolean {
|
|
||||||
return Object(value) !== value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRewritten(value: any): boolean {
|
|
||||||
return isMetadataGlobalReferenceExpression(value) && value.name.startsWith(REWRITE_PREFIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLiteralFieldNamed(node: ts.Node, names: Set<string>): boolean {
|
|
||||||
if (node.parent && node.parent.kind == ts.SyntaxKind.PropertyAssignment) {
|
|
||||||
const property = node.parent as ts.PropertyAssignment;
|
|
||||||
if (property.parent && property.parent.kind == ts.SyntaxKind.ObjectLiteralExpression &&
|
|
||||||
property.name && property.name.kind == ts.SyntaxKind.Identifier) {
|
|
||||||
const propertyName = property.name as ts.Identifier;
|
|
||||||
return names.has(propertyName.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const LOWERABLE_FIELD_NAMES = new Set(['useValue', 'useFactory', 'data']);
|
|
||||||
|
|
||||||
export class LowerMetadataCache implements RequestsMap {
|
export class LowerMetadataCache implements RequestsMap {
|
||||||
private collector: MetadataCollector;
|
private collector: MetadataCollector;
|
||||||
private metadataCache = new Map<string, MetadataAndLoweringRequests>();
|
private metadataCache = new Map<string, MetadataAndLoweringRequests>();
|
||||||
|
@ -232,24 +208,8 @@ export class LowerMetadataCache implements RequestsMap {
|
||||||
|
|
||||||
private getMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
|
private getMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
|
||||||
let identNumber = 0;
|
let identNumber = 0;
|
||||||
const freshIdent = () => REWRITE_PREFIX + identNumber++;
|
const freshIdent = () => '\u0275' + identNumber++;
|
||||||
const requests = new Map<number, LoweringRequest>();
|
const requests = new Map<number, LoweringRequest>();
|
||||||
|
|
||||||
const isExportedSymbol = (() => {
|
|
||||||
let exportTable: Set<string>;
|
|
||||||
return (node: ts.Node) => {
|
|
||||||
if (node.kind == ts.SyntaxKind.Identifier) {
|
|
||||||
const ident = node as ts.Identifier;
|
|
||||||
|
|
||||||
if (!exportTable) {
|
|
||||||
exportTable = createExportTableFor(sourceFile);
|
|
||||||
}
|
|
||||||
return exportTable.has(ident.text);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
const replaceNode = (node: ts.Node) => {
|
const replaceNode = (node: ts.Node) => {
|
||||||
const name = freshIdent();
|
const name = freshIdent();
|
||||||
requests.set(node.pos, {name, kind: node.kind, location: node.pos, end: node.end});
|
requests.set(node.pos, {name, kind: node.kind, location: node.pos, end: node.end});
|
||||||
|
@ -257,16 +217,10 @@ export class LowerMetadataCache implements RequestsMap {
|
||||||
};
|
};
|
||||||
|
|
||||||
const substituteExpression = (value: MetadataValue, node: ts.Node): MetadataValue => {
|
const substituteExpression = (value: MetadataValue, node: ts.Node): MetadataValue => {
|
||||||
if (!isPrimitive(value) && !isRewritten(value)) {
|
if ((node.kind === ts.SyntaxKind.ArrowFunction ||
|
||||||
if ((node.kind === ts.SyntaxKind.ArrowFunction ||
|
node.kind === ts.SyntaxKind.FunctionExpression) &&
|
||||||
node.kind === ts.SyntaxKind.FunctionExpression) &&
|
shouldLower(node)) {
|
||||||
shouldLower(node)) {
|
return replaceNode(node);
|
||||||
return replaceNode(node);
|
|
||||||
}
|
|
||||||
if (isLiteralFieldNamed(node, LOWERABLE_FIELD_NAMES) && shouldLower(node) &&
|
|
||||||
!isExportedSymbol(node)) {
|
|
||||||
return replaceNode(node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
@ -275,44 +229,4 @@ export class LowerMetadataCache implements RequestsMap {
|
||||||
|
|
||||||
return {metadata, requests};
|
return {metadata, requests};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createExportTableFor(sourceFile: ts.SourceFile): Set<string> {
|
|
||||||
const exportTable = new Set<string>();
|
|
||||||
// Lazily collect all the exports from the source file
|
|
||||||
ts.forEachChild(sourceFile, function scan(node) {
|
|
||||||
switch (node.kind) {
|
|
||||||
case ts.SyntaxKind.ClassDeclaration:
|
|
||||||
case ts.SyntaxKind.FunctionDeclaration:
|
|
||||||
case ts.SyntaxKind.InterfaceDeclaration:
|
|
||||||
if ((ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) != 0) {
|
|
||||||
const classDeclaration =
|
|
||||||
node as(ts.ClassDeclaration | ts.FunctionDeclaration | ts.InterfaceDeclaration);
|
|
||||||
const name = classDeclaration.name;
|
|
||||||
if (name) exportTable.add(name.text);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.VariableStatement:
|
|
||||||
const variableStatement = node as ts.VariableStatement;
|
|
||||||
for (const declaration of variableStatement.declarationList.declarations) {
|
|
||||||
scan(declaration);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.VariableDeclaration:
|
|
||||||
const variableDeclaration = node as ts.VariableDeclaration;
|
|
||||||
if ((ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) != 0 &&
|
|
||||||
variableDeclaration.name.kind == ts.SyntaxKind.Identifier) {
|
|
||||||
const name = variableDeclaration.name as ts.Identifier;
|
|
||||||
exportTable.add(name.text);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.ExportDeclaration:
|
|
||||||
const exportDeclaration = node as ts.ExportDeclaration;
|
|
||||||
const {moduleSpecifier, exportClause} = exportDeclaration;
|
|
||||||
if (!moduleSpecifier && exportClause) {
|
|
||||||
exportClause.elements.forEach(spec => { exportTable.add(spec.name.text); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return exportTable;
|
|
||||||
}
|
|
|
@ -12,7 +12,6 @@ import * as ts from 'typescript';
|
||||||
|
|
||||||
import {TypeChecker} from '../../src/diagnostics/check_types';
|
import {TypeChecker} from '../../src/diagnostics/check_types';
|
||||||
import {Diagnostic} from '../../src/transformers/api';
|
import {Diagnostic} from '../../src/transformers/api';
|
||||||
import {LowerMetadataCache} from '../../src/transformers/lower_expressions';
|
|
||||||
|
|
||||||
function compile(
|
function compile(
|
||||||
rootDirs: MockData, options: AotCompilerOptions = {},
|
rootDirs: MockData, options: AotCompilerOptions = {},
|
||||||
|
@ -20,7 +19,7 @@ function compile(
|
||||||
const rootDirArr = toMockFileArray(rootDirs);
|
const rootDirArr = toMockFileArray(rootDirs);
|
||||||
const scriptNames = rootDirArr.map(entry => entry.fileName).filter(isSource);
|
const scriptNames = rootDirArr.map(entry => entry.fileName).filter(isSource);
|
||||||
const host = new MockCompilerHost(scriptNames, arrayToMockDir(rootDirArr));
|
const host = new MockCompilerHost(scriptNames, arrayToMockDir(rootDirArr));
|
||||||
const aotHost = new MockAotCompilerHost(host, new LowerMetadataCache({}));
|
const aotHost = new MockAotCompilerHost(host);
|
||||||
const tsSettings = {...settings, ...tsOptions};
|
const tsSettings = {...settings, ...tsOptions};
|
||||||
const program = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
|
const program = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
|
||||||
const ngChecker = new TypeChecker(program, tsSettings, host, aotHost, options);
|
const ngChecker = new TypeChecker(program, tsSettings, host, aotHost, options);
|
||||||
|
@ -81,12 +80,6 @@ describe('ng type checker', () => {
|
||||||
it('should accept a safe property access of a nullable field reference of a method result',
|
it('should accept a safe property access of a nullable field reference of a method result',
|
||||||
() => { a('{{getMaybePerson()?.name}}'); });
|
() => { a('{{getMaybePerson()?.name}}'); });
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with lowered expressions', () => {
|
|
||||||
it('should not report lowered expressions as errors', () => {
|
|
||||||
expectNoDiagnostics(compile([angularFiles, LOWERING_QUICKSTART]));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function appComponentSource(template: string): string {
|
function appComponentSource(template: string): string {
|
||||||
|
@ -141,38 +134,8 @@ const QUICKSTART: MockDirectory = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const LOWERING_QUICKSTART: MockDirectory = {
|
|
||||||
quickstart: {
|
|
||||||
app: {
|
|
||||||
'app.component.ts': appComponentSource('<h1>Hello {{name}}</h1>'),
|
|
||||||
'app.module.ts': `
|
|
||||||
import { NgModule, Component } from '@angular/core';
|
|
||||||
import { toString } from './utils';
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
|
|
||||||
class Foo {}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
template: '',
|
|
||||||
providers: [
|
|
||||||
{provide: 'someToken', useFactory: () => new Foo()}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class Bar {}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [ AppComponent, Bar ],
|
|
||||||
bootstrap: [ AppComponent ]
|
|
||||||
})
|
|
||||||
export class AppModule { }
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function expectNoDiagnostics(diagnostics: Diagnostic[]) {
|
function expectNoDiagnostics(diagnostics: Diagnostic[]) {
|
||||||
if (diagnostics && diagnostics.length) {
|
if (diagnostics && diagnostics.length) {
|
||||||
throw new Error(diagnostics.map(d => `${d.span}: ${d.message}`).join('\n'));
|
throw new Error(diagnostics.map(d => `${d.span}: ${d.message}`).join('\n'));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -848,78 +848,4 @@ describe('ngc transformer command-line', () => {
|
||||||
shouldExist('app/main.js');
|
shouldExist('app/main.js');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('expression lowering', () => {
|
|
||||||
const shouldExist = (fileName: string) => {
|
|
||||||
if (!fs.existsSync(path.resolve(basePath, fileName))) {
|
|
||||||
throw new Error(`Expected ${fileName} to be emitted (basePath: ${basePath})`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
it('should be able to lower supported expressions', () => {
|
|
||||||
writeConfig(`{
|
|
||||||
"extends": "./tsconfig-base.json",
|
|
||||||
"files": ["module.ts"]
|
|
||||||
}`);
|
|
||||||
write('module.ts', `
|
|
||||||
import {NgModule, InjectionToken} from '@angular/core';
|
|
||||||
import {AppComponent} from './app';
|
|
||||||
|
|
||||||
export interface Info {
|
|
||||||
route: string;
|
|
||||||
data: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const T1 = new InjectionToken<string>('t1');
|
|
||||||
export const T2 = new InjectionToken<string>('t2');
|
|
||||||
export const T3 = new InjectionToken<number>('t3');
|
|
||||||
export const T4 = new InjectionToken<Info[]>('t4');
|
|
||||||
|
|
||||||
enum SomeEnum {
|
|
||||||
OK,
|
|
||||||
Cancel
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateString() {
|
|
||||||
return 'someValue';
|
|
||||||
}
|
|
||||||
|
|
||||||
const routeLikeData = [{
|
|
||||||
route: '/home',
|
|
||||||
data: calculateString()
|
|
||||||
}];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [AppComponent],
|
|
||||||
providers: [
|
|
||||||
{ provide: T1, useValue: calculateString() },
|
|
||||||
{ provide: T2, useFactory: () => 'someValue' },
|
|
||||||
{ provide: T3, useValue: SomeEnum.OK },
|
|
||||||
{ provide: T4, useValue: routeLikeData }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class MyModule {}
|
|
||||||
`);
|
|
||||||
write('app.ts', `
|
|
||||||
import {Component, Inject} from '@angular/core';
|
|
||||||
import * as m from './module';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'my-app',
|
|
||||||
template: ''
|
|
||||||
})
|
|
||||||
export class AppComponent {
|
|
||||||
constructor(
|
|
||||||
@Inject(m.T1) private t1: string,
|
|
||||||
@Inject(m.T2) private t2: string,
|
|
||||||
@Inject(m.T3) private t3: number,
|
|
||||||
@Inject(m.T4) private t4: m.Info[],
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
expect(mainSync(['-p', basePath], s => {})).toBe(0);
|
|
||||||
shouldExist('built/module.js');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,92 +6,29 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ModuleMetadata} from '@angular/tsc-wrapped';
|
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {LowerMetadataCache, LoweringRequest, RequestLocationMap, getExpressionLoweringTransformFactory} from '../../src/transformers/lower_expressions';
|
import {LoweringRequest, RequestLocationMap, getExpressionLoweringTransformFactory} from '../../src/transformers/lower_expressions';
|
||||||
import {Directory, MockAotContext, MockCompilerHost} from '../mocks';
|
import {Directory, MockAotContext, MockCompilerHost} from '../mocks';
|
||||||
|
|
||||||
describe('Expression lowering', () => {
|
describe('Expression lowering', () => {
|
||||||
describe('transform', () => {
|
it('should be able to lower a simple expression', () => {
|
||||||
it('should be able to lower a simple expression', () => {
|
expect(convert('const a = 1 +◊b: 2◊;')).toBe('const b = 2; const a = 1 + b; export { b };');
|
||||||
expect(convert('const a = 1 +◊b: 2◊;')).toBe('const b = 2; const a = 1 + b; export { b };');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to lower an expression in a decorator', () => {
|
|
||||||
expect(convert(`
|
|
||||||
import {Component} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
provider: [{provide: 'someToken', useFactory:◊l: () => null◊}]
|
|
||||||
})
|
|
||||||
class MyClass {}
|
|
||||||
`)).toContain('const l = () => null; exports.l = l;');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('collector', () => {
|
it('should be able to lower an expression in a decorator', () => {
|
||||||
it('should request a lowering for useValue', () => {
|
expect(convert(`
|
||||||
const collected = collect(`
|
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
enum SomeEnum {
|
|
||||||
OK,
|
|
||||||
NotOK
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
provider: [{provide: 'someToken', useValue:◊enum: SomeEnum.OK◊}]
|
provider: [{provide: 'someToken', useFactory:◊l: () => null◊}]
|
||||||
})
|
})
|
||||||
export class MyClass {}
|
class MyClass {}
|
||||||
`);
|
`)).toContain('const l = () => null; exports.l = l;');
|
||||||
expect(collected.requests.has(collected.annotations[0].start))
|
|
||||||
.toBeTruthy('did not find the useValue');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should request a lowering for useFactory', () => {
|
|
||||||
const collected = collect(`
|
|
||||||
import {Component} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
provider: [{provide: 'someToken', useFactory:◊lambda: () => null◊}]
|
|
||||||
})
|
|
||||||
export class MyClass {}
|
|
||||||
`);
|
|
||||||
expect(collected.requests.has(collected.annotations[0].start))
|
|
||||||
.toBeTruthy('did not find the useFactory');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should request a lowering for data', () => {
|
|
||||||
const collected = collect(`
|
|
||||||
import {Component} from '@angular/core';
|
|
||||||
|
|
||||||
enum SomeEnum {
|
|
||||||
OK,
|
|
||||||
NotOK
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
provider: [{provide: 'someToken', data:◊enum: SomeEnum.OK◊}]
|
|
||||||
})
|
|
||||||
export class MyClass {}
|
|
||||||
`);
|
|
||||||
expect(collected.requests.has(collected.annotations[0].start))
|
|
||||||
.toBeTruthy('did not find the data field');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Helpers
|
function convert(annotatedSource: string) {
|
||||||
|
|
||||||
interface Annotation {
|
|
||||||
start: number;
|
|
||||||
length: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAnnotations(annotatedSource: string):
|
|
||||||
{unannotatedSource: string, annotations: Annotation[]} {
|
|
||||||
const annotations: {start: number, length: number, name: string}[] = [];
|
const annotations: {start: number, length: number, name: string}[] = [];
|
||||||
let adjustment = 0;
|
let adjustment = 0;
|
||||||
const unannotatedSource = annotatedSource.replace(
|
const unannotatedSource = annotatedSource.replace(
|
||||||
|
@ -101,13 +38,6 @@ function getAnnotations(annotatedSource: string):
|
||||||
adjustment -= text.length - source.length;
|
adjustment -= text.length - source.length;
|
||||||
return source;
|
return source;
|
||||||
});
|
});
|
||||||
return {unannotatedSource, annotations};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform helpers
|
|
||||||
|
|
||||||
function convert(annotatedSource: string) {
|
|
||||||
const {annotations, unannotatedSource} = getAnnotations(annotatedSource);
|
|
||||||
|
|
||||||
const baseFileName = 'someFile';
|
const baseFileName = 'someFile';
|
||||||
const moduleName = '/' + baseFileName;
|
const moduleName = '/' + baseFileName;
|
||||||
|
@ -173,16 +103,3 @@ function normalizeResult(result: string): string {
|
||||||
.replace(/^ /g, '')
|
.replace(/^ /g, '')
|
||||||
.replace(/ $/g, '');
|
.replace(/ $/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collector helpers
|
|
||||||
|
|
||||||
function collect(annotatedSource: string) {
|
|
||||||
const {annotations, unannotatedSource} = getAnnotations(annotatedSource);
|
|
||||||
const cache = new LowerMetadataCache({});
|
|
||||||
const sourceFile = ts.createSourceFile(
|
|
||||||
'someName.ts', unannotatedSource, ts.ScriptTarget.Latest, /* setParentNodes */ true);
|
|
||||||
return {
|
|
||||||
metadata: cache.getMetadata(sourceFile),
|
|
||||||
requests: cache.getRequests(sourceFile), annotations
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -35,14 +35,12 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T
|
||||||
return ctx.toSource();
|
return ctx.toSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReferenceFilter = (reference: o.ExternalReference) => boolean;
|
|
||||||
|
|
||||||
export class TypeScriptEmitter implements OutputEmitter {
|
export class TypeScriptEmitter implements OutputEmitter {
|
||||||
emitStatementsAndContext(
|
emitStatementsAndContext(
|
||||||
srcFilePath: string, genFilePath: string, stmts: o.Statement[], preamble: string = '',
|
srcFilePath: string, genFilePath: string, stmts: o.Statement[], preamble: string = '',
|
||||||
emitSourceMaps: boolean = true,
|
emitSourceMaps: boolean = true): {sourceText: string, context: EmitterVisitorContext} {
|
||||||
referenceFilter?: ReferenceFilter): {sourceText: string, context: EmitterVisitorContext} {
|
const converter = new _TsEmitterVisitor();
|
||||||
const converter = new _TsEmitterVisitor(referenceFilter);
|
|
||||||
|
|
||||||
const ctx = EmitterVisitorContext.createRoot();
|
const ctx = EmitterVisitorContext.createRoot();
|
||||||
|
|
||||||
|
@ -80,11 +78,10 @@ export class TypeScriptEmitter implements OutputEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
||||||
private typeExpression = 0;
|
private typeExpression = 0;
|
||||||
|
|
||||||
constructor(private referenceFilter?: ReferenceFilter) { super(false); }
|
constructor() { super(false); }
|
||||||
|
|
||||||
importsWithPrefixes = new Map<string, string>();
|
importsWithPrefixes = new Map<string, string>();
|
||||||
reexports = new Map<string, {name: string, as: string}[]>();
|
reexports = new Map<string, {name: string, as: string}[]>();
|
||||||
|
@ -380,10 +377,6 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
private _visitIdentifier(
|
private _visitIdentifier(
|
||||||
value: o.ExternalReference, typeParams: o.Type[]|null, ctx: EmitterVisitorContext): void {
|
value: o.ExternalReference, typeParams: o.Type[]|null, ctx: EmitterVisitorContext): void {
|
||||||
const {name, moduleName} = value;
|
const {name, moduleName} = value;
|
||||||
if (this.referenceFilter && this.referenceFilter(value)) {
|
|
||||||
ctx.print(null, '(null as any)');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (moduleName) {
|
if (moduleName) {
|
||||||
let prefix = this.importsWithPrefixes.get(moduleName);
|
let prefix = this.importsWithPrefixes.get(moduleName);
|
||||||
if (prefix == null) {
|
if (prefix == null) {
|
||||||
|
|
|
@ -12,8 +12,6 @@ import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
export interface MetadataProvider { getMetadata(source: ts.SourceFile): ModuleMetadata|undefined; }
|
|
||||||
|
|
||||||
let nodeModulesPath: string;
|
let nodeModulesPath: string;
|
||||||
let angularSourcePath: string;
|
let angularSourcePath: string;
|
||||||
let rootPath: string;
|
let rootPath: string;
|
||||||
|
@ -329,13 +327,12 @@ const DTS = /\.d\.ts$/;
|
||||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
||||||
|
|
||||||
export class MockAotCompilerHost implements AotCompilerHost {
|
export class MockAotCompilerHost implements AotCompilerHost {
|
||||||
|
private metadataCollector = new MetadataCollector();
|
||||||
private metadataVisible: boolean = true;
|
private metadataVisible: boolean = true;
|
||||||
private dtsAreSource: boolean = true;
|
private dtsAreSource: boolean = true;
|
||||||
private resolveModuleNameHost: ts.ModuleResolutionHost;
|
private resolveModuleNameHost: ts.ModuleResolutionHost;
|
||||||
|
|
||||||
constructor(
|
constructor(private tsHost: MockCompilerHost) {
|
||||||
private tsHost: MockCompilerHost,
|
|
||||||
private metadataProvider: MetadataProvider = new MetadataCollector()) {
|
|
||||||
this.resolveModuleNameHost = Object.create(tsHost);
|
this.resolveModuleNameHost = Object.create(tsHost);
|
||||||
this.resolveModuleNameHost.fileExists = (fileName) => {
|
this.resolveModuleNameHost.fileExists = (fileName) => {
|
||||||
fileName = stripNgResourceSuffix(fileName);
|
fileName = stripNgResourceSuffix(fileName);
|
||||||
|
@ -362,7 +359,7 @@ export class MockAotCompilerHost implements AotCompilerHost {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const sf = this.tsHost.getSourceFile(modulePath, ts.ScriptTarget.Latest);
|
const sf = this.tsHost.getSourceFile(modulePath, ts.ScriptTarget.Latest);
|
||||||
const metadata = this.metadataProvider.getMetadata(sf);
|
const metadata = this.metadataCollector.getMetadata(sf);
|
||||||
return metadata ? [metadata] : [];
|
return metadata ? [metadata] : [];
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
@ -284,9 +284,7 @@ export class Evaluator {
|
||||||
error = propertyValue;
|
error = propertyValue;
|
||||||
return true; // Stop the forEachChild.
|
return true; // Stop the forEachChild.
|
||||||
} else {
|
} else {
|
||||||
obj[<string>propertyName] = isPropertyAssignment(assignment) ?
|
obj[<string>propertyName] = propertyValue;
|
||||||
recordEntry(propertyValue, assignment.initializer) :
|
|
||||||
propertyValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue