diff --git a/packages/language-service/src/typescript_host.ts b/packages/language-service/src/typescript_host.ts index dc5af0b247..8c7fcbd599 100644 --- a/packages/language-service/src/typescript_host.ts +++ b/packages/language-service/src/typescript_host.ts @@ -7,6 +7,7 @@ */ import {AotSummaryResolver, CompileDirectiveMetadata, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, InterpolationConfig, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticAndDynamicReflectionCapabilities, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, SummaryResolver, UrlResolver, analyzeNgModules, componentModuleUrl, createOfflineCompileUrlResolver, extractProgramSymbols} from '@angular/compiler'; +import {AngularCompilerOptions} from '@angular/compiler-cli'; import {Type, ViewEncapsulation, ɵConsole as Console} from '@angular/core'; import * as fs from 'fs'; import * as path from 'path'; @@ -17,6 +18,7 @@ import {ReflectorHost} from './reflector_host'; import {BuiltinType, CompletionKind, Declaration, DeclarationError, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types'; + // In TypeScript 2.1 these flags moved // These helpers work for both 2.0 and 2.1. const isPrivate = (ts as any).ModifierFlags ? @@ -386,9 +388,13 @@ export class TypeScriptServiceHost implements LanguageServiceHost { const tsConfigPath = findTsConfig(source.fileName); const basePath = path.dirname(tsConfigPath || this.context); - - result = this._reflectorHost = new ReflectorHost( - () => this.tsService.getProgram(), this.host, {basePath, genDir: basePath}); + const options: AngularCompilerOptions = {basePath, genDir: basePath}; + const compilerOptions = this.host.getCompilationSettings(); + if (compilerOptions && compilerOptions.baseUrl) { + options.baseUrl = compilerOptions.baseUrl; + } + result = this._reflectorHost = + new ReflectorHost(() => this.tsService.getProgram(), this.host, options); } return result; } diff --git a/packages/language-service/test/diagnostics_spec.ts b/packages/language-service/test/diagnostics_spec.ts index abb5d6c10b..0fc6629a56 100644 --- a/packages/language-service/test/diagnostics_spec.ts +++ b/packages/language-service/test/diagnostics_spec.ts @@ -281,6 +281,35 @@ describe('diagnostics', () => { fileName => expectOnlyModuleDiagnostics(ngService.getDiagnostics(fileName))); }); + it('should be able to resolve modules using baseUrl', () => { + const app_component = ` + import { Component } from '@angular/core'; + import { NgForm } from '@angular/common'; + import { Server } from 'app/server'; + + @Component({ + selector: 'example-app', + template: '...', + providers: [Server] + }) + export class AppComponent { + onSubmit(form: NgForm) {} + } + `; + const app_server = ` + export class Server {} + `; + const fileName = '/app/app.component.ts'; + mockHost.override(fileName, app_component); + mockHost.addScript('/other/files/app/server.ts', app_server); + mockHost.overrideOptions(options => { + options.baseUrl = '/other/files'; + return options; + }); + const diagnostic = ngService.getDiagnostics(fileName); + expect(diagnostic).toEqual([]); + }); + function addCode(code: string, cb: (fileName: string, content?: string) => void) { const fileName = '/app/app.component.ts'; const originalContent = mockHost.getFileContent(fileName); diff --git a/packages/language-service/test/test_utils.ts b/packages/language-service/test/test_utils.ts index 0cd46fcd0e..399dc3aa53 100644 --- a/packages/language-service/test/test_utils.ts +++ b/packages/language-service/test/test_utils.ts @@ -69,6 +69,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { private overrides = new Map(); private projectVersion = 0; private options: ts.CompilerOptions; + private overrideDirectory = new Set(); constructor(private scriptNames: string[], private data: MockData) { const moduleFilename = module.filename.replace(/\\/g, '/'); @@ -97,6 +98,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { } if (content) { this.overrides.set(fileName, content); + this.overrideDirectory.add(path.dirname(fileName)); } else { this.overrides.delete(fileName); } @@ -105,13 +107,14 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { addScript(fileName: string, content: string) { this.projectVersion++; this.overrides.set(fileName, content); + this.overrideDirectory.add(path.dirname(fileName)); this.scriptNames.push(fileName); } forgetAngular() { this.angularPath = undefined; } overrideOptions(cb: (options: ts.CompilerOptions) => ts.CompilerOptions) { - this.options = cb(this.options); + this.options = cb((Object as any).assign({}, this.options)); this.projectVersion++; } @@ -136,6 +139,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; } directoryExists(directoryName: string): boolean { + if (this.overrideDirectory.has(directoryName)) return true; let effectiveName = this.getEffectiveName(directoryName); if (effectiveName === directoryName) return directoryExists(directoryName, this.data);