fix(language-service): ensure correct paths are passed to TypeScript (#21812)
The 2.6 version of TypeScript's `resolveModuleName` started to require paths passed to be separated by '/' instead of being able to handle '\'. `ngc` and `ng` already do this transformation. Fixes: #21811 PR Close #21812
This commit is contained in:
parent
2b68e8d98a
commit
676d9c2c4b
|
@ -69,7 +69,7 @@ export class ReflectorHost implements StaticSymbolResolverHost {
|
||||||
throw new Error('Resolution of relative paths requires a containing file.');
|
throw new Error('Resolution of relative paths requires a containing file.');
|
||||||
}
|
}
|
||||||
// Any containing file gives the same result for absolute imports
|
// Any containing file gives the same result for absolute imports
|
||||||
containingFile = path.join(this.options.basePath !, 'index.ts');
|
containingFile = path.join(this.options.basePath !, 'index.ts').replace(/\\/g, '/');
|
||||||
}
|
}
|
||||||
const resolved =
|
const resolved =
|
||||||
ts.resolveModuleName(moduleName, containingFile !, this.options, this.hostAdapter)
|
ts.resolveModuleName(moduleName, containingFile !, this.options, this.hostAdapter)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* @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 * as path from 'path';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
import {createLanguageService} from '../src/language_service';
|
||||||
|
import {ReflectorHost} from '../src/reflector_host';
|
||||||
|
import {Completions, LanguageService} from '../src/types';
|
||||||
|
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||||
|
|
||||||
|
import {toh} from './test_data';
|
||||||
|
import {MockTypescriptHost} from './test_utils';
|
||||||
|
|
||||||
|
describe('reflector_host_spec', () => {
|
||||||
|
|
||||||
|
// Regression #21811
|
||||||
|
it('should be able to find angular under windows', () => {
|
||||||
|
const originalJoin = path.join;
|
||||||
|
let mockHost = new MockTypescriptHost(
|
||||||
|
['/app/main.ts', '/app/parsing-cases.ts'], toh, 'app/node_modules',
|
||||||
|
{...path, join: (...args: string[]) => originalJoin.apply(path, args)});
|
||||||
|
let service = ts.createLanguageService(mockHost);
|
||||||
|
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||||
|
let ngService = createLanguageService(ngHost);
|
||||||
|
const reflectorHost = new ReflectorHost(() => undefined as any, mockHost, {basePath: '\\app'});
|
||||||
|
|
||||||
|
spyOn(path, 'join').and.callFake((...args: string[]) => { return path.win32.join(...args); });
|
||||||
|
const result = reflectorHost.moduleNameToFileName('@angular/core');
|
||||||
|
expect(result).not.toBeNull('could not find @angular/core using path.win32');
|
||||||
|
});
|
||||||
|
});
|
|
@ -71,14 +71,16 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
|
||||||
private options: ts.CompilerOptions;
|
private options: ts.CompilerOptions;
|
||||||
private overrideDirectory = new Set<string>();
|
private overrideDirectory = new Set<string>();
|
||||||
|
|
||||||
constructor(private scriptNames: string[], private data: MockData) {
|
constructor(
|
||||||
|
private scriptNames: string[], private data: MockData,
|
||||||
|
private node_modules: string = 'node_modules', private myPath: typeof path = path) {
|
||||||
const moduleFilename = module.filename.replace(/\\/g, '/');
|
const moduleFilename = module.filename.replace(/\\/g, '/');
|
||||||
let angularIndex = moduleFilename.indexOf('@angular');
|
let angularIndex = moduleFilename.indexOf('@angular');
|
||||||
if (angularIndex >= 0)
|
if (angularIndex >= 0)
|
||||||
this.angularPath = moduleFilename.substr(0, angularIndex).replace('/all/', '/all/@angular/');
|
this.angularPath = moduleFilename.substr(0, angularIndex).replace('/all/', '/all/@angular/');
|
||||||
let distIndex = moduleFilename.indexOf('/dist/all');
|
let distIndex = moduleFilename.indexOf('/dist/all');
|
||||||
if (distIndex >= 0)
|
if (distIndex >= 0)
|
||||||
this.nodeModulesPath = path.join(moduleFilename.substr(0, distIndex), 'node_modules');
|
this.nodeModulesPath = myPath.join(moduleFilename.substr(0, distIndex), 'node_modules');
|
||||||
this.options = {
|
this.options = {
|
||||||
target: ts.ScriptTarget.ES5,
|
target: ts.ScriptTarget.ES5,
|
||||||
module: ts.ModuleKind.CommonJS,
|
module: ts.ModuleKind.CommonJS,
|
||||||
|
@ -141,11 +143,14 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
|
||||||
directoryExists(directoryName: string): boolean {
|
directoryExists(directoryName: string): boolean {
|
||||||
if (this.overrideDirectory.has(directoryName)) return true;
|
if (this.overrideDirectory.has(directoryName)) return true;
|
||||||
let effectiveName = this.getEffectiveName(directoryName);
|
let effectiveName = this.getEffectiveName(directoryName);
|
||||||
if (effectiveName === directoryName)
|
if (effectiveName === directoryName) {
|
||||||
return directoryExists(directoryName, this.data);
|
return directoryExists(directoryName, this.data);
|
||||||
else
|
} else if (effectiveName == '/' + this.node_modules) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
return fs.existsSync(effectiveName);
|
return fs.existsSync(effectiveName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fileExists(fileName: string): boolean { return this.getRawFileContent(fileName) != null; }
|
fileExists(fileName: string): boolean { return this.getRawFileContent(fileName) != null; }
|
||||||
|
|
||||||
|
@ -175,7 +180,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
|
||||||
let basename = path.basename(fileName);
|
let basename = path.basename(fileName);
|
||||||
if (/^lib.*\.d\.ts$/.test(basename)) {
|
if (/^lib.*\.d\.ts$/.test(basename)) {
|
||||||
let libPath = ts.getDefaultLibFilePath(this.getCompilationSettings());
|
let libPath = ts.getDefaultLibFilePath(this.getCompilationSettings());
|
||||||
return fs.readFileSync(path.join(path.dirname(libPath), basename), 'utf8');
|
return fs.readFileSync(this.myPath.join(path.dirname(libPath), basename), 'utf8');
|
||||||
} else {
|
} else {
|
||||||
if (missingCache.has(fileName)) {
|
if (missingCache.has(fileName)) {
|
||||||
cacheUsed.add(fileName);
|
cacheUsed.add(fileName);
|
||||||
|
@ -199,18 +204,18 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getEffectiveName(name: string): string {
|
private getEffectiveName(name: string): string {
|
||||||
const node_modules = 'node_modules';
|
const node_modules = this.node_modules;
|
||||||
const at_angular = '/@angular';
|
const at_angular = '/@angular';
|
||||||
if (name.startsWith('/' + node_modules)) {
|
if (name.startsWith('/' + node_modules)) {
|
||||||
if (this.nodeModulesPath && !name.startsWith('/' + node_modules + at_angular)) {
|
if (this.nodeModulesPath && !name.startsWith('/' + node_modules + at_angular)) {
|
||||||
let result = path.join(this.nodeModulesPath, name.substr(node_modules.length + 1));
|
let result = this.myPath.join(this.nodeModulesPath, name.substr(node_modules.length + 1));
|
||||||
if (!name.match(rxjsts))
|
if (!name.match(rxjsts))
|
||||||
if (fs.existsSync(result)) {
|
if (fs.existsSync(result)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.angularPath && name.startsWith('/' + node_modules + at_angular)) {
|
if (this.angularPath && name.startsWith('/' + node_modules + at_angular)) {
|
||||||
return path.join(
|
return this.myPath.join(
|
||||||
this.angularPath, name.substr(node_modules.length + at_angular.length + 1));
|
this.angularPath, name.substr(node_modules.length + at_angular.length + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue