From 4de96abd8fc5d678d6b494a953d3127dd2acf871 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 16 Jun 2017 14:37:39 -0700 Subject: [PATCH] Use the same logic of getting current directory as the one used when emitting files through project It means we would use currentDirectory as project Root or script info's directory as the current directory Fixes issue reported in https://developercommunity.visualstudio.com/content/problem/57099/typescript-generated-source-maps-have-invalid-path.html --- src/compiler/program.ts | 12 +++---- src/compiler/types.ts | 6 +++- src/harness/unittests/compileOnSave.ts | 45 +++++++++++++++++++++++++- src/server/builder.ts | 4 +-- src/server/project.ts | 11 ++++++- src/services/services.ts | 4 +-- src/services/transpile.ts | 2 +- src/services/types.ts | 2 +- 8 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bbc0fa09780..30c61f50dff 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -874,12 +874,12 @@ namespace ts { return oldProgram.structureIsReused = StructureIsReused.Completely; } - function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost { + function getEmitHost(writeFileCallback?: WriteFileCallback, getCurrentDirectoryCallback?: GetCurrentDirectoryCallback): EmitHost { return { getCanonicalFileName, getCommonSourceDirectory: program.getCommonSourceDirectory, getCompilerOptions: program.getCompilerOptions, - getCurrentDirectory: () => currentDirectory, + getCurrentDirectory: getCurrentDirectoryCallback || (() => currentDirectory), getNewLine: () => host.getNewLine(), getSourceFile: program.getSourceFile, getSourceFileByPath: program.getSourceFileByPath, @@ -907,15 +907,15 @@ namespace ts { return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false)); } - function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers): EmitResult { - return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers)); + function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, getCurrentDirectoryCallback?: GetCurrentDirectoryCallback): EmitResult { + return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, getCurrentDirectoryCallback)); } function isEmitBlocked(emitFileName: string): boolean { return hasEmitBlockingDiagnostics.contains(toPath(emitFileName, currentDirectory, getCanonicalFileName)); } - function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult { + function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, getCurrentDirectoryCallback?: GetCurrentDirectoryCallback): EmitResult { let declarationDiagnostics: Diagnostic[] = []; if (options.noEmit) { @@ -960,7 +960,7 @@ namespace ts { const transformers = emitOnlyDtsFiles ? [] : getTransformers(options, customTransformers); const emitResult = emitFiles( emitResolver, - getEmitHost(writeFileCallback), + getEmitHost(writeFileCallback, getCurrentDirectoryCallback), sourceFile, emitOnlyDtsFiles, transformers); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ac64624e1a6..ac5de153226 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2405,6 +2405,10 @@ namespace ts { (fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void, sourceFiles?: SourceFile[]): void; } + export interface GetCurrentDirectoryCallback { + (): string; + } + export class OperationCanceledException { } export interface CancellationToken { @@ -2436,7 +2440,7 @@ namespace ts { * used for writing the JavaScript and declaration files. Otherwise, the writeFile parameter * will be invoked when writing the JavaScript and declaration files. */ - emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult; + emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, getCurrentDirectoryCallback?: GetCurrentDirectoryCallback): EmitResult; getOptionsDiagnostics(cancellationToken?: CancellationToken): Diagnostic[]; getGlobalDiagnostics(cancellationToken?: CancellationToken): Diagnostic[]; diff --git a/src/harness/unittests/compileOnSave.ts b/src/harness/unittests/compileOnSave.ts index 7e262a1b257..3183ffd71b0 100644 --- a/src/harness/unittests/compileOnSave.ts +++ b/src/harness/unittests/compileOnSave.ts @@ -600,5 +600,48 @@ namespace ts.projectSystem { assert.isTrue(outFileContent.indexOf(file2.content) === -1); assert.isTrue(outFileContent.indexOf(file3.content) === -1); }); + + it("should use project root as current directory so that compile on save results in correct file mapping", () => { + const inputFileName = "Foo.ts"; + const file1 = { + path: `/root/TypeScriptProject3/TypeScriptProject3/${inputFileName}`, + content: "consonle.log('file1');" + }; + const externalProjectName = "/root/TypeScriptProject3/TypeScriptProject3/TypeScriptProject3.csproj"; + const host = createServerHost([file1, libFile]); + const session = createSession(host); + const projectService = session.getProjectService(); + + const outFileName = "bar.js"; + projectService.openExternalProject({ + rootFiles: toExternalFiles([file1.path]), + options: { + outFile: outFileName, + sourceMap: true, + compileOnSave: true + }, + projectFileName: externalProjectName + }); + + const emitRequest = makeSessionRequest(CommandNames.CompileOnSaveEmitFile, { file: file1.path }); + session.executeCommand(emitRequest); + + // Verify js file + const expectedOutFileName = "/root/TypeScriptProject3/TypeScriptProject3/" + outFileName; + assert.isTrue(host.fileExists(expectedOutFileName)); + const outFileContent = host.readFile(expectedOutFileName); + verifyContentHasString(outFileContent, file1.content); + verifyContentHasString(outFileContent, `//# sourceMappingURL=${outFileName}.map`); + + // Verify map file + const expectedMapFileName = expectedOutFileName + ".map"; + assert.isTrue(host.fileExists(expectedMapFileName)); + const mapFileContent = host.readFile(expectedMapFileName); + verifyContentHasString(mapFileContent, `"sources":["${inputFileName}"]`); + + function verifyContentHasString(content: string, string: string) { + assert.isTrue(content.indexOf(string) !== -1, `Expected "${content}" to have "${string}"`); + } + }); }); -} \ No newline at end of file +} diff --git a/src/server/builder.ts b/src/server/builder.ts index 895732ebece..711045d0ae6 100644 --- a/src/server/builder.ts +++ b/src/server/builder.ts @@ -148,9 +148,9 @@ namespace ts.server { const { emitSkipped, outputFiles } = this.project.getFileEmitOutput(fileInfo.scriptInfo, /*emitOnlyDtsFiles*/ false); if (!emitSkipped) { - const projectRootPath = this.project.getProjectRootPath(); + const currentDirectoryForEmit = this.project.getCurrentDirectoryForScriptInfoEmit(scriptInfo); for (const outputFile of outputFiles) { - const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, projectRootPath ? projectRootPath : getDirectoryPath(scriptInfo.fileName)); + const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, currentDirectoryForEmit); writeFile(outputFileAbsoluteFileName, outputFile.text, outputFile.writeByteOrderMark); } } diff --git a/src/server/project.ts b/src/server/project.ts index ac040a77ace..a7d8605315b 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -367,7 +367,16 @@ namespace ts.server { if (!this.languageServiceEnabled) { return undefined; } - return this.getLanguageService().getEmitOutput(info.fileName, emitOnlyDtsFiles); + + const getCurrentDirectoryCallback = memoize( + () => this.getCurrentDirectoryForScriptInfoEmit(info) + ); + return this.getLanguageService().getEmitOutput(info.fileName, emitOnlyDtsFiles, getCurrentDirectoryCallback); + } + + getCurrentDirectoryForScriptInfoEmit(info: ScriptInfo) { + const projectRootPath = this.getProjectRootPath(); + return projectRootPath || getDirectoryPath(info.fileName); } getFileNames(excludeFilesFromExternalLibraries?: boolean, excludeConfigFiles?: boolean) { diff --git a/src/services/services.ts b/src/services/services.ts index b508285b182..11061181ee6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1528,7 +1528,7 @@ namespace ts { return ts.NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount, excludeDtsFiles); } - function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput { + function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, getCurrentDirectoryCallback?: GetCurrentDirectoryCallback): EmitOutput { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); @@ -1543,7 +1543,7 @@ namespace ts { } const customTransformers = host.getCustomTransformers && host.getCustomTransformers(); - const emitOutput = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); + const emitOutput = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, getCurrentDirectoryCallback); return { outputFiles, diff --git a/src/services/transpile.ts b/src/services/transpile.ts index 561c188c6cd..5ba393a90c9 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -104,7 +104,7 @@ namespace ts { addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); } // Emit - program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, transpileOptions.transformers); + program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, transpileOptions.transformers, /*getCurrentDirectoryCallback*/ undefined); Debug.assert(outputText !== undefined, "Output generation failed"); diff --git a/src/services/types.ts b/src/services/types.ts index 2d47da2fd1d..07aaaeeb4b4 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -269,7 +269,7 @@ namespace ts { getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined; - getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; + getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, getCurrentDirectoryCallBack?: GetCurrentDirectoryCallback): EmitOutput; getProgram(): Program;