Repository: vuejs/eslint-plugin-vue
Pull request #1528 (2021-07-01 05:25:34)

Deprecated `vue/experimental-script-setup-vars` rule


Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index a93d5a64..c5b80f1f 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -24,7 +24,6 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | Rule ID | Description |    |
 |:--------|:------------|:---|
 | [vue/comment-directive](./comment-directive.md) | support comment-directives in `<template>` |  |
-| [vue/experimental-script-setup-vars](./experimental-script-setup-vars.md) | prevent variables defined in `<script setup>` to be marked as undefined |  |
 | [vue/jsx-uses-vars](./jsx-uses-vars.md) | prevent variables used in JSX to be marked as unused |  |
 
 ## Priority A: Essential (Error Prevention) <badge text="for Vue.js 3.x" vertical="middle">for Vue.js 3.x</badge>
@@ -379,5 +378,6 @@ The following rules extend the rules provided by ESLint itself and apply them to
 
 | Rule ID | Replaced by |
 |:--------|:------------|
+| [vue/experimental-script-setup-vars](./experimental-script-setup-vars.md) | (no replacement) |
 | [vue/name-property-casing](./name-property-casing.md) | [vue/component-definition-name-casing](./component-definition-name-casing.md) |
 | [vue/no-confusing-v-for-v-if](./no-confusing-v-for-v-if.md) | [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) |
diff --git a/docs/rules/experimental-script-setup-vars.md b/docs/rules/experimental-script-setup-vars.md
index 9608384a..bf89fa9a 100644
--- a/docs/rules/experimental-script-setup-vars.md
+++ b/docs/rules/experimental-script-setup-vars.md
@@ -9,7 +9,7 @@ since: v7.0.0
 
 > prevent variables defined in `<script setup>` to be marked as undefined
 
-- :gear: This rule is included in all of `"plugin:vue/base"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-essential"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/recommended"` and `"plugin:vue/vue3-recommended"`.
+- :warning: This rule was **deprecated**.
 
 :::warning
 This rule is an experimental rule. It may be removed without notice.
diff --git a/lib/configs/base.js b/lib/configs/base.js
index eb129643..2521a623 100644
--- a/lib/configs/base.js
+++ b/lib/configs/base.js
@@ -16,7 +16,6 @@ module.exports = {
   plugins: ['vue'],
   rules: {
     'vue/comment-directive': 'error',
-    'vue/experimental-script-setup-vars': 'error',
     'vue/jsx-uses-vars': 'error'
   }
 }
