fix(language-service): getSourceFile() should only be called on TS files (#31920)
PR Close #31920
This commit is contained in:
parent
9e9179e915
commit
e8b8f6d09b
|
@ -117,22 +117,29 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
return this.templateReferences || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Angular template in the file, if any. If TS file is provided then
|
||||
* return the inline template, otherwise return the external template.
|
||||
* @param fileName Either TS or HTML file
|
||||
* @param position Only used if file is TS
|
||||
*/
|
||||
getTemplateAt(fileName: string, position: number): TemplateSource|undefined {
|
||||
let sourceFile = this.getSourceFile(fileName);
|
||||
if (sourceFile) {
|
||||
this.context = sourceFile.fileName;
|
||||
let node = this.findNode(sourceFile, position);
|
||||
if (node) {
|
||||
return this.getSourceFromNode(
|
||||
fileName, this.host.getScriptVersion(sourceFile.fileName), node);
|
||||
if (fileName.endsWith('.ts')) {
|
||||
const sourceFile = this.getSourceFile(fileName);
|
||||
if (sourceFile) {
|
||||
this.context = sourceFile.fileName;
|
||||
const node = this.findNode(sourceFile, position);
|
||||
if (node) {
|
||||
return this.getSourceFromNode(
|
||||
fileName, this.host.getScriptVersion(sourceFile.fileName), node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.ensureTemplateMap();
|
||||
// TODO: Cannocalize the file?
|
||||
const componentType = this.fileToComponent.get(fileName);
|
||||
if (componentType) {
|
||||
const componentSymbol = this.fileToComponent.get(fileName);
|
||||
if (componentSymbol) {
|
||||
return this.getSourceFromType(
|
||||
fileName, this.host.getScriptVersion(fileName), componentType);
|
||||
fileName, this.host.getScriptVersion(fileName), componentSymbol);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
|
@ -164,14 +171,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
}
|
||||
|
||||
getTemplates(fileName: string): TemplateSources {
|
||||
this.ensureTemplateMap();
|
||||
const componentType = this.fileToComponent.get(fileName);
|
||||
if (componentType) {
|
||||
const templateSource = this.getTemplateAt(fileName, 0);
|
||||
if (templateSource) {
|
||||
return [templateSource];
|
||||
}
|
||||
} else {
|
||||
if (fileName.endsWith('.ts')) {
|
||||
let version = this.host.getScriptVersion(fileName);
|
||||
let result: TemplateSource[] = [];
|
||||
|
||||
|
@ -191,10 +191,22 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
ts.forEachChild(sourceFile, visit);
|
||||
}
|
||||
return result.length ? result : undefined;
|
||||
} else {
|
||||
this.ensureTemplateMap();
|
||||
const componentSymbol = this.fileToComponent.get(fileName);
|
||||
if (componentSymbol) {
|
||||
const templateSource = this.getTemplateAt(fileName, 0);
|
||||
if (templateSource) {
|
||||
return [templateSource];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getDeclarations(fileName: string): Declarations {
|
||||
if (!fileName.endsWith('.ts')) {
|
||||
return [];
|
||||
}
|
||||
const result: Declarations = [];
|
||||
const sourceFile = this.getSourceFile(fileName);
|
||||
if (sourceFile) {
|
||||
|
@ -212,6 +224,9 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
}
|
||||
|
||||
getSourceFile(fileName: string): ts.SourceFile|undefined {
|
||||
if (!fileName.endsWith('.ts')) {
|
||||
throw new Error(`Non-TS source file requested: ${fileName}`);
|
||||
}
|
||||
return this.tsService.getProgram() !.getSourceFile(fileName);
|
||||
}
|
||||
|
||||
|
@ -383,7 +398,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
// The host's getCurrentDirectory() is not reliable as it is always "" in
|
||||
// tsserver. We don't need the exact base directory, just one that contains
|
||||
// a source file.
|
||||
const source = this.tsService.getProgram() !.getSourceFile(this.context);
|
||||
const source = this.getSourceFile(this.context);
|
||||
if (!source) {
|
||||
throw new Error('Internal error: no context could be determined');
|
||||
}
|
||||
|
|
|
@ -15,51 +15,55 @@ import {toh} from './test_data';
|
|||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
|
||||
describe('completions', () => {
|
||||
let host: ts.LanguageServiceHost;
|
||||
let service: ts.LanguageService;
|
||||
let ngHost: TypeScriptServiceHost;
|
||||
|
||||
beforeEach(() => {
|
||||
host = new MockTypescriptHost(['/app/main.ts'], toh);
|
||||
service = ts.createLanguageService(host);
|
||||
describe('TypeScriptServiceHost', () => {
|
||||
it('should be able to create a typescript host', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts'], toh);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
expect(() => new TypeScriptServiceHost(tsLSHost, tsLS)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should be able to create a typescript host',
|
||||
() => { expect(() => new TypeScriptServiceHost(host, service)).not.toThrow(); });
|
||||
it('should be able to analyze modules', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts'], toh);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
expect(ngLSHost.getAnalyzedModules()).toBeDefined();
|
||||
});
|
||||
|
||||
beforeEach(() => { ngHost = new TypeScriptServiceHost(host, service); });
|
||||
|
||||
it('should be able to analyze modules',
|
||||
() => { expect(ngHost.getAnalyzedModules()).toBeDefined(); });
|
||||
|
||||
it('should be able to analyze modules in without a tsconfig.json file', () => {
|
||||
host = new MockTypescriptHost(['foo.ts'], toh);
|
||||
service = ts.createLanguageService(host);
|
||||
ngHost = new TypeScriptServiceHost(host, service);
|
||||
expect(ngHost.getAnalyzedModules()).toBeDefined();
|
||||
it('should be able to analyze modules without a tsconfig.json file', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['foo.ts'], toh);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
expect(ngLSHost.getAnalyzedModules()).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not throw if there is no script names', () => {
|
||||
host = new MockTypescriptHost([], toh);
|
||||
service = ts.createLanguageService(host);
|
||||
ngHost = new TypeScriptServiceHost(host, service);
|
||||
expect(() => ngHost.getAnalyzedModules()).not.toThrow();
|
||||
const tsLSHost = new MockTypescriptHost([], toh);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
expect(() => ngLSHost.getAnalyzedModules()).not.toThrow();
|
||||
});
|
||||
|
||||
it('should clear the caches if program changes', () => {
|
||||
// First create a TypescriptHost with empty script names
|
||||
host = new MockTypescriptHost([], toh);
|
||||
service = ts.createLanguageService(host);
|
||||
ngHost = new TypeScriptServiceHost(host, service);
|
||||
expect(ngHost.getAnalyzedModules().ngModules).toEqual([]);
|
||||
const tsLSHost = new MockTypescriptHost([], toh);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
expect(ngLSHost.getAnalyzedModules().ngModules).toEqual([]);
|
||||
// Now add a script, this would change the program
|
||||
const fileName = '/app/main.ts';
|
||||
const content = (host as MockTypescriptHost).getFileContent(fileName) !;
|
||||
(host as MockTypescriptHost).addScript(fileName, content);
|
||||
const content = (tsLSHost as MockTypescriptHost).getFileContent(fileName) !;
|
||||
(tsLSHost as MockTypescriptHost).addScript(fileName, content);
|
||||
// If the caches are not cleared, we would get back an empty array.
|
||||
// But if the caches are cleared then the analyzed modules will be non-empty.
|
||||
expect(ngHost.getAnalyzedModules().ngModules.length).not.toEqual(0);
|
||||
expect(ngLSHost.getAnalyzedModules().ngModules.length).not.toEqual(0);
|
||||
});
|
||||
|
||||
it('should throw if getSourceFile is called on non-TS file', () => {
|
||||
const tsLSHost = new MockTypescriptHost([], toh);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
expect(() => {
|
||||
ngLSHost.getSourceFile('/src/test.ng');
|
||||
}).toThrowError('Non-TS source file requested: /src/test.ng');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue