fix(language-service): update to use `CompilerHost` from compiler-cli (#13189)
This commit is contained in:
parent
dfd8140084
commit
3ff6554cbc
5
build.sh
5
build.sh
|
@ -145,6 +145,11 @@ do
|
||||||
$TSC -p ${SRCDIR}/tsconfig-testing.json
|
$TSC -p ${SRCDIR}/tsconfig-testing.json
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -e ${SRCDIR}/tsconfig-2015.json ]]; then
|
||||||
|
echo "====== COMPILING ESM: ${TSC} -p ${SRCDIR}/tsconfig-2015.json"
|
||||||
|
${TSC} -p ${SRCDIR}/tsconfig-2015.json
|
||||||
|
fi
|
||||||
|
|
||||||
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
|
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
|
||||||
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
||||||
if [ "$(uname)" == "Darwin" ]; then
|
if [ "$(uname)" == "Darwin" ]; then
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"declaration": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"module": "es2015",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "../../../dist/packages-dist/esm/compiler-cli",
|
||||||
|
"paths": {
|
||||||
|
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||||
|
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||||
|
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
||||||
|
"@angular/platform-server": ["../../../dist/packages-dist/platform-server"],
|
||||||
|
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||||
|
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
|
||||||
|
},
|
||||||
|
"rootDir": ".",
|
||||||
|
"sourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"exclude": ["integrationtest"],
|
||||||
|
"files": [
|
||||||
|
"index.ts",
|
||||||
|
"src/main.ts",
|
||||||
|
"src/extract_i18n.ts",
|
||||||
|
"../../../node_modules/@types/node/index.d.ts",
|
||||||
|
"../../../node_modules/@types/jasmine/index.d.ts",
|
||||||
|
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||||
|
]
|
||||||
|
}
|
|
@ -81,7 +81,8 @@ export class StaticReflector implements ReflectorReader {
|
||||||
private host: StaticReflectorHost,
|
private host: StaticReflectorHost,
|
||||||
private staticSymbolCache: StaticSymbolCache = new StaticSymbolCache(),
|
private staticSymbolCache: StaticSymbolCache = new StaticSymbolCache(),
|
||||||
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
|
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
|
||||||
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = []) {
|
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [],
|
||||||
|
private errorRecorder?: (error: any, fileName: string) => void) {
|
||||||
this.initializeConversionMap();
|
this.initializeConversionMap();
|
||||||
knownMetadataClasses.forEach(
|
knownMetadataClasses.forEach(
|
||||||
(kc) => this._registerDecoratorOrConstructor(
|
(kc) => this._registerDecoratorOrConstructor(
|
||||||
|
@ -155,7 +156,10 @@ export class StaticReflector implements ReflectorReader {
|
||||||
|
|
||||||
public parameters(type: StaticSymbol): any[] {
|
public parameters(type: StaticSymbol): any[] {
|
||||||
if (!(type instanceof StaticSymbol)) {
|
if (!(type instanceof StaticSymbol)) {
|
||||||
throw new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
this.reportError(
|
||||||
|
new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`),
|
||||||
|
type);
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let parameters = this.parameterCache.get(type);
|
let parameters = this.parameterCache.get(type);
|
||||||
|
@ -219,8 +223,10 @@ export class StaticReflector implements ReflectorReader {
|
||||||
|
|
||||||
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
||||||
if (!(type instanceof StaticSymbol)) {
|
if (!(type instanceof StaticSymbol)) {
|
||||||
throw new Error(
|
this.reportError(
|
||||||
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
new Error(
|
||||||
|
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`),
|
||||||
|
type);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return !!this._methodNames(type)[lcProperty];
|
return !!this._methodNames(type)[lcProperty];
|
||||||
|
@ -301,11 +307,21 @@ export class StaticReflector implements ReflectorReader {
|
||||||
return this.staticSymbolCache.get(declarationFile, name, members);
|
return this.staticSymbolCache.get(declarationFile, name, members);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private reportError(error: Error, context: StaticSymbol, path?: string) {
|
||||||
|
if (this.errorRecorder) {
|
||||||
|
this.errorRecorder(error, (context && context.filePath) || path);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
|
private resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
|
||||||
const resolveModule = (moduleName: string): string => {
|
const resolveModule = (moduleName: string): string => {
|
||||||
const resolvedModulePath = this.host.moduleNameToFileName(moduleName, filePath);
|
const resolvedModulePath = this.host.moduleNameToFileName(moduleName, filePath);
|
||||||
if (!resolvedModulePath) {
|
if (!resolvedModulePath) {
|
||||||
throw new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`);
|
this.reportError(
|
||||||
|
new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`),
|
||||||
|
null, filePath);
|
||||||
}
|
}
|
||||||
return resolvedModulePath;
|
return resolvedModulePath;
|
||||||
};
|
};
|
||||||
|
@ -338,7 +354,12 @@ export class StaticReflector implements ReflectorReader {
|
||||||
if (typeof exportSymbol !== 'string') {
|
if (typeof exportSymbol !== 'string') {
|
||||||
symName = exportSymbol.name;
|
symName = exportSymbol.name;
|
||||||
}
|
}
|
||||||
staticSymbol = this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
|
const resolvedModule = resolveModule(moduleExport.from);
|
||||||
|
if (resolvedModule) {
|
||||||
|
staticSymbol =
|
||||||
|
this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,10 +369,12 @@ export class StaticReflector implements ReflectorReader {
|
||||||
for (const moduleExport of metadata['exports']) {
|
for (const moduleExport of metadata['exports']) {
|
||||||
if (!moduleExport.export) {
|
if (!moduleExport.export) {
|
||||||
const resolvedModule = resolveModule(moduleExport.from);
|
const resolvedModule = resolveModule(moduleExport.from);
|
||||||
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
|
if (resolvedModule) {
|
||||||
if (candidateSymbol) {
|
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
|
||||||
staticSymbol = candidateSymbol;
|
if (candidateSymbol) {
|
||||||
break;
|
staticSymbol = candidateSymbol;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -689,7 +712,16 @@ export class StaticReflector implements ReflectorReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = simplifyInContext(context, value, 0);
|
const recordedSimplifyInContext = (context: StaticSymbol, value: any, depth: number) => {
|
||||||
|
try {
|
||||||
|
return simplifyInContext(context, value, depth);
|
||||||
|
} catch (e) {
|
||||||
|
this.reportError(e, context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = this.errorRecorder ? recordedSimplifyInContext(context, value, 0) :
|
||||||
|
simplifyInContext(context, value, 0);
|
||||||
if (shouldIgnore(result)) {
|
if (shouldIgnore(result)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -717,8 +749,10 @@ export class StaticReflector implements ReflectorReader {
|
||||||
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
|
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
|
||||||
}
|
}
|
||||||
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
||||||
throw new Error(
|
this.reportError(
|
||||||
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`);
|
new Error(
|
||||||
|
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`),
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
this.metadataCache.set(module, moduleMetadata);
|
this.metadataCache.set(module, moduleMetadata);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ var locations = {
|
||||||
'tsc-wrapped': normalize('../../../dist/tools/@angular') + '/',
|
'tsc-wrapped': normalize('../../../dist/tools/@angular') + '/',
|
||||||
};
|
};
|
||||||
|
|
||||||
var esm_suffixes = {};
|
var esm_suffixes = {'compiler-cli': esm};
|
||||||
|
|
||||||
function normalize(fileName) {
|
function normalize(fileName) {
|
||||||
return path.resolve(__dirname, fileName);
|
return path.resolve(__dirname, fileName);
|
||||||
|
|
|
@ -55,12 +55,14 @@ export function getDeclarationDiagnostics(
|
||||||
|
|
||||||
let directives: Set<StaticSymbol>|undefined = undefined;
|
let directives: Set<StaticSymbol>|undefined = undefined;
|
||||||
for (const declaration of declarations) {
|
for (const declaration of declarations) {
|
||||||
let report = (message: string) => {
|
const report = (message: string, span?: Span) => {
|
||||||
results.push(
|
results.push(<Diagnostic>{
|
||||||
<Diagnostic>{kind: DiagnosticKind.Error, span: declaration.declarationSpan, message});
|
kind: DiagnosticKind.Error,
|
||||||
|
span: span || declaration.declarationSpan, message
|
||||||
|
});
|
||||||
};
|
};
|
||||||
if (declaration.error) {
|
for (const error of declaration.errors) {
|
||||||
report(declaration.error);
|
report(error.message, error.span);
|
||||||
}
|
}
|
||||||
if (declaration.metadata) {
|
if (declaration.metadata) {
|
||||||
if (declaration.metadata.isComponent) {
|
if (declaration.metadata.isComponent) {
|
||||||
|
|
|
@ -6,28 +6,16 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StaticReflectorHost, StaticSymbol} from '@angular/compiler';
|
import {AngularCompilerOptions, AotCompilerHost, CompilerHost, ModuleResolutionHostAdapter} from '@angular/compiler-cli';
|
||||||
import {MetadataCollector} from '@angular/tsc-wrapped/src/collector';
|
|
||||||
import {ModuleMetadata} from '@angular/tsc-wrapped/src/schema';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
|
||||||
const DTS = /\.d\.ts$/;
|
|
||||||
|
|
||||||
let serialNumber = 0;
|
|
||||||
|
|
||||||
class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
|
class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
|
||||||
private forceExists: string[] = [];
|
|
||||||
|
|
||||||
constructor(private host: ts.LanguageServiceHost) {
|
constructor(private host: ts.LanguageServiceHost) {
|
||||||
if (host.directoryExists)
|
if (host.directoryExists)
|
||||||
this.directoryExists = directoryName => this.host.directoryExists(directoryName);
|
this.directoryExists = directoryName => this.host.directoryExists(directoryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
fileExists(fileName: string): boolean {
|
fileExists(fileName: string): boolean { return !!this.host.getScriptSnapshot(fileName); }
|
||||||
return !!this.host.getScriptSnapshot(fileName) || this.forceExists.indexOf(fileName) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
readFile(fileName: string): string {
|
readFile(fileName: string): string {
|
||||||
let snapshot = this.host.getScriptSnapshot(fileName);
|
let snapshot = this.host.getScriptSnapshot(fileName);
|
||||||
|
@ -37,122 +25,19 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryExists: (directoryName: string) => boolean;
|
directoryExists: (directoryName: string) => boolean;
|
||||||
|
|
||||||
forceExist(fileName: string): void { this.forceExists.push(fileName); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ReflectorHost implements StaticReflectorHost {
|
export class ReflectorHost extends CompilerHost {
|
||||||
private metadataCollector: MetadataCollector;
|
|
||||||
private moduleResolverHost: ReflectorModuleModuleResolutionHost;
|
|
||||||
private _typeChecker: ts.TypeChecker;
|
|
||||||
private metadataCache = new Map<string, MetadataCacheEntry>();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private getProgram: () => ts.Program, private serviceHost: ts.LanguageServiceHost,
|
private getProgram: () => ts.Program, serviceHost: ts.LanguageServiceHost,
|
||||||
private options: ts.CompilerOptions, private basePath: string) {
|
options: AngularCompilerOptions) {
|
||||||
this.moduleResolverHost = new ReflectorModuleModuleResolutionHost(serviceHost);
|
super(
|
||||||
this.metadataCollector = new MetadataCollector();
|
null, options,
|
||||||
|
new ModuleResolutionHostAdapter(new ReflectorModuleModuleResolutionHost(serviceHost)));
|
||||||
}
|
}
|
||||||
|
|
||||||
getCanonicalFileName(fileName: string): string { return fileName; }
|
protected get program() { return this.getProgram(); }
|
||||||
|
protected set program(value: ts.Program) {
|
||||||
private get program() { return this.getProgram(); }
|
// Discard the result set by ancestor constructor
|
||||||
|
|
||||||
public moduleNameToFileName(moduleName: string, containingFile: string) {
|
|
||||||
if (!containingFile || !containingFile.length) {
|
|
||||||
if (moduleName.indexOf('.') === 0) {
|
|
||||||
throw new Error('Resolution of relative paths requires a containing file.');
|
|
||||||
}
|
|
||||||
// Any containing file gives the same result for absolute imports
|
|
||||||
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
|
|
||||||
}
|
|
||||||
moduleName = moduleName.replace(EXT, '');
|
|
||||||
const resolved =
|
|
||||||
ts.resolveModuleName(moduleName, containingFile, this.options, this.moduleResolverHost)
|
|
||||||
.resolvedModule;
|
|
||||||
return resolved ? resolved.resolvedFileName : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We want a moduleId that will appear in import statements in the generated code.
|
|
||||||
* These need to be in a form that system.js can load, so absolute file paths don't work.
|
|
||||||
* Relativize the paths by checking candidate prefixes of the absolute path, to see if
|
|
||||||
* they are resolvable by the moduleResolution strategy from the CompilerHost.
|
|
||||||
*/
|
|
||||||
fileNameToModuleName(importedFile: string, containingFile: string) {
|
|
||||||
// TODO(tbosch): if a file does not yet exist (because we compile it later),
|
|
||||||
// we still need to create it so that the `resolve` method works!
|
|
||||||
if (!this.moduleResolverHost.fileExists(importedFile)) {
|
|
||||||
this.moduleResolverHost.forceExist(importedFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts = importedFile.replace(EXT, '').split(path.sep).filter(p => !!p);
|
|
||||||
|
|
||||||
for (let index = parts.length - 1; index >= 0; index--) {
|
|
||||||
let candidate = parts.slice(index, parts.length).join(path.sep);
|
|
||||||
if (this.moduleNameToFileName('.' + path.sep + candidate, containingFile) === importedFile) {
|
|
||||||
return `./${candidate}`;
|
|
||||||
}
|
|
||||||
if (this.moduleNameToFileName(candidate, containingFile) === importedFile) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error(
|
|
||||||
`Unable to find any resolvable import for ${importedFile} relative to ${containingFile}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
private get typeChecker(): ts.TypeChecker {
|
|
||||||
let result = this._typeChecker;
|
|
||||||
if (!result) {
|
|
||||||
result = this._typeChecker = this.program.getTypeChecker();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private typeCache = new Map<string, StaticSymbol>();
|
|
||||||
|
|
||||||
// TODO(alexeagle): take a statictype
|
|
||||||
getMetadataFor(filePath: string): ModuleMetadata[] {
|
|
||||||
if (!this.moduleResolverHost.fileExists(filePath)) {
|
|
||||||
throw new Error(`No such file '${filePath}'`);
|
|
||||||
}
|
|
||||||
if (DTS.test(filePath)) {
|
|
||||||
const metadataPath = filePath.replace(DTS, '.metadata.json');
|
|
||||||
if (this.moduleResolverHost.fileExists(metadataPath)) {
|
|
||||||
return this.readMetadata(metadataPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let sf = this.program.getSourceFile(filePath);
|
|
||||||
if (!sf) {
|
|
||||||
throw new Error(`Source file ${filePath} not present in program.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = this.metadataCache.get(sf.path);
|
|
||||||
const version = this.serviceHost.getScriptVersion(sf.path);
|
|
||||||
if (entry && entry.version == version) {
|
|
||||||
if (!entry.content) return undefined;
|
|
||||||
return [entry.content];
|
|
||||||
}
|
|
||||||
const metadata = this.metadataCollector.getMetadata(sf);
|
|
||||||
this.metadataCache.set(sf.path, {version, content: metadata});
|
|
||||||
if (metadata) return [metadata];
|
|
||||||
}
|
|
||||||
|
|
||||||
readMetadata(filePath: string) {
|
|
||||||
try {
|
|
||||||
const text = this.moduleResolverHost.readFile(filePath);
|
|
||||||
const result = JSON.parse(text);
|
|
||||||
if (!Array.isArray(result)) return [result];
|
|
||||||
return result;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Failed to read JSON file ${filePath}`);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MetadataCacheEntry {
|
|
||||||
version: string;
|
|
||||||
content: ModuleMetadata;
|
|
||||||
}
|
|
|
@ -87,6 +87,25 @@ export interface TemplateSource {
|
||||||
*/
|
*/
|
||||||
export type TemplateSources = TemplateSource[] /* | undefined */;
|
export type TemplateSources = TemplateSource[] /* | undefined */;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error information found getting declaration information
|
||||||
|
*
|
||||||
|
* A host type; see `LanagueServiceHost`.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
export interface DeclarationError {
|
||||||
|
/**
|
||||||
|
* The span of the error in the declaration's module.
|
||||||
|
*/
|
||||||
|
readonly span: Span;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The message to display describing the error.
|
||||||
|
*/
|
||||||
|
readonly message: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about the component declarations.
|
* Information about the component declarations.
|
||||||
*
|
*
|
||||||
|
@ -117,11 +136,10 @@ export interface Declaration {
|
||||||
*/
|
*/
|
||||||
readonly metadata?: CompileDirectiveMetadata;
|
readonly metadata?: CompileDirectiveMetadata;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error reported trying to get the metadata.
|
* Error reported trying to get the metadata.
|
||||||
*/
|
*/
|
||||||
readonly error?: string;
|
readonly errors: DeclarationError[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,8 +27,7 @@ import * as ts from 'typescript';
|
||||||
|
|
||||||
import {createLanguageService} from './language_service';
|
import {createLanguageService} from './language_service';
|
||||||
import {ReflectorHost} from './reflector_host';
|
import {ReflectorHost} from './reflector_host';
|
||||||
import {BuiltinType, CompletionKind, Declaration, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types';
|
import {BuiltinType, CompletionKind, Declaration, DeclarationError, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,6 +88,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
private service: LanguageService;
|
private service: LanguageService;
|
||||||
private fileToComponent: Map<string, StaticSymbol>;
|
private fileToComponent: Map<string, StaticSymbol>;
|
||||||
private templateReferences: string[];
|
private templateReferences: string[];
|
||||||
|
private collectedErrors: Map<string, any[]>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
typescript: typeof ts, private host: ts.LanguageServiceHost,
|
typescript: typeof ts, private host: ts.LanguageServiceHost,
|
||||||
|
@ -158,6 +158,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
|
|
||||||
getAnalyzedModules(): NgAnalyzedModules {
|
getAnalyzedModules(): NgAnalyzedModules {
|
||||||
this.validate();
|
this.validate();
|
||||||
|
return this.ensureAnalyzedModules();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ensureAnalyzedModules(): NgAnalyzedModules {
|
||||||
let analyzedModules = this.analyzedModules;
|
let analyzedModules = this.analyzedModules;
|
||||||
if (!analyzedModules) {
|
if (!analyzedModules) {
|
||||||
const programSymbols = extractProgramSymbols(
|
const programSymbols = extractProgramSymbols(
|
||||||
|
@ -221,9 +225,14 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAnalyzedModules() {
|
updateAnalyzedModules() {
|
||||||
|
this.validate();
|
||||||
if (this.modulesOutOfDate) {
|
if (this.modulesOutOfDate) {
|
||||||
this.analyzedModules = null;
|
this.analyzedModules = null;
|
||||||
this.getAnalyzedModules();
|
this._reflector = null;
|
||||||
|
this.templateReferences = null;
|
||||||
|
this.fileToComponent = null;
|
||||||
|
this.ensureAnalyzedModules();
|
||||||
|
this.modulesOutOfDate = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,10 +258,8 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
this._checker = null;
|
this._checker = null;
|
||||||
this._typeCache = [];
|
this._typeCache = [];
|
||||||
this._resolver = null;
|
this._resolver = null;
|
||||||
this._reflector = null;
|
this.collectedErrors = null;
|
||||||
this.modulesOutOfDate = true;
|
this.modulesOutOfDate = true;
|
||||||
this.templateReferences = null;
|
|
||||||
this.fileToComponent = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ensureTemplateMap() {
|
private ensureTemplateMap() {
|
||||||
|
@ -361,19 +368,34 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
throw new Error('Internal error: no context could be determined');
|
throw new Error('Internal error: no context could be determined');
|
||||||
}
|
}
|
||||||
|
|
||||||
const tsConfigPath = findTsConfig(source.path);
|
const tsConfigPath = findTsConfig(source.fileName);
|
||||||
const basePath = path.dirname(tsConfigPath || this.context);
|
const basePath = path.dirname(tsConfigPath || this.context);
|
||||||
|
|
||||||
result = this._reflectorHost = new ReflectorHost(
|
result = this._reflectorHost = new ReflectorHost(
|
||||||
() => this.tsService.getProgram(), this.host, this.host.getCompilationSettings(),
|
() => this.tsService.getProgram(), this.host, {basePath, genDir: basePath});
|
||||||
basePath);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private collectError(error: any, filePath: string) {
|
||||||
|
let errorMap = this.collectedErrors;
|
||||||
|
if (!errorMap) {
|
||||||
|
errorMap = this.collectedErrors = new Map();
|
||||||
|
}
|
||||||
|
let errors = errorMap.get(filePath);
|
||||||
|
if (!errors) {
|
||||||
|
errors = [];
|
||||||
|
this.collectedErrors.set(filePath, errors);
|
||||||
|
}
|
||||||
|
errors.push(error);
|
||||||
|
}
|
||||||
|
|
||||||
private get reflector(): StaticReflector {
|
private get reflector(): StaticReflector {
|
||||||
let result = this._reflector;
|
let result = this._reflector;
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = this._reflector = new StaticReflector(this.reflectorHost, this._staticSymbolCache);
|
result = this._reflector = new StaticReflector(
|
||||||
|
this.reflectorHost, this._staticSymbolCache, [], [],
|
||||||
|
(e, filePath) => this.collectError(e, filePath));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -440,6 +462,14 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
return [declaration, callTarget];
|
return [declaration, callTarget];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getCollectedErrors(defaultSpan: Span, sourceFile: ts.SourceFile): DeclarationError[] {
|
||||||
|
const errors = (this.collectedErrors && this.collectedErrors.get(sourceFile.fileName));
|
||||||
|
return (errors && errors.map((e: any) => {
|
||||||
|
return {message: e.message, span: spanAt(sourceFile, e.line, e.column) || defaultSpan};
|
||||||
|
})) ||
|
||||||
|
[];
|
||||||
|
}
|
||||||
|
|
||||||
private getDeclarationFromNode(sourceFile: ts.SourceFile, node: ts.Node): Declaration|undefined {
|
private getDeclarationFromNode(sourceFile: ts.SourceFile, node: ts.Node): Declaration|undefined {
|
||||||
if (node.kind == ts.SyntaxKind.ClassDeclaration && node.decorators &&
|
if (node.kind == ts.SyntaxKind.ClassDeclaration && node.decorators &&
|
||||||
(node as ts.ClassDeclaration).name) {
|
(node as ts.ClassDeclaration).name) {
|
||||||
|
@ -457,14 +487,22 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
if (this.resolver.isDirective(staticSymbol as any)) {
|
if (this.resolver.isDirective(staticSymbol as any)) {
|
||||||
const {metadata} =
|
const {metadata} =
|
||||||
this.resolver.getNonNormalizedDirectiveMetadata(staticSymbol as any);
|
this.resolver.getNonNormalizedDirectiveMetadata(staticSymbol as any);
|
||||||
return {type: staticSymbol, declarationSpan: spanOf(target), metadata};
|
const declarationSpan = spanOf(target);
|
||||||
|
return {
|
||||||
|
type: staticSymbol,
|
||||||
|
declarationSpan,
|
||||||
|
metadata,
|
||||||
|
errors: this.getCollectedErrors(declarationSpan, sourceFile)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message) {
|
if (e.message) {
|
||||||
|
this.collectError(e, sourceFile.fileName);
|
||||||
|
const declarationSpan = spanOf(target);
|
||||||
return {
|
return {
|
||||||
type: staticSymbol,
|
type: staticSymbol,
|
||||||
declarationSpan: spanAt(sourceFile, e.line, e.column) || spanOf(target),
|
declarationSpan,
|
||||||
error: e.message
|
errors: this.getCollectedErrors(declarationSpan, sourceFile)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,7 @@ export class MyComponent {
|
||||||
const originalContent = mockHost.getFileContent(fileName);
|
const originalContent = mockHost.getFileContent(fileName);
|
||||||
const newContent = originalContent + code;
|
const newContent = originalContent + code;
|
||||||
mockHost.override(fileName, originalContent + code);
|
mockHost.override(fileName, originalContent + code);
|
||||||
|
ngHost.updateAnalyzedModules();
|
||||||
try {
|
try {
|
||||||
cb(fileName, newContent);
|
cb(fileName, newContent);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -120,6 +120,7 @@ describe('diagnostics', () => {
|
||||||
const originalContent = mockHost.getFileContent(fileName);
|
const originalContent = mockHost.getFileContent(fileName);
|
||||||
const newContent = originalContent + code;
|
const newContent = originalContent + code;
|
||||||
mockHost.override(fileName, originalContent + code);
|
mockHost.override(fileName, originalContent + code);
|
||||||
|
ngHost.updateAnalyzedModules();
|
||||||
try {
|
try {
|
||||||
cb(fileName, newContent);
|
cb(fileName, newContent);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@angular/common": ["../../../dist/packages-dist/common"],
|
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||||
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
||||||
"@angular/compiler/*": ["../../../dist/packages-dist/compiler/*"],
|
"@angular/compiler/*": ["../../../dist/packages-dist/compiler/*"],
|
||||||
|
"@angular/compiler-cli": ["../../../dist/packages-dist/compiler-cli"],
|
||||||
"@angular/platform-server": ["../../../dist/packages-dist/platform-server"],
|
"@angular/platform-server": ["../../../dist/packages-dist/platform-server"],
|
||||||
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||||
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"],
|
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"],
|
||||||
|
|
Loading…
Reference in New Issue