Make direct assignments to cjs exports considered literal contexts (#39816)

* Make direct assignments to cjs exports considered literal contexts

* Style feedback from PR

* Trailing whitespaaaaace
This commit is contained in:
Wesley Wigham
2022-03-09 13:27:39 -08:00
committed by GitHub
parent f76452cb27
commit 3f63804878
35 changed files with 300 additions and 199 deletions

View File

@@ -7014,9 +7014,13 @@ namespace ts {
else {
// A Class + Property merge is made for a `module.exports.Member = class {}`, and it doesn't serialize well as either a class _or_ a property symbol - in fact, _it behaves like an alias!_
// `var` is `FunctionScopedVariable`, `const` and `let` are `BlockScopedVariable`, and `module.exports.thing =` is `Property`
const flags = !(symbol.flags & SymbolFlags.BlockScopedVariable) ? undefined
: isConstVariable(symbol) ? NodeFlags.Const
: NodeFlags.Let;
const flags = !(symbol.flags & SymbolFlags.BlockScopedVariable)
? symbol.parent?.valueDeclaration && isSourceFile(symbol.parent?.valueDeclaration)
? NodeFlags.Const // exports are immutable in es6, which is what we emulate and check; so it's safe to mark all exports as `const` (there's no difference to consumers, but it allows unique symbol type declarations)
: undefined
: isConstVariable(symbol)
? NodeFlags.Const
: NodeFlags.Let;
const name = (needsPostExportDefault || !(symbol.flags & SymbolFlags.Property)) ? localName : getUnusedName(localName, symbol);
let textRange: Node | undefined = symbol.declarations && find(symbol.declarations, d => isVariableDeclaration(d));
if (textRange && isVariableDeclarationList(textRange.parent) && textRange.parent.declarations.length === 1) {
@@ -9180,7 +9184,10 @@ namespace ts {
if (containsSameNamedThisProperty(expression.left, expression.right)) {
return anyType;
}
const type = resolvedSymbol ? getTypeOfSymbol(resolvedSymbol) : getWidenedLiteralType(checkExpressionCached(expression.right));
const isDirectExport = kind === AssignmentDeclarationKind.ExportsProperty && (isPropertyAccessExpression(expression.left) || isElementAccessExpression(expression.left)) && (isModuleExportsAccessExpression(expression.left.expression) || (isIdentifier(expression.left.expression) && isExportsIdentifier(expression.left.expression)));
const type = resolvedSymbol ? getTypeOfSymbol(resolvedSymbol)
: isDirectExport ? getRegularTypeOfLiteralType(checkExpressionCached(expression.right))
: getWidenedLiteralType(checkExpressionCached(expression.right));
if (type.flags & TypeFlags.Object &&
kind === AssignmentDeclarationKind.ModuleExports &&
symbol.escapedName === InternalSymbolName.ExportEquals) {
@@ -16418,9 +16425,11 @@ namespace ts {
function getESSymbolLikeTypeForNode(node: Node) {
if (isValidESSymbolDeclaration(node)) {
const symbol = getSymbolOfNode(node);
const links = getSymbolLinks(symbol);
return links.uniqueESSymbolType || (links.uniqueESSymbolType = createUniqueESSymbolType(symbol));
const symbol = isCommonJsExportPropertyAssignment(node) ? getSymbolOfNode((node as BinaryExpression).left) : getSymbolOfNode(node);
if (symbol) {
const links = getSymbolLinks(symbol);
return links.uniqueESSymbolType || (links.uniqueESSymbolType = createUniqueESSymbolType(symbol));
}
}
return esSymbolType;
}
@@ -34209,7 +34218,7 @@ namespace ts {
function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type {
const type = checkExpression(node, checkMode, forceTuple);
return isConstContext(node) ? getRegularTypeOfLiteralType(type) :
return isConstContext(node) || isCommonJsExportedExpression(node) ? getRegularTypeOfLiteralType(type) :
isTypeAssertion(node) ? type :
getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(arguments.length === 2 ? getContextualType(node) : contextualType, node));
}

View File

@@ -1520,10 +1520,21 @@ namespace ts {
&& node.parent.parent.kind === SyntaxKind.VariableStatement;
}
export function isValidESSymbolDeclaration(node: Node): node is VariableDeclaration | PropertyDeclaration | SignatureDeclaration {
return isVariableDeclaration(node) ? isVarConst(node) && isIdentifier(node.name) && isVariableDeclarationInVariableStatement(node) :
export function isCommonJsExportedExpression(node: Node) {
if (!isInJSFile(node)) return false;
return (isObjectLiteralExpression(node.parent) && isBinaryExpression(node.parent.parent) && getAssignmentDeclarationKind(node.parent.parent) === AssignmentDeclarationKind.ModuleExports) ||
isCommonJsExportPropertyAssignment(node.parent);
}
export function isCommonJsExportPropertyAssignment(node: Node) {
if (!isInJSFile(node)) return false;
return (isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ExportsProperty);
}
export function isValidESSymbolDeclaration(node: Node): boolean {
return (isVariableDeclaration(node) ? isVarConst(node) && isIdentifier(node.name) && isVariableDeclarationInVariableStatement(node) :
isPropertyDeclaration(node) ? hasEffectiveReadonlyModifier(node) && hasStaticModifier(node) :
isPropertySignature(node) && hasEffectiveReadonlyModifier(node);
isPropertySignature(node) && hasEffectiveReadonlyModifier(node)) || isCommonJsExportPropertyAssignment(node);
}
export function introducesArgumentsExoticObject(node: Node) {