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 || [];
|
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 {
|
getTemplateAt(fileName: string, position: number): TemplateSource|undefined {
|
||||||
let sourceFile = this.getSourceFile(fileName);
|
if (fileName.endsWith('.ts')) {
|
||||||
if (sourceFile) {
|
const sourceFile = this.getSourceFile(fileName);
|
||||||
this.context = sourceFile.fileName;
|
if (sourceFile) {
|
||||||
let node = this.findNode(sourceFile, position);
|
this.context = sourceFile.fileName;
|
||||||
if (node) {
|
const node = this.findNode(sourceFile, position);
|
||||||
return this.getSourceFromNode(
|
if (node) {
|
||||||
fileName, this.host.getScriptVersion(sourceFile.fileName), node);
|
return this.getSourceFromNode(
|
||||||
|
fileName, this.host.getScriptVersion(sourceFile.fileName), node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.ensureTemplateMap();
|
this.ensureTemplateMap();
|
||||||
// TODO: Cannocalize the file?
|
const componentSymbol = this.fileToComponent.get(fileName);
|
||||||
const componentType = this.fileToComponent.get(fileName);
|
if (componentSymbol) {
|
||||||
if (componentType) {
|
|
||||||
return this.getSourceFromType(
|
return this.getSourceFromType(
|
||||||
fileName, this.host.getScriptVersion(fileName), componentType);
|
fileName, this.host.getScriptVersion(fileName), componentSymbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -164,14 +171,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTemplates(fileName: string): TemplateSources {
|
getTemplates(fileName: string): TemplateSources {
|
||||||
this.ensureTemplateMap();
|
if (fileName.endsWith('.ts')) {
|
||||||
const componentType = this.fileToComponent.get(fileName);
|
|
||||||
if (componentType) {
|
|
||||||
const templateSource = this.getTemplateAt(fileName, 0);
|
|
||||||
if (templateSource) {
|
|
||||||
return [templateSource];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let version = this.host.getScriptVersion(fileName);
|
let version = this.host.getScriptVersion(fileName);
|
||||||
let result: TemplateSource[] = [];
|
let result: TemplateSource[] = [];
|
||||||
|
|
||||||
@ -191,10 +191,22 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||||||
ts.forEachChild(sourceFile, visit);
|
ts.forEachChild(sourceFile, visit);
|
||||||
}
|
}
|
||||||
return result.length ? result : undefined;
|
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 {
|
getDeclarations(fileName: string): Declarations {
|
||||||
|
if (!fileName.endsWith('.ts')) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const result: Declarations = [];
|
const result: Declarations = [];
|
||||||
const sourceFile = this.getSourceFile(fileName);
|
const sourceFile = this.getSourceFile(fileName);
|
||||||
if (sourceFile) {
|
if (sourceFile) {
|
||||||
@ -212,6 +224,9 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSourceFile(fileName: string): ts.SourceFile|undefined {
|
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);
|
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
|
// 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
|
// tsserver. We don't need the exact base directory, just one that contains
|
||||||
// a source file.
|
// a source file.
|
||||||
const source = this.tsService.getProgram() !.getSourceFile(this.context);
|
const source = this.getSourceFile(this.context);
|
||||||
if (!source) {
|
if (!source) {
|
||||||
throw new Error('Internal error: no context could be determined');
|
throw new Error('Internal error: no context could be determined');
|
||||||
}
|
}
|
||||||
|
@ -15,51 +15,55 @@ import {toh} from './test_data';
|
|||||||
import {MockTypescriptHost} from './test_utils';
|
import {MockTypescriptHost} from './test_utils';
|
||||||
|
|
||||||
|
|
||||||
describe('completions', () => {
|
describe('TypeScriptServiceHost', () => {
|
||||||
let host: ts.LanguageServiceHost;
|
it('should be able to create a typescript host', () => {
|
||||||
let service: ts.LanguageService;
|
const tsLSHost = new MockTypescriptHost(['/app/main.ts'], toh);
|
||||||
let ngHost: TypeScriptServiceHost;
|
const tsLS = ts.createLanguageService(tsLSHost);
|
||||||
|
expect(() => new TypeScriptServiceHost(tsLSHost, tsLS)).not.toThrow();
|
||||||
beforeEach(() => {
|
|
||||||
host = new MockTypescriptHost(['/app/main.ts'], toh);
|
|
||||||
service = ts.createLanguageService(host);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to create a typescript host',
|
it('should be able to analyze modules', () => {
|
||||||
() => { expect(() => new TypeScriptServiceHost(host, service)).not.toThrow(); });
|
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 without a tsconfig.json file', () => {
|
||||||
|
const tsLSHost = new MockTypescriptHost(['foo.ts'], toh);
|
||||||
it('should be able to analyze modules',
|
const tsLS = ts.createLanguageService(tsLSHost);
|
||||||
() => { expect(ngHost.getAnalyzedModules()).toBeDefined(); });
|
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||||
|
expect(ngLSHost.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 not throw if there is no script names', () => {
|
it('should not throw if there is no script names', () => {
|
||||||
host = new MockTypescriptHost([], toh);
|
const tsLSHost = new MockTypescriptHost([], toh);
|
||||||
service = ts.createLanguageService(host);
|
const tsLS = ts.createLanguageService(tsLSHost);
|
||||||
ngHost = new TypeScriptServiceHost(host, service);
|
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||||
expect(() => ngHost.getAnalyzedModules()).not.toThrow();
|
expect(() => ngLSHost.getAnalyzedModules()).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear the caches if program changes', () => {
|
it('should clear the caches if program changes', () => {
|
||||||
// First create a TypescriptHost with empty script names
|
// First create a TypescriptHost with empty script names
|
||||||
host = new MockTypescriptHost([], toh);
|
const tsLSHost = new MockTypescriptHost([], toh);
|
||||||
service = ts.createLanguageService(host);
|
const tsLS = ts.createLanguageService(tsLSHost);
|
||||||
ngHost = new TypeScriptServiceHost(host, service);
|
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||||
expect(ngHost.getAnalyzedModules().ngModules).toEqual([]);
|
expect(ngLSHost.getAnalyzedModules().ngModules).toEqual([]);
|
||||||
// Now add a script, this would change the program
|
// Now add a script, this would change the program
|
||||||
const fileName = '/app/main.ts';
|
const fileName = '/app/main.ts';
|
||||||
const content = (host as MockTypescriptHost).getFileContent(fileName) !;
|
const content = (tsLSHost as MockTypescriptHost).getFileContent(fileName) !;
|
||||||
(host as MockTypescriptHost).addScript(fileName, content);
|
(tsLSHost as MockTypescriptHost).addScript(fileName, content);
|
||||||
// If the caches are not cleared, we would get back an empty array.
|
// 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.
|
// 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…
x
Reference in New Issue
Block a user