diff --git a/modules/@angular/compiler-cli/src/static_reflector.ts b/modules/@angular/compiler-cli/src/static_reflector.ts index df1a4a17c96f0..cb9e61c32fa6f 100644 --- a/modules/@angular/compiler-cli/src/static_reflector.ts +++ b/modules/@angular/compiler-cli/src/static_reflector.ts @@ -22,6 +22,8 @@ import { InjectMetadata, } from "@angular/core"; import {ReflectorReader} from "./core_private"; + +const SUPPORTED_SCHEMA_VERSION = 1; /** * The host of the static resolver is expected to be able to provide module metadata in the form of @@ -379,8 +381,14 @@ export class StaticReflector implements ReflectorReader { let moduleMetadata = this.metadataCache.get(module); if (!moduleMetadata) { moduleMetadata = this.host.getMetadataFor(module); + if (Array.isArray(moduleMetadata)) { + moduleMetadata = (>moduleMetadata).find(element => element.version === SUPPORTED_SCHEMA_VERSION) || moduleMetadata[0]; + } if (!moduleMetadata) { - moduleMetadata = {__symbolic: "module", module: module, metadata: {}}; + moduleMetadata = {__symbolic: "module", version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}}; + } + if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) { + throw new Error(`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`); } this.metadataCache.set(module, moduleMetadata); } diff --git a/modules/@angular/compiler-cli/test/static_reflector_spec.ts b/modules/@angular/compiler-cli/test/static_reflector_spec.ts index 79bb24c1e3fca..70bc017d4b349 100644 --- a/modules/@angular/compiler-cli/test/static_reflector_spec.ts +++ b/modules/@angular/compiler-cli/test/static_reflector_spec.ts @@ -64,6 +64,11 @@ describe('StaticReflector', () => { .toEqual([[host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor')]]); }); + it('should throw and exception for unsupported metadata versions', () => { + let e = host.findDeclaration('src/version-error', 'e'); + expect(() => reflector.annotations(e)).toThrow(new Error('Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 1')); + }); + it('should get and empty annotation list for an unknown class', () => { let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass'); let annotations = reflector.annotations(UnknownClass); @@ -300,8 +305,9 @@ class MockReflectorHost implements StaticReflectorHost { getMetadataFor(moduleId: string): any { let data: {[key: string]: any} = { - '/tmp/angular2/src/common/forms/directives.d.ts': { + '/tmp/angular2/src/common/forms/directives.d.ts': [{ "__symbolic": "module", + "version": 1, "metadata": { "FORM_DIRECTIVES": [ { @@ -311,9 +317,10 @@ class MockReflectorHost implements StaticReflectorHost { } ] } - }, + }], '/tmp/angular2/src/common/directives/ng_for.d.ts': { "__symbolic": "module", + "version": 1, "metadata": { "NgFor": { "__symbolic": "class", @@ -366,15 +373,16 @@ class MockReflectorHost implements StaticReflectorHost { } }, '/tmp/angular2/src/core/linker/view_container_ref.d.ts': - {"metadata": {"ViewContainerRef": {"__symbolic": "class"}}}, + {version: 1, "metadata": {"ViewContainerRef": {"__symbolic": "class"}}}, '/tmp/angular2/src/core/linker/template_ref.d.ts': - {"module": "./template_ref", "metadata": {"TemplateRef": {"__symbolic": "class"}}}, + {version: 1, "module": "./template_ref", "metadata": {"TemplateRef": {"__symbolic": "class"}}}, '/tmp/angular2/src/core/change_detection/differs/iterable_differs.d.ts': - {"metadata": {"IterableDiffers": {"__symbolic": "class"}}}, + {version: 1, "metadata": {"IterableDiffers": {"__symbolic": "class"}}}, '/tmp/angular2/src/core/change_detection/change_detector_ref.d.ts': - {"metadata": {"ChangeDetectorRef": {"__symbolic": "class"}}}, + {version: 1, "metadata": {"ChangeDetectorRef": {"__symbolic": "class"}}}, '/tmp/src/app/hero-detail.component.d.ts': { "__symbolic": "module", + "version": 1, "metadata": { "HeroDetailComponent": { "__symbolic": "class", @@ -422,7 +430,8 @@ class MockReflectorHost implements StaticReflectorHost { } } }, - '/src/extern.d.ts': {"__symbolic": "module", metadata: {s: "s"}} + '/src/extern.d.ts': {"__symbolic": "module", "version": 1, metadata: {s: "s"}}, + '/tmp/src/version-error.d.ts': {"__symbolic": "module", "version": 100, metadata: {e: "s"}}, }; return data[moduleId]; } diff --git a/tools/@angular/tsc-wrapped/src/collector.ts b/tools/@angular/tsc-wrapped/src/collector.ts index a74c52e75667f..6ca076c0847ed 100644 --- a/tools/@angular/tsc-wrapped/src/collector.ts +++ b/tools/@angular/tsc-wrapped/src/collector.ts @@ -1,7 +1,7 @@ import * as ts from 'typescript'; import {Evaluator, ImportMetadata, ImportSpecifierMetadata, isPrimitive} from './evaluator'; -import {ClassMetadata, ConstructorMetadata, ModuleMetadata, MemberMetadata, MetadataError, MetadataMap, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, isMetadataError, isMetadataSymbolicReferenceExpression,} from './schema'; +import {ClassMetadata, ConstructorMetadata, ModuleMetadata, MemberMetadata, MetadataError, MetadataMap, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, isMetadataError, isMetadataSymbolicReferenceExpression, VERSION} from './schema'; import {Symbols} from './symbols'; /** @@ -207,6 +207,6 @@ export class MetadataCollector { } }); - return metadata && {__symbolic: 'module', metadata}; + return metadata && {__symbolic: 'module', version: VERSION, metadata}; } } diff --git a/tools/@angular/tsc-wrapped/src/schema.ts b/tools/@angular/tsc-wrapped/src/schema.ts index a40a7ebbb3724..5aa008a9a2957 100644 --- a/tools/@angular/tsc-wrapped/src/schema.ts +++ b/tools/@angular/tsc-wrapped/src/schema.ts @@ -1,5 +1,17 @@ +// Metadata Schema + +// If you make a backwards incompatible change to the schema, increment the VERSION number. + +// If you make a backwards compatible change to the metadata (such as adding an option field) then +// leave VERSION the same. If possible, as many versions of the metadata that can represent the +// semantics of the file in an array. For example, when generating a version 2 file, if version 1 +// can accurately represent the metadata, generate both version 1 and version 2 in an array. + +export const VERSION = 1; + export interface ModuleMetadata { __symbolic: 'module'; + version: number; metadata: {[name: string]: (ClassMetadata | MetadataValue)}; } export function isModuleMetadata(value: any): value is ModuleMetadata {