Skip to content

Commit c97b389

Browse files
committed
Simplifying the json conversion notifier
1 parent b60de87 commit c97b389

File tree

3 files changed

+112
-59
lines changed

3 files changed

+112
-59
lines changed

src/compiler/commandLineParser.ts

Lines changed: 108 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -917,19 +917,19 @@ namespace ts {
917917
{
918918
name: "compilerOptions",
919919
type: "object",
920-
optionDeclarations: commandLineOptionsToMap(optionDeclarations),
920+
elementOptions: commandLineOptionsToMap(optionDeclarations),
921921
extraKeyDiagnosticMessage: Diagnostics.Unknown_compiler_option_0
922922
},
923923
{
924924
name: "typingOptions",
925925
type: "object",
926-
optionDeclarations: commandLineOptionsToMap(typeAcquisitionDeclarations),
926+
elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations),
927927
extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0
928928
},
929929
{
930930
name: "typeAcquisition",
931931
type: "object",
932-
optionDeclarations: commandLineOptionsToMap(typeAcquisitionDeclarations),
932+
elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations),
933933
extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0
934934
},
935935
{
@@ -967,37 +967,64 @@ namespace ts {
967967
}
968968

969969
interface JsonConversionNotifier {
970-
/** Notifies options object is being set with the optionKey and optionValue is being set */
971-
onSetOptionKeyValue(optionsObject: string, option: CommandLineOption, value: CompilerOptionsValue): void;
972-
/** Notify when root key value is being set */
973-
onRootKeyValue(key: string, propertyName: PropertyName, value: CompilerOptionsValue, node: Expression): void;
970+
/**
971+
* Notifies parent option object is being set with the optionKey and a valid optionValue
972+
* Currently it notifies only if there is element with type object (parentOption) and
973+
* has element's option declarations map associated with it
974+
* @param parentOption parent option name in which the option and value are being set
975+
* @param option option declaration which is being set with the value
976+
* @param value value of the option
977+
*/
978+
onSetValidOptionKeyValueInParent(parentOption: string, option: CommandLineOption, value: CompilerOptionsValue): void;
979+
/**
980+
* Notify when valid root key value option is being set
981+
* @param key option key
982+
* @param keyNode node corresponding to node in the source file
983+
* @param value computed value of the key
984+
* @param ValueNode node corresponding to value in the source file
985+
*/
986+
onSetValidOptionKeyValueInRoot(key: string, keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression): void;
987+
/**
988+
* Notify when unknown root key value option is being set
989+
* @param key option key
990+
* @param keyNode node corresponding to node in the source file
991+
* @param value computed value of the key
992+
* @param ValueNode node corresponding to value in the source file
993+
*/
994+
onSetUnknownOptionKeyValueInRoot(key: string, keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression): void;
974995
}
975996

976997
/**
977998
* Convert the json syntax tree into the json value
978-
* @param jsonNode
979-
* @param errors
980999
*/
9811000
export function convertToObject(sourceFile: JsonSourceFile, errors: Diagnostic[]): any {
982-
return convertToObjectWorker(sourceFile, errors);
1001+
return convertToObjectWorker(sourceFile, errors, /*knownRootOptions*/ undefined, /*jsonConversionNotifier*/ undefined);
9831002
}
9841003

9851004
/**
9861005
* Convert the json syntax tree into the json value
987-
* @param jsonNode
988-
* @param errors
9891006
*/
990-
function convertToObjectWorker(sourceFile: JsonSourceFile, errors: Diagnostic[], knownRootOptions?: Map<CommandLineOption>, optionsIterator?: JsonConversionNotifier): any {
1007+
function convertToObjectWorker(
1008+
sourceFile: JsonSourceFile,
1009+
errors: Diagnostic[],
1010+
knownRootOptions: Map<CommandLineOption> | undefined,
1011+
jsonConversionNotifier: JsonConversionNotifier | undefined): any {
9911012
if (!sourceFile.jsonObject) {
9921013
if (sourceFile.endOfFileToken) {
9931014
return {};
9941015
}
9951016
return undefined;
9961017
}
9971018

998-
return convertObjectLiteralExpressionToJson(sourceFile.jsonObject, knownRootOptions);
1019+
return convertObjectLiteralExpressionToJson(sourceFile.jsonObject, knownRootOptions,
1020+
/*extraKeyDiagnosticMessage*/ undefined, /*parentOption*/ undefined);
9991021

1000-
function convertObjectLiteralExpressionToJson(node: ObjectLiteralExpression, knownOptions: Map<CommandLineOption>, extraKeyDiagnosticMessage?: DiagnosticMessage, optionsObject?: string): any {
1022+
function convertObjectLiteralExpressionToJson(
1023+
node: ObjectLiteralExpression,
1024+
knownOptions: Map<CommandLineOption> | undefined,
1025+
extraKeyDiagnosticMessage: DiagnosticMessage | undefined,
1026+
parentOption: string | undefined
1027+
): any {
10011028
const result: any = {};
10021029
for (const element of node.properties) {
10031030
if (element.kind !== SyntaxKind.PropertyAssignment) {
@@ -1021,32 +1048,45 @@ namespace ts {
10211048
if (typeof keyText !== undefined && typeof value !== undefined) {
10221049
result[keyText] = value;
10231050
// Notify key value set, if user asked for it
1024-
if (optionsIterator &&
1025-
(optionsObject || knownOptions === knownRootOptions)) {
1051+
if (jsonConversionNotifier &&
1052+
// Current callbacks are only on known parent option or if we are setting values in the root
1053+
(parentOption || knownOptions === knownRootOptions)) {
10261054
const isValidOptionValue = isCompilerOptionsValue(option, value);
1027-
if (optionsObject && isValidOptionValue) {
1028-
optionsIterator.onSetOptionKeyValue(optionsObject, option, value);
1055+
if (parentOption) {
1056+
if (isValidOptionValue) {
1057+
// Notify option set in the parent if its a valid option value
1058+
jsonConversionNotifier.onSetValidOptionKeyValueInParent(parentOption, option, value);
1059+
}
10291060
}
1030-
if (knownOptions === knownRootOptions && (isValidOptionValue || !option)) {
1031-
optionsIterator.onRootKeyValue(keyText, element.name, value, element.initializer);
1061+
else if (knownOptions === knownRootOptions) {
1062+
if (isValidOptionValue) {
1063+
// Notify about the valid root key value being set
1064+
jsonConversionNotifier.onSetValidOptionKeyValueInRoot(keyText, element.name, value, element.initializer);
1065+
}
1066+
else if (!option) {
1067+
// Notify about the unknown root key value being set
1068+
jsonConversionNotifier.onSetUnknownOptionKeyValueInRoot(keyText, element.name, value, element.initializer);
1069+
}
10321070
}
10331071
}
1034-
10351072
}
10361073
}
10371074
return result;
10381075
}
10391076

1040-
function convertArrayLiteralExpressionToJson(elements: NodeArray<Expression>, option?: CommandLineOption): any[] {
1077+
function convertArrayLiteralExpressionToJson(
1078+
elements: NodeArray<Expression>,
1079+
elementOption: CommandLineOption | undefined
1080+
): any[] {
10411081
const result: any[] = [];
10421082
for (const element of elements) {
1043-
result.push(convertPropertyValueToJson(element, option));
1083+
result.push(convertPropertyValueToJson(element, elementOption));
10441084
}
10451085
return result;
10461086
}
10471087

1048-
function convertPropertyValueToJson(node: Expression, option: CommandLineOption): any {
1049-
switch (node.kind) {
1088+
function convertPropertyValueToJson(valueExpression: Expression, option: CommandLineOption): any {
1089+
switch (valueExpression.kind) {
10501090
case SyntaxKind.TrueKeyword:
10511091
reportInvalidOptionValue(option && option.type !== "boolean");
10521092
return true;
@@ -1060,19 +1100,19 @@ namespace ts {
10601100
return null; // tslint:disable-line:no-null-keyword
10611101

10621102
case SyntaxKind.StringLiteral:
1063-
if (!isDoubleQuotedString(node)) {
1064-
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, node, Diagnostics.String_literal_with_double_quotes_expected));
1103+
if (!isDoubleQuotedString(valueExpression)) {
1104+
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.String_literal_with_double_quotes_expected));
10651105
}
10661106
reportInvalidOptionValue(option && (typeof option.type === "string" && option.type !== "string"));
1067-
const text = (<StringLiteral>node).text;
1107+
const text = (<StringLiteral>valueExpression).text;
10681108
if (option && typeof option.type !== "string") {
10691109
const customOption = <CommandLineOptionOfCustomType>option;
10701110
// Validate custom option type
10711111
if (!customOption.type.has(text)) {
10721112
errors.push(
10731113
createDiagnosticForInvalidCustomType(
10741114
customOption,
1075-
(message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1)
1115+
(message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, message, arg0, arg1)
10761116
)
10771117
);
10781118
}
@@ -1081,37 +1121,49 @@ namespace ts {
10811121

10821122
case SyntaxKind.NumericLiteral:
10831123
reportInvalidOptionValue(option && option.type !== "number");
1084-
return Number((<NumericLiteral>node).text);
1124+
return Number((<NumericLiteral>valueExpression).text);
10851125

10861126
case SyntaxKind.ObjectLiteralExpression:
10871127
reportInvalidOptionValue(option && option.type !== "object");
1088-
const objectOption = <TsConfigOnlyOption>option;
1089-
const optionDeclarations = option && objectOption.optionDeclarations;
1090-
return convertObjectLiteralExpressionToJson(
1091-
<ObjectLiteralExpression>node,
1092-
optionDeclarations,
1093-
option && objectOption.extraKeyDiagnosticMessage,
1094-
optionDeclarations && option.name
1095-
);
1128+
const objectLiteralExpression = <ObjectLiteralExpression>valueExpression;
1129+
1130+
// Currently having element option declaration in the tsconfig with type "object"
1131+
// determines if it needs onSetValidOptionKeyValueInParent callback or not
1132+
// At moment there are only "compilerOptions", "typeAcquisition" and "typingOptions"
1133+
// that satifies it and need it to modify options set in them (for normalizing file paths)
1134+
// vs what we set in the json
1135+
// If need arises, we can modify this interface and callbacks as needed
1136+
if (option) {
1137+
const { elementOptions, extraKeyDiagnosticMessage, name: optionName } = <TsConfigOnlyOption>option;
1138+
return convertObjectLiteralExpressionToJson(objectLiteralExpression,
1139+
elementOptions, extraKeyDiagnosticMessage, optionName);
1140+
}
1141+
else {
1142+
return convertObjectLiteralExpressionToJson(
1143+
objectLiteralExpression, /* knownOptions*/ undefined,
1144+
/*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined);
1145+
}
10961146

10971147
case SyntaxKind.ArrayLiteralExpression:
10981148
reportInvalidOptionValue(option && option.type !== "list");
1099-
return convertArrayLiteralExpressionToJson((<ArrayLiteralExpression>node).elements, option && (<CommandLineOptionOfListType>option).element);
1149+
return convertArrayLiteralExpressionToJson(
1150+
(<ArrayLiteralExpression>valueExpression).elements,
1151+
option && (<CommandLineOptionOfListType>option).element);
11001152
}
11011153

11021154
// Not in expected format
11031155
if (option) {
11041156
reportInvalidOptionValue(/*isError*/ true);
11051157
}
11061158
else {
1107-
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, node, Diagnostics.Property_value_can_only_be_string_literal_numeric_literal_true_false_null_object_literal_or_array_literal));
1159+
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.Property_value_can_only_be_string_literal_numeric_literal_true_false_null_object_literal_or_array_literal));
11081160
}
11091161

11101162
return undefined;
11111163

11121164
function reportInvalidOptionValue(isError: boolean) {
11131165
if (isError) {
1114-
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, node, Diagnostics.Compiler_option_0_requires_a_value_of_type_1, option.name, getCompilerOptionValueTypeString(option)));
1166+
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.Compiler_option_0_requires_a_value_of_type_1, option.name, getCompilerOptionValueTypeString(option)));
11151167
}
11161168
}
11171169
}
@@ -1542,15 +1594,17 @@ namespace ts {
15421594
let extendedConfigPath: Path;
15431595

15441596
const optionsIterator: JsonConversionNotifier = {
1545-
onSetOptionKeyValue(optionsObject: string, option: CommandLineOption, value: CompilerOptionsValue) {
1546-
Debug.assert(optionsObject === "compilerOptions" || optionsObject === "typeAcquisition" || optionsObject === "typingOptions");
1547-
const currentOption = optionsObject === "compilerOptions" ? options :
1548-
optionsObject === "typeAcquisition" ? (typeAcquisition || (typeAcquisition = getDefaultTypeAcquisition(configFileName))) :
1597+
onSetValidOptionKeyValueInParent(parentOption: string, option: CommandLineOption, value: CompilerOptionsValue) {
1598+
Debug.assert(parentOption === "compilerOptions" || parentOption === "typeAcquisition" || parentOption === "typingOptions");
1599+
const currentOption = parentOption === "compilerOptions" ?
1600+
options :
1601+
parentOption === "typeAcquisition" ?
1602+
(typeAcquisition || (typeAcquisition = getDefaultTypeAcquisition(configFileName))) :
15491603
(typingOptionstypeAcquisition || (typingOptionstypeAcquisition = getDefaultTypeAcquisition(configFileName)));
15501604

15511605
currentOption[option.name] = normalizeOptionValue(option, basePath, value);
15521606
},
1553-
onRootKeyValue(key: string, propertyName: PropertyName, value: CompilerOptionsValue, node: Expression) {
1607+
onSetValidOptionKeyValueInRoot(key: string, _keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression) {
15541608
switch (key) {
15551609
case "extends":
15561610
extendedConfigPath = getExtendsConfigPath(
@@ -1560,18 +1614,20 @@ namespace ts {
15601614
getCanonicalFileName,
15611615
errors,
15621616
(message, arg0) =>
1563-
createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0)
1617+
createDiagnosticForNodeInSourceFile(sourceFile, valueNode, message, arg0)
15641618
);
15651619
return;
1566-
case "excludes":
1567-
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, propertyName, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
1568-
return;
15691620
case "files":
15701621
if ((<string[]>value).length === 0) {
1571-
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, node, Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json"));
1622+
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueNode, Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json"));
15721623
}
15731624
return;
15741625
}
1626+
},
1627+
onSetUnknownOptionKeyValueInRoot(key: string, keyNode: PropertyName, _value: CompilerOptionsValue, _valueNode: Expression) {
1628+
if (key === "excludes") {
1629+
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, keyNode, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
1630+
}
15751631
}
15761632
};
15771633
const json = convertToObjectWorker(sourceFile, errors, getTsconfigRootOptionsMap(), optionsIterator);

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3603,7 +3603,7 @@ namespace ts {
36033603
/* @internal */
36043604
export interface TsConfigOnlyOption extends CommandLineOptionBase {
36053605
type: "object";
3606-
optionDeclarations?: Map<CommandLineOption>;
3606+
elementOptions?: Map<CommandLineOption>;
36073607
extraKeyDiagnosticMessage?: DiagnosticMessage;
36083608
}
36093609

src/harness/unittests/matchFiles.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,7 @@ namespace ts {
460460
"c:/dev/x": ts.WatchDirectoryFlags.None
461461
},
462462
};
463-
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
464-
assertParsed(actual, expected);
463+
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
465464
});
466465

467466
it("same named declarations are excluded", () => {
@@ -963,8 +962,7 @@ namespace ts {
963962
"c:/dev": ts.WatchDirectoryFlags.Recursive
964963
}
965964
};
966-
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
967-
assertParsed(actual, expected);
965+
validateMatches(expected, json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
968966
});
969967
it("with jsx=none, allowJs=true", () => {
970968
const json = {
@@ -1040,8 +1038,7 @@ namespace ts {
10401038
"c:/dev": ts.WatchDirectoryFlags.Recursive
10411039
}
10421040
};
1043-
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
1044-
assertParsed(actual, expected);
1041+
validateMatches(expected, json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
10451042
});
10461043
it("exclude .min.js files using wildcards", () => {
10471044
const json = {

0 commit comments

Comments
 (0)