From f476e8426bc1dffe5ef82e672779337f3cb031dd Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 14 Jan 2022 01:06:45 +0000 Subject: [PATCH] Separate our usages of utilities that expect variables initialized to require(...) and require(...).foo. --- src/compiler/binder.ts | 4 ++-- src/compiler/checker.ts | 4 ++-- src/compiler/types.ts | 5 +++++ src/compiler/utilities.ts | 17 +++++++++++++++-- src/services/findAllReferences.ts | 2 +- src/services/goToDefinition.ts | 2 +- src/services/importTracker.ts | 2 +- 7 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index eefc41fdaf8b5..4ae6a2bd31bf9 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3274,7 +3274,7 @@ namespace ts { return isEnumConst(node) ? bindBlockScopedDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) : bindBlockScopedDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); - } + } function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { if (inStrictMode) { @@ -3282,7 +3282,7 @@ namespace ts { } if (!isBindingPattern(node.name)) { - if (isInJSFile(node) && isRequireVariableDeclaration(node) && !getJSDocTypeTag(node)) { + if (isInJSFile(node) && isAccessedOrBareRequireVariableDeclaration(node) && !getJSDocTypeTag(node)) { declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes); } else if (isBlockOrCatchScoped(node)) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 578313bc7993c..84352d74cbfe6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2596,7 +2596,7 @@ namespace ts { && isAliasableOrJsExpression(node.parent.right) || node.kind === SyntaxKind.ShorthandPropertyAssignment || node.kind === SyntaxKind.PropertyAssignment && isAliasableOrJsExpression((node as PropertyAssignment).initializer) - || isRequireVariableDeclaration(node); + || isAccessedOrBareRequireVariableDeclaration(node); } function isAliasableOrJsExpression(e: Expression) { @@ -36984,7 +36984,7 @@ namespace ts { } // For a commonjs `const x = require`, validate the alias and exit const symbol = getSymbolOfNode(node); - if (symbol.flags & SymbolFlags.Alias && isRequireVariableDeclaration(node)) { + if (symbol.flags & SymbolFlags.Alias && isAccessedOrBareRequireVariableDeclaration(node)) { checkAliasSymbol(node); return; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index dcea2e7e38526..224fb27ef919a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4668,6 +4668,11 @@ namespace ts { readonly initializer: RequireOrImportCall; } + /* @internal */ + export interface AccessedOrBareRequireVariableDeclaration extends VariableDeclaration { + readonly initializer: RequireOrImportCall | AccessExpression; + } + /* @internal */ export interface RequireVariableStatement extends VariableStatement { readonly declarationList: RequireVariableDeclarationList; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 98d89d4612824..635bb02765aa5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2046,7 +2046,7 @@ namespace ts { } export function getExternalModuleRequireArgument(node: Node) { - return isRequireVariableDeclaration(node) && (getLeftmostAccessExpression(node.initializer) as CallExpression).arguments[0] as StringLiteral; + return isAccessedOrBareRequireVariableDeclaration(node) && (getLeftmostAccessExpression(node.initializer) as CallExpression).arguments[0] as StringLiteral; } export function isInternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration { @@ -2114,10 +2114,23 @@ namespace ts { * This function does not test if the node is in a JavaScript file or not. */ export function isRequireVariableDeclaration(node: Node): node is RequireVariableDeclaration { + return isVariableDeclarationInitializedWithRequireHelper(node, /*allowAccessedRequire*/ false); + } + + /** + * Like `isRequireVariableDeclaration` but allows things like `require("...").foo.bar` or `require("...")["baz"]`. + */ + export function isAccessedOrBareRequireVariableDeclaration(node: Node): node is AccessedOrBareRequireVariableDeclaration { + return isVariableDeclarationInitializedWithRequireHelper(node, /*allowAccessedRequire*/ true); + } + + function isVariableDeclarationInitializedWithRequireHelper(node: Node, allowAccessedRequire: boolean) { if (node.kind === SyntaxKind.BindingElement) { node = node.parent.parent; } - return isVariableDeclaration(node) && !!node.initializer && isRequireCall(getLeftmostAccessExpression(node.initializer), /*requireStringLiteralLikeArgument*/ true); + return isVariableDeclaration(node) && + !!node.initializer && + isRequireCall(allowAccessedRequire ? getLeftmostAccessExpression(node.initializer) : node.initializer, /*requireStringLiteralLikeArgument*/ true); } export function isRequireVariableStatement(node: Node): node is RequireVariableStatement { diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index aebc52b2fb0f8..7dc65cee20689 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -1531,7 +1531,7 @@ namespace ts.FindAllReferences { // Use the parent symbol if the location is commonjs require syntax on javascript files only. if (isInJSFile(referenceLocation) && referenceLocation.parent.kind === SyntaxKind.BindingElement - && isRequireVariableDeclaration(referenceLocation.parent)) { + && isAccessedOrBareRequireVariableDeclaration(referenceLocation.parent)) { referenceSymbol = referenceLocation.parent.symbol; // The parent will not have a symbol if it's an ObjectBindingPattern (when destructuring is used). In // this case, just skip it, since the bound identifiers are not an alias of the import. diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 9ae8dda40f84c..f73e54c6419d1 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -290,7 +290,7 @@ namespace ts.GoToDefinition { return declaration.parent.kind === SyntaxKind.NamedImports; case SyntaxKind.BindingElement: case SyntaxKind.VariableDeclaration: - return isInJSFile(declaration) && isRequireVariableDeclaration(declaration); + return isInJSFile(declaration) && isAccessedOrBareRequireVariableDeclaration(declaration); default: return false; } diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index d3c95854f9bbe..dc069f5e0b657 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -621,7 +621,7 @@ namespace ts.FindAllReferences { Debug.assert((parent as ImportClause | NamespaceImport).name === node); return true; case SyntaxKind.BindingElement: - return isInJSFile(node) && isRequireVariableDeclaration(parent); + return isInJSFile(node) && isAccessedOrBareRequireVariableDeclaration(parent); default: return false; }