diff --git a/lib/rules/experimental-script-setup-vars.js b/lib/rules/experimental-script-setup-vars.js
index d215a27b..b7fa2a81 100644
--- a/lib/rules/experimental-script-setup-vars.js
+++ b/lib/rules/experimental-script-setup-vars.js
@@ -21,9 +21,10 @@ module.exports = {
     docs: {
       description:
         'prevent variables defined in `<script setup>` to be marked as undefined', // eslint-disable-line consistent-docs-description
-      categories: ['base'],
+      categories: undefined,
       url: 'https://eslint.vuejs.org/rules/experimental-script-setup-vars.html'
     },
+    deprecated: true,
     schema: []
   },
   /**
diff --git a/lib/utils/indent-common.js b/lib/utils/indent-common.js
index 8ec6bbe4..5d80b83b 100644
--- a/lib/utils/indent-common.js
+++ b/lib/utils/indent-common.js
@@ -1824,6 +1824,8 @@ module.exports.defineVisitor = function create(
       let firstToken = tokenStore.getFirstToken(node)
 
       while (
+        leftToken &&
+        rightToken &&
         isOpeningParenToken(leftToken) &&
         isClosingParenToken(rightToken)
       ) {
diff --git a/lib/utils/indent-ts.js b/lib/utils/indent-ts.js
index 4801ab21..9afcdb34 100644
--- a/lib/utils/indent-ts.js
+++ b/lib/utils/indent-ts.js
@@ -19,6 +19,7 @@ const {
  * @typedef { { type: string } & HasLocation } MaybeNode
  */
 /**
+ * @typedef {import('@typescript-eslint/types').TSESTree.Node} TSESTreeNode
  * @typedef {import('@typescript-eslint/types').TSESTree.ClassExpression} ClassExpression
  * @typedef {import('@typescript-eslint/types').TSESTree.ClassDeclaration} ClassDeclaration
  * @typedef {import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} TSTypeAliasDeclaration
@@ -125,6 +126,48 @@ function defineVisitor({
   processSemicolons,
   getFirstAndLastTokens
 }) {
+  /**
+   * Check whether a given token is the first token of:
+   *
+   * - A parameter of TSTypeParameterInstantiation
+   * - An element of TSTupleType
+   *
+   * @param {Token} token The token to check.
+   * @param {TSUnionType | TSIntersectionType} belongingNode The node that the token is belonging to.
+   * @returns {boolean} `true` if the token is the first token of an element.
+   */
+  function isBeginningOfElement(token, belongingNode) {
+    /** @type {TSESTreeNode | null} */
+    let node = belongingNode
+
+    while (node != null && node.parent != null) {
+      /** @type {TSESTreeNode} */
+      const parent = node.parent
+      if (parent.type === 'TSTypeParameterInstantiation') {
+        return (
+          parent.params.length >= 2 &&
+          parent.params.some(
+            (param) =>
+              getFirstAndLastTokens(param).firstToken.range[0] ===
+              token.range[0]
+          )
+        )
+      }
+      if (parent.type === 'TSTupleType') {
+        return parent.elementTypes.some(
+          (element) =>
+            element != null &&
+            getFirstAndLastTokens(element).firstToken.range[0] ===
+              token.range[0]
+        )
+      }
+
+      node = parent
+    }
+
+    return false
+  }
+
   return {
     // Support TypeScript
     /** @param {ClassDeclaration | ClassExpression} node */
@@ -463,16 +506,34 @@ function defineVisitor({
       // A | B
       // A & B
       const firstToken = tokenStore.getFirstToken(node)
-      const types = [...node.types]
-      if (getFirstAndLastTokens(types[0]).firstToken === firstToken) {
-        types.shift()
+
+      const prevToken = tokenStore.getTokenBefore(firstToken)
+      const shouldIndent =
+        prevToken == null ||
+        prevToken.loc.end.line === firstToken.loc.start.line ||
+        isBeginningOfElement(firstToken, node)
+      const offset = shouldIndent ? 1 : 0
+
+      const typeTokensList = node.types.map(getFirstAndLastTokens)
+      const typeTokens = typeTokensList.shift()
+      if (!typeTokens) {
+        return
+      }
+      let lastToken
+      if (typeTokens.firstToken === firstToken) {
+        lastToken = typeTokens.lastToken
+      } else {
+        typeTokensList.unshift(typeTokens)
+        lastToken = firstToken
+      }
+      for (const typeTokens of typeTokensList) {
+        setOffset(
+          tokenStore.getTokensBetween(lastToken, typeTokens.firstToken),
+          offset,
+          firstToken
+        )
+        setOffset(typeTokens.firstToken, offset, firstToken)
       }
-      processNodeList(
-        types,
-        firstToken,
-        null,
-        isBeginningOfLine(firstToken) ? 0 : 1
-      )
     },
     TSParenthesizedType(node) {
       // (T)
@@ -1253,12 +1314,4 @@ function defineVisitor({
     // ----------------------------------------------------------------------
     TSLiteralType() {}
   }
-  /**
-   * Check whether the given node or token is the beginning of a line.
-   * @param {MaybeNode} node
-   */
-  function isBeginningOfLine(node) {
-    const prevToken = tokenStore.getTokenBefore(node, { includeComments: true })
-    return !prevToken || prevToken.loc.end.line < node.loc.start.line
-  }
 }


Test patch:
diff --git a/tests/fixtures/script-indent/ts-union-intersection-02.vue b/tests/fixtures/script-indent/ts-union-intersection-02.vue
new file mode 100644
index 00000000..04b72221
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-union-intersection-02.vue
@@ -0,0 +1,21 @@
+<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
+<script lang="ts">
+type T =
+  a
+  | b
+type U =
+  | a
+  | b
+type V =
+  a
+  & b
+type W =
+  & a
+  & b
+type X =
+  &
+  (
+    a
+    | b
+  )
+</script>
diff --git a/tests/fixtures/script-indent/ts-union-intersection-03.vue b/tests/fixtures/script-indent/ts-union-intersection-03.vue
new file mode 100644
index 00000000..78928f2e
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-union-intersection-03.vue
@@ -0,0 +1,19 @@
+<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
+<script lang="ts">
+type T = a
+  | b
+  | c
+type U = | a
+  | b
+  | c
+type V = a
+  |
+  b
+  |
+  c
+const a = a
+  |
+  b
+  |
+  c
+</script>
diff --git a/tests/fixtures/script-indent/ts-union-intersection-04.vue b/tests/fixtures/script-indent/ts-union-intersection-04.vue
new file mode 100644
index 00000000..7396edc4
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-union-intersection-04.vue
@@ -0,0 +1,17 @@
+<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
+<script lang="ts">
+type T = U< a
+  | b
+  | c
+>
+type T = U<
+  | a
+  | b
+  | c
+>
+type T = U<
+  & a
+  & b
+  & c
+>
+</script>
diff --git a/tests/fixtures/script-indent/ts-union-intersection-05.vue b/tests/fixtures/script-indent/ts-union-intersection-05.vue
new file mode 100644
index 00000000..d6013535
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-union-intersection-05.vue
@@ -0,0 +1,19 @@
+<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
+<script lang="ts">
+type T = U<
+  a
+    | b
+    | c,
+  a
+    | b
+    | c
+>
+type T = U<
+  a
+    & b
+    & c,
+  a
+    & b
+    & c
+>
+</script>
diff --git a/tests/fixtures/script-indent/ts-union-intersection-06.vue b/tests/fixtures/script-indent/ts-union-intersection-06.vue
new file mode 100644
index 00000000..a363a284
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-union-intersection-06.vue
@@ -0,0 +1,27 @@
+<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
+<script lang="ts">
+type T = [
+  a
+    | b
+    | c
+]
+type U = [
+  | a
+    | b
+    | c
+]
+type T2 = [ a
+              | b
+              | c,
+            a
+              | b
+              | c
+]
+type U2 = [ | a
+              | b
+              | c,
+            | a
+              | b
+              | c
+]
+</script>