Repository: vuejs/eslint-plugin-vue
Pull request #1559 (2021-07-05 12:21:52)

Add `vue/no-export-in-script-setup` rule
This PR adds `vue/no-export-in-script-setup` rule that disallows ES export in `<script setup>`.

Related to #1248

TODO 

https://github.com/vuejs/vue-eslint-parser/pull/115


Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index ae82c5d0..df3441d3 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -301,6 +301,7 @@ For example:
 | [vue/no-deprecated-v-is](./no-deprecated-v-is.md) | disallow deprecated `v-is` directive (in Vue.js 3.1.0+) | :wrench: |
 | [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` |  |
 | [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty |  |
+| [vue/no-export-in-script-setup](./no-export-in-script-setup.md) | disallow `export` in `<script setup>` |  |
 | [vue/no-invalid-model-keys](./no-invalid-model-keys.md) | require valid keys in model option |  |
 | [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class |  |
 | [vue/no-potential-component-option-typo](./no-potential-component-option-typo.md) | disallow a potential typo in your component property |  |
diff --git a/docs/rules/no-export-in-script-setup.md b/docs/rules/no-export-in-script-setup.md
new file mode 100644
index 00000000..4ab2a31c
--- /dev/null
+++ b/docs/rules/no-export-in-script-setup.md
@@ -0,0 +1,55 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-export-in-script-setup
+description: disallow `export` in `<script setup>`
+---
+# vue/no-export-in-script-setup
+
+> disallow `export` in `<script setup>`
+
+- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
+
+## :book: Rule Details
+
+This rule warns ES module exports in `<script setup>`.
+
+The previous version of `<script setup>` RFC used `export` to define variables used in templates, but the new `<script setup>` RFC has been updated to define without using `export`.
+See [Vue RFCs - 0040-script-setup] for more details.
+
+<eslint-code-block :rules="{'vue/no-export-in-script-setup': ['error']}">
+
+```vue
+<script setup>
+/* ✓ GOOD */
+let msg = 'Hello!'
+</script>
+```
+
+</eslint-code-block>
+
+<eslint-code-block :rules="{'vue/no-export-in-script-setup': ['error']}">
+
+```vue
+<script setup>
+/* ✗ BAD */
+export let msg = 'Hello!'
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [Vue RFCs - 0040-script-setup]
+
+[Vue RFCs - 0040-script-setup]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-export-in-script-setup.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-export-in-script-setup.js)
diff --git a/docs/user-guide/README.md b/docs/user-guide/README.md
index 45a3b72a..0b3c3d82 100644
--- a/docs/user-guide/README.md
+++ b/docs/user-guide/README.md
@@ -103,6 +103,8 @@ If you want to use custom parsers such as [babel-eslint](https://www.npmjs.com/p
   }
 ```
 
+The `parserOptions.parser` option can also specify an object to specify multiple parsers. See [vue-eslint-parser README](https://github.com/vuejs/vue-eslint-parser#readme) for more details.
+
 ### How does ESLint detect components?
 
 All component-related rules are applied to code that passes any of the following checks:
diff --git a/lib/index.js b/lib/index.js
index 0fcef5d9..c321450f 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -81,6 +81,7 @@ module.exports = {
     'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
     'no-empty-component-block': require('./rules/no-empty-component-block'),
     'no-empty-pattern': require('./rules/no-empty-pattern'),
+    'no-export-in-script-setup': require('./rules/no-export-in-script-setup'),
     'no-extra-parens': require('./rules/no-extra-parens'),
     'no-invalid-model-keys': require('./rules/no-invalid-model-keys'),
     'no-irregular-whitespace': require('./rules/no-irregular-whitespace'),
diff --git a/lib/rules/no-export-in-script-setup.js b/lib/rules/no-export-in-script-setup.js
new file mode 100644
index 00000000..fcd7f316
--- /dev/null
+++ b/lib/rules/no-export-in-script-setup.js
@@ -0,0 +1,41 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const utils = require('../utils')
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow `export` in `<script setup>`',
+      // TODO Switch in the major version.
+      // categories: ['vue3-essential'],
+      categories: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-export-in-script-setup.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      forbidden: '`<script setup>` cannot contain ES module exports.'
+    }
+  },
+  /** @param {RuleContext} context */
+  create(context) {
+    /** @param {ExportAllDeclaration | ExportDefaultDeclaration | ExportNamedDeclaration} node */
+    function report(node) {
+      context.report({
+        node,
+        messageId: 'forbidden'
+      })
+    }
+
+    return utils.defineScriptSetupVisitor(context, {
+      ExportAllDeclaration: report,
+      ExportDefaultDeclaration: report,
+      ExportNamedDeclaration: report
+    })
+  }
+}
diff --git a/package.json b/package.json
index df2f8fbf..ab778581 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,7 @@
     "eslint-utils": "^2.1.0",
     "natural-compare": "^1.4.0",
     "semver": "^7.3.2",
-    "vue-eslint-parser": "^7.7.2"
+    "vue-eslint-parser": "^7.8.0"
   },
   "devDependencies": {
     "@types/eslint": "^7.2.0",


Test patch:
diff --git a/tests/lib/rules/no-export-in-script-setup.js b/tests/lib/rules/no-export-in-script-setup.js
new file mode 100644
index 00000000..5bc759f4
--- /dev/null
+++ b/tests/lib/rules/no-export-in-script-setup.js
@@ -0,0 +1,107 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const eslint = require('eslint')
+const rule = require('../../../lib/rules/no-export-in-script-setup')
+
+const RuleTester = eslint.RuleTester
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: {
+    ecmaVersion: 6,
+    sourceType: 'module'
+  }
+})
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+ruleTester.run('no-export-in-script-setup', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export * from 'foo'
+      export default {}
+      export class A {}
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export * from 'foo'
+      export default {}
+      export class A {}
+      </script>
+      <script setup>
+      let foo;
+      </script>
+      `
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup>
+      export * from 'foo'
+      export default {}
+      export class A {}
+      </script>
+      `,
+      errors: [
+        {
+          message: '`<script setup>` cannot contain ES module exports.',
+          line: 3
+        },
+        {
+          message: '`<script setup>` cannot contain ES module exports.',
+          line: 4
+        },
+        {
+          message: '`<script setup>` cannot contain ES module exports.',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      let foo;
+      </script>
+      <script setup>
+      export * from 'foo'
+      export default {}
+      export class A {}
+      </script>
+      `,
+      errors: [
+        {
+          message: '`<script setup>` cannot contain ES module exports.',
+          line: 6
+        },
+        {
+          message: '`<script setup>` cannot contain ES module exports.',
+          line: 7
+        },
+        {
+          message: '`<script setup>` cannot contain ES module exports.',
+          line: 8
+        }
+      ]
+    }
+  ]
+})