fix(language-service): do not treat file URIs as general URLs (#39917) · angular/angular@829988b · GitHub
Skip to content

Commit 829988b

Browse files
ayazhafizmhevery
authored andcommitted
fix(language-service): do not treat file URIs as general URLs (#39917)
In the past, the legacy (VE-based) language service would use a `UrlResolver` instance to resolve file paths, primarily for compiler resources like external templates. The problem with this is that the UrlResolver is designed to resolve URLs in general, and so for a path like `/a/b/#c`, `#c` is treated as hash/fragment rather than as part of the path, which can lead to unexpected path resolution (f.x., `resolve('a/b/#c/d.ts', './d.html')` would produce `'a/b/d.html'` rather than the expected `'a/b/#c/d.html'`). This commit resolves the issue by using Node's `path` module to resolve file paths directly, which aligns more with how resources are resolved in the Ivy compiler. The testing story here is not great, and the API for validating a file path could be a little bit prettier/robust. However, since the VE-based language service is going into more of a "maintenance mode" now that there is a clear path for the Ivy-based LS moving forward, I think it is okay not to spend too much time here. Closes angular/vscode-ng-language-service#892 Closes angular/vscode-ng-language-service#1001 PR Close #39917
1 parent 35309bb commit 829988b

7 files changed

Lines changed: 50 additions & 12 deletions

File tree

packages/language-service/src/typescript_host.ts

Lines changed: 15 additions & 5 deletions
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Component} from '@angular/core';
10+
11+
@Component({
12+
selector: 'inner',
13+
templateUrl: './inner.html',
14+
})
15+
export class InnerComponent {
16+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div>Hello</div>

packages/language-service/test/project/app/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
import {CommonModule} from '@angular/common';
1010
import {NgModule} from '@angular/core';
1111
import {FormsModule} from '@angular/forms';
12+
import {InnerComponent} from './#inner/component';
1213
import {AppComponent} from './app.component';
1314
import * as ParsingCases from './parsing-cases';
1415

1516
@NgModule({
1617
imports: [CommonModule, FormsModule],
1718
declarations: [
1819
AppComponent,
20+
InnerComponent,
1921
ParsingCases.CounterDirective,
2022
ParsingCases.HintModel,
2123
ParsingCases.NumberModel,

packages/language-service/test/test_utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function loadTourOfHeroes(): ReadonlyMap<string, string> {
6969
const value = fs.readFileSync(absPath, 'utf8');
7070
files.set(key, value);
7171
} else {
72-
const key = path.join('/', filePath);
72+
const key = path.join('/', path.relative(root, absPath));
7373
files.set(key, '[[directory]]');
7474
dirs.push(absPath);
7575
}
@@ -189,7 +189,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
189189
if (this.overrideDirectory.has(directoryName)) return true;
190190
const effectiveName = this.getEffectiveName(directoryName);
191191
if (effectiveName === directoryName) {
192-
return TOH.has(directoryName);
192+
return TOH.get(directoryName) === '[[directory]]';
193193
}
194194
if (effectiveName === '/' + this.node_modules) {
195195
return true;

packages/language-service/test/ts_plugin_spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ describe('plugin', () => {
5858
const compilerDiags = tsLS.getCompilerOptionsDiagnostics();
5959
expect(compilerDiags).toEqual([]);
6060
const sourceFiles = program.getSourceFiles().filter(f => !f.fileName.endsWith('.d.ts'));
61-
// there are three .ts files in the test project
62-
expect(sourceFiles.length).toBe(3);
61+
// there are four .ts files in the test project
62+
expect(sourceFiles.length).toBe(4);
6363
for (const {fileName} of sourceFiles) {
6464
const syntacticDiags = tsLS.getSyntacticDiagnostics(fileName);
6565
expect(syntacticDiags).toEqual([]);
@@ -133,9 +133,10 @@ describe('plugin', () => {
133133

134134
it('should return external templates when getExternalFiles() is called', () => {
135135
const externalTemplates = getExternalFiles(mockProject);
136-
expect(externalTemplates).toEqual([
136+
expect(new Set(externalTemplates)).toEqual(new Set([
137137
'/app/test.ng',
138-
]);
138+
'/app/#inner/inner.html',
139+
]));
139140
});
140141

141142
it('should not return external template that does not exist', () => {

packages/language-service/test/typescript_host_spec.ts

Lines changed: 9 additions & 1 deletion

0 commit comments

Comments
 (0)