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

Issue:
require-direct-export rule doesn't work with `export default Vue.extend({})`
<!--
  Before posting the issue, please confirm that the problem you're getting
  is not related with your code editor configuration.
  To make sure it's not, run: yarn eslint src/your-file.vue
-->

**Tell us about your environment**	
* **ESLint version:**	5.16.0
* **eslint-plugin-vue version:**	5.0.0
* **Node version:**	12.2.0

**Please show your full configuration:**
<!-- Paste content of your .eslintrc file -->
```js
module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: ['plugin:vue/essential', '@vue/prettier', '@vue/typescript'],
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'vue/require-direct-export': 'error',
  },
  parserOptions: {
    parser: '@typescript-eslint/parser',
  },
}

```

**What did you do?**	
<!-- Please include the actual source code causing the issue. -->
```vue
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
  </div>
</template>

<script lang="ts">
import Vue from "vue";
import HelloWorld from "./components/HelloWorld.vue";

export default Vue.extend({
  name: "app",
  components: {
    HelloWorld
  }
});
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
```

**What did you expect to happen?**
No error

**What actually happened?**
```
  12:1  error  Expected the component literal to be directly exported  vue/require-direct-export

✖ 1 problem (1 error, 0 warnings)

error Command failed with exit code 1.
```


Discussion:
@ota-meshi @mysticatea @michalsnik This issue was opened one year ago and so far there's no response.

Is that behavior of `require-direct-export` by design? This also relates to `defineComponent` in Vue 3.



Patch:
diff --git a/lib/rules/require-direct-export.js b/lib/rules/require-direct-export.js
index c3eaebd01..18897eb07 100644
--- a/lib/rules/require-direct-export.js
+++ b/lib/rules/require-direct-export.js
@@ -55,6 +55,25 @@ module.exports = {
           // OK
           return
         }
+        if (node.type === 'CallExpression') {
+          const {
+            callee,
+            arguments: [firstArg]
+          } = node
+          if (firstArg && firstArg.type === 'ObjectExpression') {
+            if (
+              (callee.type === 'Identifier' &&
+                callee.name === 'defineComponent') ||
+              (callee.type === 'MemberExpression' &&
+                callee.object.type === 'Identifier' &&
+                callee.object.name === 'Vue' &&
+                callee.property.type === 'Identifier' &&
+                callee.property.name === 'extend')
+            ) {
+              return
+            }
+          }
+        }
         if (!disallowFunctional) {
           if (node.type === 'ArrowFunctionExpression') {
             if (node.body.type !== 'BlockStatement') {


Test patch:
diff --git a/tests/lib/rules/require-direct-export.js b/tests/lib/rules/require-direct-export.js
index d8dc568b1..4f16e0fcd 100644
--- a/tests/lib/rules/require-direct-export.js
+++ b/tests/lib/rules/require-direct-export.js
@@ -74,6 +74,20 @@ ruleTester.run('require-direct-export', rule, {
       import { h } from 'vue'
       export default props => h('div', props.msg)
       `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import Vue from 'vue'
+      export default Vue.extend({})
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import { defineComponent } from 'vue'
+      export default defineComponent({})
+      `
     }
   ],
 
@@ -224,6 +238,40 @@ ruleTester.run('require-direct-export', rule, {
       `,
       options: [{ disallowFunctionalComponentFunction: true }],
       errors: ['Expected the component literal to be directly exported.']
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import Vue from 'vue'
+      export default Vue.extend()
+      `,
+      errors: ['Expected the component literal to be directly exported.']
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import Vue from 'vue'
+      const A = {}
+      export default Vue.extend(A)
+      `,
+      errors: ['Expected the component literal to be directly exported.']
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import { defineComponent } from 'vue'
+      export default defineComponent()
+      `,
+      errors: ['Expected the component literal to be directly exported.']
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      import { defineComponent } from 'vue'
+      const A = {}
+      export default defineComponent(A)
+      `,
+      errors: ['Expected the component literal to be directly exported.']
     }
   ]
 })