Skip to content

Commit

Permalink
refactor(compiler): bump metadata version to 4 (#19338)
Browse files Browse the repository at this point in the history
Also adds auto upgrade from lower version based
on the .d.ts file (e.g. from version 3 to 4).

This is needed as we are now also capturing type aliases
in metadata files (and we rely on this),
see 6e3498c.
  • Loading branch information
chuckjaz authored and vicb committed Sep 26, 2017
1 parent 86ffacf commit a75040d
Show file tree
Hide file tree
Showing 13 changed files with 152 additions and 87 deletions.
1 change: 1 addition & 0 deletions packages/compiler-cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export {getClassMembersFromDeclaration, getPipesTable, getSymbolQuery} from './s
export {Extractor} from './src/extractor';
export {VERSION} from './src/version';

export * from './src/metadata';
export * from './src/transformers/api';
export * from './src/transformers/entry_points';

Expand Down
67 changes: 41 additions & 26 deletions packages/compiler-cli/src/compiler_host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';

import {CollectorOptions, MetadataCollector, ModuleMetadata} from './metadata/index';
import {CollectorOptions, METADATA_VERSION, MetadataCollector, ModuleMetadata} from './metadata/index';
import {CompilerOptions} from './transformers/api';

const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
Expand Down Expand Up @@ -76,9 +76,9 @@ export abstract class BaseAotCompilerHost<C extends BaseAotCompilerHostContext>
let metadatas = this.readMetadata(filePath);
if (!metadatas) {
// If there is a .d.ts file but no metadata file we need to produce a
// v3 metadata from the .d.ts file as v3 includes the exports we need
// to resolve symbols.
metadatas = [this.upgradeVersion1Metadata(
// metadata from the .d.ts file as metadata files capture reexports
// (starting with v3).
metadatas = [this.upgradeMetadataWithDtsData(
{'__symbolic': 'module', 'version': 1, 'metadata': {}}, filePath)];
}
return metadatas;
Expand All @@ -104,10 +104,11 @@ export abstract class BaseAotCompilerHost<C extends BaseAotCompilerHostContext>
const metadatas: ModuleMetadata[] = metadataOrMetadatas ?
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
[];
const v1Metadata = metadatas.find(m => m.version === 1);
let v3Metadata = metadatas.find(m => m.version === 3);
if (!v3Metadata && v1Metadata) {
metadatas.push(this.upgradeVersion1Metadata(v1Metadata, dtsFilePath));
if (metadatas.length) {
let maxMetadata = metadatas.reduce((p, c) => p.version > c.version ? p : c);
if (maxMetadata.version < METADATA_VERSION) {
metadatas.push(this.upgradeMetadataWithDtsData(maxMetadata, dtsFilePath));
}
}
this.resolverCache.set(dtsFilePath, metadatas);
return metadatas;
Expand All @@ -117,30 +118,44 @@ export abstract class BaseAotCompilerHost<C extends BaseAotCompilerHostContext>
}
}

private upgradeVersion1Metadata(v1Metadata: ModuleMetadata, dtsFilePath: string): ModuleMetadata {
// patch up v1 to v3 by merging the metadata with metadata collected from the d.ts file
// as the only difference between the versions is whether all exports are contained in
// the metadata and the `extends` clause.
let v3Metadata: ModuleMetadata = {'__symbolic': 'module', 'version': 3, 'metadata': {}};
if (v1Metadata.exports) {
v3Metadata.exports = v1Metadata.exports;
private upgradeMetadataWithDtsData(oldMetadata: ModuleMetadata, dtsFilePath: string):
ModuleMetadata {
// patch v1 to v3 by adding exports and the `extends` clause.
// patch v3 to v4 by adding `interface` symbols for TypeAlias
let newMetadata: ModuleMetadata = {
'__symbolic': 'module',
'version': METADATA_VERSION,
'metadata': {...oldMetadata.metadata},
};
if (oldMetadata.exports) {
newMetadata.exports = oldMetadata.exports;
}
for (let prop in v1Metadata.metadata) {
v3Metadata.metadata[prop] = v1Metadata.metadata[prop];
if (oldMetadata.importAs) {
newMetadata.importAs = oldMetadata.importAs;
}

const exports = this.getMetadataForSourceFile(dtsFilePath);
if (exports) {
for (let prop in exports.metadata) {
if (!v3Metadata.metadata[prop]) {
v3Metadata.metadata[prop] = exports.metadata[prop];
if (oldMetadata.origins) {
newMetadata.origins = oldMetadata.origins;
}
const dtsMetadata = this.getMetadataForSourceFile(dtsFilePath);
if (dtsMetadata) {
for (let prop in dtsMetadata.metadata) {
if (!newMetadata.metadata[prop]) {
newMetadata.metadata[prop] = dtsMetadata.metadata[prop];
}
}
if (exports.exports) {
v3Metadata.exports = exports.exports;

// Only copy exports from exports from metadata prior to version 3.
// Starting with version 3 the collector began collecting exports and
// this should be redundant. Also, with bundler will rewrite the exports
// which will hoist the exports from modules referenced indirectly causing
// the imports to be different than the .d.ts files and using the .d.ts file
// exports would cause the StaticSymbolResolver to redirect symbols to the
// incorrect location.
if ((!oldMetadata.version || oldMetadata.version < 3) && dtsMetadata.exports) {
newMetadata.exports = dtsMetadata.exports;
}
}
return v3Metadata;
return newMetadata;
}

loadResource(filePath: string): Promise<string>|string {
Expand Down
4 changes: 2 additions & 2 deletions packages/compiler-cli/src/metadata/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as path from 'path';
import * as ts from 'typescript';

import {MetadataCollector} from '../metadata/collector';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicExpression, isMethodMetadata} from '../metadata/schema';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, METADATA_VERSION, MemberMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicExpression, isMethodMetadata} from '../metadata/schema';



Expand Down Expand Up @@ -114,7 +114,7 @@ export class MetadataBundler {
return {
metadata: {
__symbolic: 'module',
version: VERSION,
version: METADATA_VERSION,
exports: exports.length ? exports : undefined, metadata, origins,
importAs: this.importAs !
},
Expand Down
4 changes: 2 additions & 2 deletions packages/compiler-cli/src/metadata/collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import * as ts from 'typescript';

import {Evaluator, errorSymbol} from './evaluator';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, METADATA_VERSION, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema';
import {Symbols} from './symbols';

const isStatic = (node: ts.Node) => ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Static;
Expand Down Expand Up @@ -549,7 +549,7 @@ export class MetadataCollector {
}
const result: ModuleMetadata = {
__symbolic: 'module',
version: this.options.version || VERSION, metadata
version: this.options.version || METADATA_VERSION, metadata
};
if (exports) result.exports = exports;
return result;
Expand Down
11 changes: 6 additions & 5 deletions packages/compiler-cli/src/metadata/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@

// Metadata Schema

// If you make a backwards incompatible change to the schema, increment the VERSION number.
// If you make a backwards incompatible change to the schema, increment the METADTA_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.
// leave METADATA_VERSION the same. If possible, supply 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 = 3;
export const METADATA_VERSION = 4;

export type MetadataEntry = ClassMetadata | InterfaceMetadata | FunctionMetadata | MetadataValue;

Expand Down
58 changes: 48 additions & 10 deletions packages/compiler-cli/test/aot_host_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ModuleMetadata} from '@angular/compiler-cli/src/metadata/index';
import {METADATA_VERSION, ModuleMetadata} from '@angular/compiler-cli';
import * as ts from 'typescript';

import {CompilerHost} from '../src/compiler_host';
Expand Down Expand Up @@ -163,7 +163,7 @@ describe('CompilerHost', () => {

it('should be able to read a metadata file', () => {
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts')).toEqual([
{__symbolic: 'module', version: 3, metadata: {foo: {__symbolic: 'class'}}}
{__symbolic: 'module', version: METADATA_VERSION, metadata: {foo: {__symbolic: 'class'}}}
]);
});

Expand All @@ -181,13 +181,14 @@ describe('CompilerHost', () => {
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')).toBeUndefined();
});

it('should add missing v3 metadata from v1 metadata and .d.ts files', () => {
it(`should add missing v${METADATA_VERSION} metadata from v1 metadata and .d.ts files`, () => {
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1.d.ts')).toEqual([
{__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, {
__symbolic: 'module',
version: 3,
version: METADATA_VERSION,
metadata: {
foo: {__symbolic: 'class'},
aType: {__symbolic: 'interface'},
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}},
ReExport: {__symbolic: 'reference', module: './lib/utils2', name: 'ReExport'},
Expand All @@ -197,17 +198,37 @@ describe('CompilerHost', () => {
]);
});

it('should upgrade a missing metadata file into v3', () => {
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1_empty.d.ts')).toEqual([
{__symbolic: 'module', version: 3, metadata: {}, exports: [{from: './lib/utils'}]}
it(`should upgrade a missing metadata file into v${METADATA_VERSION}`, () => {
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1_empty.d.ts')).toEqual([{
__symbolic: 'module',
version: METADATA_VERSION,
metadata: {},
exports: [{from: './lib/utils'}]
}]);
});

it(`should upgrade v3 metadata into v${METADATA_VERSION}`, () => {
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v3.d.ts')).toEqual([
{__symbolic: 'module', version: 3, metadata: {foo: {__symbolic: 'class'}}}, {
__symbolic: 'module',
version: METADATA_VERSION,
metadata: {
foo: {__symbolic: 'class'},
aType: {__symbolic: 'interface'},
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}},
ReExport: {__symbolic: 'reference', module: './lib/utils2', name: 'ReExport'},
}
// Note: exports is missing because it was elided in the original.
}
]);
});
});

const dummyModule = 'export let foo: any[];';
const dummyMetadata: ModuleMetadata = {
__symbolic: 'module',
version: 3,
version: METADATA_VERSION,
metadata:
{foo: {__symbolic: 'error', message: 'Variable not initialized', line: 0, character: 11}}
};
Expand All @@ -230,7 +251,7 @@ const FILES: Entry = {
'@angular': {
'core.d.ts': dummyModule,
'core.metadata.json':
`{"__symbolic":"module", "version": 3, "metadata": {"foo": {"__symbolic": "class"}}}`,
`{"__symbolic":"module", "version": ${METADATA_VERSION}, "metadata": {"foo": {"__symbolic": "class"}}}`,
'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
'unused.d.ts': dummyModule,
'empty.d.ts': 'export declare var a: string;',
Expand All @@ -244,6 +265,8 @@ const FILES: Entry = {
export {Export} from './lib/utils2';
export type aType = number;
export declare class Bar {
ngOnInit() {}
}
Expand All @@ -253,7 +276,22 @@ const FILES: Entry = {
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
'v1_empty.d.ts': `
export * from './lib/utils';
`
`,
'v3.d.ts': `
import {ReExport} from './lib/utils2';
export {ReExport};
export {Export} from './lib/utils2';
export type aType = number;
export declare class Bar {
ngOnInit() {}
}
export declare class BarChild extends Bar {}
`,
'v3.metadata.json':
`{"__symbolic":"module", "version": 3, "metadata": {"foo": {"__symbolic": "class"}}}`,
}
}
}
Expand Down
30 changes: 18 additions & 12 deletions packages/compiler-cli/test/metadata/collector_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import * as ts from 'typescript';

import {MetadataCollector} from '../../src/metadata/collector';
import {ClassMetadata, ConstructorMetadata, MetadataEntry, ModuleMetadata, isClassMetadata, isMetadataGlobalReferenceExpression} from '../../src/metadata/schema';
import {ClassMetadata, ConstructorMetadata, METADATA_VERSION, MetadataEntry, ModuleMetadata, isClassMetadata, isMetadataGlobalReferenceExpression} from '../../src/metadata/schema';

import {Directory, Host, expectValidSources} from './typescript.mocks';

Expand Down Expand Up @@ -73,7 +73,7 @@ describe('Collector', () => {
const metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
version: METADATA_VERSION,
metadata: {
DeclaredClass: {__symbolic: 'class'},
declaredFn: {__symbolic: 'function'},
Expand All @@ -84,23 +84,29 @@ describe('Collector', () => {
it('should return an interface reference for types', () => {
const sourceFile = program.getSourceFile('/exported-type.ts');
const metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual(
{__symbolic: 'module', version: 3, metadata: {SomeType: {__symbolic: 'interface'}}});
expect(metadata).toEqual({
__symbolic: 'module',
version: METADATA_VERSION,
metadata: {SomeType: {__symbolic: 'interface'}}
});
});

it('should return an interface reference for interfaces', () => {
const sourceFile = program.getSourceFile('app/hero.ts');
const metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual(
{__symbolic: 'module', version: 3, metadata: {Hero: {__symbolic: 'interface'}}});
expect(metadata).toEqual({
__symbolic: 'module',
version: METADATA_VERSION,
metadata: {Hero: {__symbolic: 'interface'}}
});
});

it('should be able to collect a simple component\'s metadata', () => {
const sourceFile = program.getSourceFile('app/hero-detail.component.ts');
const metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
version: METADATA_VERSION,
metadata: {
HeroDetailComponent: {
__symbolic: 'class',
Expand Down Expand Up @@ -141,7 +147,7 @@ describe('Collector', () => {
const metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
version: METADATA_VERSION,
metadata: {
AppComponent: {
__symbolic: 'class',
Expand Down Expand Up @@ -195,7 +201,7 @@ describe('Collector', () => {
const metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
version: METADATA_VERSION,
metadata: {
HEROES: [
{'id': 11, 'name': 'Mr. Nice', '$quoted$': ['id', 'name']},
Expand Down Expand Up @@ -274,7 +280,7 @@ describe('Collector', () => {
const metadata = collector.getMetadata(unsupported1);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
version: METADATA_VERSION,
metadata: {
a: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 16},
b: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 19},
Expand Down Expand Up @@ -316,7 +322,7 @@ describe('Collector', () => {
const metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
version: METADATA_VERSION,
metadata: {
SimpleClass: {__symbolic: 'class'},
AbstractClass: {__symbolic: 'class'},
Expand All @@ -330,7 +336,7 @@ describe('Collector', () => {
const metadata = collector.getMetadata(exportedFunctions);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
version: METADATA_VERSION,
metadata: {
one: {
__symbolic: 'function',
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/src/aot/static_symbol_resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface StaticSymbolResolverHost {
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string;
}

const SUPPORTED_SCHEMA_VERSION = 3;
const SUPPORTED_SCHEMA_VERSION = 4;

/**
* This class is responsible for loading metadata per symbol,
Expand Down
Loading

0 comments on commit a75040d

Please sign in to comment.