Skip to content

Commit 0dd944a

Browse files
committed
Clean up api so that parsing returns JsonSourceFile
1 parent 6419de9 commit 0dd944a

15 files changed

+117
-115
lines changed

src/compiler/commandLineParser.ts

+37-39
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ namespace ts {
462462
];
463463

464464
/* @internal */
465-
export let typeAcquisitionDeclarations: CommandLineOption[] = [
465+
export const typeAcquisitionDeclarations: CommandLineOption[] = [
466466
{
467467
/* @deprecated typingOptions.enableAutoDiscovery
468468
* Use typeAcquisition.enable instead.
@@ -723,24 +723,24 @@ namespace ts {
723723
* @param jsonText The text of the config file
724724
*/
725725
export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
726-
const { node, errors } = parseJsonText(fileName, jsonText);
726+
const jsonSourceFile = parseJsonText(fileName, jsonText);
727727
return {
728-
config: convertToJson(node, errors),
729-
error: errors.length ? errors[0] : undefined
728+
config: convertToJson(jsonSourceFile, jsonSourceFile.parseDiagnostics),
729+
error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : undefined
730730
};
731731
}
732732

733733
/**
734734
* Read tsconfig.json file
735735
* @param fileName The path to the config file
736736
*/
737-
export function readConfigFileToJsonNode(fileName: string, readFile: (path: string) => string): ParsedNodeResults<JsonNode> {
737+
export function readConfigFileToJsonSourceFile(fileName: string, readFile: (path: string) => string): JsonSourceFile {
738738
let text = "";
739739
try {
740740
text = readFile(fileName);
741741
}
742742
catch (e) {
743-
return { errors: [createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message)] };
743+
return <JsonSourceFile>{ parseDiagnostics: [createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message)] };
744744
}
745745
return parseJsonText(fileName, text);
746746
}
@@ -819,26 +819,24 @@ namespace ts {
819819
* @param jsonNode
820820
* @param errors
821821
*/
822-
export function convertToJson(jsonNode: JsonNode, errors: Diagnostic[]): any {
823-
return convertToJsonWorker(jsonNode, errors);
822+
export function convertToJson(sourceFile: JsonSourceFile, errors: Diagnostic[]): any {
823+
return convertToJsonWorker(sourceFile, errors);
824824
}
825825

826826
/**
827827
* Convert the json syntax tree into the json value
828828
* @param jsonNode
829829
* @param errors
830830
*/
831-
function convertToJsonWorker(jsonNode: JsonNode, errors: Diagnostic[], knownRootOptions?: Map<CommandLineOption>, optionsIterator?: JsonConversionNotifier): any {
832-
if (!jsonNode) {
831+
function convertToJsonWorker(sourceFile: JsonSourceFile, errors: Diagnostic[], knownRootOptions?: Map<CommandLineOption>, optionsIterator?: JsonConversionNotifier): any {
832+
if (!sourceFile.jsonObject) {
833+
if (sourceFile.endOfFileToken) {
834+
return {};
835+
}
833836
return undefined;
834837
}
835838

836-
if (jsonNode.kind === SyntaxKind.EndOfFileToken) {
837-
return {};
838-
}
839-
840-
const sourceFile = <SourceFile>jsonNode.parent;
841-
return convertObjectLiteralExpressionToJson(jsonNode, knownRootOptions);
839+
return convertObjectLiteralExpressionToJson(sourceFile.jsonObject, knownRootOptions);
842840

843841
function convertObjectLiteralExpressionToJson(node: ObjectLiteralExpression, options?: Map<CommandLineOption>, extraKeyDiagnosticMessage?: DiagnosticMessage, optionsObject?: string): any {
844842
const result: any = {};
@@ -1076,7 +1074,7 @@ namespace ts {
10761074
* file to. e.g. outDir
10771075
*/
10781076
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[]): ParsedCommandLine {
1079-
return parseJsonConfigFileContentWorker(json, /*jsonNode*/ undefined, host, basePath, existingOptions, configFileName, resolutionStack);
1077+
return parseJsonConfigFileContentWorker(json, /*sourceFile*/ undefined, host, basePath, existingOptions, configFileName, resolutionStack);
10801078
}
10811079

10821080
/**
@@ -1086,8 +1084,8 @@ namespace ts {
10861084
* @param basePath A root directory to resolve relative path entries in the config
10871085
* file to. e.g. outDir
10881086
*/
1089-
export function parseJsonNodeConfigFileContent(jsonNode: JsonNode, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[]): ParsedCommandLine {
1090-
return parseJsonConfigFileContentWorker(/*json*/ undefined, jsonNode, host, basePath, existingOptions, configFileName, resolutionStack);
1087+
export function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[]): ParsedCommandLine {
1088+
return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack);
10911089
}
10921090

10931091
/**
@@ -1097,8 +1095,8 @@ namespace ts {
10971095
* @param basePath A root directory to resolve relative path entries in the config
10981096
* file to. e.g. outDir
10991097
*/
1100-
function parseJsonConfigFileContentWorker(json: any, jsonNode: JsonNode, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = []): ParsedCommandLine {
1101-
Debug.assert((json === undefined && jsonNode !== undefined) || (json !== undefined && jsonNode === undefined));
1098+
function parseJsonConfigFileContentWorker(json: any, sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = []): ParsedCommandLine {
1099+
Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined));
11021100
const errors: Diagnostic[] = [];
11031101
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
11041102
const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName);
@@ -1107,7 +1105,7 @@ namespace ts {
11071105
options: {},
11081106
fileNames: [],
11091107
typeAcquisition: {},
1110-
raw: json || convertToJson(jsonNode, errors),
1108+
raw: json || convertToJson(sourceFile, errors),
11111109
errors: errors.concat(createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))),
11121110
wildcardDirectories: {}
11131111
};
@@ -1141,7 +1139,7 @@ namespace ts {
11411139
switch (key) {
11421140
case "extends":
11431141
const extendsDiagnostic = getExtendsConfigPath(<string>value, (message, arg0) =>
1144-
createDiagnosticForNodeInSourceFile(<SourceFile>jsonNode.parent, node, message, arg0));
1142+
createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0));
11451143
if ((<Diagnostic>extendsDiagnostic).messageText) {
11461144
errors.push(<Diagnostic>extendsDiagnostic);
11471145
hasExtendsError = true;
@@ -1151,11 +1149,11 @@ namespace ts {
11511149
}
11521150
return;
11531151
case "excludes":
1154-
errors.push(createDiagnosticForNodeInSourceFile(<SourceFile>jsonNode.parent, propertyName, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
1152+
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, propertyName, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
11551153
return;
11561154
case "files":
11571155
if ((<string[]>value).length === 0) {
1158-
errors.push(createDiagnosticForNodeInSourceFile(<SourceFile>jsonNode.parent, node, Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json"));
1156+
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, node, Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json"));
11591157
}
11601158
return;
11611159
case "compileOnSave":
@@ -1164,7 +1162,7 @@ namespace ts {
11641162
}
11651163
}
11661164
};
1167-
json = convertToJsonWorker(jsonNode, errors, getTsconfigRootOptionsMap(), optionsIterator);
1165+
json = convertToJsonWorker(sourceFile, errors, getTsconfigRootOptionsMap(), optionsIterator);
11681166
if (!typeAcquisition) {
11691167
if (typingOptionstypeAcquisition) {
11701168
typeAcquisition = (typingOptionstypeAcquisition.enableAutoDiscovery !== undefined) ?
@@ -1244,16 +1242,16 @@ namespace ts {
12441242
extendedConfigPath = `${extendedConfigPath}.json` as Path;
12451243
}
12461244

1247-
const extendedResult = readConfigFileToJsonNode(extendedConfigPath, path => host.readFile(path));
1248-
if (extendedResult.errors.length) {
1249-
errors.push(...extendedResult.errors);
1245+
const extendedResult = readConfigFileToJsonSourceFile(extendedConfigPath, path => host.readFile(path));
1246+
if (extendedResult.parseDiagnostics.length) {
1247+
errors.push(...extendedResult.parseDiagnostics);
12501248
return;
12511249
}
12521250
const extendedDirname = getDirectoryPath(extendedConfigPath);
12531251
const relativeDifference = convertToRelativePath(extendedDirname, basePath, getCanonicalFileName);
12541252
const updatePath: (path: string) => string = path => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
12551253
// Merge configs (copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios)
1256-
const result = parseJsonNodeConfigFileContent(extendedResult.node, host, extendedDirname, /*existingOptions*/undefined, getBaseFileName(extendedConfigPath), resolutionStack.concat([resolvedPath]));
1254+
const result = parseJsonSourceFileConfigFileContent(extendedResult, host, extendedDirname, /*existingOptions*/undefined, getBaseFileName(extendedConfigPath), resolutionStack.concat([resolvedPath]));
12571255
errors.push(...result.errors);
12581256
const [include, exclude, files] = map(["include", "exclude", "files"], key => {
12591257
if (!json[key] && result.raw[key]) {
@@ -1313,7 +1311,7 @@ namespace ts {
13131311
includeSpecs = ["**/*"];
13141312
}
13151313

1316-
const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors, jsonNode);
1314+
const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors, sourceFile);
13171315

13181316
if (result.fileNames.length === 0 && !hasProperty(json, "files") && resolutionStack.length === 0) {
13191317
errors.push(
@@ -1328,7 +1326,7 @@ namespace ts {
13281326
}
13291327

13301328
function createCompilerDiagnosticForJson(message: DiagnosticMessage, arg0?: string, arg1?: string) {
1331-
if (!jsonNode) {
1329+
if (!sourceFile) {
13321330
errors.push(createCompilerDiagnostic(message, arg0, arg1));
13331331
}
13341332
}
@@ -1548,7 +1546,7 @@ namespace ts {
15481546
* @param host The host used to resolve files and directories.
15491547
* @param errors An array for diagnostic reporting.
15501548
*/
1551-
function matchFileNames(fileNames: string[], include: string[], exclude: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[], jsonNode: JsonNode): ExpandResult {
1549+
function matchFileNames(fileNames: string[], include: string[], exclude: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[], jsonSourceFile: JsonSourceFile): ExpandResult {
15521550
basePath = normalizePath(basePath);
15531551

15541552
// The exclude spec list is converted into a regular expression, which allows us to quickly
@@ -1567,11 +1565,11 @@ namespace ts {
15671565
const wildcardFileMap = createMap<string>();
15681566

15691567
if (include) {
1570-
include = validateSpecs(include, errors, /*allowTrailingRecursion*/ false, jsonNode, "include");
1568+
include = validateSpecs(include, errors, /*allowTrailingRecursion*/ false, jsonSourceFile, "include");
15711569
}
15721570

15731571
if (exclude) {
1574-
exclude = validateSpecs(exclude, errors, /*allowTrailingRecursion*/ true, jsonNode, "exclude");
1572+
exclude = validateSpecs(exclude, errors, /*allowTrailingRecursion*/ true, jsonSourceFile, "exclude");
15751573
}
15761574

15771575
// Wildcard directories (provided as part of a wildcard path) are stored in a
@@ -1627,7 +1625,7 @@ namespace ts {
16271625
};
16281626
}
16291627

1630-
function validateSpecs(specs: string[], errors: Diagnostic[], allowTrailingRecursion: boolean, jsonNode: JsonNode, specKey: string) {
1628+
function validateSpecs(specs: string[], errors: Diagnostic[], allowTrailingRecursion: boolean, jsonSourceFile: JsonSourceFile, specKey: string) {
16311629
const validSpecs: string[] = [];
16321630
for (const spec of specs) {
16331631
if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) {
@@ -1647,13 +1645,13 @@ namespace ts {
16471645
return validSpecs;
16481646

16491647
function createDiagnostic(message: DiagnosticMessage, spec: string): Diagnostic {
1650-
if (jsonNode && jsonNode.kind === SyntaxKind.ObjectLiteralExpression) {
1651-
for (const property of jsonNode.properties) {
1648+
if (jsonSourceFile && jsonSourceFile.jsonObject) {
1649+
for (const property of jsonSourceFile.jsonObject.properties) {
16521650
if (property.kind === SyntaxKind.PropertyAssignment && getTextOfPropertyName(property.name) === specKey) {
16531651
const specsNode = <ArrayLiteralExpression>property.initializer;
16541652
for (const element of specsNode.elements) {
16551653
if (element.kind === SyntaxKind.StringLiteral && (<StringLiteral>element).text === spec) {
1656-
return createDiagnosticForNodeInSourceFile(<SourceFile>jsonNode.parent, element, message, spec);
1654+
return createDiagnosticForNodeInSourceFile(jsonSourceFile, element, message, spec);
16571655
}
16581656
}
16591657
}

src/compiler/core.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,8 @@ namespace ts {
19281928
return ScriptKind.TS;
19291929
case ".tsx":
19301930
return ScriptKind.TSX;
1931+
case ".json":
1932+
return ScriptKind.JSON;
19311933
default:
19321934
return ScriptKind.Unknown;
19331935
}

src/compiler/parser.ts

+13-17
Original file line numberDiff line numberDiff line change
@@ -450,14 +450,12 @@ namespace ts {
450450
return Parser.parseIsolatedEntityName(text, languageVersion);
451451
}
452452

453-
export type ParsedNodeResults<T extends Node> = { node?: T; errors: Diagnostic[] };
454-
455453
/**
456454
* Parse json text into SyntaxTree and return node and parse errors if any
457455
* @param fileName
458456
* @param sourceText
459457
*/
460-
export function parseJsonText(fileName: string, sourceText: string): ParsedNodeResults<JsonNode> {
458+
export function parseJsonText(fileName: string, sourceText: string): JsonSourceFile {
461459
return Parser.parseJsonText(fileName, sourceText);
462460
}
463461

@@ -622,36 +620,34 @@ namespace ts {
622620
return isInvalid ? entityName : undefined;
623621
}
624622

625-
export function parseJsonText(fileName: string, sourceText: string): ParsedNodeResults<JsonNode> {
626-
initializeState(sourceText, ScriptTarget.ES2015, /*syntaxCursor*/ undefined, ScriptKind.JS);
623+
export function parseJsonText(fileName: string, sourceText: string): JsonSourceFile {
624+
initializeState(sourceText, ScriptTarget.ES2015, /*syntaxCursor*/ undefined, ScriptKind.JSON);
627625
// Set source file so that errors will be reported with this file name
628-
sourceFile = <SourceFile>{ kind: SyntaxKind.SourceFile, text: sourceText, fileName };
629-
let node: JsonNode;
626+
sourceFile = createSourceFile(fileName, ScriptTarget.ES2015, ScriptKind.JSON);
627+
const result = <JsonSourceFile>sourceFile;
628+
630629
// Prime the scanner.
631630
nextToken();
632631
if (token() === SyntaxKind.EndOfFileToken) {
633-
node = <EndOfFileToken>parseTokenNode();
632+
sourceFile.endOfFileToken = <EndOfFileToken>parseTokenNode();
634633
}
635634
else if (token() === SyntaxKind.OpenBraceToken ||
636635
lookAhead(() => token() === SyntaxKind.StringLiteral)) {
637-
node = parseObjectLiteralExpression();
638-
parseExpected(SyntaxKind.EndOfFileToken, Diagnostics.Unexpected_token);
636+
result.jsonObject = parseObjectLiteralExpression();
637+
sourceFile.endOfFileToken = parseExpectedToken(SyntaxKind.EndOfFileToken, /*reportAtCurrentPosition*/ false, Diagnostics.Unexpected_token);
639638
}
640639
else {
641640
parseExpected(SyntaxKind.OpenBraceToken);
642641
}
643642

644-
if (node) {
645-
node.parent = sourceFile;
646-
}
647-
const errors = parseDiagnostics;
643+
sourceFile.parseDiagnostics = parseDiagnostics;
648644
clearState();
649-
return { node, errors };
645+
return result;
650646
}
651647

652648
function getLanguageVariant(scriptKind: ScriptKind) {
653649
// .tsx and .jsx files are treated as jsx language variant.
654-
return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS ? LanguageVariant.JSX : LanguageVariant.Standard;
650+
return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSON ? LanguageVariant.JSX : LanguageVariant.Standard;
655651
}
656652

657653
function initializeState(_sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, scriptKind: ScriptKind) {
@@ -669,7 +665,7 @@ namespace ts {
669665
identifierCount = 0;
670666
nodeCount = 0;
671667

672-
contextFlags = scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX ? NodeFlags.JavaScriptFile : NodeFlags.None;
668+
contextFlags = scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JSON ? NodeFlags.JavaScriptFile : NodeFlags.None;
673669
parseErrorBeforeNextFinishedNode = false;
674670

675671
// Initialize and prime the scanner before parsing the source elements.

src/compiler/tsc.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -307,13 +307,13 @@ namespace ts {
307307
}
308308

309309
const result = parseJsonText(configFileName, cachedConfigFileText);
310-
reportDiagnostics(result.errors, /* compilerHost */ undefined);
311-
if (!result.node) {
310+
reportDiagnostics(result.parseDiagnostics, /* compilerHost */ undefined);
311+
if (!result.endOfFileToken) {
312312
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
313313
return;
314314
}
315315
const cwd = sys.getCurrentDirectory();
316-
const configParseResult = parseJsonNodeConfigFileContent(result.node, sys, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), commandLine.options, getNormalizedAbsolutePath(configFileName, cwd));
316+
const configParseResult = parseJsonSourceFileConfigFileContent(result, sys, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), commandLine.options, getNormalizedAbsolutePath(configFileName, cwd));
317317
if (configParseResult.errors.length > 0) {
318318
reportDiagnostics(configParseResult.errors, /* compilerHost */ undefined);
319319
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);

0 commit comments

Comments
 (0)