forked from angular/angular
-
Notifications
You must be signed in to change notification settings - Fork 1
/
analyzer.ts
109 lines (94 loc) · 3.95 KB
/
analyzer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/**
* @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 {ConstantPool} from '@angular/compiler';
import * as fs from 'fs';
import * as ts from 'typescript';
import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from '../../ngtsc/annotations';
import {CompileResult, DecoratorHandler} from '../../ngtsc/transform';
import {NgccReflectionHost} from './host/ngcc_host';
import {ParsedClass} from './parsing/parsed_class';
import {ParsedFile} from './parsing/parsed_file';
import {isDefined} from './utils';
export interface AnalyzedClass<A = any, M = any> extends ParsedClass {
handler: DecoratorHandler<A, M>;
analysis: any;
diagnostics?: ts.Diagnostic[];
compilation: CompileResult[];
}
export interface AnalyzedFile {
analyzedClasses: AnalyzedClass[];
sourceFile: ts.SourceFile;
constantPool: ConstantPool;
}
export interface MatchingHandler<A, M> {
handler: DecoratorHandler<A, M>;
match: M;
}
/**
* `ResourceLoader` which directly uses the filesystem to resolve resources synchronously.
*/
export class FileResourceLoader implements ResourceLoader {
load(url: string): string { return fs.readFileSync(url, 'utf8'); }
}
export class Analyzer {
resourceLoader = new FileResourceLoader();
scopeRegistry = new SelectorScopeRegistry(this.typeChecker, this.host);
handlers: DecoratorHandler<any, any>[] = [
new BaseDefDecoratorHandler(this.typeChecker, this.host),
new ComponentDecoratorHandler(
this.typeChecker, this.host, this.scopeRegistry, this.isCore, this.resourceLoader,
this.rootDirs),
new DirectiveDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, this.isCore),
new InjectableDecoratorHandler(this.host, this.isCore),
new NgModuleDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, this.isCore),
new PipeDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, this.isCore),
];
constructor(
private typeChecker: ts.TypeChecker, private host: NgccReflectionHost,
private rootDirs: string[], private isCore: boolean) {}
/**
* Analyize a parsed file to generate the information about decorated classes that
* should be converted to use ivy definitions.
* @param file The file to be analysed for decorated classes.
*/
analyzeFile(file: ParsedFile): AnalyzedFile {
const constantPool = new ConstantPool();
const analyzedClasses =
file.decoratedClasses.map(clazz => this.analyzeClass(constantPool, clazz))
.filter(isDefined);
return {
analyzedClasses,
sourceFile: file.sourceFile, constantPool,
};
}
protected analyzeClass(pool: ConstantPool, clazz: ParsedClass): AnalyzedClass|undefined {
const matchingHandlers = this.handlers
.map(handler => ({
handler,
match: handler.detect(clazz.declaration, clazz.decorators),
}))
.filter(isMatchingHandler);
if (matchingHandlers.length > 1) {
throw new Error('TODO.Diagnostic: Class has multiple Angular decorators.');
}
if (matchingHandlers.length === 0) {
return undefined;
}
const {handler, match} = matchingHandlers[0];
const {analysis, diagnostics} = handler.analyze(clazz.declaration, match);
let compilation = handler.compile(clazz.declaration, analysis, pool);
if (!Array.isArray(compilation)) {
compilation = [compilation];
}
return {...clazz, handler, analysis, diagnostics, compilation};
}
}
function isMatchingHandler<A, M>(handler: Partial<MatchingHandler<A, M>>):
handler is MatchingHandler<A, M> {
return !!handler.match;
}