diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 350d75429b7..f930ce295fb 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -583,19 +583,40 @@ namespace ts { }; } + function recursiveCreateDirectory(directoryPath: string, sys: System) { + const basePath = getDirectoryPath(directoryPath); + const shouldCreateParent = directoryPath !== basePath && !sys.directoryExists(basePath); + if (shouldCreateParent) { + recursiveCreateDirectory(basePath, sys); + } + if (shouldCreateParent || !sys.directoryExists(directoryPath)) { + sys.createDirectory(directoryPath); + } + } + + let sys: System; if (typeof ChakraHost !== "undefined") { - return getChakraSystem(); + sys = getChakraSystem(); } else if (typeof WScript !== "undefined" && typeof ActiveXObject === "function") { - return getWScriptSystem(); + sys = getWScriptSystem(); } else if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof require !== "undefined") { // process and process.nextTick checks if current environment is node-like // process.browser check excludes webpack and browserify - return getNodeSystem(); + sys = getNodeSystem(); } - else { - return undefined; // Unsupported host + if (sys) { + // patch writefile to create folder before writing the file + const originalWriteFile = sys.writeFile; + sys.writeFile = function(path, data, writeBom) { + const directoryPath = getDirectoryPath(normalizeSlashes(path)); + if (!sys.directoryExists(directoryPath)) { + recursiveCreateDirectory(directoryPath, sys); + } + originalWriteFile.call(sys, path, data, writeBom); + }; } + return sys; })(); } diff --git a/src/server/builder.ts b/src/server/builder.ts index 1c5ceaf8eb2..639c41d2b63 100644 --- a/src/server/builder.ts +++ b/src/server/builder.ts @@ -140,8 +140,10 @@ namespace ts.server { const { emitSkipped, outputFiles } = this.project.getFileEmitOutput(fileInfo.scriptInfo, /*emitOnlyDtsFiles*/ false); if (!emitSkipped) { + const projectRootPath = this.project.getProjectRootPath(); for (const outputFile of outputFiles) { - writeFile(outputFile.name, outputFile.text, outputFile.writeByteOrderMark); + const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, projectRootPath ? projectRootPath : getDirectoryPath(scriptInfo.fileName)); + writeFile(outputFileAbsoluteFileName, outputFile.text, outputFile.writeByteOrderMark); } } return !emitSkipped; diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 1f82f2cbe0a..32aa40591ad 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -183,7 +183,7 @@ namespace ts.server { constructor(public readonly host: ServerHost, public readonly logger: Logger, public readonly cancellationToken: HostCancellationToken, - private readonly useSingleInferredProject: boolean, + public readonly useSingleInferredProject: boolean, readonly typingsInstaller: ITypingsInstaller = nullTypingsInstaller, private readonly eventHandler?: ProjectServiceEventHandler) { diff --git a/src/server/project.ts b/src/server/project.ts index 7708cf797f0..9b4c7619fbb 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -143,6 +143,7 @@ namespace ts.server { } abstract getProjectName(): string; + abstract getProjectRootPath(): string | undefined; abstract getTypingOptions(): TypingOptions; getSourceFile(path: Path) { @@ -537,6 +538,15 @@ namespace ts.server { return this.inferredProjectName; } + getProjectRootPath() { + // Single inferred project does not have a project root. + if (this.projectService.useSingleInferredProject) { + return undefined; + } + const rootFiles = this.getRootFiles(); + return getDirectoryPath(rootFiles[0]); + } + close() { super.close(); @@ -574,6 +584,10 @@ namespace ts.server { super(ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, languageServiceEnabled, compilerOptions, compileOnSaveEnabled); } + getProjectRootPath() { + return getDirectoryPath(this.configFileName); + } + setProjectErrors(projectErrors: Diagnostic[]) { this.projectErrors = projectErrors; } @@ -662,10 +676,21 @@ namespace ts.server { documentRegistry: ts.DocumentRegistry, compilerOptions: CompilerOptions, languageServiceEnabled: boolean, - public compileOnSaveEnabled: boolean) { + public compileOnSaveEnabled: boolean, + private readonly projectFilePath?: string) { super(ProjectKind.External, projectService, documentRegistry, /*hasExplicitListOfFiles*/ true, languageServiceEnabled, compilerOptions, compileOnSaveEnabled); } + getProjectRootPath() { + if (this.projectFilePath) { + return getDirectoryPath(this.projectFilePath); + } + // if the projectFilePath is not given, we make the assumption that the project name + // is the path of the project file. AS the project name is provided by VS, we need to + // normalize slashes before using it as a file name. + return getDirectoryPath(normalizeSlashes(this.externalProjectName)); + } + getTypingOptions() { return this.typingOptions; }