build: produce metadata bundles for @angular modules (#14509)

Closes #14509
This commit is contained in:
Chuck Jazdzewski
2017-02-15 13:30:40 -08:00
committed by Igor Minar
parent 3b896709a9
commit 724ca373e7
35 changed files with 348 additions and 165 deletions

View File

@ -14,15 +14,7 @@ import {syntaxError} from '../util';
import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
const ANGULAR_IMPORT_LOCATIONS = {
coreDecorators: '@angular/core/src/metadata',
diDecorators: '@angular/core/src/di/metadata',
diMetadata: '@angular/core/src/di/metadata',
diInjectionToken: '@angular/core/src/di/injection_token',
diOpaqueToken: '@angular/core/src/di/injection_token',
animationMetadata: '@angular/core/src/animation/metadata',
provider: '@angular/core/src/di/provider'
};
const ANGULAR_CORE = '@angular/core';
const HIDDEN_KEY = /^\$.*\$$/;
@ -241,56 +233,53 @@ export class StaticReflector implements ReflectorReader {
}
private initializeConversionMap(): void {
const {coreDecorators, diDecorators, diMetadata, diInjectionToken,
diOpaqueToken, animationMetadata, provider} = ANGULAR_IMPORT_LOCATIONS;
this.injectionToken = this.findDeclaration(diInjectionToken, 'InjectionToken');
this.opaqueToken = this.findDeclaration(diInjectionToken, 'OpaqueToken');
this.injectionToken = this.findDeclaration(ANGULAR_CORE, 'InjectionToken');
this.opaqueToken = this.findDeclaration(ANGULAR_CORE, 'OpaqueToken');
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Host'), Host);
this._registerDecoratorOrConstructor(
this.findDeclaration(diDecorators, 'Injectable'), Injectable);
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self);
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject);
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional);
this.findDeclaration(ANGULAR_CORE, 'Injectable'), Injectable);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Self'), Self);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'SkipSelf'), SkipSelf);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Inject'), Inject);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Optional'), Optional);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'Attribute'), Attribute);
this.findDeclaration(ANGULAR_CORE, 'Attribute'), Attribute);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
this.findDeclaration(ANGULAR_CORE, 'ContentChild'), ContentChild);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
this.findDeclaration(ANGULAR_CORE, 'ContentChildren'), ContentChildren);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
this.findDeclaration(ANGULAR_CORE, 'ViewChild'), ViewChild);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input);
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output);
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe);
this.findDeclaration(ANGULAR_CORE, 'ViewChildren'), ViewChildren);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Input'), Input);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Output'), Output);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Pipe'), Pipe);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
this.findDeclaration(ANGULAR_CORE, 'HostBinding'), HostBinding);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'HostListener'), HostListener);
this.findDeclaration(ANGULAR_CORE, 'HostListener'), HostListener);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'Directive'), Directive);
this.findDeclaration(ANGULAR_CORE, 'Directive'), Directive);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'Component'), Component);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'NgModule'), NgModule);
this.findDeclaration(ANGULAR_CORE, 'Component'), Component);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'NgModule'), NgModule);
// Note: Some metadata classes can be used directly with Provider.deps.
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host);
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self);
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Host'), Host);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Self'), Self);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'SkipSelf'), SkipSelf);
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Optional'), Optional);
this._registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger);
this._registerFunction(this.findDeclaration(animationMetadata, 'state'), state);
this._registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition);
this._registerFunction(this.findDeclaration(animationMetadata, 'style'), style);
this._registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate);
this._registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes);
this._registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence);
this._registerFunction(this.findDeclaration(animationMetadata, 'group'), group);
this._registerFunction(this.findDeclaration(ANGULAR_CORE, 'trigger'), trigger);
this._registerFunction(this.findDeclaration(ANGULAR_CORE, 'state'), state);
this._registerFunction(this.findDeclaration(ANGULAR_CORE, 'transition'), transition);
this._registerFunction(this.findDeclaration(ANGULAR_CORE, 'style'), style);
this._registerFunction(this.findDeclaration(ANGULAR_CORE, 'animate'), animate);
this._registerFunction(this.findDeclaration(ANGULAR_CORE, 'keyframes'), keyframes);
this._registerFunction(this.findDeclaration(ANGULAR_CORE, 'sequence'), sequence);
this._registerFunction(this.findDeclaration(ANGULAR_CORE, 'group'), group);
}
/**

View File

@ -202,9 +202,16 @@ export class StaticSymbolResolver {
new Set<string>(Object.keys(metadata['metadata']).map(unescapeIdentifier));
Object.keys(metadata['metadata']).forEach((metadataKey) => {
const symbolMeta = metadata['metadata'][metadataKey];
resolvedSymbols.push(this.createResolvedSymbol(
this.getStaticSymbol(filePath, unescapeIdentifier(metadataKey)), topLevelSymbolNames,
symbolMeta));
const name = unescapeIdentifier(metadataKey);
const canonicalSymbol = this.getStaticSymbol(filePath, name);
if (metadata['importAs']) {
// Index bundle indexes should use the importAs module name instead of a reference
// to the .d.ts file directly.
const importSymbol = this.getStaticSymbol(metadata['importAs'], name);
this.recordImportAs(canonicalSymbol, importSymbol);
}
resolvedSymbols.push(
this.createResolvedSymbol(canonicalSymbol, topLevelSymbolNames, symbolMeta));
});
}
@ -372,7 +379,7 @@ export class StaticSymbolResolver {
return this.host.moduleNameToFileName(module, containingFile);
} catch (e) {
console.error(`Could not resolve module '${module}' relative to file ${containingFile}`);
this.reportError(new e, null, containingFile);
this.reportError(e, null, containingFile);
}
}
}

View File

@ -9,15 +9,18 @@
import {AotCompiler, AotCompilerHost, createAotCompiler} from '@angular/compiler';
import {RenderComponentType} from '@angular/core';
import {async} from '@angular/core/testing';
import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped';
import * as path from 'path';
import * as ts from 'typescript';
import {ReflectionCapabilities, reflector} from './private_import_core';
import {EmittingCompilerHost, MockAotCompilerHost, MockCompilerHost, MockData, settings} from './test_util';
import {EmittingCompilerHost, MockAotCompilerHost, MockCompilerHost, MockData, MockMetadataBundlerHost, settings} from './test_util';
const DTS = /\.d\.ts$/;
const minCoreIndex = `
export * from './src/application_module';
export * from './src/change_detection';
export * from './src/metadata';
export * from './src/di/metadata';
export * from './src/di/injector';
@ -28,7 +31,7 @@ const minCoreIndex = `
export * from './src/codegen_private_exports';
`;
describe('compiler', () => {
describe('compiler (unbundled Angular)', () => {
let angularFiles: Map<string, string>;
beforeAll(() => {
@ -55,17 +58,64 @@ describe('compiler', () => {
it('should compile',
async(() => compile(host, aotHost, expectNoDiagnostics).then(generatedFiles => {
expect(generatedFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
.not.toBeUndefined();
.toBeDefined();
expect(generatedFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl)))
.not.toBeUndefined();
.toBeDefined();
})));
it('should compile using summaries',
async(() => summaryCompile(host, aotHost).then(generatedFiles => {
expect(generatedFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
.not.toBeUndefined();
.toBeDefined();
expect(generatedFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl)))
.not.toBeUndefined();
.toBeDefined();
})));
});
});
describe('compiler (bundled Angular)', () => {
let angularFiles: Map<string, string>;
beforeAll(() => {
const emittingHost = new EmittingCompilerHost(['@angular/core/index'], {emitMetadata: false});
// Create the metadata bundled
const indexModule = emittingHost.effectiveName('@angular/core/index');
const bundler = new MetadataBundler(
indexModule, '@angular/core', new MockMetadataBundlerHost(emittingHost));
const bundle = bundler.getMetadataBundle();
const metadata = JSON.stringify(bundle.metadata, null, ' ');
const bundleIndexSource = privateEntriesToIndex('./index', bundle.privates);
emittingHost.override('@angular/core/bundle_index.ts', bundleIndexSource);
emittingHost.addWrittenFile(
'@angular/core/package.json', JSON.stringify({typings: 'bundle_index.d.ts'}));
emittingHost.addWrittenFile('@angular/core/bundle_index.metadata.json', metadata);
// Emit the sources
const bundleIndexName = emittingHost.effectiveName('@angular/core/bundle_index.ts');
const emittingProgram = ts.createProgram([bundleIndexName], settings, emittingHost);
emittingProgram.emit();
angularFiles = emittingHost.written;
});
describe('Quickstart', () => {
let host: MockCompilerHost;
let aotHost: MockAotCompilerHost;
beforeEach(() => {
host = new MockCompilerHost(QUICKSTART, FILES, angularFiles);
aotHost = new MockAotCompilerHost(host);
});
// Restore reflector since AoT compiler will update it with a new static reflector
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
it('should compile',
async(() => compile(host, aotHost, expectNoDiagnostics).then(generatedFiles => {
expect(generatedFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
.toBeDefined();
expect(generatedFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl)))
.toBeDefined();
})));
});
});

View File

@ -44,14 +44,10 @@ describe('StaticReflector', () => {
it('should get constructor for NgFor', () => {
const NgFor = reflector.findDeclaration('@angular/common/src/directives/ng_for', 'NgFor');
const ViewContainerRef = reflector.findDeclaration(
'@angular/core/src/linker/view_container_ref', 'ViewContainerRef');
const TemplateRef =
reflector.findDeclaration('@angular/core/src/linker/template_ref', 'TemplateRef');
const IterableDiffers = reflector.findDeclaration(
'@angular/core/src/change_detection/differs/iterable_differs', 'IterableDiffers');
const ChangeDetectorRef = reflector.findDeclaration(
'@angular/core/src/change_detection/change_detector_ref', 'ChangeDetectorRef');
const ViewContainerRef = reflector.findDeclaration('@angular/core', 'ViewContainerRef');
const TemplateRef = reflector.findDeclaration('@angular/core', 'TemplateRef');
const IterableDiffers = reflector.findDeclaration('@angular/core', 'IterableDiffers');
const ChangeDetectorRef = reflector.findDeclaration('@angular/core', 'ChangeDetectorRef');
const parameters = reflector.parameters(NgFor);
expect(parameters).toEqual([
@ -732,7 +728,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression': {
'__symbolic': 'reference',
'name': 'Directive',
'module': '@angular/core/src/metadata'
'module': '@angular/core'
},
'arguments': [
{
@ -749,22 +745,22 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'parameters': [
{
'__symbolic': 'reference',
'module': '@angular/core/src/linker/view_container_ref',
'module': '@angular/core',
'name': 'ViewContainerRef'
},
{
'__symbolic': 'reference',
'module': '@angular/core/src/linker/template_ref',
'module': '@angular/core',
'name': 'TemplateRef'
},
{
'__symbolic': 'reference',
'module': '@angular/core/src/change_detection/differs/iterable_differs',
'module': '@angular/core',
'name': 'IterableDiffers'
},
{
'__symbolic': 'reference',
'module': '@angular/core/src/change_detection/change_detector_ref',
'module': '@angular/core',
'name': 'ChangeDetectorRef'
}
]
@ -794,7 +790,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression': {
'__symbolic': 'reference',
'name': 'Component',
'module': '@angular/core/src/metadata'
'module': '@angular/core'
},
'arguments': [
{
@ -806,7 +802,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression': {
'__symbolic': 'reference',
'name': 'trigger',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments': [
'myAnimation',
@ -814,7 +810,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression': {
'__symbolic': 'reference',
'name': 'state',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments': [
'state1',
@ -822,7 +818,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression': {
'__symbolic': 'reference',
'name': 'style',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments': [
{ 'background':'white' }
@ -834,7 +830,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression': {
'__symbolic':'reference',
'name':'transition',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments': [
'* => *',
@ -843,20 +839,20 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression':{
'__symbolic':'reference',
'name':'sequence',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments':[[{ '__symbolic': 'call',
'expression': {
'__symbolic':'reference',
'name':'group',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments':[[{
'__symbolic': 'call',
'expression': {
'__symbolic':'reference',
'name':'animate',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments':[
'1s 0.5s',
@ -864,13 +860,13 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression': {
'__symbolic':'reference',
'name':'keyframes',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments':[[{ '__symbolic': 'call',
'expression': {
'__symbolic':'reference',
'name':'style',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments':[ { 'background': 'blue'} ]
}, {
@ -878,7 +874,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression': {
'__symbolic':'reference',
'name':'style',
'module': '@angular/core/src/animation/metadata'
'module': '@angular/core'
},
'arguments':[ { 'background': 'red'} ]
}]]
@ -904,7 +900,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'expression': {
'__symbolic': 'reference',
'name': 'Input',
'module': '@angular/core/src/metadata'
'module': '@angular/core'
}
}
]
@ -918,7 +914,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
'__symbolic': 'call',
'expression': {
'__symbolic': 'reference',
'module': '@angular/core/src/metadata',
'module': '@angular/core',
'name': 'HostListener'
},
'arguments': [
@ -948,7 +944,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
expression: {
__symbolic: 'reference',
name: 'Component',
module: '@angular/core/src/metadata'
module: '@angular/core'
},
arguments: [
{
@ -1149,7 +1145,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
`,
'/tmp/src/invalid-calls.ts': `
import {someFunction} from './nvalid-calll-definitions.ts';
import {Component} from '@angular/core/src/metadata';
import {Component} from '@angular/core';
import {NgIf} from '@angular/common';
@Component({
@ -1193,7 +1189,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
}
`,
'/tmp/src/static-method-call.ts': `
import {Component} from '@angular/core/src/metadata';
import {Component} from '@angular/core';
import {MyModule} from './static-method';
@Component({
@ -1225,7 +1221,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
}
`,
'/tmp/src/static-field-reference.ts': `
import {Component} from '@angular/core/src/metadata';
import {Component} from '@angular/core';
import {MyModule} from './static-field';
@Component({
@ -1239,7 +1235,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
}
`,
'/tmp/src/static-method-ref.ts': `
import {Component} from '@angular/core/src/metadata';
import {Component} from '@angular/core';
import {ClassWithStatics} from './static-method-def';
@Component({
@ -1250,7 +1246,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
}
`,
'/tmp/src/invalid-metadata.ts': `
import {Component} from '@angular/core/src/metadata';
import {Component} from '@angular/core';
@Component({
providers: [ { provider: 'a', useValue: (() => 1)() }]
@ -1259,8 +1255,8 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
`,
'/tmp/src/forward-ref.ts': `
import {forwardRef} from '@angular/core';
import {Component} from '@angular/core/src/metadata';
import {Inject} from '@angular/core/src/di/metadata';
import {Component} from '@angular/core';
import {Inject} from '@angular/core';
@Component({})
export class Forward {
constructor(@Inject(forwardRef(() => Dep)) d: Dep) {}

View File

@ -7,7 +7,7 @@
*/
import {AotCompilerHost} from '@angular/compiler';
import {MetadataCollector} from '@angular/tsc-wrapped';
import {MetadataBundlerHost, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
@ -216,12 +216,15 @@ export class MockCompilerHost implements ts.CompilerHost {
}
const effectiveName = this.getEffectiveName(fileName);
if (effectiveName == fileName) {
return open(fileName, this.data) != null;
let result = open(fileName, this.data) != null;
return result;
} else {
if (fileName.match(rxjs)) {
return fs.existsSync(effectiveName);
let result = fs.existsSync(effectiveName);
return result;
}
return this.angular.has(effectiveName);
let result = this.angular.has(effectiveName);
return result;
}
}
@ -389,6 +392,17 @@ export class MockAotCompilerHost implements AotCompilerHost {
}
}
export class MockMetadataBundlerHost implements MetadataBundlerHost {
private collector = new MetadataCollector();
constructor(private host: ts.CompilerHost) {}
getMetadataFor(moduleName: string): ModuleMetadata {
const source = this.host.getSourceFile(moduleName + '.ts', ts.ScriptTarget.Latest);
return this.collector.getMetadata(source);
}
}
function find(fileName: string, data: MockData): MockData|undefined {
let names = fileName.split('/');
if (names.length && !names[0].length) names.shift();