From 80ae52b7e9f70a2595cc64f9888d77ced4b58459 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Tue, 21 Apr 2015 13:24:02 -0700 Subject: [PATCH 1/7] expose the config file processing throught the LS and add a callback to enumerate files in a directory --- src/compiler/commandLineParser.ts | 4 +-- src/compiler/tsc.ts | 2 +- src/compiler/types.ts | 4 +++ src/harness/harnessLanguageService.ts | 8 ++++- src/server/editorServices.ts | 2 +- src/services/shims.ts | 49 ++++++++++++++++++++++++--- 6 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 8169421ca17..fc1293d6040 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -292,7 +292,7 @@ module ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseConfigFile(json: any, basePath?: string): ParsedCommandLine { + export function parseConfigFile(json: any, host: ParseConfigHost, basePath?: string): ParsedCommandLine { var errors: Diagnostic[] = []; return { @@ -351,7 +351,7 @@ module ts { } } else { - var sysFiles = sys.readDirectory(basePath, ".ts"); + var sysFiles = host.readDirectory(basePath, ".ts"); for (var i = 0; i < sysFiles.length; i++) { var name = sysFiles[i]; if (!fileExtensionIs(name, ".d.ts") || !contains(sysFiles, name.substr(0, name.length - 5) + ".ts")) { diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 0cbeff8204a..7eb86efbfae 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -213,7 +213,7 @@ module ts { reportDiagnostic(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, configFileName)); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } - var configParseResult = parseConfigFile(configObject, getDirectoryPath(configFileName)); + var configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName)); if (configParseResult.errors.length > 0) { reportDiagnostics(configParseResult.errors); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9191a6999bf..7decd8bd215 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1032,6 +1032,10 @@ module ts { getCurrentDirectory(): string; } + export interface ParseConfigHost { + readDirectory(rootDir: string, extension: string): string[]; + } + export interface WriteFileCallback { (fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void): void; } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index bce4c9828fa..e7c60e4d862 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -186,6 +186,7 @@ module Harness.LanguageService { var script = this.getScriptInfo(fileName); return script ? script.version.toString() : undefined; } + log(s: string): void { } trace(s: string): void { } error(s: string): void { } @@ -203,7 +204,7 @@ module Harness.LanguageService { } /// Shim adapter - class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceShimHost { + class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceShimHost, ts.CoreServicesShimHost { private nativeHost: NativeLanguageServiceHost; constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) { super(cancellationToken, options); @@ -227,6 +228,11 @@ module Harness.LanguageService { } getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); } getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); } + + readDirectory(rootDir: string, extension: string): string { + throw new Error("NYI"); + } + log(s: string): void { this.nativeHost.log(s); } trace(s: string): void { this.nativeHost.trace(s); } error(s: string): void { this.nativeHost.error(s); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 8b4ab2cffa8..9ed31c91968 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -910,7 +910,7 @@ module ts.server { return { errorMsg: "tsconfig syntax error" }; } else { - var parsedCommandLine = ts.parseConfigFile(rawConfig, dirPath); + var parsedCommandLine = ts.parseConfigFile(rawConfig, ts.sys, dirPath); if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) { return { errorMsg: "tsconfig option errors" }; } diff --git a/src/services/shims.ts b/src/services/shims.ts index 110090daf5c..a8de3b817c4 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -57,6 +57,12 @@ module ts { getNewLine?(): string; } + /** Public interface of the the of a config service shim instance.*/ + export interface CoreServicesShimHost extends Logger { + /** Returns a JSON-encoded value of the type: string[] */ + readDirectory(rootDir: string, extension: string): string; + } + /// /// Pre-processing /// @@ -77,7 +83,7 @@ module ts { export interface Shim { dispose(dummy: any): void; } - + export interface LanguageServiceShim extends Shim { languageService: LanguageService; @@ -188,6 +194,7 @@ module ts { export interface CoreServicesShim extends Shim { getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string; + getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getDefaultCompilationSettings(): string; } @@ -302,6 +309,17 @@ module ts { } } } + + export class CoreServicesShimHostAdapter implements ParseConfigHost { + + constructor(private shimHost: CoreServicesShimHost) { + } + + public readDirectory(rootDir: string, extension: string): string[] { + var encoded = this.shimHost.readDirectory(rootDir, extension); + return JSON.parse(encoded); + } + } function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any): any { logger.log(actionDescription); @@ -741,7 +759,8 @@ module ts { } class CoreServicesShimObject extends ShimBase implements CoreServicesShim { - constructor(factory: ShimFactory, public logger: Logger) { + + constructor(factory: ShimFactory, public logger: Logger, private host: CoreServicesShimHostAdapter) { super(factory); } @@ -779,6 +798,25 @@ module ts { }); } + public getTSConfigFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string { + return this.forwardJSONCall( + "getTSConfigFileInfo('" + fileName + "')", + () => { + var text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()); + var json = /\S/.test(text) ? JSON.parse(text) : {}; + + var configFile = parseConfigFile(json, this.host, getDirectoryPath(normalizeSlashes(fileName))); + + var realErrors = realizeDiagnostics(configFile.errors, '\r\n'); + + return { + options: configFile.options, + files: configFile.fileNames, + errors: realErrors + }; + }); + } + public getDefaultCompilationSettings(): string { return this.forwardJSONCall( "getDefaultCompilationSettings()", @@ -821,12 +859,13 @@ module ts { } } - public createCoreServicesShim(logger: Logger): CoreServicesShim { + public createCoreServicesShim(host: CoreServicesShimHost): CoreServicesShim { try { - return new CoreServicesShimObject(this, logger); + var adapter = new CoreServicesShimHostAdapter(host); + return new CoreServicesShimObject(this, host, adapter); } catch (err) { - logInternalError(logger, err); + logInternalError(host, err); throw err; } } From b1472d99f2c898105fcfee6164097d27aae45972 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Wed, 22 Apr 2015 14:27:05 -0700 Subject: [PATCH 2/7] Add error handling. --- src/services/shims.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/services/shims.ts b/src/services/shims.ts index a8de3b817c4..99df0e093a4 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -802,17 +802,27 @@ module ts { return this.forwardJSONCall( "getTSConfigFileInfo('" + fileName + "')", () => { - var text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()); - var json = /\S/.test(text) ? JSON.parse(text) : {}; + let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()); - var configFile = parseConfigFile(json, this.host, getDirectoryPath(normalizeSlashes(fileName))); + try { + var json = /\S/.test(text) ? JSON.parse(text) : {}; + } + catch (e) { + return { + options: {}, + files: [], + errors: realizeDiagnostic(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, fileName), '\r\n') + } + } - var realErrors = realizeDiagnostics(configFile.errors, '\r\n'); + if (json) { + var configFile = parseConfigFile(json, this.host, getDirectoryPath(normalizeSlashes(fileName))); + } return { options: configFile.options, files: configFile.fileNames, - errors: realErrors + errors: realizeDiagnostics(configFile.errors, '\r\n') }; }); } From 5c44a0ff3e93628a4cf59509c103dc827504d317 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Wed, 22 Apr 2015 15:58:04 -0700 Subject: [PATCH 3/7] Improve error message when encountering an invalid tsconfig.json file. --- src/compiler/commandLineParser.ts | 2 +- src/compiler/diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/tsc.ts | 9 ++++++--- src/services/shims.ts | 2 +- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 3645695ca76..a83e742a122 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -299,7 +299,7 @@ module ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseConfigFile(json: any, host: ParseConfigHost, basePath?: string): ParsedCommandLine { + export function parseConfigFile(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine { var errors: Diagnostic[] = []; return { diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 90f0ed0a0df..df7928d5717 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -439,6 +439,7 @@ module ts { Cannot_find_the_common_subdirectory_path_for_the_input_files: { code: 5009, category: DiagnosticCategory.Error, key: "Cannot find the common subdirectory path for the input files." }, Cannot_read_file_0_Colon_1: { code: 5012, category: DiagnosticCategory.Error, key: "Cannot read file '{0}': {1}" }, Unsupported_file_encoding: { code: 5013, category: DiagnosticCategory.Error, key: "Unsupported file encoding." }, + Failed_to_parse_file_0_Colon_1: { code: 5014, category: DiagnosticCategory.Error, key: "Failed to parse file '{0}': {1}." }, Unknown_compiler_option_0: { code: 5023, category: DiagnosticCategory.Error, key: "Unknown compiler option '{0}'." }, Compiler_option_0_requires_a_value_of_type_1: { code: 5024, category: DiagnosticCategory.Error, key: "Compiler option '{0}' requires a value of type {1}." }, Could_not_write_file_0_Colon_1: { code: 5033, category: DiagnosticCategory.Error, key: "Could not write file '{0}': {1}" }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 48ef245c766..afd21593f81 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1744,6 +1744,10 @@ "category": "Error", "code": 5013 }, + "Failed to parse file '{0}': {1}.": { + "category": "Error", + "code": 5014 + }, "Unknown compiler option '{0}'.": { "category": "Error", "code": 5023 diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 7eb86efbfae..89464e6b6e4 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -208,9 +208,12 @@ module ts { if (!cachedProgram) { if (configFileName) { - var configObject = readConfigFile(configFileName); - if (!configObject) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, configFileName)); + try { + var configObject = readConfigFile(configFileName); + } + catch (e) + { + reportDiagnostic(createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, configFileName, e.message)); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } var configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName)); diff --git a/src/services/shims.ts b/src/services/shims.ts index 99df0e093a4..6caec3cd23f 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -811,7 +811,7 @@ module ts { return { options: {}, files: [], - errors: realizeDiagnostic(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, fileName), '\r\n') + errors: realizeDiagnostic(createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message), '\r\n') } } From f8424d0b0c91a487a2c8cc226c89ffb359816b7b Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Wed, 22 Apr 2015 18:00:09 -0700 Subject: [PATCH 4/7] Minor clean up to make it more readable. --- src/compiler/commandLineParser.ts | 5 +++-- src/compiler/tsc.ts | 16 ++++++++-------- src/services/shims.ts | 24 +++++++++++++++--------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index a83e742a122..b477c97fec8 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -284,12 +284,13 @@ module ts { * Read tsconfig.json file * @param fileName The path to the config file */ - export function readConfigFile(fileName: string): any { + export function readConfigFile(fileName: string): { config?: any; error?: Diagnostic } { try { var text = sys.readFile(fileName); - return /\S/.test(text) ? JSON.parse(text) : {}; + return { config: /\S/.test(text) ? JSON.parse(text) : {} }; } catch (e) { + return { error: createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message) }; } } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 89464e6b6e4..15cc665e351 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -208,15 +208,15 @@ module ts { if (!cachedProgram) { if (configFileName) { - try { - var configObject = readConfigFile(configFileName); - } - catch (e) - { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, configFileName, e.message)); + + let result = readConfigFile(configFileName); + if (result.error) { + reportDiagnostic(result.error); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } - var configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName)); + + let configObject = result.config; + let configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName)); if (configParseResult.errors.length > 0) { reportDiagnostics(configParseResult.errors); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); @@ -233,7 +233,7 @@ module ts { compilerHost.getSourceFile = getSourceFile; } - var compileResult = compile(rootFileNames, compilerOptions, compilerHost); + let compileResult = compile(rootFileNames, compilerOptions, compilerHost); if (!compilerOptions.watch) { return sys.exit(compileResult.exitStatus); diff --git a/src/services/shims.ts b/src/services/shims.ts index 6caec3cd23f..ddcc32b8640 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -804,20 +804,17 @@ module ts { () => { let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()); - try { - var json = /\S/.test(text) ? JSON.parse(text) : {}; - } - catch (e) { + let result = this.parseConfigFileText(fileName, text); + + if (result.error) { return { options: {}, files: [], - errors: realizeDiagnostic(createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message), '\r\n') - } + errors: [realizeDiagnostic(result.error, '/r/n')] + }; } - if (json) { - var configFile = parseConfigFile(json, this.host, getDirectoryPath(normalizeSlashes(fileName))); - } + var configFile = parseConfigFile(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName))); return { options: configFile.options, @@ -827,6 +824,15 @@ module ts { }); } + private parseConfigFileText(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { + try { + return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} }; + } + catch (e) { + return { error: createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message) }; + } + } + public getDefaultCompilationSettings(): string { return this.forwardJSONCall( "getDefaultCompilationSettings()", From 8955d7ba572803cbb9f4353f1cc641bdf6f52bf1 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Wed, 22 Apr 2015 18:09:55 -0700 Subject: [PATCH 5/7] More refactoring --- src/compiler/commandLineParser.ts | 18 ++++++++++++++++-- src/services/shims.ts | 11 +---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index b477c97fec8..a4b25b08d23 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -284,10 +284,24 @@ module ts { * Read tsconfig.json file * @param fileName The path to the config file */ - export function readConfigFile(fileName: string): { config?: any; error?: Diagnostic } { + export function readConfigFile(fileName: string): { config?: any; error?: Diagnostic } { try { var text = sys.readFile(fileName); - return { config: /\S/.test(text) ? JSON.parse(text) : {} }; + return parseConfigFileText(fileName, text); + } + catch (e) { + return { error: createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message) }; + } + } + + /** + * Parse the text of the tsconfig.json file + * @param fileName The path to the config file + * @param jsonText The text of the config file + */ + export function parseConfigFileText(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { + try { + return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} }; } catch (e) { return { error: createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message) }; diff --git a/src/services/shims.ts b/src/services/shims.ts index ddcc32b8640..5c08e503b1c 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -804,7 +804,7 @@ module ts { () => { let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()); - let result = this.parseConfigFileText(fileName, text); + let result = parseConfigFileText(fileName, text); if (result.error) { return { @@ -824,15 +824,6 @@ module ts { }); } - private parseConfigFileText(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { - try { - return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} }; - } - catch (e) { - return { error: createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message) }; - } - } - public getDefaultCompilationSettings(): string { return this.forwardJSONCall( "getDefaultCompilationSettings()", From b6477607acd3e89383a468685ec6e5b2fca3987b Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Thu, 23 Apr 2015 10:17:57 -0700 Subject: [PATCH 6/7] correct the slashes for a new line. --- src/services/shims.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/shims.ts b/src/services/shims.ts index 5c08e503b1c..993627619cb 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -810,7 +810,7 @@ module ts { return { options: {}, files: [], - errors: [realizeDiagnostic(result.error, '/r/n')] + errors: [realizeDiagnostic(result.error, '\r\n')] }; } From a3885e5af085446ce926095c00519bf8b0915123 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Thu, 23 Apr 2015 13:36:54 -0700 Subject: [PATCH 7/7] Don't double wrap exceptions. --- src/compiler/commandLineParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index a4b25b08d23..ad05002fbfe 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -287,11 +287,11 @@ module ts { export function readConfigFile(fileName: string): { config?: any; error?: Diagnostic } { try { var text = sys.readFile(fileName); - return parseConfigFileText(fileName, text); } catch (e) { return { error: createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message) }; } + return parseConfigFileText(fileName, text); } /**