From b5ed7f3edaccf373adc07dd4018cad85b819cc43 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Tue, 19 Jan 2016 16:30:52 -0800 Subject: [PATCH 1/4] Add support for jsconfig.json in language service --- src/compiler/commandLineParser.ts | 11 ++++++++--- src/compiler/tsc.ts | 2 +- src/server/editorServices.ts | 16 ++++++++++------ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 247a204e9bdb2..af795377bc0fe 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -493,8 +493,9 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}): ParsedCommandLine { - const { options: optionsFromJsonConfigFile, errors } = convertCompilerOptionsFromJson(json["compilerOptions"], basePath); + export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, configFileName: string, existingOptions: CompilerOptions = {}): ParsedCommandLine { + const basePath = getDirectoryPath(configFileName); + const { options: optionsFromJsonConfigFile, errors } = convertCompilerOptionsFromJson(json["compilerOptions"], basePath, configFileName); const options = extend(existingOptions, optionsFromJsonConfigFile); return { @@ -547,10 +548,14 @@ namespace ts { } } - export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string): { options: CompilerOptions, errors: Diagnostic[] } { + export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } { const options: CompilerOptions = {}; const errors: Diagnostic[] = []; + if (configFileName && getBaseFileName(configFileName) === "jsconfig.json") { + options.allowJs = true; + } + if (!jsonOptions) { return { options, errors }; } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 808ee6da8046d..94dc8b1884739 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -376,7 +376,7 @@ namespace ts { sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); return; } - const configParseResult = parseJsonConfigFileContent(configObject, sys, getDirectoryPath(configFileName), commandLine.options); + const configParseResult = parseJsonConfigFileContent(configObject, sys, configFileName, commandLine.options); if (configParseResult.errors.length > 0) { reportDiagnostics(configParseResult.errors, /* compilerHost */ undefined); sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 7cc3a6c96a67c..a43252868d8de 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1026,10 +1026,16 @@ namespace ts.server { // the newly opened file. findConfigFile(searchPath: string): string { while (true) { - const fileName = ts.combinePaths(searchPath, "tsconfig.json"); - if (this.host.fileExists(fileName)) { - return fileName; + const tsconfigFileName = ts.combinePaths(searchPath, "tsconfig.json"); + if (this.host.fileExists(tsconfigFileName)) { + return tsconfigFileName; + } + + const jsconfigFileName = ts.combinePaths(searchPath, "jsconfig.json"); + if (this.host.fileExists(jsconfigFileName)) { + return jsconfigFileName; } + const parentPath = ts.getDirectoryPath(searchPath); if (parentPath === searchPath) { break; @@ -1172,15 +1178,13 @@ namespace ts.server { configFileToProjectOptions(configFilename: string): { succeeded: boolean, projectOptions?: ProjectOptions, error?: ProjectOpenResult } { configFilename = ts.normalizePath(configFilename); - // file references will be relative to dirPath (or absolute) - const dirPath = ts.getDirectoryPath(configFilename); const contents = this.host.readFile(configFilename); const rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileTextToJson(configFilename, contents); if (rawConfig.error) { return { succeeded: false, error: rawConfig.error }; } else { - const parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, dirPath); + const parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, configFilename); Debug.assert(!!parsedCommandLine.fileNames); if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) { From d64b603e4bf07935d129ad38d4488b0bfaba11c0 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Wed, 20 Jan 2016 16:53:15 -0800 Subject: [PATCH 2/4] revert breaking changes --- src/compiler/commandLineParser.ts | 5 ++--- src/compiler/tsc.ts | 4 ++-- src/server/editorServices.ts | 32 ++++++++++++++++--------------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index af795377bc0fe..2e6727749f1c1 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -493,8 +493,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, configFileName: string, existingOptions: CompilerOptions = {}): ParsedCommandLine { - const basePath = getDirectoryPath(configFileName); + export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string): ParsedCommandLine { const { options: optionsFromJsonConfigFile, errors } = convertCompilerOptionsFromJson(json["compilerOptions"], basePath, configFileName); const options = extend(existingOptions, optionsFromJsonConfigFile); @@ -524,7 +523,7 @@ namespace ts { for (const extension of supportedExtensions) { const filesInDirWithExtension = host.readDirectory(basePath, extension, exclude); for (const fileName of filesInDirWithExtension) { - // .ts extension would read the .d.ts extension files too but since .d.ts is lower priority extension, + // .ts extension would read the .d.ts extension files too but since .d.ts is lower priority extension, // lets pick them when its turn comes up if (extension === ".ts" && fileExtensionIs(fileName, ".d.ts")) { continue; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 94dc8b1884739..9b2457d091692 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -340,7 +340,7 @@ namespace ts { if (sys.watchDirectory && configFileName) { const directory = ts.getDirectoryPath(configFileName); directoryWatcher = sys.watchDirectory( - // When the configFileName is just "tsconfig.json", the watched directory should be + // When the configFileName is just "tsconfig.json", the watched directory should be // the current direcotry; if there is a given "project" parameter, then the configFileName // is an absolute file name. directory == "" ? "." : directory, @@ -376,7 +376,7 @@ namespace ts { sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); return; } - const configParseResult = parseJsonConfigFileContent(configObject, sys, configFileName, commandLine.options); + const configParseResult = parseJsonConfigFileContent(configObject, sys, getDirectoryPath(configFileName), commandLine.options); if (configParseResult.errors.length > 0) { reportDiagnostics(configParseResult.errors, /* compilerHost */ undefined); sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index a43252868d8de..e2ec5cc159f8f 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -120,7 +120,7 @@ namespace ts.server { if (!resolution) { const existingResolution = currentResolutionsInFile && ts.lookUp(currentResolutionsInFile, moduleName); if (moduleResolutionIsValid(existingResolution)) { - // ok, it is safe to use existing module resolution results + // ok, it is safe to use existing module resolution results resolution = existingResolution; } else { @@ -145,8 +145,8 @@ namespace ts.server { } if (resolution.resolvedModule) { - // TODO: consider checking failedLookupLocations - // TODO: use lastCheckTime to track expiration for module name resolution + // TODO: consider checking failedLookupLocations + // TODO: use lastCheckTime to track expiration for module name resolution return true; } @@ -483,7 +483,7 @@ namespace ts.server { openFileRootsConfigured: ScriptInfo[] = []; // a path to directory watcher map that detects added tsconfig files directoryWatchersForTsconfig: ts.Map = {}; - // count of how many projects are using the directory watcher. If the + // count of how many projects are using the directory watcher. If the // number becomes 0 for a watcher, then we should close it. directoryWatchersRefCount: ts.Map = {}; hostConfiguration: HostConfiguration; @@ -564,11 +564,11 @@ namespace ts.server { // We check if the project file list has changed. If so, we update the project. if (!arrayIsEqualTo(currentRootFiles && currentRootFiles.sort(), newRootFiles && newRootFiles.sort())) { // For configured projects, the change is made outside the tsconfig file, and - // it is not likely to affect the project for other files opened by the client. We can + // it is not likely to affect the project for other files opened by the client. We can // just update the current project. this.updateConfiguredProject(project); - // Call updateProjectStructure to clean up inferred projects we may have + // Call updateProjectStructure to clean up inferred projects we may have // created for the new files this.updateProjectStructure(); } @@ -792,8 +792,8 @@ namespace ts.server { * @param info The file that has been closed or newly configured */ closeOpenFile(info: ScriptInfo) { - // Closing file should trigger re-reading the file content from disk. This is - // because the user may chose to discard the buffer content before saving + // Closing file should trigger re-reading the file content from disk. This is + // because the user may chose to discard the buffer content before saving // to the disk, and the server's version of the file can be out of sync. info.svc.reloadFromFile(info.fileName); @@ -891,8 +891,8 @@ namespace ts.server { } /** - * This function is to update the project structure for every projects. - * It is called on the premise that all the configured projects are + * This function is to update the project structure for every projects. + * It is called on the premise that all the configured projects are * up to date. */ updateProjectStructure() { @@ -946,7 +946,7 @@ namespace ts.server { if (rootFile.defaultProject && rootFile.defaultProject.isConfiguredProject()) { // If the root file has already been added into a configured project, - // meaning the original inferred project is gone already. + // meaning the original inferred project is gone already. if (!rootedProject.isConfiguredProject()) { this.removeProject(rootedProject); } @@ -1059,9 +1059,9 @@ namespace ts.server { } /** - * This function tries to search for a tsconfig.json for the given file. If we found it, + * This function tries to search for a tsconfig.json for the given file. If we found it, * we first detect if there is already a configured project created for it: if so, we re-read - * the tsconfig file content and update the project; otherwise we create a new one. + * the tsconfig file content and update the project; otherwise we create a new one. */ openOrUpdateConfiguredProjectForFile(fileName: string) { const searchPath = ts.normalizePath(getDirectoryPath(fileName)); @@ -1178,13 +1178,15 @@ namespace ts.server { configFileToProjectOptions(configFilename: string): { succeeded: boolean, projectOptions?: ProjectOptions, error?: ProjectOpenResult } { configFilename = ts.normalizePath(configFilename); + // file references will be relative to dirPath (or absolute) + const dirPath = ts.getDirectoryPath(configFilename); const contents = this.host.readFile(configFilename); const rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileTextToJson(configFilename, contents); if (rawConfig.error) { return { succeeded: false, error: rawConfig.error }; } else { - const parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, configFilename); + const parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, dirPath, /*existingOptions*/ {}, configFilename); Debug.assert(!!parsedCommandLine.fileNames); if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) { @@ -1263,7 +1265,7 @@ namespace ts.server { info = this.openFile(fileName, /*openedByClient*/ false); } else { - // if the root file was opened by client, it would belong to either + // if the root file was opened by client, it would belong to either // openFileRoots or openFileReferenced. if (info.isOpen) { if (this.openFileRoots.indexOf(info) >= 0) { From ea94a05feb963db390b875894d52dfedcde40d5f Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Thu, 21 Jan 2016 10:43:07 -0800 Subject: [PATCH 3/4] Add support for jsconfig in shims --- src/services/shims.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/shims.ts b/src/services/shims.ts index 3dc28763b1769..9ca3f19244d89 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -946,7 +946,8 @@ namespace ts { }; } - const configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName))); + const normalizedFileName = normalizeSlashes(fileName); + const configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizedFileName), /*existingOptions*/ {}, normalizedFileName); return { options: configFile.options, From 31f5502f2b4ae4e4ad1253c0f63b5e0241c8447f Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Thu, 21 Jan 2016 16:05:44 -0800 Subject: [PATCH 4/4] set default module to commonjs for jsconfig.json --- src/compiler/commandLineParser.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 2e6727749f1c1..733e3158593df 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -552,6 +552,7 @@ namespace ts { const errors: Diagnostic[] = []; if (configFileName && getBaseFileName(configFileName) === "jsconfig.json") { + options.module = ModuleKind.CommonJS; options.allowJs = true; }