From 0dd944aa14b2325a283044749a9fbbb4fe173b33 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 23 Nov 2016 16:09:34 -0800 Subject: [PATCH] Clean up api so that parsing returns JsonSourceFile --- src/compiler/commandLineParser.ts | 76 +++++++++---------- src/compiler/core.ts | 2 + src/compiler/parser.ts | 30 ++++---- src/compiler/tsc.ts | 6 +- src/compiler/types.ts | 9 ++- src/harness/harness.ts | 4 +- src/harness/projectsRunner.ts | 15 +--- src/harness/rwcRunner.ts | 2 +- .../unittests/configurationExtension.ts | 32 ++++---- .../convertCompilerOptionsFromJson.ts | 8 +- .../convertTypeAcquisitionFromJson.ts | 8 +- src/harness/unittests/matchFiles.ts | 9 ++- src/harness/unittests/tsconfigParsing.ts | 9 ++- src/server/editorServices.ts | 14 ++-- src/services/shims.ts | 8 +- 15 files changed, 117 insertions(+), 115 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 57a3931dda4..2ce46232a25 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -462,7 +462,7 @@ namespace ts { ]; /* @internal */ - export let typeAcquisitionDeclarations: CommandLineOption[] = [ + export const typeAcquisitionDeclarations: CommandLineOption[] = [ { /* @deprecated typingOptions.enableAutoDiscovery * Use typeAcquisition.enable instead. @@ -723,10 +723,10 @@ namespace ts { * @param jsonText The text of the config file */ export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { - const { node, errors } = parseJsonText(fileName, jsonText); + const jsonSourceFile = parseJsonText(fileName, jsonText); return { - config: convertToJson(node, errors), - error: errors.length ? errors[0] : undefined + config: convertToJson(jsonSourceFile, jsonSourceFile.parseDiagnostics), + error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : undefined }; } @@ -734,13 +734,13 @@ namespace ts { * Read tsconfig.json file * @param fileName The path to the config file */ - export function readConfigFileToJsonNode(fileName: string, readFile: (path: string) => string): ParsedNodeResults { + export function readConfigFileToJsonSourceFile(fileName: string, readFile: (path: string) => string): JsonSourceFile { let text = ""; try { text = readFile(fileName); } catch (e) { - return { errors: [createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message)] }; + return { parseDiagnostics: [createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message)] }; } return parseJsonText(fileName, text); } @@ -819,8 +819,8 @@ namespace ts { * @param jsonNode * @param errors */ - export function convertToJson(jsonNode: JsonNode, errors: Diagnostic[]): any { - return convertToJsonWorker(jsonNode, errors); + export function convertToJson(sourceFile: JsonSourceFile, errors: Diagnostic[]): any { + return convertToJsonWorker(sourceFile, errors); } /** @@ -828,17 +828,15 @@ namespace ts { * @param jsonNode * @param errors */ - function convertToJsonWorker(jsonNode: JsonNode, errors: Diagnostic[], knownRootOptions?: Map, optionsIterator?: JsonConversionNotifier): any { - if (!jsonNode) { + function convertToJsonWorker(sourceFile: JsonSourceFile, errors: Diagnostic[], knownRootOptions?: Map, optionsIterator?: JsonConversionNotifier): any { + if (!sourceFile.jsonObject) { + if (sourceFile.endOfFileToken) { + return {}; + } return undefined; } - if (jsonNode.kind === SyntaxKind.EndOfFileToken) { - return {}; - } - - const sourceFile = jsonNode.parent; - return convertObjectLiteralExpressionToJson(jsonNode, knownRootOptions); + return convertObjectLiteralExpressionToJson(sourceFile.jsonObject, knownRootOptions); function convertObjectLiteralExpressionToJson(node: ObjectLiteralExpression, options?: Map, extraKeyDiagnosticMessage?: DiagnosticMessage, optionsObject?: string): any { const result: any = {}; @@ -1076,7 +1074,7 @@ namespace ts { * file to. e.g. outDir */ export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[]): ParsedCommandLine { - return parseJsonConfigFileContentWorker(json, /*jsonNode*/ undefined, host, basePath, existingOptions, configFileName, resolutionStack); + return parseJsonConfigFileContentWorker(json, /*sourceFile*/ undefined, host, basePath, existingOptions, configFileName, resolutionStack); } /** @@ -1086,8 +1084,8 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonNodeConfigFileContent(jsonNode: JsonNode, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[]): ParsedCommandLine { - return parseJsonConfigFileContentWorker(/*json*/ undefined, jsonNode, host, basePath, existingOptions, configFileName, resolutionStack); + export function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[]): ParsedCommandLine { + return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack); } /** @@ -1097,8 +1095,8 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - function parseJsonConfigFileContentWorker(json: any, jsonNode: JsonNode, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = []): ParsedCommandLine { - Debug.assert((json === undefined && jsonNode !== undefined) || (json !== undefined && jsonNode === undefined)); + function parseJsonConfigFileContentWorker(json: any, sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = []): ParsedCommandLine { + Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined)); const errors: Diagnostic[] = []; const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName); @@ -1107,7 +1105,7 @@ namespace ts { options: {}, fileNames: [], typeAcquisition: {}, - raw: json || convertToJson(jsonNode, errors), + raw: json || convertToJson(sourceFile, errors), errors: errors.concat(createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))), wildcardDirectories: {} }; @@ -1141,7 +1139,7 @@ namespace ts { switch (key) { case "extends": const extendsDiagnostic = getExtendsConfigPath(value, (message, arg0) => - createDiagnosticForNodeInSourceFile(jsonNode.parent, node, message, arg0)); + createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0)); if ((extendsDiagnostic).messageText) { errors.push(extendsDiagnostic); hasExtendsError = true; @@ -1151,11 +1149,11 @@ namespace ts { } return; case "excludes": - errors.push(createDiagnosticForNodeInSourceFile(jsonNode.parent, propertyName, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude)); + errors.push(createDiagnosticForNodeInSourceFile(sourceFile, propertyName, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude)); return; case "files": if ((value).length === 0) { - errors.push(createDiagnosticForNodeInSourceFile(jsonNode.parent, node, Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json")); + errors.push(createDiagnosticForNodeInSourceFile(sourceFile, node, Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json")); } return; case "compileOnSave": @@ -1164,7 +1162,7 @@ namespace ts { } } }; - json = convertToJsonWorker(jsonNode, errors, getTsconfigRootOptionsMap(), optionsIterator); + json = convertToJsonWorker(sourceFile, errors, getTsconfigRootOptionsMap(), optionsIterator); if (!typeAcquisition) { if (typingOptionstypeAcquisition) { typeAcquisition = (typingOptionstypeAcquisition.enableAutoDiscovery !== undefined) ? @@ -1244,16 +1242,16 @@ namespace ts { extendedConfigPath = `${extendedConfigPath}.json` as Path; } - const extendedResult = readConfigFileToJsonNode(extendedConfigPath, path => host.readFile(path)); - if (extendedResult.errors.length) { - errors.push(...extendedResult.errors); + const extendedResult = readConfigFileToJsonSourceFile(extendedConfigPath, path => host.readFile(path)); + if (extendedResult.parseDiagnostics.length) { + errors.push(...extendedResult.parseDiagnostics); return; } const extendedDirname = getDirectoryPath(extendedConfigPath); const relativeDifference = convertToRelativePath(extendedDirname, basePath, getCanonicalFileName); const updatePath: (path: string) => string = path => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path); // Merge configs (copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios) - const result = parseJsonNodeConfigFileContent(extendedResult.node, host, extendedDirname, /*existingOptions*/undefined, getBaseFileName(extendedConfigPath), resolutionStack.concat([resolvedPath])); + const result = parseJsonSourceFileConfigFileContent(extendedResult, host, extendedDirname, /*existingOptions*/undefined, getBaseFileName(extendedConfigPath), resolutionStack.concat([resolvedPath])); errors.push(...result.errors); const [include, exclude, files] = map(["include", "exclude", "files"], key => { if (!json[key] && result.raw[key]) { @@ -1313,7 +1311,7 @@ namespace ts { includeSpecs = ["**/*"]; } - const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors, jsonNode); + const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors, sourceFile); if (result.fileNames.length === 0 && !hasProperty(json, "files") && resolutionStack.length === 0) { errors.push( @@ -1328,7 +1326,7 @@ namespace ts { } function createCompilerDiagnosticForJson(message: DiagnosticMessage, arg0?: string, arg1?: string) { - if (!jsonNode) { + if (!sourceFile) { errors.push(createCompilerDiagnostic(message, arg0, arg1)); } } @@ -1548,7 +1546,7 @@ namespace ts { * @param host The host used to resolve files and directories. * @param errors An array for diagnostic reporting. */ - function matchFileNames(fileNames: string[], include: string[], exclude: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[], jsonNode: JsonNode): ExpandResult { + function matchFileNames(fileNames: string[], include: string[], exclude: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[], jsonSourceFile: JsonSourceFile): ExpandResult { basePath = normalizePath(basePath); // The exclude spec list is converted into a regular expression, which allows us to quickly @@ -1567,11 +1565,11 @@ namespace ts { const wildcardFileMap = createMap(); if (include) { - include = validateSpecs(include, errors, /*allowTrailingRecursion*/ false, jsonNode, "include"); + include = validateSpecs(include, errors, /*allowTrailingRecursion*/ false, jsonSourceFile, "include"); } if (exclude) { - exclude = validateSpecs(exclude, errors, /*allowTrailingRecursion*/ true, jsonNode, "exclude"); + exclude = validateSpecs(exclude, errors, /*allowTrailingRecursion*/ true, jsonSourceFile, "exclude"); } // Wildcard directories (provided as part of a wildcard path) are stored in a @@ -1627,7 +1625,7 @@ namespace ts { }; } - function validateSpecs(specs: string[], errors: Diagnostic[], allowTrailingRecursion: boolean, jsonNode: JsonNode, specKey: string) { + function validateSpecs(specs: string[], errors: Diagnostic[], allowTrailingRecursion: boolean, jsonSourceFile: JsonSourceFile, specKey: string) { const validSpecs: string[] = []; for (const spec of specs) { if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { @@ -1647,13 +1645,13 @@ namespace ts { return validSpecs; function createDiagnostic(message: DiagnosticMessage, spec: string): Diagnostic { - if (jsonNode && jsonNode.kind === SyntaxKind.ObjectLiteralExpression) { - for (const property of jsonNode.properties) { + if (jsonSourceFile && jsonSourceFile.jsonObject) { + for (const property of jsonSourceFile.jsonObject.properties) { if (property.kind === SyntaxKind.PropertyAssignment && getTextOfPropertyName(property.name) === specKey) { const specsNode = property.initializer; for (const element of specsNode.elements) { if (element.kind === SyntaxKind.StringLiteral && (element).text === spec) { - return createDiagnosticForNodeInSourceFile(jsonNode.parent, element, message, spec); + return createDiagnosticForNodeInSourceFile(jsonSourceFile, element, message, spec); } } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 89057dd2939..c1f4c61c272 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1928,6 +1928,8 @@ namespace ts { return ScriptKind.TS; case ".tsx": return ScriptKind.TSX; + case ".json": + return ScriptKind.JSON; default: return ScriptKind.Unknown; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b6885cb7ade..d534f42e171 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -450,14 +450,12 @@ namespace ts { return Parser.parseIsolatedEntityName(text, languageVersion); } - export type ParsedNodeResults = { node?: T; errors: Diagnostic[] }; - /** * Parse json text into SyntaxTree and return node and parse errors if any * @param fileName * @param sourceText */ - export function parseJsonText(fileName: string, sourceText: string): ParsedNodeResults { + export function parseJsonText(fileName: string, sourceText: string): JsonSourceFile { return Parser.parseJsonText(fileName, sourceText); } @@ -622,36 +620,34 @@ namespace ts { return isInvalid ? entityName : undefined; } - export function parseJsonText(fileName: string, sourceText: string): ParsedNodeResults { - initializeState(sourceText, ScriptTarget.ES2015, /*syntaxCursor*/ undefined, ScriptKind.JS); + export function parseJsonText(fileName: string, sourceText: string): JsonSourceFile { + initializeState(sourceText, ScriptTarget.ES2015, /*syntaxCursor*/ undefined, ScriptKind.JSON); // Set source file so that errors will be reported with this file name - sourceFile = { kind: SyntaxKind.SourceFile, text: sourceText, fileName }; - let node: JsonNode; + sourceFile = createSourceFile(fileName, ScriptTarget.ES2015, ScriptKind.JSON); + const result = sourceFile; + // Prime the scanner. nextToken(); if (token() === SyntaxKind.EndOfFileToken) { - node = parseTokenNode(); + sourceFile.endOfFileToken = parseTokenNode(); } else if (token() === SyntaxKind.OpenBraceToken || lookAhead(() => token() === SyntaxKind.StringLiteral)) { - node = parseObjectLiteralExpression(); - parseExpected(SyntaxKind.EndOfFileToken, Diagnostics.Unexpected_token); + result.jsonObject = parseObjectLiteralExpression(); + sourceFile.endOfFileToken = parseExpectedToken(SyntaxKind.EndOfFileToken, /*reportAtCurrentPosition*/ false, Diagnostics.Unexpected_token); } else { parseExpected(SyntaxKind.OpenBraceToken); } - if (node) { - node.parent = sourceFile; - } - const errors = parseDiagnostics; + sourceFile.parseDiagnostics = parseDiagnostics; clearState(); - return { node, errors }; + return result; } function getLanguageVariant(scriptKind: ScriptKind) { // .tsx and .jsx files are treated as jsx language variant. - return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS ? LanguageVariant.JSX : LanguageVariant.Standard; + return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSON ? LanguageVariant.JSX : LanguageVariant.Standard; } function initializeState(_sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, scriptKind: ScriptKind) { @@ -669,7 +665,7 @@ namespace ts { identifierCount = 0; nodeCount = 0; - contextFlags = scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX ? NodeFlags.JavaScriptFile : NodeFlags.None; + contextFlags = scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JSON ? NodeFlags.JavaScriptFile : NodeFlags.None; parseErrorBeforeNextFinishedNode = false; // Initialize and prime the scanner before parsing the source elements. diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 129c800d58f..43c3533f7e4 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -307,13 +307,13 @@ namespace ts { } const result = parseJsonText(configFileName, cachedConfigFileText); - reportDiagnostics(result.errors, /* compilerHost */ undefined); - if (!result.node) { + reportDiagnostics(result.parseDiagnostics, /* compilerHost */ undefined); + if (!result.endOfFileToken) { sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); return; } const cwd = sys.getCurrentDirectory(); - const configParseResult = parseJsonNodeConfigFileContent(result.node, sys, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), commandLine.options, getNormalizedAbsolutePath(configFileName, cwd)); + const configParseResult = parseJsonSourceFileConfigFileContent(result, sys, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), commandLine.options, getNormalizedAbsolutePath(configFileName, cwd)); if (configParseResult.errors.length > 0) { reportDiagnostics(configParseResult.errors, /* compilerHost */ undefined); sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cb57016c4ce..b4753287fb5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -527,8 +527,6 @@ namespace ts { export type AtToken = Token; export type ReadonlyToken = Token; - export type JsonNode = ObjectLiteralExpression | EndOfFileToken; - export type Modifier = Token | Token @@ -2189,6 +2187,10 @@ namespace ts { /* @internal */ ambientModuleNames: string[]; } + export interface JsonSourceFile extends SourceFile { + jsonObject?: ObjectLiteralExpression; + } + export interface ScriptReferenceHost { getCompilerOptions(): CompilerOptions; getSourceFile(fileName: string): SourceFile; @@ -3261,7 +3263,8 @@ namespace ts { JS = 1, JSX = 2, TS = 3, - TSX = 4 + TSX = 4, + JSON = 5 } export const enum ScriptTarget { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 63155e6a782..e58a4f507a7 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1877,12 +1877,12 @@ namespace Harness { const data = testUnitData[i]; if (ts.getBaseFileName(data.name).toLowerCase() === "tsconfig.json") { const configJson = ts.parseJsonText(data.name, data.content); - assert.isTrue(configJson.node !== undefined); + assert.isTrue(configJson.endOfFileToken !== undefined); let baseDir = ts.normalizePath(ts.getDirectoryPath(data.name)); if (rootDir) { baseDir = ts.getNormalizedAbsolutePath(baseDir, rootDir); } - tsConfig = ts.parseJsonNodeConfigFileContent(configJson.node, parseConfigHost, baseDir); + tsConfig = ts.parseJsonSourceFileConfigFileContent(configJson, parseConfigHost, baseDir); tsConfig.options.configFilePath = data.name; // delete entry from the list diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index de22ff314fa..a85f7a13e16 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -210,30 +210,23 @@ class ProjectRunner extends RunnerBase { let errors: ts.Diagnostic[]; if (configFileName) { - const result = ts.readConfigFileToJsonNode(configFileName, getSourceFileText); - if (!result.node) { - return { - moduleKind, - errors: result.errors - }; - } - + const result = ts.readConfigFileToJsonSourceFile(configFileName, getSourceFileText); const configParseHost: ts.ParseConfigHost = { useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(), fileExists, readDirectory, readFile }; - const configParseResult = ts.parseJsonNodeConfigFileContent(result.node, configParseHost, ts.getDirectoryPath(configFileName), compilerOptions); + const configParseResult = ts.parseJsonSourceFileConfigFileContent(result, configParseHost, ts.getDirectoryPath(configFileName), compilerOptions); if (configParseResult.errors.length > 0) { return { moduleKind, - errors: result.errors.concat(configParseResult.errors) + errors: result.parseDiagnostics.concat(configParseResult.errors) }; } inputFiles = configParseResult.fileNames; compilerOptions = configParseResult.options; - errors = result.errors; + errors = result.parseDiagnostics; } const projectCompilerResult = compileProjectFiles(moduleKind, () => inputFiles, getSourceFileText, writeFile, compilerOptions); diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 6ae5dd63daa..567519165c7 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -81,7 +81,7 @@ namespace RWC { readDirectory: Harness.IO.readDirectory, readFile: Harness.IO.readFile }; - const configParseResult = ts.parseJsonNodeConfigFileContent(parsedTsconfigFileContents.node, configParseHost, ts.getDirectoryPath(tsconfigFile.path)); + const configParseResult = ts.parseJsonSourceFileConfigFileContent(parsedTsconfigFileContents, configParseHost, ts.getDirectoryPath(tsconfigFile.path)); fileNames = configParseResult.fileNames; opts.options = ts.extend(opts.options, configParseResult.options); } diff --git a/src/harness/unittests/configurationExtension.ts b/src/harness/unittests/configurationExtension.ts index 4077f54ef3d..e1136e5c0b0 100644 --- a/src/harness/unittests/configurationExtension.ts +++ b/src/harness/unittests/configurationExtension.ts @@ -110,21 +110,29 @@ namespace ts { ["under a case insensitive host", caseInsensitiveBasePath, caseInsensitiveHost], ["under a case sensitive host", caseSensitiveBasePath, caseSensitiveHost] ], ([testName, basePath, host]) => { + function getParseCommandLine(entry: string) { + const {config, error} = ts.readConfigFile(entry, name => host.readFile(name)); + assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n")); + return ts.parseJsonConfigFileContent(config, host, basePath, {}, entry); + } + + function getParseCommandLineJsonSourceFile(entry: string) { + const jsonSourceFile = ts.readConfigFileToJsonSourceFile(entry, name => host.readFile(name)); + assert(jsonSourceFile.endOfFileToken && !jsonSourceFile.parseDiagnostics.length, flattenDiagnosticMessageText(jsonSourceFile.parseDiagnostics[0] && jsonSourceFile.parseDiagnostics[0].messageText, "\n")); + return ts.parseJsonSourceFileConfigFileContent(jsonSourceFile, host, basePath, {}, entry); + } + function testSuccess(name: string, entry: string, expected: CompilerOptions, expectedFiles: string[]) { it(name, () => { - const {config, error} = ts.readConfigFile(entry, name => host.readFile(name)); - assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n")); - const parsed = ts.parseJsonConfigFileContent(config, host, basePath, {}, entry); + const parsed = getParseCommandLine(entry); assert(!parsed.errors.length, flattenDiagnosticMessageText(parsed.errors[0] && parsed.errors[0].messageText, "\n")); expected.configFilePath = entry; assert.deepEqual(parsed.options, expected); assert.deepEqual(parsed.fileNames, expectedFiles); }); - it(name, () => { - const {node, errors} = ts.readConfigFileToJsonNode(entry, name => host.readFile(name)); - assert(node && !errors.length, flattenDiagnosticMessageText(errors[0] && errors[0].messageText, "\n")); - const parsed = ts.parseJsonNodeConfigFileContent(node, host, basePath, {}, entry); + it(name + "with jsonSourceFile", () => { + const parsed = getParseCommandLineJsonSourceFile(entry); assert(!parsed.errors.length, flattenDiagnosticMessageText(parsed.errors[0] && parsed.errors[0].messageText, "\n")); expected.configFilePath = entry; assert.deepEqual(parsed.options, expected); @@ -134,16 +142,12 @@ namespace ts { function testFailure(name: string, entry: string, expectedDiagnostics: { code: number, category: DiagnosticCategory, messageText: string }[]) { it(name, () => { - const {config, error} = ts.readConfigFile(entry, name => host.readFile(name)); - assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n")); - const parsed = ts.parseJsonConfigFileContent(config, host, basePath, {}, entry); + const parsed = getParseCommandLine(entry); verifyDiagnostics(parsed.errors, expectedDiagnostics); }); - it(name, () => { - const {node, errors} = ts.readConfigFileToJsonNode(entry, name => host.readFile(name)); - assert(node && !errors.length, flattenDiagnosticMessageText(errors[0] && errors[0].messageText, "\n")); - const parsed = ts.parseJsonNodeConfigFileContent(node, host, basePath, {}, entry); + it(name + "with jsonSourceFile", () => { + const parsed = getParseCommandLineJsonSourceFile(entry); verifyDiagnostics(parsed.errors, expectedDiagnostics); }); } diff --git a/src/harness/unittests/convertCompilerOptionsFromJson.ts b/src/harness/unittests/convertCompilerOptionsFromJson.ts index d191e2d2f3b..a00ac5ef56b 100644 --- a/src/harness/unittests/convertCompilerOptionsFromJson.ts +++ b/src/harness/unittests/convertCompilerOptionsFromJson.ts @@ -28,11 +28,11 @@ namespace ts { function assertCompilerOptionsWithJsonNode(json: any, configFileName: string, expectedResult: { compilerOptions: CompilerOptions, errors: Diagnostic[] }) { const fileText = JSON.stringify(json); - const { node, errors } = parseJsonText(configFileName, fileText); - assert(!errors.length); - assert(!!node); + const result = parseJsonText(configFileName, fileText); + assert(!result.parseDiagnostics.length); + assert(!!result.endOfFileToken); const host: ParseConfigHost = new Utils.MockParseConfigHost("/apath/", true, []); - const { options: actualCompilerOptions, errors: actualParseErrors } = parseJsonNodeConfigFileContent(node, host, "/apath/", /*existingOptions*/ undefined, configFileName); + const { options: actualCompilerOptions, errors: actualParseErrors } = parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName); expectedResult.compilerOptions["configFilePath"] = configFileName; const parsedCompilerOptions = JSON.stringify(actualCompilerOptions); diff --git a/src/harness/unittests/convertTypeAcquisitionFromJson.ts b/src/harness/unittests/convertTypeAcquisitionFromJson.ts index 535e7555f4f..aae4ee38382 100644 --- a/src/harness/unittests/convertTypeAcquisitionFromJson.ts +++ b/src/harness/unittests/convertTypeAcquisitionFromJson.ts @@ -40,11 +40,11 @@ namespace ts { function assertTypeAcquisitionWithJsonNode(json: any, configFileName: string, expectedResult: ExpectedResult) { const fileText = JSON.stringify(json); - const { node, errors } = parseJsonText(configFileName, fileText); - assert(!errors.length); - assert(!!node); + const result = parseJsonText(configFileName, fileText); + assert(!result.parseDiagnostics.length); + assert(!!result.endOfFileToken); const host: ParseConfigHost = new Utils.MockParseConfigHost("/apath/", true, []); - const { typeAcquisition: actualTypeAcquisition, errors: actualParseErrors } = parseJsonNodeConfigFileContent(node, host, "/apath/", /*existingOptions*/ undefined, configFileName); + const { typeAcquisition: actualTypeAcquisition, errors: actualParseErrors } = parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName); verifyAcquisition(actualTypeAcquisition, expectedResult); const actualErrors = filter(actualParseErrors, error => error.code !== Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); diff --git a/src/harness/unittests/matchFiles.ts b/src/harness/unittests/matchFiles.ts index 99614f07573..f2d1f1e2ac7 100644 --- a/src/harness/unittests/matchFiles.ts +++ b/src/harness/unittests/matchFiles.ts @@ -98,8 +98,13 @@ namespace ts { function validateMatches(expected: ts.ParsedCommandLine, json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[]) { { const jsonText = JSON.stringify(json); - const {node} = parseJsonText(caseInsensitiveTsconfigPath, jsonText); - const actual = ts.parseJsonNodeConfigFileContent(node, host, basePath, existingOptions, configFileName, resolutionStack); + const result = parseJsonText(caseInsensitiveTsconfigPath, jsonText); + const actual = ts.parseJsonSourceFileConfigFileContent(result, host, basePath, existingOptions, configFileName, resolutionStack); + for (const error of expected.errors) { + if (error.file) { + error.file = result; + } + } assertParsed(actual, expected); } { diff --git a/src/harness/unittests/tsconfigParsing.ts b/src/harness/unittests/tsconfigParsing.ts index c31608ee2fe..39f3a1b3006 100644 --- a/src/harness/unittests/tsconfigParsing.ts +++ b/src/harness/unittests/tsconfigParsing.ts @@ -23,7 +23,7 @@ namespace ts { } { const parsed = ts.parseJsonText("/apath/tsconfig.json", jsonText); - const parsedCommand = ts.parseJsonNodeConfigFileContent(parsed.node, ts.sys, "tests/cases/unittests"); + const parsedCommand = ts.parseJsonSourceFileConfigFileContent(parsed, ts.sys, "tests/cases/unittests"); assert.isTrue(parsedCommand.errors && parsedCommand.errors.length === 1 && parsedCommand.errors[0].code === ts.Diagnostics.Unknown_option_excludes_Did_you_mean_exclude.code); } @@ -38,7 +38,7 @@ namespace ts { function getParsedCommandJsonNode(jsonText: string, configFileName: string, basePath: string, allFileList: string[]) { const parsed = ts.parseJsonText(configFileName, jsonText); const host: ParseConfigHost = new Utils.MockParseConfigHost(basePath, true, allFileList); - return ts.parseJsonNodeConfigFileContent(parsed.node, host, basePath, /*existingOptions*/ undefined, configFileName); + return ts.parseJsonSourceFileConfigFileContent(parsed, host, basePath, /*existingOptions*/ undefined, configFileName); } function assertParseFileList(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedFileList: string[]) { @@ -231,8 +231,9 @@ namespace ts { } "files": ["file1.ts"] }`; - const { node, errors: diagnostics } = parseJsonText("config.json", content); - const configJsonObject = convertToJson(node, diagnostics); + const result = parseJsonText("config.json", content); + const diagnostics = result.parseDiagnostics; + const configJsonObject = convertToJson(result, diagnostics); const expectedResult = { compilerOptions: { allowJs: true, diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 4c7193be921..9637f6e20f8 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -790,18 +790,18 @@ namespace ts.server { return findProjectByName(projectFileName, this.externalProjects); } - private getDefaultParsedJsonNode(): EndOfFileToken { - return { kind: SyntaxKind.EndOfFileToken }; - } - private convertConfigFileContentToProjectOptions(configFilename: string): ConfigFileConversionResult { configFilename = normalizePath(configFilename); const configFileContent = this.host.readFile(configFilename); - const { node = this.getDefaultParsedJsonNode(), errors } = parseJsonText(configFilename, configFileContent); - const parsedCommandLine = parseJsonNodeConfigFileContent( - node, + const result = parseJsonText(configFilename, configFileContent); + if (!result.endOfFileToken) { + result.endOfFileToken = { kind: SyntaxKind.EndOfFileToken }; + } + const errors = result.parseDiagnostics; + const parsedCommandLine = parseJsonSourceFileConfigFileContent( + result, this.host, getDirectoryPath(configFilename), /*existingOptions*/ {}, diff --git a/src/services/shims.ts b/src/services/shims.ts index b6988198ad9..22fd093561e 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -1135,25 +1135,25 @@ namespace ts { const result = parseJsonText(fileName, text); - if (!result.node) { + if (!result.endOfFileToken) { return { options: {}, typeAcquisition: {}, files: [], raw: {}, - errors: realizeDiagnostics(result.errors, "\r\n") + errors: realizeDiagnostics(result.parseDiagnostics, "\r\n") }; } const normalizedFileName = normalizeSlashes(fileName); - const configFile = parseJsonNodeConfigFileContent(result.node, this.host, getDirectoryPath(normalizedFileName), /*existingOptions*/ {}, normalizedFileName); + const configFile = parseJsonSourceFileConfigFileContent(result, this.host, getDirectoryPath(normalizedFileName), /*existingOptions*/ {}, normalizedFileName); return { options: configFile.options, typeAcquisition: configFile.typeAcquisition, files: configFile.fileNames, raw: configFile.raw, - errors: realizeDiagnostics(result.errors.concat(configFile.errors), "\r\n") + errors: realizeDiagnostics(result.parseDiagnostics.concat(configFile.errors), "\r\n") }; }); }