-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Resolve the modue names with "modulename.json" to json files when doing node module resolution #22167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resolve the modue names with "modulename.json" to json files when doing node module resolution #22167
Changes from 21 commits
5771adb
ee2e267
3572fad
00b6c32
a1922fd
ca590d6
a790a92
4257c2f
23a7e2f
4f1640d
c154b81
2071466
2d9af0b
28a988d
b4e0062
9f72415
a143963
9d3ad54
ce08af4
579748b
c7b2d92
97df079
15f9ea3
4e6586d
c4143ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -500,6 +500,12 @@ namespace ts { | |
category: Diagnostics.Advanced_Options, | ||
description: Diagnostics.Enable_tracing_of_the_name_resolution_process | ||
}, | ||
{ | ||
name: "resolveJsonModule", | ||
type: "boolean", | ||
category: Diagnostics.Advanced_Options, | ||
description: Diagnostics.Resolve_module_name_imported_with_json_extension_to_the_json_source_file | ||
}, | ||
{ | ||
name: "listFiles", | ||
type: "boolean", | ||
|
@@ -952,9 +958,9 @@ namespace ts { | |
* Read tsconfig.json file | ||
* @param fileName The path to the config file | ||
*/ | ||
export function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): JsonSourceFile { | ||
export function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): TsConfigSourceFile { | ||
const textOrDiagnostic = tryReadFile(fileName, readFile); | ||
return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : <JsonSourceFile>{ parseDiagnostics: [textOrDiagnostic] }; | ||
return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : <TsConfigSourceFile>{ parseDiagnostics: [textOrDiagnostic] }; | ||
} | ||
|
||
function tryReadFile(fileName: string, readFile: (path: string) => string | undefined): string | Diagnostic { | ||
|
@@ -972,58 +978,62 @@ namespace ts { | |
return arrayToMap(options, option => option.name); | ||
} | ||
|
||
let _tsconfigRootOptions: Map<CommandLineOption>; | ||
let _tsconfigRootOptions: TsConfigOnlyOption; | ||
function getTsconfigRootOptionsMap() { | ||
if (_tsconfigRootOptions === undefined) { | ||
_tsconfigRootOptions = commandLineOptionsToMap([ | ||
{ | ||
name: "compilerOptions", | ||
type: "object", | ||
elementOptions: commandLineOptionsToMap(optionDeclarations), | ||
extraKeyDiagnosticMessage: Diagnostics.Unknown_compiler_option_0 | ||
}, | ||
{ | ||
name: "typingOptions", | ||
type: "object", | ||
elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations), | ||
extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0 | ||
}, | ||
{ | ||
name: "typeAcquisition", | ||
type: "object", | ||
elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations), | ||
extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0 | ||
}, | ||
{ | ||
name: "extends", | ||
type: "string" | ||
}, | ||
{ | ||
name: "files", | ||
type: "list", | ||
element: { | ||
name: "files", | ||
_tsconfigRootOptions = { | ||
name: undefined, // should never be needed since this is root | ||
type: "object", | ||
elementOptions: commandLineOptionsToMap([ | ||
{ | ||
name: "compilerOptions", | ||
type: "object", | ||
elementOptions: commandLineOptionsToMap(optionDeclarations), | ||
extraKeyDiagnosticMessage: Diagnostics.Unknown_compiler_option_0 | ||
}, | ||
{ | ||
name: "typingOptions", | ||
type: "object", | ||
elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations), | ||
extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0 | ||
}, | ||
{ | ||
name: "typeAcquisition", | ||
type: "object", | ||
elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations), | ||
extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0 | ||
}, | ||
{ | ||
name: "extends", | ||
type: "string" | ||
} | ||
}, | ||
{ | ||
name: "include", | ||
type: "list", | ||
element: { | ||
}, | ||
{ | ||
name: "files", | ||
type: "list", | ||
element: { | ||
name: "files", | ||
type: "string" | ||
} | ||
}, | ||
{ | ||
name: "include", | ||
type: "string" | ||
} | ||
}, | ||
{ | ||
name: "exclude", | ||
type: "list", | ||
element: { | ||
type: "list", | ||
element: { | ||
name: "include", | ||
type: "string" | ||
} | ||
}, | ||
{ | ||
name: "exclude", | ||
type: "string" | ||
} | ||
}, | ||
compileOnSaveCommandLineOption | ||
]); | ||
type: "list", | ||
element: { | ||
name: "exclude", | ||
type: "string" | ||
} | ||
}, | ||
compileOnSaveCommandLineOption | ||
]) | ||
}; | ||
} | ||
return _tsconfigRootOptions; | ||
} | ||
|
@@ -1060,31 +1070,36 @@ namespace ts { | |
* Convert the json syntax tree into the json value | ||
*/ | ||
export function convertToObject(sourceFile: JsonSourceFile, errors: Push<Diagnostic>): any { | ||
return convertToObjectWorker(sourceFile, errors, /*knownRootOptions*/ undefined, /*jsonConversionNotifier*/ undefined); | ||
return convertToObjectWorker(sourceFile, errors, /*returnValue*/ true, /*knownRootOptions*/ undefined, /*jsonConversionNotifier*/ undefined); | ||
} | ||
|
||
/** | ||
* Convert the json syntax tree into the json value | ||
* Convert the json syntax tree into the json value and report errors | ||
*/ | ||
function convertToObjectWorker( | ||
/*@internal*/ | ||
export function convertToObjectWorker( | ||
sourceFile: JsonSourceFile, | ||
errors: Push<Diagnostic>, | ||
knownRootOptions: Map<CommandLineOption> | undefined, | ||
returnValue: boolean, | ||
knownRootOptions: CommandLineOption | undefined, | ||
jsonConversionNotifier: JsonConversionNotifier | undefined): any { | ||
if (!sourceFile.jsonObject) { | ||
return {}; | ||
if (!sourceFile.statements.length) { | ||
return returnValue ? {} : undefined; | ||
} | ||
|
||
return convertObjectLiteralExpressionToJson(sourceFile.jsonObject, knownRootOptions, | ||
/*extraKeyDiagnosticMessage*/ undefined, /*parentOption*/ undefined); | ||
return convertPropertyValueToJson(sourceFile.statements[0].expression, knownRootOptions); | ||
|
||
function isRootOptionMap(knownOptions: Map<CommandLineOption> | undefined) { | ||
return knownRootOptions && (knownRootOptions as TsConfigOnlyOption).elementOptions === knownOptions; | ||
} | ||
|
||
function convertObjectLiteralExpressionToJson( | ||
node: ObjectLiteralExpression, | ||
knownOptions: Map<CommandLineOption> | undefined, | ||
extraKeyDiagnosticMessage: DiagnosticMessage | undefined, | ||
parentOption: string | undefined | ||
): any { | ||
const result: any = {}; | ||
const result: any = returnValue ? {} : undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this returnValue check just for efficiency, to avoid building up a value that's not needed. Actually, why isn't it needed, when existing callers needed it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We dont need value when we are importing modules: We just want the error reporting on valid json syntax. So creating (potentially large) json object isnt needed hence the flag. In case of config file, we do need json object as well as checks which can be done simultaneously. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a comment on the function header? |
||
for (const element of node.properties) { | ||
if (element.kind !== SyntaxKind.PropertyAssignment) { | ||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element, Diagnostics.Property_assignment_expected)); | ||
|
@@ -1105,19 +1120,21 @@ namespace ts { | |
} | ||
const value = convertPropertyValueToJson(element.initializer, option); | ||
if (typeof keyText !== "undefined") { | ||
result[keyText] = value; | ||
if (returnValue) { | ||
result[keyText] = value; | ||
} | ||
// Notify key value set, if user asked for it | ||
if (jsonConversionNotifier && | ||
// Current callbacks are only on known parent option or if we are setting values in the root | ||
(parentOption || knownOptions === knownRootOptions)) { | ||
(parentOption || isRootOptionMap(knownOptions))) { | ||
const isValidOptionValue = isCompilerOptionsValue(option, value); | ||
if (parentOption) { | ||
if (isValidOptionValue) { | ||
// Notify option set in the parent if its a valid option value | ||
jsonConversionNotifier.onSetValidOptionKeyValueInParent(parentOption, option, value); | ||
} | ||
} | ||
else if (knownOptions === knownRootOptions) { | ||
else if (isRootOptionMap(knownOptions)) { | ||
if (isValidOptionValue) { | ||
// Notify about the valid root key value being set | ||
jsonConversionNotifier.onSetValidOptionKeyValueInRoot(keyText, element.name, value, element.initializer); | ||
|
@@ -1136,8 +1153,8 @@ namespace ts { | |
function convertArrayLiteralExpressionToJson( | ||
elements: NodeArray<Expression>, | ||
elementOption: CommandLineOption | undefined | ||
): any[] { | ||
return elements.map(element => convertPropertyValueToJson(element, elementOption)); | ||
): any[] | void { | ||
return (returnValue ? elements.map : elements.forEach).call(elements, (element: Expression) => convertPropertyValueToJson(element, elementOption)); | ||
} | ||
|
||
function convertPropertyValueToJson(valueExpression: Expression, option: CommandLineOption): any { | ||
|
@@ -1432,12 +1449,12 @@ namespace ts { | |
* @param basePath A root directory to resolve relative path entries in the config | ||
* file to. e.g. outDir | ||
*/ | ||
export function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray<JsFileExtensionInfo>): ParsedCommandLine { | ||
export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray<JsFileExtensionInfo>): ParsedCommandLine { | ||
return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions); | ||
} | ||
|
||
/*@internal*/ | ||
export function setConfigFileInOptions(options: CompilerOptions, configFile: JsonSourceFile) { | ||
export function setConfigFileInOptions(options: CompilerOptions, configFile: TsConfigSourceFile) { | ||
if (configFile) { | ||
Object.defineProperty(options, "configFile", { enumerable: false, writable: false, value: configFile }); | ||
} | ||
|
@@ -1465,7 +1482,7 @@ namespace ts { | |
*/ | ||
function parseJsonConfigFileContentWorker( | ||
json: any, | ||
sourceFile: JsonSourceFile, | ||
sourceFile: TsConfigSourceFile, | ||
host: ParseConfigHost, | ||
basePath: string, | ||
existingOptions: CompilerOptions = {}, | ||
|
@@ -1586,7 +1603,7 @@ namespace ts { | |
*/ | ||
function parseConfig( | ||
json: any, | ||
sourceFile: JsonSourceFile, | ||
sourceFile: TsConfigSourceFile, | ||
host: ParseConfigHost, | ||
basePath: string, | ||
configFileName: string, | ||
|
@@ -1663,7 +1680,7 @@ namespace ts { | |
} | ||
|
||
function parseOwnConfigOfJsonSourceFile( | ||
sourceFile: JsonSourceFile, | ||
sourceFile: TsConfigSourceFile, | ||
host: ParseConfigHost, | ||
basePath: string, | ||
configFileName: string | undefined, | ||
|
@@ -1710,7 +1727,7 @@ namespace ts { | |
} | ||
} | ||
}; | ||
const json = convertToObjectWorker(sourceFile, errors, getTsconfigRootOptionsMap(), optionsIterator); | ||
const json = convertToObjectWorker(sourceFile, errors, /*returnValue*/ true, getTsconfigRootOptionsMap(), optionsIterator); | ||
if (!typeAcquisition) { | ||
if (typingOptionstypeAcquisition) { | ||
typeAcquisition = (typingOptionstypeAcquisition.enableAutoDiscovery !== undefined) ? | ||
|
@@ -1753,7 +1770,7 @@ namespace ts { | |
} | ||
|
||
function getExtendedConfig( | ||
sourceFile: JsonSourceFile, | ||
sourceFile: TsConfigSourceFile, | ||
extendedConfigPath: string, | ||
host: ParseConfigHost, | ||
basePath: string, | ||
|
@@ -2005,7 +2022,7 @@ namespace ts { | |
host: ParseConfigHost, | ||
errors: Push<Diagnostic>, | ||
extraFileExtensions: ReadonlyArray<JsFileExtensionInfo>, | ||
jsonSourceFile: JsonSourceFile | ||
jsonSourceFile: TsConfigSourceFile | ||
): ExpandResult { | ||
basePath = normalizePath(basePath); | ||
let validatedIncludeSpecs: ReadonlyArray<string>, validatedExcludeSpecs: ReadonlyArray<string>; | ||
|
@@ -2106,7 +2123,7 @@ namespace ts { | |
}; | ||
} | ||
|
||
function validateSpecs(specs: ReadonlyArray<string>, errors: Push<Diagnostic>, allowTrailingRecursion: boolean, jsonSourceFile: JsonSourceFile, specKey: string): ReadonlyArray<string> { | ||
function validateSpecs(specs: ReadonlyArray<string>, errors: Push<Diagnostic>, allowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile, specKey: string): ReadonlyArray<string> { | ||
return specs.filter(spec => { | ||
const diag = specToDiagnostic(spec, allowTrailingRecursion); | ||
if (diag !== undefined) { | ||
|
@@ -2116,18 +2133,10 @@ namespace ts { | |
}); | ||
|
||
function createDiagnostic(message: DiagnosticMessage, spec: string): Diagnostic { | ||
if (jsonSourceFile && jsonSourceFile.jsonObject) { | ||
for (const property of getPropertyAssignment(jsonSourceFile.jsonObject, specKey)) { | ||
if (isArrayLiteralExpression(property.initializer)) { | ||
for (const element of property.initializer.elements) { | ||
if (isStringLiteral(element) && element.text === spec) { | ||
return createDiagnosticForNodeInSourceFile(jsonSourceFile, element, message, spec); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return createCompilerDiagnostic(message, spec); | ||
const element = getTsConfigPropArrayElementValue(jsonSourceFile, specKey, spec); | ||
return element ? | ||
createDiagnosticForNodeInSourceFile(jsonSourceFile, element, message, spec) : | ||
createCompilerDiagnostic(message, spec); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isSourceFile(declaration)
and save the cast few lines in