feat(compiler-cli): reflect static methods added to classes in metadata (#21926)
PR Close #21926
This commit is contained in:
parent
1aa2947f70
commit
eb8ddd2983
|
@ -76,6 +76,9 @@ export class MetadataCollector {
|
|||
}
|
||||
|
||||
function recordEntry<T extends MetadataEntry>(entry: T, node: ts.Node): T {
|
||||
if (composedSubstituter) {
|
||||
entry = composedSubstituter(entry as MetadataValue, node) as T;
|
||||
}
|
||||
return recordMapEntry(entry, node, nodeMap, sourceFile);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import {createLoweredSymbol, isLoweredSymbol} from '@angular/compiler';
|
|||
import * as ts from 'typescript';
|
||||
|
||||
import {CollectorOptions, MetadataCollector, MetadataValue, ModuleMetadata, isMetadataGlobalReferenceExpression} from '../metadata/index';
|
||||
import {MetadataCache, MetadataTransformer, ValueTransform} from './metadata_cache';
|
||||
|
||||
export interface LoweringRequest {
|
||||
kind: ts.SyntaxKind;
|
||||
|
@ -249,35 +250,39 @@ function isLiteralFieldNamed(node: ts.Node, names: Set<string>): boolean {
|
|||
|
||||
const LOWERABLE_FIELD_NAMES = new Set(['useValue', 'useFactory', 'data']);
|
||||
|
||||
export class LowerMetadataCache implements RequestsMap {
|
||||
private collector: MetadataCollector;
|
||||
private metadataCache = new Map<string, MetadataAndLoweringRequests>();
|
||||
|
||||
constructor(options: CollectorOptions, private strict?: boolean) {
|
||||
this.collector = new MetadataCollector(options);
|
||||
}
|
||||
|
||||
getMetadata(sourceFile: ts.SourceFile): ModuleMetadata|undefined {
|
||||
return this.ensureMetadataAndRequests(sourceFile).metadata;
|
||||
}
|
||||
export class LowerMetadataTransform implements RequestsMap, MetadataTransformer {
|
||||
private cache: MetadataCache;
|
||||
private requests = new Map<string, RequestLocationMap>();
|
||||
|
||||
// RequestMap
|
||||
getRequests(sourceFile: ts.SourceFile): RequestLocationMap {
|
||||
return this.ensureMetadataAndRequests(sourceFile).requests;
|
||||
}
|
||||
|
||||
private ensureMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
|
||||
let result = this.metadataCache.get(sourceFile.fileName);
|
||||
let result = this.requests.get(sourceFile.fileName);
|
||||
if (!result) {
|
||||
result = this.getMetadataAndRequests(sourceFile);
|
||||
this.metadataCache.set(sourceFile.fileName, result);
|
||||
// Force the metadata for this source file to be collected which
|
||||
// will recursively call start() populating the request map;
|
||||
this.cache.getMetadata(sourceFile);
|
||||
|
||||
// If we still don't have the requested metadata, the file is not a module
|
||||
// or is a declaration file so return an empty map.
|
||||
result = this.requests.get(sourceFile.fileName) || new Map<number, LoweringRequest>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private getMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
|
||||
// MetadataTransformer
|
||||
connect(cache: MetadataCache): void { this.cache = cache; }
|
||||
|
||||
start(sourceFile: ts.SourceFile): ValueTransform|undefined {
|
||||
let identNumber = 0;
|
||||
const freshIdent = () => createLoweredSymbol(identNumber++);
|
||||
const requests = new Map<number, LoweringRequest>();
|
||||
this.requests.set(sourceFile.fileName, requests);
|
||||
|
||||
const replaceNode = (node: ts.Node) => {
|
||||
const name = freshIdent();
|
||||
requests.set(node.pos, {name, kind: node.kind, location: node.pos, end: node.end});
|
||||
return {__symbolic: 'reference', name};
|
||||
};
|
||||
|
||||
const isExportedSymbol = (() => {
|
||||
let exportTable: Set<string>;
|
||||
|
@ -303,13 +308,8 @@ export class LowerMetadataCache implements RequestsMap {
|
|||
}
|
||||
return false;
|
||||
};
|
||||
const replaceNode = (node: ts.Node) => {
|
||||
const name = freshIdent();
|
||||
requests.set(node.pos, {name, kind: node.kind, location: node.pos, end: node.end});
|
||||
return {__symbolic: 'reference', name};
|
||||
};
|
||||
|
||||
const substituteExpression = (value: MetadataValue, node: ts.Node): MetadataValue => {
|
||||
return (value: MetadataValue, node: ts.Node): MetadataValue => {
|
||||
if (!isPrimitive(value) && !isRewritten(value)) {
|
||||
if ((node.kind === ts.SyntaxKind.ArrowFunction ||
|
||||
node.kind === ts.SyntaxKind.FunctionExpression) &&
|
||||
|
@ -323,18 +323,6 @@ export class LowerMetadataCache implements RequestsMap {
|
|||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
// Do not validate or lower metadata in a declaration file. Declaration files are requested
|
||||
// when we need to update the version of the metadata to add information that might be missing
|
||||
// in the out-of-date version that can be recovered from the .d.ts file.
|
||||
const declarationFile = sourceFile.isDeclarationFile;
|
||||
const moduleFile = ts.isExternalModule(sourceFile);
|
||||
|
||||
const metadata = this.collector.getMetadata(
|
||||
sourceFile, this.strict && !declarationFile,
|
||||
moduleFile && !declarationFile ? substituteExpression : undefined);
|
||||
|
||||
return {metadata, requests};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {MetadataCollector, MetadataValue, ModuleMetadata} from '../metadata/index';
|
||||
|
||||
import {MetadataProvider} from './compiler_host';
|
||||
|
||||
export type ValueTransform = (value: MetadataValue, node: ts.Node) => MetadataValue;
|
||||
|
||||
export interface MetadataTransformer {
|
||||
connect?(cache: MetadataCache): void;
|
||||
start(sourceFile: ts.SourceFile): ValueTransform|undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache, and potentially transform, metadata as it is being collected.
|
||||
*/
|
||||
export class MetadataCache implements MetadataProvider {
|
||||
private metadataCache = new Map<string, ModuleMetadata|undefined>();
|
||||
|
||||
constructor(
|
||||
private collector: MetadataCollector, private strict: boolean,
|
||||
private transformers: MetadataTransformer[]) {
|
||||
for (let transformer of transformers) {
|
||||
if (transformer.connect) {
|
||||
transformer.connect(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getMetadata(sourceFile: ts.SourceFile): ModuleMetadata|undefined {
|
||||
if (this.metadataCache.has(sourceFile.fileName)) {
|
||||
return this.metadataCache.get(sourceFile.fileName);
|
||||
}
|
||||
let substitute: ValueTransform|undefined = undefined;
|
||||
|
||||
// Only process transformers on modules that are not declaration files.
|
||||
const declarationFile = sourceFile.isDeclarationFile;
|
||||
const moduleFile = ts.isExternalModule(sourceFile);
|
||||
if (!declarationFile && moduleFile) {
|
||||
for (let transform of this.transformers) {
|
||||
const transformSubstitute = transform.start(sourceFile);
|
||||
if (transformSubstitute) {
|
||||
if (substitute) {
|
||||
const previous: ValueTransform = substitute;
|
||||
substitute = (value: MetadataValue, node: ts.Node) =>
|
||||
transformSubstitute(previous(value, node), node);
|
||||
} else {
|
||||
substitute = transformSubstitute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = this.collector.getMetadata(sourceFile, this.strict, substitute);
|
||||
this.metadataCache.set(sourceFile.fileName, result);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -13,16 +13,19 @@ import * as path from 'path';
|
|||
import * as ts from 'typescript';
|
||||
|
||||
import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diagnostics';
|
||||
import {ModuleMetadata, createBundleIndexHost} from '../metadata/index';
|
||||
import {MetadataCollector, ModuleMetadata, createBundleIndexHost} from '../metadata/index';
|
||||
|
||||
import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, DiagnosticMessageChain, EmitFlags, LazyRoute, LibrarySummary, Program, SOURCE, TsEmitArguments, TsEmitCallback} from './api';
|
||||
import {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host';
|
||||
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||
import {LowerMetadataTransform, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||
import {MetadataCache, MetadataTransformer} from './metadata_cache';
|
||||
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
||||
import {PartialModuleMetadataTransformer} from './r3_metadata_transform';
|
||||
import {getAngularClassTransformerFactory} from './r3_transform';
|
||||
import {GENERATED_FILES, StructureIsReused, createMessageDiagnostic, isInRootDir, ngToTsDiagnostic, tsStructureIsReused, userError} from './util';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Maximum number of files that are emitable via calling ts.Program.emit
|
||||
* passing individual targetSourceFiles.
|
||||
|
@ -43,7 +46,8 @@ const defaultEmitCallback: TsEmitCallback =
|
|||
|
||||
class AngularCompilerProgram implements Program {
|
||||
private rootNames: string[];
|
||||
private metadataCache: LowerMetadataCache;
|
||||
private metadataCache: MetadataCache;
|
||||
private loweringMetadataTransform: LowerMetadataTransform;
|
||||
private oldProgramLibrarySummaries: Map<string, LibrarySummary>|undefined;
|
||||
private oldProgramEmittedGeneratedFiles: Map<string, GeneratedFile>|undefined;
|
||||
private oldProgramEmittedSourceFiles: Map<string, ts.SourceFile>|undefined;
|
||||
|
@ -93,7 +97,14 @@ class AngularCompilerProgram implements Program {
|
|||
this.host = bundleHost;
|
||||
}
|
||||
}
|
||||
this.metadataCache = new LowerMetadataCache({quotedNames: true}, !!options.strictMetadataEmit);
|
||||
this.loweringMetadataTransform = new LowerMetadataTransform();
|
||||
this.metadataCache = this.createMetadataCache([this.loweringMetadataTransform]);
|
||||
}
|
||||
|
||||
private createMetadataCache(transformers: MetadataTransformer[]) {
|
||||
return new MetadataCache(
|
||||
new MetadataCollector({quotedNames: true}), !!this.options.strictMetadataEmit,
|
||||
transformers);
|
||||
}
|
||||
|
||||
getLibrarySummaries(): Map<string, LibrarySummary> {
|
||||
|
@ -183,7 +194,7 @@ class AngularCompilerProgram implements Program {
|
|||
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
||||
return this.compiler.loadFilesAsync(sourceFiles).then(analyzedModules => {
|
||||
if (this._analyzedModules) {
|
||||
throw new Error('Angular structure loaded both synchronously and asynchronsly');
|
||||
throw new Error('Angular structure loaded both synchronously and asynchronously');
|
||||
}
|
||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
||||
});
|
||||
|
@ -231,7 +242,7 @@ class AngularCompilerProgram implements Program {
|
|||
|
||||
const emitOnlyDtsFiles = (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS;
|
||||
|
||||
const tsCustomTansformers = this.calculateTransforms(
|
||||
const tsCustomTransformers = this.calculateTransforms(
|
||||
/* genFiles */ undefined, /* partialModules */ modules, customTransformers);
|
||||
|
||||
const emitResult = emitCallback({
|
||||
|
@ -239,7 +250,7 @@ class AngularCompilerProgram implements Program {
|
|||
host: this.host,
|
||||
options: this.options,
|
||||
writeFile: writeTsFile, emitOnlyDtsFiles,
|
||||
customTransformers: tsCustomTansformers
|
||||
customTransformers: tsCustomTransformers
|
||||
});
|
||||
|
||||
return emitResult;
|
||||
|
@ -293,7 +304,7 @@ class AngularCompilerProgram implements Program {
|
|||
}
|
||||
this.writeFile(outFileName, outData, writeByteOrderMark, onError, genFile, sourceFiles);
|
||||
};
|
||||
const tsCustomTansformers = this.calculateTransforms(
|
||||
const tsCustomTransformers = this.calculateTransforms(
|
||||
genFileByFileName, /* partialModules */ undefined, customTransformers);
|
||||
const emitOnlyDtsFiles = (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS;
|
||||
// Restore the original references before we emit so TypeScript doesn't emit
|
||||
|
@ -330,7 +341,7 @@ class AngularCompilerProgram implements Program {
|
|||
host: this.host,
|
||||
options: this.options,
|
||||
writeFile: writeTsFile, emitOnlyDtsFiles,
|
||||
customTransformers: tsCustomTansformers,
|
||||
customTransformers: tsCustomTransformers,
|
||||
targetSourceFile: this.tsProgram.getSourceFile(fileName),
|
||||
})));
|
||||
emittedUserTsCount = sourceFilesToEmit.length;
|
||||
|
@ -340,7 +351,7 @@ class AngularCompilerProgram implements Program {
|
|||
host: this.host,
|
||||
options: this.options,
|
||||
writeFile: writeTsFile, emitOnlyDtsFiles,
|
||||
customTransformers: tsCustomTansformers
|
||||
customTransformers: tsCustomTransformers
|
||||
});
|
||||
emittedUserTsCount = this.tsProgram.getSourceFiles().length - genTsFiles.length;
|
||||
}
|
||||
|
@ -454,13 +465,19 @@ class AngularCompilerProgram implements Program {
|
|||
customTransformers?: CustomTransformers): ts.CustomTransformers {
|
||||
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||
if (!this.options.disableExpressionLowering) {
|
||||
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache, this.tsProgram));
|
||||
beforeTs.push(
|
||||
getExpressionLoweringTransformFactory(this.loweringMetadataTransform, this.tsProgram));
|
||||
}
|
||||
if (genFiles) {
|
||||
beforeTs.push(getAngularEmitterTransformFactory(genFiles, this.getTsProgram()));
|
||||
}
|
||||
if (partialModules) {
|
||||
beforeTs.push(getAngularClassTransformerFactory(partialModules));
|
||||
|
||||
// If we have partial modules, the cached metadata might be incorrect as it doesn't reflect
|
||||
// the partial module transforms.
|
||||
this.metadataCache = this.createMetadataCache(
|
||||
[this.loweringMetadataTransform, new PartialModuleMetadataTransformer(partialModules)]);
|
||||
}
|
||||
if (customTransformers && customTransformers.beforeTs) {
|
||||
beforeTs.push(...customTransformers.beforeTs);
|
||||
|
@ -505,7 +522,7 @@ class AngularCompilerProgram implements Program {
|
|||
sourceFiles: string[],
|
||||
} {
|
||||
if (this._analyzedModules) {
|
||||
throw new Error(`Internal Error: already initalized!`);
|
||||
throw new Error(`Internal Error: already initialized!`);
|
||||
}
|
||||
// Note: This is important to not produce a memory leak!
|
||||
const oldTsProgram = this.oldTsProgram;
|
||||
|
@ -522,7 +539,7 @@ class AngularCompilerProgram implements Program {
|
|||
if (this.options.generateCodeForLibraries !== false) {
|
||||
// if we should generateCodeForLibraries, never include
|
||||
// generated files in the program as otherwise we will
|
||||
// ovewrite them and typescript will report the error
|
||||
// overwrite them and typescript will report the error
|
||||
// TS5055: Cannot write file ... because it would overwrite input file.
|
||||
rootNames = rootNames.filter(fn => !GENERATED_FILES.test(fn));
|
||||
}
|
||||
|
@ -551,7 +568,7 @@ class AngularCompilerProgram implements Program {
|
|||
if (sf.fileName.endsWith('.ngfactory.ts')) {
|
||||
const {generate, baseFileName} = this.hostAdapter.shouldGenerateFile(sf.fileName);
|
||||
if (generate) {
|
||||
// Note: ! is ok as hostAdapter.shouldGenerateFile will always return a basefileName
|
||||
// Note: ! is ok as hostAdapter.shouldGenerateFile will always return a baseFileName
|
||||
// for .ngfactory.ts files.
|
||||
const genFile = this.compiler.emitTypeCheckStub(sf.fileName, baseFileName !);
|
||||
if (genFile) {
|
||||
|
@ -674,7 +691,7 @@ class AngularCompilerProgram implements Program {
|
|||
this.emittedLibrarySummaries.push({fileName: genFile.genFileUrl, text: outData});
|
||||
if (!this.options.declaration) {
|
||||
// If we don't emit declarations, still record an empty .ngfactory.d.ts file,
|
||||
// as we might need it lateron for resolving module names from summaries.
|
||||
// as we might need it later on for resolving module names from summaries.
|
||||
const ngFactoryDts =
|
||||
genFile.genFileUrl.substring(0, genFile.genFileUrl.length - 15) + '.ngfactory.d.ts';
|
||||
this.emittedLibrarySummaries.push({fileName: ngFactoryDts, text: ''});
|
||||
|
@ -688,7 +705,7 @@ class AngularCompilerProgram implements Program {
|
|||
}
|
||||
}
|
||||
// Filter out generated files for which we didn't generate code.
|
||||
// This can happen as the stub caclulation is not completely exact.
|
||||
// This can happen as the stub calculation is not completely exact.
|
||||
// Note: sourceFile refers to the .ngfactory.ts / .ngsummary.ts file
|
||||
// node_emitter_transform already set the file contents to be empty,
|
||||
// so this code only needs to skip the file if !allowEmptyCodegenFiles.
|
||||
|
@ -855,7 +872,7 @@ export function i18nSerialize(
|
|||
}
|
||||
|
||||
function getPathNormalizer(basePath?: string) {
|
||||
// normalize sourcepaths by removing the base path and always using "/" as a separator
|
||||
// normalize source paths by removing the base path and always using "/" as a separator
|
||||
return (sourcePath: string) => {
|
||||
sourcePath = basePath ? path.relative(basePath, sourcePath) : sourcePath;
|
||||
return sourcePath.split(path.sep).join('/');
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ClassStmt, PartialModule, Statement, StmtModifier} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {MetadataCollector, MetadataValue, ModuleMetadata, isClassMetadata} from '../metadata/index';
|
||||
|
||||
import {MetadataTransformer, ValueTransform} from './metadata_cache';
|
||||
|
||||
export class PartialModuleMetadataTransformer implements MetadataTransformer {
|
||||
private moduleMap: Map<string, PartialModule>;
|
||||
|
||||
constructor(modules: PartialModule[]) {
|
||||
this.moduleMap = new Map(modules.map<[string, PartialModule]>(m => [m.fileName, m]));
|
||||
}
|
||||
|
||||
start(sourceFile: ts.SourceFile): ValueTransform|undefined {
|
||||
const partialModule = this.moduleMap.get(sourceFile.fileName);
|
||||
if (partialModule) {
|
||||
const classMap = new Map<string, ClassStmt>(
|
||||
partialModule.statements.filter(isClassStmt).map<[string, ClassStmt]>(s => [s.name, s]));
|
||||
if (classMap.size > 0) {
|
||||
return (value: MetadataValue, node: ts.Node): MetadataValue => {
|
||||
// For class metadata that is going to be transformed to have a static method ensure the
|
||||
// metadata contains a static declaration the new static method.
|
||||
if (isClassMetadata(value) && node.kind === ts.SyntaxKind.ClassDeclaration) {
|
||||
const classDeclaration = node as ts.ClassDeclaration;
|
||||
if (classDeclaration.name) {
|
||||
const partialClass = classMap.get(classDeclaration.name.text);
|
||||
if (partialClass) {
|
||||
for (const field of partialClass.fields) {
|
||||
if (field.name && field.modifiers &&
|
||||
field.modifiers.some(modifier => modifier === StmtModifier.Static)) {
|
||||
value.statics = {...(value.statics || {}), [field.name]: {}};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isClassStmt(v: Statement): v is ClassStmt {
|
||||
return v instanceof ClassStmt;
|
||||
}
|
|
@ -8,8 +8,9 @@
|
|||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ModuleMetadata} from '../../src/metadata/index';
|
||||
import {LowerMetadataCache, LoweringRequest, RequestLocationMap, getExpressionLoweringTransformFactory} from '../../src/transformers/lower_expressions';
|
||||
import {MetadataCollector, ModuleMetadata} from '../../src/metadata/index';
|
||||
import {LowerMetadataTransform, LoweringRequest, RequestLocationMap, getExpressionLoweringTransformFactory} from '../../src/transformers/lower_expressions';
|
||||
import {MetadataCache} from '../../src/transformers/metadata_cache';
|
||||
import {Directory, MockAotContext, MockCompilerHost} from '../mocks';
|
||||
|
||||
describe('Expression lowering', () => {
|
||||
|
@ -110,7 +111,8 @@ describe('Expression lowering', () => {
|
|||
});
|
||||
|
||||
it('should throw a validation exception for invalid files', () => {
|
||||
const cache = new LowerMetadataCache({}, /* strict */ true);
|
||||
const cache = new MetadataCache(
|
||||
new MetadataCollector({}), /* strict */ true, [new LowerMetadataTransform()]);
|
||||
const sourceFile = ts.createSourceFile(
|
||||
'foo.ts', `
|
||||
import {Injectable} from '@angular/core';
|
||||
|
@ -126,7 +128,8 @@ describe('Expression lowering', () => {
|
|||
});
|
||||
|
||||
it('should not report validation errors on a .d.ts file', () => {
|
||||
const cache = new LowerMetadataCache({}, /* strict */ true);
|
||||
const cache = new MetadataCache(
|
||||
new MetadataCollector({}), /* strict */ true, [new LowerMetadataTransform()]);
|
||||
const dtsFile = ts.createSourceFile(
|
||||
'foo.d.ts', `
|
||||
import {Injectable} from '@angular/core';
|
||||
|
@ -241,11 +244,12 @@ function normalizeResult(result: string): string {
|
|||
|
||||
function collect(annotatedSource: string) {
|
||||
const {annotations, unannotatedSource} = getAnnotations(annotatedSource);
|
||||
const cache = new LowerMetadataCache({});
|
||||
const transformer = new LowerMetadataTransform();
|
||||
const cache = new MetadataCache(new MetadataCollector({}), false, [transformer]);
|
||||
const sourceFile = ts.createSourceFile(
|
||||
'someName.ts', unannotatedSource, ts.ScriptTarget.Latest, /* setParentNodes */ true);
|
||||
return {
|
||||
metadata: cache.getMetadata(sourceFile),
|
||||
requests: cache.getRequests(sourceFile), annotations
|
||||
requests: transformer.getRequests(sourceFile), annotations
|
||||
};
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ClassField, ClassMethod, ClassStmt, PartialModule, Statement, StmtModifier} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {MetadataCollector, isClassMetadata} from '../../src/metadata/index';
|
||||
import {MetadataCache} from '../../src/transformers/metadata_cache';
|
||||
import {PartialModuleMetadataTransformer} from '../../src/transformers/r3_metadata_transform';
|
||||
|
||||
describe('r3_transform_spec', () => {
|
||||
|
||||
it('should add a static method to collected metadata', () => {
|
||||
const fileName = '/some/directory/someFileName.ts';
|
||||
const className = 'SomeClass';
|
||||
const newFieldName = 'newStaticField';
|
||||
const source = `
|
||||
export class ${className} {
|
||||
myMethod(): void {}
|
||||
}
|
||||
`;
|
||||
|
||||
const sourceFile =
|
||||
ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, /* setParentNodes */ true);
|
||||
const partialModule: PartialModule = {
|
||||
fileName,
|
||||
statements: [new ClassStmt(
|
||||
className, /* parent */ null, /* fields */[new ClassField(
|
||||
/* name */ newFieldName, /* type */ null, /* modifiers */[StmtModifier.Static])],
|
||||
/* getters */[],
|
||||
/* constructorMethod */ new ClassMethod(/* name */ null, /* params */[], /* body */[]),
|
||||
/* methods */[])]
|
||||
};
|
||||
|
||||
const cache = new MetadataCache(
|
||||
new MetadataCollector(), /* strict */ true,
|
||||
[new PartialModuleMetadataTransformer([partialModule])]);
|
||||
const metadata = cache.getMetadata(sourceFile);
|
||||
expect(metadata).toBeDefined('Expected metadata from test source file');
|
||||
if (metadata) {
|
||||
const classData = metadata.metadata[className];
|
||||
expect(classData && isClassMetadata(classData))
|
||||
.toBeDefined(`Expected metadata to contain data for "${className}"`);
|
||||
if (classData && isClassMetadata(classData)) {
|
||||
const statics = classData.statics;
|
||||
expect(statics).toBeDefined(`Expected "${className}" metadata to contain statics`);
|
||||
if (statics) {
|
||||
expect(statics[newFieldName]).toEqual({}, 'Expected new field to recorded as a function');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
|
@ -67,7 +67,7 @@ export * from './ml_parser/html_tags';
|
|||
export * from './ml_parser/interpolation_config';
|
||||
export * from './ml_parser/tags';
|
||||
export {NgModuleCompiler} from './ng_module_compiler';
|
||||
export {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, collectExternalReferences} from './output/output_ast';
|
||||
export {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, collectExternalReferences} from './output/output_ast';
|
||||
export {EmitterVisitorContext} from './output/abstract_emitter';
|
||||
export * from './output/ts_emitter';
|
||||
export * from './parse_util';
|
||||
|
|
Loading…
Reference in New Issue