diff --git a/docs/rules/no-unsupported-features.md b/docs/rules/no-unsupported-features.md
index cd6c3dcf9..2e7d67501 100644
--- a/docs/rules/no-unsupported-features.md
+++ b/docs/rules/no-unsupported-features.md
@@ -29,6 +29,9 @@ This rule reports unsupported Vue.js syntax on the specified version.
- `version` ... The `version` option accepts [the valid version range of `node-semver`](https://github.com/npm/node-semver#range-grammar). Set the version of Vue.js you are using. This option is required.
- `ignores` ... You can use this `ignores` option to ignore the given features.
The `"ignores"` option accepts an array of the following strings.
+ - Vue.js 3.3.0+
+ - `"define-slots"` ... `defineSlots()` macro.
+ - `"define-options"` ... `defineOptions()` macro.
- Vue.js 3.2.0+
- `"v-memo"` ... [v-memo](https://vuejs.org/api/built-in-directives.html#v-memo) directive.
- `"v-bind-prop-modifier-shorthand"` ... `v-bind` with `.prop` modifier shorthand.
@@ -100,6 +103,8 @@ The `"ignores"` option accepts an array of the following strings.
## :books: Further Reading
+- [API - defineOptions()](https://vuejs.org/api/sfc-script-setup.html#defineoptions)
+- [API - defineSlots()](https://vuejs.org/api/sfc-script-setup.html#defineslots)
- [API - v-memo](https://vuejs.org/api/built-in-directives.html#v-memo)
- [API - v-is](https://v3.vuejs.org/api/directives.html#v-is)
- [API - v-is (Old)](https://github.com/vuejs/docs-next/blob/008613756c3d781128d96b64a2d27f7598f8f548/src/api/directives.md#v-is)
diff --git a/lib/rules/no-unsupported-features.js b/lib/rules/no-unsupported-features.js
index 803a62ca5..0bcd61a55 100644
--- a/lib/rules/no-unsupported-features.js
+++ b/lib/rules/no-unsupported-features.js
@@ -32,7 +32,10 @@ const FEATURES = {
// Vue.js 3.2.0+
'v-memo': require('./syntaxes/v-memo'),
'v-bind-prop-modifier-shorthand': require('./syntaxes/v-bind-prop-modifier-shorthand'),
- 'v-bind-attr-modifier': require('./syntaxes/v-bind-attr-modifier')
+ 'v-bind-attr-modifier': require('./syntaxes/v-bind-attr-modifier'),
+ // Vue.js 3.3.0+
+ 'define-options': require('./syntaxes/define-options'),
+ 'define-slots': require('./syntaxes/define-slots')
}
const SYNTAX_NAMES = /** @type {(keyof FEATURES)[]} */ (Object.keys(FEATURES))
@@ -115,7 +118,12 @@ module.exports = {
forbiddenVBindPropModifierShorthand:
'`.prop` shorthand are not supported until Vue.js "3.2.0".',
forbiddenVBindAttrModifier:
- '`.attr` modifiers on `v-bind` are not supported until Vue.js "3.2.0".'
+ '`.attr` modifiers on `v-bind` are not supported until Vue.js "3.2.0".',
+ // Vue.js 3.3.0+
+ forbiddenDefineOptions:
+ '`defineOptions()` macros are not supported until Vue.js "3.3.0".',
+ forbiddenDefineSlots:
+ '`defineSlots()` macros are not supported until Vue.js "3.3.0".'
}
},
/** @param {RuleContext} context */
diff --git a/lib/rules/syntaxes/define-options.js b/lib/rules/syntaxes/define-options.js
new file mode 100644
index 000000000..0ef008ca7
--- /dev/null
+++ b/lib/rules/syntaxes/define-options.js
@@ -0,0 +1,74 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const utils = require('../../utils/index')
+
+module.exports = {
+ supported: '>=3.3.0',
+ /** @param {RuleContext} context @returns {RuleListener} */
+ createScriptVisitor(context) {
+ const sourceCode = context.getSourceCode()
+ return utils.defineScriptSetupVisitor(context, {
+ onDefineOptionsEnter(node) {
+ context.report({
+ node,
+ messageId: 'forbiddenDefineOptions',
+ fix(fixer) {
+ return fix(fixer, node)
+ }
+ })
+ }
+ })
+
+ /**
+ * @param {RuleFixer} fixer
+ * @param {CallExpression} node defineOptions() node
+ */
+ function fix(fixer, node) {
+ if (node.arguments.length === 0) return null
+ const scriptSetup = utils.getScriptSetupElement(context)
+ if (!scriptSetup) return null
+ if (
+ scriptSetup.parent.children
+ .filter(utils.isVElement)
+ .some(
+ (node) =>
+ node.name === 'script' && !utils.hasAttribute(node, 'setup')
+ )
+ ) {
+ // has `\n`
+ ),
+ fixer.removeRange(removeRange)
+ ]
+ }
+ }
+}
diff --git a/lib/rules/syntaxes/define-slots.js b/lib/rules/syntaxes/define-slots.js
new file mode 100644
index 000000000..450ec3e89
--- /dev/null
+++ b/lib/rules/syntaxes/define-slots.js
@@ -0,0 +1,22 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const utils = require('../../utils/index')
+
+module.exports = {
+ supported: '>=3.3.0',
+ /** @param {RuleContext} context @returns {RuleListener} */
+ createScriptVisitor(context) {
+ return utils.defineScriptSetupVisitor(context, {
+ onDefineSlotsEnter(node) {
+ context.report({
+ node,
+ messageId: 'forbiddenDefineSlots'
+ })
+ }
+ })
+ }
+}
diff --git a/tests/lib/rules/no-unsupported-features/define-options.js b/tests/lib/rules/no-unsupported-features/define-options.js
new file mode 100644
index 000000000..10b2069b2
--- /dev/null
+++ b/tests/lib/rules/no-unsupported-features/define-options.js
@@ -0,0 +1,88 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../../lib/rules/no-unsupported-features')
+const utils = require('./utils')
+
+const buildOptions = utils.optionsBuilder('define-options', '^3.2.0')
+const tester = new RuleTester({
+ parser: require.resolve('vue-eslint-parser'),
+ parserOptions: {
+ ecmaVersion: 2019,
+ sourceType: 'module'
+ }
+})
+
+tester.run('no-unsupported-features/define-options', rule, {
+ valid: [
+ {
+ code: `
+ `,
+ options: buildOptions({ version: '^3.3.0' })
+ },
+ {
+ code: `
+ `,
+ options: buildOptions()
+ },
+ {
+ code: `
+ `,
+ options: buildOptions({ version: '^3.0.0', ignores: ['define-options'] })
+ }
+ ],
+ invalid: [
+ {
+ code: `
+ `,
+ options: buildOptions(),
+ output: `
+
+`,
+ errors: [
+ {
+ message:
+ '`defineOptions()` macros are not supported until Vue.js "3.3.0".',
+ line: 3
+ }
+ ]
+ },
+ {
+ code: `
+ `,
+ options: buildOptions(),
+ output: `
+
+`,
+ errors: [
+ {
+ message:
+ '`defineOptions()` macros are not supported until Vue.js "3.3.0".',
+ line: 3
+ }
+ ]
+ }
+ ]
+})
diff --git a/tests/lib/rules/no-unsupported-features/define-slots.js b/tests/lib/rules/no-unsupported-features/define-slots.js
new file mode 100644
index 000000000..2fef117c7
--- /dev/null
+++ b/tests/lib/rules/no-unsupported-features/define-slots.js
@@ -0,0 +1,59 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../../lib/rules/no-unsupported-features')
+const utils = require('./utils')
+
+const buildOptions = utils.optionsBuilder('define-slots', '^3.2.0')
+const tester = new RuleTester({
+ parser: require.resolve('vue-eslint-parser'),
+ parserOptions: {
+ ecmaVersion: 2019
+ }
+})
+
+tester.run('no-unsupported-features/define-slots', rule, {
+ valid: [
+ {
+ code: `
+ `,
+ options: buildOptions({ version: '^3.3.0' })
+ },
+ {
+ code: `
+ `,
+ options: buildOptions()
+ },
+ {
+ code: `
+ `,
+ options: buildOptions({ version: '^3.0.0', ignores: ['define-slots'] })
+ }
+ ],
+ invalid: [
+ {
+ code: `
+ `,
+ options: buildOptions(),
+ errors: [
+ {
+ message:
+ '`defineSlots()` macros are not supported until Vue.js "3.3.0".',
+ line: 3
+ }
+ ]
+ }
+ ]
+})