Repository: vuejs/eslint-plugin-vue (JavaScript)

Issue:
vue/multi-word-component-names doesn't catch SFCs with setup script?
Not sure if this is a bug or a question, but it's certainly strange to me. If it's intentional it should be noted somewhere.

I was linting a project made with `vue-cli`, and noticed that some single-word component files weren't being caught by the linter, and some were. The ones that were being caught were ones with just a `<template>`, and the ones that weren't had a `<script setup>`.

Here's a minimum reproducible example (at least in my project setup); three variations of a file **incorrectly** named `Test.vue`:

Caught ✅  (lint check fails)

```vue
<template>
  <p>test</p>
</template>
```

Caught ✅  (lint check fails)

```vue
<template>
  <AppButton />
</template>

<script lang="ts">
import { defineComponent } from "vue";
import AppButton from "@/components/AppButton.vue";

export default defineComponent({ components: { AppButton } });
</script>
```

Not caught ❌  (lint check passes)

```vue
<template>
  <AppButton />
</template>

<script setup lang="ts">
import AppButton from "@/components/AppButton.vue";
</script>
```



<details>

<summary>Here is my `package.json`:</summary>

```json
{
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "test:unit": "vue-cli-service test:unit /unit",
    "test:e2e": "vue-cli-service test:e2e --headless",
    "lint": "vue-cli-service lint && prettier --write \"**/*.{scss,js,ts,json,vue,svg,yaml}\" --no-error-on-unmatched-pattern",
    "fresh": "yarn cache clean && rm -rf node_modules yarn.lock && yarn install",
    "test": "yarn test:lint && yarn test:unit && yarn test:e2e && yarn test:axe",
    "test:axe": "vue-cli-service test:unit /axe",
    "test:gui": "vue-cli-service test:e2e",
    "test:lint": "vue-cli-service lint --no-fix && prettier --check \"**/*.{scss,js,ts,json,vue,svg,yaml}\" --no-error-on-unmatched-pattern"
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^1.3.0",
    "@fortawesome/free-brands-svg-icons": "^6.0.0",
    "@fortawesome/free-regular-svg-icons": "^6.0.0",
    "@fortawesome/free-solid-svg-icons": "^6.0.0",
    "@fortawesome/vue-fontawesome": "^3.0.0-5",
    "@vueuse/core": "^8.2.4",
    "body-scroll-lock": "^4.0.0-beta.0",
    "core-js": "^3.6.5",
    "file-saver": "^2.0.5",
    "lodash": "^4.17.21",
    "micromark": "^3.0.5",
    "normalize.css": "^8.0.1",
    "tippy.js": "^6.3.2",
    "ua-parser-js": "^1.0.2",
    "vue": "^3.0.0",
    "vue-inline-svg": "^3.1.0",
    "vue-router": "^4.0.0-0",
    "vue-tippy": "^6.0.0-alpha.52",
    "vuex": "^4.0.0-0",
    "wicg-inert": "^3.1.1"
  },
  "devDependencies": {
    "@types/body-scroll-lock": "^3.1.0",
    "@types/jest": "^24.0.19",
    "@types/jest-axe": "^3.5.3",
    "@types/lodash": "^4.14.175",
    "@types/node-fetch": "^2.6.1",
    "@types/ua-parser-js": "^0.7.36",
    "@typescript-eslint/eslint-plugin": "^4.18.0",
    "@typescript-eslint/parser": "^4.18.0",
    "@vue/cli-plugin-babel": "~4.5.15",
    "@vue/cli-plugin-e2e-cypress": "^5.0.0-rc.1",
    "@vue/cli-plugin-eslint": "~4.5.15",
    "@vue/cli-plugin-router": "~4.5.15",
    "@vue/cli-plugin-typescript": "~4.5.15",
    "@vue/cli-plugin-unit-jest": "~4.5.15",
    "@vue/cli-plugin-vuex": "~4.5.15",
    "@vue/cli-service": "~4.5.15",
    "@vue/compiler-sfc": "^3.0.0",
    "@vue/eslint-config-prettier": "^6.0.0",
    "@vue/eslint-config-typescript": "^7.0.0",
    "@vue/test-utils": "^2.0.0-0",
    "cypress": "^9.0.0",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^8.0.0",
    "eslint-plugin-vuejs-accessibility": "^0.7.1",
    "jest-axe": "^5.0.1",
    "msw": "^0.35.0",
    "node-fetch": "^2.6.0",
    "prettier": "^2.2.1",
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2",
    "tslib": "^2.3.1",
    "typescript": "^4.4.4",
    "vue-jest": "^5.0.0-0"
  },
}
```

</details>

I'm using `^8.0.0`.


Discussion:

Patch:
diff --git a/lib/rules/multi-word-component-names.js b/lib/rules/multi-word-component-names.js
index 4e4e7db28..6f73de294 100644
--- a/lib/rules/multi-word-component-names.js
+++ b/lib/rules/multi-word-component-names.js
@@ -60,7 +60,7 @@ module.exports = {
         ignores.add(casing.kebabCase(ignore))
       }
     }
-    let hasVue = false
+    let hasVue = utils.isScriptSetup(context)
     let hasName = false
 
     /**


Test patch:
diff --git a/tests/lib/rules/multi-word-component-names.js b/tests/lib/rules/multi-word-component-names.js
index bb0fba770..d11b11d3f 100644
--- a/tests/lib/rules/multi-word-component-names.js
+++ b/tests/lib/rules/multi-word-component-names.js
@@ -202,6 +202,17 @@ tester.run('multi-word-component-names', rule, {
           },
       }).$mount('#app')
       `
+    },
+    {
+      filename: 'MultiWord.vue',
+      code: `
+      <template>
+        <AppButton />
+      </template>
+      
+      <script setup lang="ts">
+      import AppButton from "@/components/AppButton.vue";
+      </script>`
     }
   ],
   invalid: [
@@ -309,6 +320,19 @@ tester.run('multi-word-component-names', rule, {
           line: 4
         }
       ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+      import Item from "@/components/Item.vue";
+      </script>`,
+      errors: [
+        {
+          message: 'Component name "test" should always be multi-word.',
+          line: 1
+        }
+      ]
     }
   ]
 })