diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 159dc27bb47..3b3127f8a41 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1416,7 +1416,8 @@ namespace ts { Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined)); const errors: Diagnostic[] = []; - const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors); + const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); + const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, getCanonicalFileName, resolutionStack, errors); const { raw } = parsedConfig; const options = extend(existingOptions, parsedConfig.options || {}); options.configFilePath = configFileName; @@ -1525,11 +1526,11 @@ namespace ts { host: ParseConfigHost, basePath: string, configFileName: string, + getCanonicalFileName: (fileName: string) => string, resolutionStack: Path[], errors: Diagnostic[], ): ParsedTsconfig { basePath = normalizeSlashes(basePath); - const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName); if (resolutionStack.indexOf(resolvedPath) >= 0) { @@ -1711,7 +1712,7 @@ namespace ts { const extendedDirname = getDirectoryPath(extendedConfigPath); const extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, extendedDirname, - getBaseFileName(extendedConfigPath), resolutionStack, errors); + getBaseFileName(extendedConfigPath), getCanonicalFileName, resolutionStack, errors); if (sourceFile) { sourceFile.extendedSourceFiles.push(...extendedResult.extendedSourceFiles); } @@ -1954,6 +1955,10 @@ namespace ts { basePath = normalizePath(basePath); let validatedIncludeSpecs: string[], validatedExcludeSpecs: string[]; + // The exclude spec list is converted into a regular expression, which allows us to quickly + // test whether a file or directory should be excluded before recursively traversing the + // file system. + if (includeSpecs) { validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*allowTrailingRecursion*/ false, jsonSourceFile, "include"); } @@ -1986,9 +1991,6 @@ namespace ts { export function getFileNamesFromConfigSpecs(spec: ConfigFileSpecs, basePath: string, options: CompilerOptions, host: ParseConfigHost, extraFileExtensions: JsFileExtensionInfo[]): ExpandResult { basePath = normalizePath(basePath); - // The exclude spec list is converted into a regular expression, which allows us to quickly - // test whether a file or directory should be excluded before recursively traversing the - // file system. const keyMapper = host.useCaseSensitiveFileNames ? caseSensitiveKeyMapper : caseInsensitiveKeyMapper; // Literal file names (provided via the "files" array in tsconfig.json) are stored in a diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 1b2793e5f2f..db7234c8824 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1198,7 +1198,7 @@ namespace ts.server { } private openConfigFile(configFileName: NormalizedPath, clientFileName?: string) { - const cachedServerHost = new CachedServerHost(this.host); + const cachedServerHost = new CachedServerHost(this.host, this.toCanonicalFileName); const { success, projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedServerHost); if (success) { this.logger.info(`Opened configuration file ${configFileName}`); @@ -1366,10 +1366,11 @@ namespace ts.server { } getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean) { - let info = this.getScriptInfoForNormalizedPath(fileName); + const path = toPath(fileName, this.host.getCurrentDirectory(), this.toCanonicalFileName); + let info = this.getScriptInfoForPath(path); if (!info) { if (openedByClient || this.host.fileExists(fileName)) { - info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent); + info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, path); this.filenameToScriptInfo.set(info.path, info); diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index b54f493be1b..a7e9c5e231c 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -11,15 +11,13 @@ namespace ts.server { readonly trace: (s: string) => void; readonly realpath?: (path: string) => string; - private getCanonicalFileName: (fileName: string) => string; private cachedReadDirectoryResult = createMap(); private readonly currentDirectory: string; - constructor(private readonly host: ServerHost) { + constructor(private readonly host: ServerHost, private getCanonicalFileName: (fileName: string) => string) { this.args = host.args; this.newLine = host.newLine; this.useCaseSensitiveFileNames = host.useCaseSensitiveFileNames; - this.getCanonicalFileName = createGetCanonicalFileName(this.useCaseSensitiveFileNames); if (host.trace) { this.trace = s => host.trace(s); } @@ -193,21 +191,23 @@ namespace ts.server { private compilationSettings: CompilerOptions; private readonly resolvedModuleNames = createMap>(); private readonly resolvedTypeReferenceDirectives = createMap>(); - /* @internal */ - readonly getCanonicalFileName: (fileName: string) => string; private filesWithChangedSetOfUnresolvedImports: Path[]; private resolveModuleName: typeof resolveModuleName; readonly trace: (s: string) => void; readonly realpath?: (path: string) => string; + /** + * This is the host that is associated with the project. This is normally same as projectService's host + * except in Configured projects where it is CachedServerHost so that we can cache the results of the + * file system entries as we would anyways be watching files in the project (so safe to cache) + */ /*@internal*/ host: ServerHost; constructor(host: ServerHost, private project: Project, private readonly cancellationToken: HostCancellationToken) { this.host = host; this.cancellationToken = new ThrottledCancellationToken(cancellationToken, project.projectService.throttleWaitMilliseconds); - this.getCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames); if (host.trace) { this.trace = s => host.trace(s); @@ -262,7 +262,7 @@ namespace ts.server { getResultFileName: (result: R) => string | undefined, logChanges: boolean): R[] { - const path = toPath(containingFile, this.host.getCurrentDirectory(), this.getCanonicalFileName); + const path = toPath(containingFile, this.host.getCurrentDirectory(), this.project.projectService.toCanonicalFileName); const currentResolutionsInFile = cache.get(path); const newResolutions: Map = createMap(); @@ -403,7 +403,7 @@ namespace ts.server { fileExists(file: string): boolean { // As an optimization, don't hit the disks for files we already know don't exist // (because we're watching for their creation). - const path = toPath(file, this.host.getCurrentDirectory(), this.getCanonicalFileName); + const path = toPath(file, this.host.getCurrentDirectory(), this.project.projectService.toCanonicalFileName); return !this.project.isWatchedMissingFile(path) && this.host.fileExists(file); } diff --git a/src/server/project.ts b/src/server/project.ts index 9db74efeb6b..5a397bdf3a4 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -495,7 +495,7 @@ namespace ts.server { // add a root file to project addMissingFileRoot(fileName: NormalizedPath) { const path = toPath(fileName, this.projectService.host.getCurrentDirectory(), - createGetCanonicalFileName(this.projectService.host.useCaseSensitiveFileNames)); + this.projectService.toCanonicalFileName); this.rootFilesMap.set(path, fileName); this.markAsDirty(); } @@ -837,11 +837,10 @@ namespace ts.server { } const currentDirectory = getDirectoryPath(path); - const getCanonicalFileName = createGetCanonicalFileName(this.projectService.host.useCaseSensitiveFileNames); // Handle triple slash references if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { for (const referencedFile of sourceFile.referencedFiles) { - const referencedPath = toPath(referencedFile.fileName, currentDirectory, getCanonicalFileName); + const referencedPath = toPath(referencedFile.fileName, currentDirectory, this.projectService.toCanonicalFileName); referencedFiles.set(referencedPath, true); } } @@ -854,7 +853,7 @@ namespace ts.server { } const fileName = resolvedTypeReferenceDirective.resolvedFileName; - const typeFilePath = toPath(fileName, currentDirectory, getCanonicalFileName); + const typeFilePath = toPath(fileName, currentDirectory, this.projectService.toCanonicalFileName); referencedFiles.set(typeFilePath, true); }); } diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 3da4cdb14af..46008e77340 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -148,7 +148,6 @@ namespace ts.server { */ readonly containingProjects: Project[] = []; private formatCodeSettings: FormatCodeSettings; - readonly path: Path; private fileWatcher: FileWatcher; private textStorage: TextStorage; @@ -159,9 +158,9 @@ namespace ts.server { private readonly host: ServerHost, readonly fileName: NormalizedPath, readonly scriptKind: ScriptKind, - public hasMixedContent = false) { + public hasMixedContent: boolean, + readonly path: Path) { - this.path = toPath(fileName, host.getCurrentDirectory(), createGetCanonicalFileName(host.useCaseSensitiveFileNames)); this.textStorage = new TextStorage(host, fileName); if (hasMixedContent) { this.textStorage.reload("");