refactor(tsc-wrapped): collect all exported functions and classes and bump metadata version from 1 to 2
This is needed to resolve symbols without `.d.ts` files. This bumps the version of the metadata from 1 to 2. This adds logic into `ng_host.ts` to automatically upgrade version 1 to version 2 metadata by adding the exported symbols from the `.d.ts` file.
This commit is contained in:
parent
bccf0e69dc
commit
dddbb1c1cb
|
@ -31,6 +31,8 @@ export class NgHost implements AotCompilerHost {
|
||||||
private isGenDirChildOfRootDir: boolean;
|
private isGenDirChildOfRootDir: boolean;
|
||||||
protected basePath: string;
|
protected basePath: string;
|
||||||
private genDir: string;
|
private genDir: string;
|
||||||
|
private resolverCache = new Map<string, ModuleMetadata[]>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected program: ts.Program, protected compilerHost: ts.CompilerHost,
|
protected program: ts.Program, protected compilerHost: ts.CompilerHost,
|
||||||
protected options: AngularCompilerOptions, context?: NgHostContext) {
|
protected options: AngularCompilerOptions, context?: NgHostContext) {
|
||||||
|
@ -138,9 +140,18 @@ export class NgHost implements AotCompilerHost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolverCache = new Map<string, ModuleMetadata>();
|
protected getSourceFile(filePath: string): ts.SourceFile {
|
||||||
|
const sf = this.program.getSourceFile(filePath);
|
||||||
|
if (!sf) {
|
||||||
|
if (this.context.fileExists(filePath)) {
|
||||||
|
const sourceText = this.context.readFile(filePath);
|
||||||
|
return ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true);
|
||||||
|
}
|
||||||
|
throw new Error(`Source file ${filePath} not present in program.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getMetadataFor(filePath: string): ModuleMetadata {
|
getMetadataFor(filePath: string): ModuleMetadata[] {
|
||||||
if (!this.context.fileExists(filePath)) {
|
if (!this.context.fileExists(filePath)) {
|
||||||
// If the file doesn't exists then we cannot return metadata for the file.
|
// If the file doesn't exists then we cannot return metadata for the file.
|
||||||
// This will occur if the user refernced a declared module for which no file
|
// This will occur if the user refernced a declared module for which no file
|
||||||
|
@ -150,27 +161,51 @@ export class NgHost implements AotCompilerHost {
|
||||||
if (DTS.test(filePath)) {
|
if (DTS.test(filePath)) {
|
||||||
const metadataPath = filePath.replace(DTS, '.metadata.json');
|
const metadataPath = filePath.replace(DTS, '.metadata.json');
|
||||||
if (this.context.fileExists(metadataPath)) {
|
if (this.context.fileExists(metadataPath)) {
|
||||||
const metadata = this.readMetadata(metadataPath);
|
return this.readMetadata(metadataPath, filePath);
|
||||||
return (Array.isArray(metadata) && metadata.length == 0) ? undefined : metadata;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const sf = this.program.getSourceFile(filePath);
|
const sf = this.getSourceFile(filePath);
|
||||||
if (!sf) {
|
const metadata = this.metadataCollector.getMetadata(sf);
|
||||||
if (this.context.fileExists(filePath)) {
|
return metadata ? [metadata] : [];
|
||||||
const sourceText = this.context.readFile(filePath);
|
|
||||||
return this.metadataCollector.getMetadata(
|
|
||||||
ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Source file ${filePath} not present in program.`);
|
|
||||||
}
|
|
||||||
return this.metadataCollector.getMetadata(sf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readMetadata(filePath: string) {
|
readMetadata(filePath: string, dtsFilePath: string): ModuleMetadata[] {
|
||||||
|
let metadatas = this.resolverCache.get(filePath);
|
||||||
|
if (metadatas) {
|
||||||
|
return metadatas;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return this.resolverCache.get(filePath) || JSON.parse(this.context.readFile(filePath));
|
const metadataOrMetadatas = JSON.parse(this.context.readFile(filePath));
|
||||||
|
const metadatas = metadataOrMetadatas ?
|
||||||
|
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
|
||||||
|
[];
|
||||||
|
const v1Metadata = metadatas.find(m => m['version'] === 1);
|
||||||
|
let v2Metadata = metadatas.find(m => m['version'] === 2);
|
||||||
|
if (!v2Metadata && v1Metadata) {
|
||||||
|
// patch up v1 to v2 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
|
||||||
|
v2Metadata = {'__symbolic': 'module', 'version': 2, 'metadata': {}};
|
||||||
|
if (v1Metadata.exports) {
|
||||||
|
v2Metadata.exports = v1Metadata.exports;
|
||||||
|
}
|
||||||
|
for (let prop in v1Metadata.metadata) {
|
||||||
|
v2Metadata.metadata[prop] = v1Metadata.metadata[prop];
|
||||||
|
}
|
||||||
|
const sourceText = this.context.readFile(dtsFilePath);
|
||||||
|
const exports = this.metadataCollector.getMetadata(this.getSourceFile(dtsFilePath));
|
||||||
|
if (exports) {
|
||||||
|
for (let prop in exports.metadata) {
|
||||||
|
if (!v2Metadata.metadata[prop]) {
|
||||||
|
v2Metadata.metadata[prop] = exports.metadata[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metadatas.push(v2Metadata);
|
||||||
|
}
|
||||||
|
this.resolverCache.set(filePath, metadatas);
|
||||||
|
return metadatas;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed to read JSON file ${filePath}`);
|
console.error(`Failed to read JSON file ${filePath}`);
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -178,15 +213,6 @@ export class NgHost implements AotCompilerHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
|
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
|
||||||
|
|
||||||
private getResolverMetadata(filePath: string): ModuleMetadata {
|
|
||||||
let metadata = this.resolverCache.get(filePath);
|
|
||||||
if (!metadata) {
|
|
||||||
metadata = this.getMetadataFor(filePath);
|
|
||||||
this.resolverCache.set(filePath, metadata);
|
|
||||||
}
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NodeNgHostContext implements NgHostContext {
|
export class NodeNgHostContext implements NgHostContext {
|
||||||
|
|
|
@ -116,7 +116,7 @@ export class PathMappedNgHost extends NgHost {
|
||||||
`Unable to find any resolvable import for ${importedFile} relative to ${containingFile}`);
|
`Unable to find any resolvable import for ${importedFile} relative to ${containingFile}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMetadataFor(filePath: string): ModuleMetadata {
|
getMetadataFor(filePath: string): ModuleMetadata[] {
|
||||||
for (const root of this.options.rootDirs || []) {
|
for (const root of this.options.rootDirs || []) {
|
||||||
const rootedPath = path.join(root, filePath);
|
const rootedPath = path.join(root, filePath);
|
||||||
if (!this.compilerHost.fileExists(rootedPath)) {
|
if (!this.compilerHost.fileExists(rootedPath)) {
|
||||||
|
@ -128,16 +128,13 @@ export class PathMappedNgHost extends NgHost {
|
||||||
if (DTS.test(rootedPath)) {
|
if (DTS.test(rootedPath)) {
|
||||||
const metadataPath = rootedPath.replace(DTS, '.metadata.json');
|
const metadataPath = rootedPath.replace(DTS, '.metadata.json');
|
||||||
if (this.context.fileExists(metadataPath)) {
|
if (this.context.fileExists(metadataPath)) {
|
||||||
const metadata = this.readMetadata(metadataPath);
|
return this.readMetadata(metadataPath, rootedPath);
|
||||||
return (Array.isArray(metadata) && metadata.length == 0) ? undefined : metadata;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const sf = this.program.getSourceFile(rootedPath);
|
const sf = this.getSourceFile(rootedPath);
|
||||||
if (!sf) {
|
|
||||||
throw new Error(`Source file ${rootedPath} not present in program.`);
|
|
||||||
}
|
|
||||||
sf.fileName = this.getCanonicalFileName(sf.fileName);
|
sf.fileName = this.getCanonicalFileName(sf.fileName);
|
||||||
return this.metadataCollector.getMetadata(sf);
|
const metadata = this.metadataCollector.getMetadata(sf);
|
||||||
|
return metadata ? [metadata] : [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,8 +141,9 @@ describe('NgHost', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to read a metadata file', () => {
|
it('should be able to read a metadata file', () => {
|
||||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts'))
|
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts')).toEqual([
|
||||||
.toEqual({__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}});
|
{__symbolic: 'module', version: 2, metadata: {foo: {__symbolic: 'class'}}}
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to read metadata from an otherwise unused .d.ts file ', () => {
|
it('should be able to read metadata from an otherwise unused .d.ts file ', () => {
|
||||||
|
@ -150,12 +151,22 @@ describe('NgHost', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to read empty metadata ', () => {
|
it('should be able to read empty metadata ', () => {
|
||||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts')).toBeUndefined();
|
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts')).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined for missing modules', () => {
|
it('should return undefined for missing modules', () => {
|
||||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')).toBeUndefined();
|
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add missing v2 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: 2,
|
||||||
|
metadata: {foo: {__symbolic: 'class'}, bar: {__symbolic: 'class'}}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const dummyModule = 'export let foo: any[];';
|
const dummyModule = 'export let foo: any[];';
|
||||||
|
@ -179,12 +190,17 @@ const FILES: Entry = {
|
||||||
'@angular': {
|
'@angular': {
|
||||||
'core.d.ts': dummyModule,
|
'core.d.ts': dummyModule,
|
||||||
'core.metadata.json':
|
'core.metadata.json':
|
||||||
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
`{"__symbolic":"module", "version": 2, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||||
'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
|
'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
|
||||||
'unused.d.ts': dummyModule,
|
'unused.d.ts': dummyModule,
|
||||||
'empty.d.ts': 'export declare var a: string;',
|
'empty.d.ts': 'export declare var a: string;',
|
||||||
'empty.metadata.json': '[]',
|
'empty.metadata.json': '[]',
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'metadata_versions': {
|
||||||
|
'v1.d.ts': 'export declare class bar {}',
|
||||||
|
'v1.metadata.json':
|
||||||
|
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ export interface AotCompilerHost {
|
||||||
* @param modulePath is a string identifier for a module as an absolute path.
|
* @param modulePath is a string identifier for a module as an absolute path.
|
||||||
* @returns the metadata for the given module.
|
* @returns the metadata for the given module.
|
||||||
*/
|
*/
|
||||||
getMetadataFor(modulePath: string): {[key: string]: any}|{[key: string]: any}[];
|
getMetadataFor(modulePath: string): {[key: string]: any}[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an import into a file path.
|
* Converts an import into a file path.
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {ReflectorReader} from '../private_import_core';
|
||||||
import {AotCompilerHost} from './compiler_host';
|
import {AotCompilerHost} from './compiler_host';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
|
|
||||||
const SUPPORTED_SCHEMA_VERSION = 1;
|
const SUPPORTED_SCHEMA_VERSION = 2;
|
||||||
const ANGULAR_IMPORT_LOCATIONS = {
|
const ANGULAR_IMPORT_LOCATIONS = {
|
||||||
coreDecorators: '@angular/core/src/metadata',
|
coreDecorators: '@angular/core/src/metadata',
|
||||||
diDecorators: '@angular/core/src/di/metadata',
|
diDecorators: '@angular/core/src/di/metadata',
|
||||||
|
@ -604,10 +604,15 @@ export class StaticReflector implements ReflectorReader {
|
||||||
public getModuleMetadata(module: string): {[key: string]: any} {
|
public getModuleMetadata(module: string): {[key: string]: any} {
|
||||||
let moduleMetadata = this.metadataCache.get(module);
|
let moduleMetadata = this.metadataCache.get(module);
|
||||||
if (!moduleMetadata) {
|
if (!moduleMetadata) {
|
||||||
moduleMetadata = this.host.getMetadataFor(module);
|
const moduleMetadatas = this.host.getMetadataFor(module);
|
||||||
if (Array.isArray(moduleMetadata)) {
|
if (moduleMetadatas) {
|
||||||
moduleMetadata = moduleMetadata.find(md => md['version'] === SUPPORTED_SCHEMA_VERSION) ||
|
let maxVersion = -1;
|
||||||
moduleMetadata[0];
|
moduleMetadatas.forEach((md) => {
|
||||||
|
if (md['version'] > maxVersion) {
|
||||||
|
maxVersion = md['version'];
|
||||||
|
moduleMetadata = md;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (!moduleMetadata) {
|
if (!moduleMetadata) {
|
||||||
moduleMetadata =
|
moduleMetadata =
|
||||||
|
@ -653,6 +658,7 @@ function expandedMessage(error: any): string {
|
||||||
if (error.context && error.context.name) {
|
if (error.context && error.context.name) {
|
||||||
return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
|
return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return error.message;
|
return error.message;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,10 +73,10 @@ describe('StaticReflector', () => {
|
||||||
])]);
|
])]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw and exception for unsupported metadata versions', () => {
|
it('should throw an exception for unsupported metadata versions', () => {
|
||||||
expect(() => reflector.findDeclaration('src/version-error', 'e'))
|
expect(() => reflector.findDeclaration('src/version-error', 'e'))
|
||||||
.toThrow(new Error(
|
.toThrow(new Error(
|
||||||
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 1'));
|
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 2'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get and empty annotation list for an unknown class', () => {
|
it('should get and empty annotation list for an unknown class', () => {
|
||||||
|
@ -342,13 +342,11 @@ describe('StaticReflector', () => {
|
||||||
try {
|
try {
|
||||||
const metadata = host.getMetadataFor('/tmp/src/invalid-metadata.ts');
|
const metadata = host.getMetadataFor('/tmp/src/invalid-metadata.ts');
|
||||||
expect(metadata).toBeDefined();
|
expect(metadata).toBeDefined();
|
||||||
if (!Array.isArray(metadata)) {
|
const moduleMetadata: any = metadata[0]['metadata'];
|
||||||
const moduleMetadata: any = metadata['metadata'];
|
|
||||||
expect(moduleMetadata).toBeDefined();
|
expect(moduleMetadata).toBeDefined();
|
||||||
const classData: any = moduleMetadata['InvalidMetadata'];
|
const classData: any = moduleMetadata['InvalidMetadata'];
|
||||||
expect(classData).toBeDefined();
|
expect(classData).toBeDefined();
|
||||||
simplify(new StaticSymbol('/tmp/src/invalid-metadata.ts', ''), classData.decorators[0]);
|
simplify(new StaticSymbol('/tmp/src/invalid-metadata.ts', ''), classData.decorators[0]);
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e.fileName).toBe('/tmp/src/invalid-metadata.ts');
|
expect(e.fileName).toBe('/tmp/src/invalid-metadata.ts');
|
||||||
threw = true;
|
threw = true;
|
||||||
|
@ -376,7 +374,7 @@ describe('StaticReflector', () => {
|
||||||
const metadata = reflector.getModuleMetadata('/tmp/src/custom-decorator-reference.ts');
|
const metadata = reflector.getModuleMetadata('/tmp/src/custom-decorator-reference.ts');
|
||||||
expect(metadata).toEqual({
|
expect(metadata).toEqual({
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
Foo: {
|
Foo: {
|
||||||
__symbolic: 'class',
|
__symbolic: 'class',
|
||||||
|
@ -564,7 +562,7 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
const data: {[key: string]: any} = {
|
const data: {[key: string]: any} = {
|
||||||
'/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{
|
'/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{
|
||||||
'__symbolic': 'module',
|
'__symbolic': 'module',
|
||||||
'version': 1,
|
'version': 2,
|
||||||
'metadata': {
|
'metadata': {
|
||||||
'FORM_DIRECTIVES': [
|
'FORM_DIRECTIVES': [
|
||||||
{
|
{
|
||||||
|
@ -577,7 +575,7 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
}],
|
}],
|
||||||
'/tmp/@angular/common/src/directives/ng_for.d.ts': {
|
'/tmp/@angular/common/src/directives/ng_for.d.ts': {
|
||||||
'__symbolic': 'module',
|
'__symbolic': 'module',
|
||||||
'version': 1,
|
'version': 2,
|
||||||
'metadata': {
|
'metadata': {
|
||||||
'NgFor': {
|
'NgFor': {
|
||||||
'__symbolic': 'class',
|
'__symbolic': 'class',
|
||||||
|
@ -630,16 +628,16 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'/tmp/@angular/core/src/linker/view_container_ref.d.ts':
|
'/tmp/@angular/core/src/linker/view_container_ref.d.ts':
|
||||||
{version: 1, 'metadata': {'ViewContainerRef': {'__symbolic': 'class'}}},
|
{version: 2, 'metadata': {'ViewContainerRef': {'__symbolic': 'class'}}},
|
||||||
'/tmp/@angular/core/src/linker/template_ref.d.ts':
|
'/tmp/@angular/core/src/linker/template_ref.d.ts':
|
||||||
{version: 1, 'module': './template_ref', 'metadata': {'TemplateRef': {'__symbolic': 'class'}}},
|
{version: 2, 'module': './template_ref', 'metadata': {'TemplateRef': {'__symbolic': 'class'}}},
|
||||||
'/tmp/@angular/core/src/change_detection/differs/iterable_differs.d.ts':
|
'/tmp/@angular/core/src/change_detection/differs/iterable_differs.d.ts':
|
||||||
{version: 1, 'metadata': {'IterableDiffers': {'__symbolic': 'class'}}},
|
{version: 2, 'metadata': {'IterableDiffers': {'__symbolic': 'class'}}},
|
||||||
'/tmp/@angular/core/src/change_detection/change_detector_ref.d.ts':
|
'/tmp/@angular/core/src/change_detection/change_detector_ref.d.ts':
|
||||||
{version: 1, 'metadata': {'ChangeDetectorRef': {'__symbolic': 'class'}}},
|
{version: 2, 'metadata': {'ChangeDetectorRef': {'__symbolic': 'class'}}},
|
||||||
'/tmp/src/app/hero-detail.component.d.ts': {
|
'/tmp/src/app/hero-detail.component.d.ts': {
|
||||||
'__symbolic': 'module',
|
'__symbolic': 'module',
|
||||||
'version': 1,
|
'version': 2,
|
||||||
'metadata': {
|
'metadata': {
|
||||||
'HeroDetailComponent': {
|
'HeroDetailComponent': {
|
||||||
'__symbolic': 'class',
|
'__symbolic': 'class',
|
||||||
|
@ -790,11 +788,11 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'/src/extern.d.ts': {'__symbolic': 'module', 'version': 1, metadata: {s: 's'}},
|
'/src/extern.d.ts': {'__symbolic': 'module', 'version': 2, metadata: {s: 's'}},
|
||||||
'/tmp/src/version-error.d.ts': {'__symbolic': 'module', 'version': 100, metadata: {e: 's'}},
|
'/tmp/src/version-error.d.ts': {'__symbolic': 'module', 'version': 100, metadata: {e: 's'}},
|
||||||
'/tmp/src/error-reporting.d.ts': {
|
'/tmp/src/error-reporting.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
SomeClass: {
|
SomeClass: {
|
||||||
__symbolic: 'class',
|
__symbolic: 'class',
|
||||||
|
@ -824,7 +822,7 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
},
|
},
|
||||||
'/tmp/src/error-references.d.ts': {
|
'/tmp/src/error-references.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
Link1: {
|
Link1: {
|
||||||
__symbolic: 'reference',
|
__symbolic: 'reference',
|
||||||
|
@ -846,7 +844,7 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
},
|
},
|
||||||
'/tmp/src/function-declaration.d.ts': {
|
'/tmp/src/function-declaration.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
one: {
|
one: {
|
||||||
__symbolic: 'function',
|
__symbolic: 'function',
|
||||||
|
@ -875,7 +873,7 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
},
|
},
|
||||||
'/tmp/src/function-reference.ts': {
|
'/tmp/src/function-reference.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
one: {
|
one: {
|
||||||
__symbolic: 'call',
|
__symbolic: 'call',
|
||||||
|
@ -917,7 +915,7 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
},
|
},
|
||||||
'/tmp/src/function-recursive.d.ts': {
|
'/tmp/src/function-recursive.d.ts': {
|
||||||
__symbolic: 'modules',
|
__symbolic: 'modules',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
recursive: {
|
recursive: {
|
||||||
__symbolic: 'function',
|
__symbolic: 'function',
|
||||||
|
@ -977,7 +975,7 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
},
|
},
|
||||||
'/tmp/src/spread.ts': {
|
'/tmp/src/spread.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
spread: [0, {__symbolic: 'spread', expression: [1, 2, 3, 4]}, 5]
|
spread: [0, {__symbolic: 'spread', expression: [1, 2, 3, 4]}, 5]
|
||||||
}
|
}
|
||||||
|
@ -1113,7 +1111,7 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
`,
|
`,
|
||||||
'/tmp/src/reexport/reexport.d.ts': {
|
'/tmp/src/reexport/reexport.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {},
|
metadata: {},
|
||||||
exports: [
|
exports: [
|
||||||
{from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]},
|
{from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]},
|
||||||
|
@ -1122,7 +1120,7 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
},
|
},
|
||||||
'/tmp/src/reexport/src/origin1.d.ts': {
|
'/tmp/src/reexport/src/origin1.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
One: {__symbolic: 'class'},
|
One: {__symbolic: 'class'},
|
||||||
Two: {__symbolic: 'class'},
|
Two: {__symbolic: 'class'},
|
||||||
|
@ -1131,26 +1129,26 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
},
|
},
|
||||||
'/tmp/src/reexport/src/origin5.d.ts': {
|
'/tmp/src/reexport/src/origin5.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
Five: {__symbolic: 'class'},
|
Five: {__symbolic: 'class'},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'/tmp/src/reexport/src/origin30.d.ts': {
|
'/tmp/src/reexport/src/origin30.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
Thirty: {__symbolic: 'class'},
|
Thirty: {__symbolic: 'class'},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'/tmp/src/reexport/src/originNone.d.ts': {
|
'/tmp/src/reexport/src/originNone.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {},
|
metadata: {},
|
||||||
},
|
},
|
||||||
'/tmp/src/reexport/src/reexport2.d.ts': {
|
'/tmp/src/reexport/src/reexport2.d.ts': {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {},
|
metadata: {},
|
||||||
exports: [{from: './originNone'}, {from: './origin30'}]
|
exports: [{from: './originNone'}, {from: './origin30'}]
|
||||||
}
|
}
|
||||||
|
@ -1166,9 +1164,14 @@ class MockAotCompilerHost implements AotCompilerHost {
|
||||||
if (diagnostics && diagnostics.length) {
|
if (diagnostics && diagnostics.length) {
|
||||||
throw Error(`Error encountered during parse of file ${moduleId}`);
|
throw Error(`Error encountered during parse of file ${moduleId}`);
|
||||||
}
|
}
|
||||||
return this.collector.getMetadata(sf);
|
return [this.collector.getMetadata(sf)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data[moduleId];
|
const result = data[moduleId];
|
||||||
|
if (result) {
|
||||||
|
return Array.isArray(result) ? result : [result];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,9 +256,11 @@ export class MetadataCollector {
|
||||||
if (classDeclaration.name) {
|
if (classDeclaration.name) {
|
||||||
const className = classDeclaration.name.text;
|
const className = classDeclaration.name.text;
|
||||||
if (node.flags & ts.NodeFlags.Export) {
|
if (node.flags & ts.NodeFlags.Export) {
|
||||||
if (classDeclaration.decorators) {
|
|
||||||
if (!metadata) metadata = {};
|
if (!metadata) metadata = {};
|
||||||
|
if (classDeclaration.decorators) {
|
||||||
metadata[className] = classMetadataOf(classDeclaration);
|
metadata[className] = classMetadataOf(classDeclaration);
|
||||||
|
} else {
|
||||||
|
metadata[className] = {__symbolic: 'class'};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,10 +271,14 @@ export class MetadataCollector {
|
||||||
// names substitution will be performed by the StaticReflector.
|
// names substitution will be performed by the StaticReflector.
|
||||||
const functionDeclaration = <ts.FunctionDeclaration>node;
|
const functionDeclaration = <ts.FunctionDeclaration>node;
|
||||||
if (node.flags & ts.NodeFlags.Export) {
|
if (node.flags & ts.NodeFlags.Export) {
|
||||||
|
if (!metadata) metadata = {};
|
||||||
const maybeFunc = maybeGetSimpleFunction(functionDeclaration);
|
const maybeFunc = maybeGetSimpleFunction(functionDeclaration);
|
||||||
if (maybeFunc) {
|
if (maybeFunc) {
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[maybeFunc.name] = recordEntry(maybeFunc.func, node);
|
metadata[maybeFunc.name] = recordEntry(maybeFunc.func, node);
|
||||||
|
} else if (functionDeclaration.name.kind == ts.SyntaxKind.Identifier) {
|
||||||
|
const nameNode = <ts.Identifier>functionDeclaration.name;
|
||||||
|
const functionName = nameNode.text;
|
||||||
|
metadata[functionName] = {__symbolic: 'function'};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
// semantics of the file in an array. For example, when generating a version 2 file, if version 1
|
// 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.
|
// can accurately represent the metadata, generate both version 1 and version 2 in an array.
|
||||||
|
|
||||||
export const VERSION = 1;
|
export const VERSION = 2;
|
||||||
|
|
||||||
export type MetadataEntry = ClassMetadata | FunctionMetadata | MetadataValue;
|
export type MetadataEntry = ClassMetadata | FunctionMetadata | MetadataValue;
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ describe('Collector', () => {
|
||||||
'/unsupported-1.ts',
|
'/unsupported-1.ts',
|
||||||
'/unsupported-2.ts',
|
'/unsupported-2.ts',
|
||||||
'import-star.ts',
|
'import-star.ts',
|
||||||
|
'exported-classes.ts',
|
||||||
'exported-functions.ts',
|
'exported-functions.ts',
|
||||||
'exported-enum.ts',
|
'exported-enum.ts',
|
||||||
'exported-consts.ts',
|
'exported-consts.ts',
|
||||||
|
@ -62,7 +63,7 @@ describe('Collector', () => {
|
||||||
const metadata = collector.getMetadata(sourceFile);
|
const metadata = collector.getMetadata(sourceFile);
|
||||||
expect(metadata).toEqual({
|
expect(metadata).toEqual({
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
HeroDetailComponent: {
|
HeroDetailComponent: {
|
||||||
__symbolic: 'class',
|
__symbolic: 'class',
|
||||||
|
@ -103,7 +104,7 @@ describe('Collector', () => {
|
||||||
const metadata = collector.getMetadata(sourceFile);
|
const metadata = collector.getMetadata(sourceFile);
|
||||||
expect(metadata).toEqual({
|
expect(metadata).toEqual({
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
AppComponent: {
|
AppComponent: {
|
||||||
__symbolic: 'class',
|
__symbolic: 'class',
|
||||||
|
@ -157,7 +158,7 @@ describe('Collector', () => {
|
||||||
const metadata = collector.getMetadata(sourceFile);
|
const metadata = collector.getMetadata(sourceFile);
|
||||||
expect(metadata).toEqual({
|
expect(metadata).toEqual({
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
HEROES: [
|
HEROES: [
|
||||||
{'id': 11, 'name': 'Mr. Nice'}, {'id': 12, 'name': 'Narco'},
|
{'id': 11, 'name': 'Mr. Nice'}, {'id': 12, 'name': 'Narco'},
|
||||||
|
@ -170,13 +171,6 @@ describe('Collector', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined for modules that have no metadata', () => {
|
|
||||||
const sourceFile = program.getSourceFile('/app/error-cases.ts');
|
|
||||||
expect(sourceFile).toBeTruthy(sourceFile);
|
|
||||||
const metadata = collector.getMetadata(sourceFile);
|
|
||||||
expect(metadata).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
let casesFile: ts.SourceFile;
|
let casesFile: ts.SourceFile;
|
||||||
let casesMetadata: ModuleMetadata;
|
let casesMetadata: ModuleMetadata;
|
||||||
|
|
||||||
|
@ -238,7 +232,7 @@ describe('Collector', () => {
|
||||||
const metadata = collector.getMetadata(unsupported1);
|
const metadata = collector.getMetadata(unsupported1);
|
||||||
expect(metadata).toEqual({
|
expect(metadata).toEqual({
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
a: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 16},
|
a: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 16},
|
||||||
b: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 19},
|
b: {__symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 19},
|
||||||
|
@ -275,12 +269,26 @@ describe('Collector', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should record all exported classes', () => {
|
||||||
|
const sourceFile = program.getSourceFile('/exported-classes.ts');
|
||||||
|
const metadata = collector.getMetadata(sourceFile);
|
||||||
|
expect(metadata).toEqual({
|
||||||
|
__symbolic: 'module',
|
||||||
|
version: 2,
|
||||||
|
metadata: {
|
||||||
|
SimpleClass: {__symbolic: 'class'},
|
||||||
|
AbstractClass: {__symbolic: 'class'},
|
||||||
|
DeclaredClass: {__symbolic: 'class'}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should be able to record functions', () => {
|
it('should be able to record functions', () => {
|
||||||
const exportedFunctions = program.getSourceFile('/exported-functions.ts');
|
const exportedFunctions = program.getSourceFile('/exported-functions.ts');
|
||||||
const metadata = collector.getMetadata(exportedFunctions);
|
const metadata = collector.getMetadata(exportedFunctions);
|
||||||
expect(metadata).toEqual({
|
expect(metadata).toEqual({
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 1,
|
version: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
one: {
|
one: {
|
||||||
__symbolic: 'function',
|
__symbolic: 'function',
|
||||||
|
@ -328,7 +336,9 @@ describe('Collector', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
complexFn: {__symbolic: 'function'},
|
||||||
|
declaredFn: {__symbolic: 'function'}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -829,6 +839,11 @@ const FILES: Directory = {
|
||||||
constructor(private f: common.NgFor) {}
|
constructor(private f: common.NgFor) {}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
'exported-classes.ts': `
|
||||||
|
export class SimpleClass {}
|
||||||
|
export abstract class AbstractClass {}
|
||||||
|
export declare class DeclaredClass {}
|
||||||
|
`,
|
||||||
'exported-functions.ts': `
|
'exported-functions.ts': `
|
||||||
export function one(a: string, b: string, c: string) {
|
export function one(a: string, b: string, c: string) {
|
||||||
return {a: a, b: b, c: c};
|
return {a: a, b: b, c: c};
|
||||||
|
@ -842,6 +857,14 @@ const FILES: Directory = {
|
||||||
export function supportsState(): boolean {
|
export function supportsState(): boolean {
|
||||||
return !!window.history.pushState;
|
return !!window.history.pushState;
|
||||||
}
|
}
|
||||||
|
export function complexFn(x: any): boolean {
|
||||||
|
if (x) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export declare function declaredFn();
|
||||||
`,
|
`,
|
||||||
'exported-enum.ts': `
|
'exported-enum.ts': `
|
||||||
import {constValue} from './exported-consts';
|
import {constValue} from './exported-consts';
|
||||||
|
|
Loading…
Reference in New Issue