From cdb9807a5a368a136856cd03048b68e0f2dfb405 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sun, 6 Sep 2020 11:28:41 -0700 Subject: [PATCH] fix(scope-manager): don't create references for intrinsic JSX elements (#2504) --- .../tests/eslint-rules/no-undef.test.ts | 21 ++++ .../src/referencer/Referencer.ts | 15 ++- .../tests/fixtures/jsx/children.tsx.shot | 8 -- .../fixtures/jsx/component-intrinsic-name.tsx | 2 + .../jsx/component-intrinsic-name.tsx.shot | 58 ++++++++++ ...mespaced.tsx => component-namespaced1.tsx} | 0 ...sx.shot => component-namespaced1.tsx.shot} | 2 +- .../fixtures/jsx/component-namespaced2.tsx | 6 + .../jsx/component-namespaced2.tsx.shot | 106 ++++++++++++++++++ 9 files changed, 208 insertions(+), 10 deletions(-) create mode 100644 packages/scope-manager/tests/fixtures/jsx/component-intrinsic-name.tsx create mode 100644 packages/scope-manager/tests/fixtures/jsx/component-intrinsic-name.tsx.shot rename packages/scope-manager/tests/fixtures/jsx/{component-namespaced.tsx => component-namespaced1.tsx} (100%) rename packages/scope-manager/tests/fixtures/jsx/{component-namespaced.tsx.shot => component-namespaced1.tsx.shot} (98%) create mode 100644 packages/scope-manager/tests/fixtures/jsx/component-namespaced2.tsx create mode 100644 packages/scope-manager/tests/fixtures/jsx/component-namespaced2.tsx.shot diff --git a/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts index 7e1d179e407..58baeeeaeac 100644 --- a/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts @@ -199,6 +199,27 @@ function Foo() {} }, }, }, + // intrinsic elements should not cause errors + { + code: ` +
; + `, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: ` +; + `, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, // https://github.com/typescript-eslint/typescript-eslint/issues/2477 ` const x = 1 as const; diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts index d329eca2d17..6cccab10428 100644 --- a/packages/scope-manager/src/referencer/Referencer.ts +++ b/packages/scope-manager/src/referencer/Referencer.ts @@ -558,6 +558,10 @@ class Referencer extends Visitor { this.visit(node.value); } + protected JSXClosingElement(): void { + // should not be counted as a reference + } + protected JSXFragment(node: TSESTree.JSXFragment): void { this.referenceJsxPragma(); this.referenceJsxFragment(); @@ -575,7 +579,16 @@ class Referencer extends Visitor { protected JSXOpeningElement(node: TSESTree.JSXOpeningElement): void { this.referenceJsxPragma(); - this.visit(node.name); + if (node.name.type === AST_NODE_TYPES.JSXIdentifier) { + if (node.name.name[0].toUpperCase() === node.name.name[0]) { + // lower cased component names are always treated as "intrinsic" names, and are converted to a string, + // not a variable by JSX transforms: + //
=> React.createElement("div", null) + this.visit(node.name); + } + } else { + this.visit(node.name); + } this.visitType(node.typeParameters); for (const attr of node.attributes) { this.visit(attr); diff --git a/packages/scope-manager/tests/fixtures/jsx/children.tsx.shot b/packages/scope-manager/tests/fixtures/jsx/children.tsx.shot index 65e4f320231..964f735f3c5 100644 --- a/packages/scope-manager/tests/fixtures/jsx/children.tsx.shot +++ b/packages/scope-manager/tests/fixtures/jsx/children.tsx.shot @@ -51,14 +51,6 @@ ScopeManager { resolved: null, }, Reference$3, - Reference$4 { - identifier: JSXIdentifier$5, - isRead: true, - isTypeReference: false, - isValueReference: true, - isWrite: false, - resolved: null, - }, ], set: Map { "const" => ImplicitGlobalConstTypeVariable, diff --git a/packages/scope-manager/tests/fixtures/jsx/component-intrinsic-name.tsx b/packages/scope-manager/tests/fixtures/jsx/component-intrinsic-name.tsx new file mode 100644 index 00000000000..300235b4fc6 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/jsx/component-intrinsic-name.tsx @@ -0,0 +1,2 @@ +function div() {} // should not be referenced +
; diff --git a/packages/scope-manager/tests/fixtures/jsx/component-intrinsic-name.tsx.shot b/packages/scope-manager/tests/fixtures/jsx/component-intrinsic-name.tsx.shot new file mode 100644 index 00000000000..f67914bac29 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/jsx/component-intrinsic-name.tsx.shot @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`jsx component-intrinsic-name 1`] = ` +ScopeManager { + variables: Array [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: Array [ + FunctionNameDefinition$1 { + name: Identifier<"div">, + node: FunctionDeclaration$1, + }, + ], + name: "div", + references: Array [], + isValueVariable: true, + isTypeVariable: false, + }, + Variable$3 { + defs: Array [], + name: "arguments", + references: Array [], + isValueVariable: true, + isTypeVariable: true, + }, + ], + scopes: Array [ + GlobalScope$1 { + block: Program$2, + isStrict: false, + references: Array [], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "div" => Variable$2, + }, + type: "global", + upper: null, + variables: Array [ + ImplicitGlobalConstTypeVariable, + Variable$2, + ], + }, + FunctionScope$2 { + block: FunctionDeclaration$1, + isStrict: false, + references: Array [], + set: Map { + "arguments" => Variable$3, + }, + type: "function", + upper: GlobalScope$1, + variables: Array [ + Variable$3, + ], + }, + ], +} +`; diff --git a/packages/scope-manager/tests/fixtures/jsx/component-namespaced.tsx b/packages/scope-manager/tests/fixtures/jsx/component-namespaced1.tsx similarity index 100% rename from packages/scope-manager/tests/fixtures/jsx/component-namespaced.tsx rename to packages/scope-manager/tests/fixtures/jsx/component-namespaced1.tsx diff --git a/packages/scope-manager/tests/fixtures/jsx/component-namespaced.tsx.shot b/packages/scope-manager/tests/fixtures/jsx/component-namespaced1.tsx.shot similarity index 98% rename from packages/scope-manager/tests/fixtures/jsx/component-namespaced.tsx.shot rename to packages/scope-manager/tests/fixtures/jsx/component-namespaced1.tsx.shot index 5a3d3327032..291e0103232 100644 --- a/packages/scope-manager/tests/fixtures/jsx/component-namespaced.tsx.shot +++ b/packages/scope-manager/tests/fixtures/jsx/component-namespaced1.tsx.shot @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`jsx component-namespaced 1`] = ` +exports[`jsx component-namespaced1 1`] = ` ScopeManager { variables: Array [ ImplicitGlobalConstTypeVariable, diff --git a/packages/scope-manager/tests/fixtures/jsx/component-namespaced2.tsx b/packages/scope-manager/tests/fixtures/jsx/component-namespaced2.tsx new file mode 100644 index 00000000000..8f8879ffb73 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/jsx/component-namespaced2.tsx @@ -0,0 +1,6 @@ +const x = { + Foo() {}, +}; +const Foo = 1; // should be unreferenced + +; // lower cased namespaces should still create a reference diff --git a/packages/scope-manager/tests/fixtures/jsx/component-namespaced2.tsx.shot b/packages/scope-manager/tests/fixtures/jsx/component-namespaced2.tsx.shot new file mode 100644 index 00000000000..29a55db86ad --- /dev/null +++ b/packages/scope-manager/tests/fixtures/jsx/component-namespaced2.tsx.shot @@ -0,0 +1,106 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`jsx component-namespaced2 1`] = ` +ScopeManager { + variables: Array [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: Array [ + VariableDefinition$1 { + name: Identifier<"x">, + node: VariableDeclarator$1, + }, + ], + name: "x", + references: Array [ + Reference$1 { + identifier: Identifier<"x">, + init: true, + isRead: false, + isTypeReference: false, + isValueReference: true, + isWrite: true, + resolved: Variable$2, + writeExpr: ObjectExpression$2, + }, + Reference$3 { + identifier: JSXIdentifier$3, + isRead: true, + isTypeReference: false, + isValueReference: true, + isWrite: false, + resolved: Variable$2, + }, + ], + isValueVariable: true, + isTypeVariable: false, + }, + Variable$3 { + defs: Array [], + name: "arguments", + references: Array [], + isValueVariable: true, + isTypeVariable: true, + }, + Variable$4 { + defs: Array [ + VariableDefinition$2 { + name: Identifier<"Foo">, + node: VariableDeclarator$4, + }, + ], + name: "Foo", + references: Array [ + Reference$2 { + identifier: Identifier<"Foo">, + init: true, + isRead: false, + isTypeReference: false, + isValueReference: true, + isWrite: true, + resolved: Variable$4, + writeExpr: Literal$5, + }, + ], + isValueVariable: true, + isTypeVariable: false, + }, + ], + scopes: Array [ + GlobalScope$1 { + block: Program$6, + isStrict: false, + references: Array [ + Reference$1, + Reference$2, + Reference$3, + ], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "x" => Variable$2, + "Foo" => Variable$4, + }, + type: "global", + upper: null, + variables: Array [ + ImplicitGlobalConstTypeVariable, + Variable$2, + Variable$4, + ], + }, + FunctionScope$2 { + block: FunctionExpression$7, + isStrict: false, + references: Array [], + set: Map { + "arguments" => Variable$3, + }, + type: "function", + upper: GlobalScope$1, + variables: Array [ + Variable$3, + ], + }, + ], +} +`;