Support resolveJsonModule in new module modes (#46434)

* Support resolveJsonModule in new module modes

* Formatting feedback
This commit is contained in:
Wesley Wigham
2021-10-27 12:30:06 -07:00
committed by GitHub
parent 8a68c8616d
commit abfd537503
14 changed files with 791 additions and 34 deletions
+74 -33
View File
@@ -2637,6 +2637,11 @@ namespace ts {
return usageMode === ModuleKind.ESNext && targetMode === ModuleKind.CommonJS;
}
function isOnlyImportedAsDefault(usage: Expression) {
const usageMode = getUsageModeForExpression(usage);
return usageMode === ModuleKind.ESNext && endsWith((usage as StringLiteralLike).text, Extension.Json);
}
function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean, usage: Expression) {
const usageMode = file && getUsageModeForExpression(usage);
if (file && usageMode !== undefined) {
@@ -2688,8 +2693,9 @@ namespace ts {
}
const file = moduleSymbol.declarations?.find(isSourceFile);
const hasDefaultOnly = isOnlyImportedAsDefault(node.parent.moduleSpecifier);
const hasSyntheticDefault = canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, node.parent.moduleSpecifier);
if (!exportDefaultSymbol && !hasSyntheticDefault) {
if (!exportDefaultSymbol && !hasSyntheticDefault && !hasDefaultOnly) {
if (hasExportAssignmentSymbol(moduleSymbol)) {
const compilerOptionName = moduleKind >= ModuleKind.ES2015 ? "allowSyntheticDefaultImports" : "esModuleInterop";
const exportEqualsSymbol = moduleSymbol.exports!.get(InternalSymbolName.ExportEquals);
@@ -2708,7 +2714,7 @@ namespace ts {
reportNonDefaultExport(moduleSymbol, node);
}
}
else if (hasSyntheticDefault) {
else if (hasSyntheticDefault || hasDefaultOnly) {
// per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present
const resolved = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
markSymbolOfAliasDeclarationIfTypeOnly(node, moduleSymbol, resolved, /*overwriteTypeOnly*/ false);
@@ -2840,7 +2846,7 @@ namespace ts {
let symbolFromModule = getExportOfModule(targetSymbol, name, specifier, dontResolveAlias);
if (symbolFromModule === undefined && name.escapedText === InternalSymbolName.Default) {
const file = moduleSymbol.declarations?.find(isSourceFile);
if (canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, moduleSpecifier)) {
if (isOnlyImportedAsDefault(moduleSpecifier) || canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, moduleSpecifier)) {
symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
}
}
@@ -3449,6 +3455,9 @@ namespace ts {
if (isSyncImport && sourceFile.impliedNodeFormat === ModuleKind.ESNext) {
error(errorNode, Diagnostics.Module_0_cannot_be_imported_using_this_construct_The_specifier_only_resolves_to_an_ES_module_which_cannot_be_imported_synchronously_Use_dynamic_import_instead, moduleReference);
}
if (mode === ModuleKind.ESNext && compilerOptions.resolveJsonModule && resolvedModule.extension === Extension.Json) {
error(errorNode, Diagnostics.JSON_imports_are_experimental_in_ES_module_mode_imports);
}
}
// merged symbol is module declaration symbol combined with all augmentations
return getMergedSymbol(sourceFile.symbol);
@@ -3611,32 +3620,26 @@ namespace ts {
return symbol;
}
if (getESModuleInterop(compilerOptions)) {
const referenceParent = referencingLocation.parent;
if (
(isImportDeclaration(referenceParent) && getNamespaceDeclarationNode(referenceParent)) ||
isImportCall(referenceParent)
) {
const type = getTypeOfSymbol(symbol);
const referenceParent = referencingLocation.parent;
if (
(isImportDeclaration(referenceParent) && getNamespaceDeclarationNode(referenceParent)) ||
isImportCall(referenceParent)
) {
const reference = isImportCall(referenceParent) ? referenceParent.arguments[0] : referenceParent.moduleSpecifier;
const type = getTypeOfSymbol(symbol);
const defaultOnlyType = getTypeWithSyntheticDefaultOnly(type, symbol, moduleSymbol!, reference);
if (defaultOnlyType) {
return cloneTypeAsModuleType(symbol, defaultOnlyType, referenceParent);
}
if (getESModuleInterop(compilerOptions)) {
let sigs = getSignaturesOfStructuredType(type, SignatureKind.Call);
if (!sigs || !sigs.length) {
sigs = getSignaturesOfStructuredType(type, SignatureKind.Construct);
}
if (sigs && sigs.length) {
const moduleType = getTypeWithSyntheticDefaultImportType(type, symbol, moduleSymbol!, isImportCall(referenceParent) ? referenceParent.arguments[0] : referenceParent.moduleSpecifier);
// Create a new symbol which has the module's type less the call and construct signatures
const result = createSymbol(symbol.flags, symbol.escapedName);
result.declarations = symbol.declarations ? symbol.declarations.slice() : [];
result.parent = symbol.parent;
result.target = symbol;
result.originatingImport = referenceParent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
if (symbol.members) result.members = new Map(symbol.members);
if (symbol.exports) result.exports = new Map(symbol.exports);
const resolvedModuleType = resolveStructuredTypeMembers(moduleType as StructuredType); // Should already be resolved from the signature checks above
result.type = createAnonymousType(result, resolvedModuleType.members, emptyArray, emptyArray, resolvedModuleType.indexInfos);
return result;
if ((sigs && sigs.length) || getPropertyOfType(type, InternalSymbolName.Default)) {
const moduleType = getTypeWithSyntheticDefaultImportType(type, symbol, moduleSymbol!, reference);
return cloneTypeAsModuleType(symbol, moduleType, referenceParent);
}
}
}
@@ -3644,6 +3647,24 @@ namespace ts {
return symbol;
}
/**
* Create a new symbol which has the module's type less the call and construct signatures
*/
function cloneTypeAsModuleType(symbol: Symbol, moduleType: Type, referenceParent: ImportDeclaration | ImportCall) {
const result = createSymbol(symbol.flags, symbol.escapedName);
result.declarations = symbol.declarations ? symbol.declarations.slice() : [];
result.parent = symbol.parent;
result.target = symbol;
result.originatingImport = referenceParent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
if (symbol.members) result.members = new Map(symbol.members);
if (symbol.exports) result.exports = new Map(symbol.exports);
const resolvedModuleType = resolveStructuredTypeMembers(moduleType as StructuredType); // Should already be resolved from the signature checks above
result.type = createAnonymousType(result, resolvedModuleType.members, emptyArray, emptyArray, resolvedModuleType.indexInfos);
return result;
}
function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean {
return moduleSymbol.exports!.get(InternalSymbolName.ExportEquals) !== undefined;
}
@@ -31007,12 +31028,38 @@ namespace ts {
if (moduleSymbol) {
const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, specifier, /*dontRecursivelyResolve*/ true, /*suppressUsageError*/ false);
if (esModuleSymbol) {
return createPromiseReturnType(node, getTypeWithSyntheticDefaultImportType(getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier));
return createPromiseReturnType(node,
getTypeWithSyntheticDefaultOnly(getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier) ||
getTypeWithSyntheticDefaultImportType(getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier)
);
}
}
return createPromiseReturnType(node, anyType);
}
function createDefaultPropertyWrapperForModule(symbol: Symbol, originalSymbol: Symbol, anonymousSymbol?: Symbol | undefined) {
const memberTable = createSymbolTable();
const newSymbol = createSymbol(SymbolFlags.Alias, InternalSymbolName.Default);
newSymbol.parent = originalSymbol;
newSymbol.nameType = getStringLiteralType("default");
newSymbol.target = resolveSymbol(symbol);
memberTable.set(InternalSymbolName.Default, newSymbol);
return createAnonymousType(anonymousSymbol, memberTable, emptyArray, emptyArray, emptyArray);
}
function getTypeWithSyntheticDefaultOnly(type: Type, symbol: Symbol, originalSymbol: Symbol, moduleSpecifier: Expression) {
const hasDefaultOnly = isOnlyImportedAsDefault(moduleSpecifier);
if (hasDefaultOnly && type && !isErrorType(type)) {
const synthType = type as SyntheticDefaultModuleType;
if (!synthType.defaultOnlyType) {
const type = createDefaultPropertyWrapperForModule(symbol, originalSymbol);
synthType.defaultOnlyType = type;
}
return synthType.defaultOnlyType;
}
return undefined;
}
function getTypeWithSyntheticDefaultImportType(type: Type, symbol: Symbol, originalSymbol: Symbol, moduleSpecifier: Expression): Type {
if (allowSyntheticDefaultImports && type && !isErrorType(type)) {
const synthType = type as SyntheticDefaultModuleType;
@@ -31020,14 +31067,8 @@ namespace ts {
const file = originalSymbol.declarations?.find(isSourceFile);
const hasSyntheticDefault = canHaveSyntheticDefault(file, originalSymbol, /*dontResolveAlias*/ false, moduleSpecifier);
if (hasSyntheticDefault) {
const memberTable = createSymbolTable();
const newSymbol = createSymbol(SymbolFlags.Alias, InternalSymbolName.Default);
newSymbol.parent = originalSymbol;
newSymbol.nameType = getStringLiteralType("default");
newSymbol.target = resolveSymbol(symbol);
memberTable.set(InternalSymbolName.Default, newSymbol);
const anonymousSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
const defaultContainingObject = createAnonymousType(anonymousSymbol, memberTable, emptyArray, emptyArray, emptyArray);
const defaultContainingObject = createDefaultPropertyWrapperForModule(symbol, originalSymbol, anonymousSymbol);
anonymousSymbol.type = defaultContainingObject;
synthType.syntheticType = isValidSpreadType(type) ? getSpreadType(type, defaultContainingObject, anonymousSymbol, /*objectFlags*/ 0, /*readonly*/ false) : defaultContainingObject;
}
+4
View File
@@ -6001,6 +6001,10 @@
"category": "Error",
"code": 7061
},
"JSON imports are experimental in ES module mode imports.": {
"category": "Error",
"code": 7062
},
"You cannot rename this element.": {
"category": "Error",
+3 -1
View File
@@ -3333,7 +3333,9 @@ namespace ts {
}
if (options.resolveJsonModule) {
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs &&
getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node12 &&
getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) {
createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_cannot_be_specified_without_node_module_resolution_strategy, "resolveJsonModule");
}
// Any emit other than common js, amd, es2015 or esnext is error
+1
View File
@@ -5579,6 +5579,7 @@ namespace ts {
/* @internal */
export interface SyntheticDefaultModuleType extends Type {
syntheticType?: Type;
defaultOnlyType?: Type;
}
export interface InstantiableType extends Type {
+2
View File
@@ -6168,6 +6168,8 @@ namespace ts {
case ModuleKind.ES2020:
case ModuleKind.ES2022:
case ModuleKind.ESNext:
case ModuleKind.Node12:
case ModuleKind.NodeNext:
return true;
default:
return false;