diff --git a/docs/rules/define-macros-order.md b/docs/rules/define-macros-order.md index 5d8dda56c..6dbe5ef07 100644 --- a/docs/rules/define-macros-order.md +++ b/docs/rules/define-macros-order.md @@ -2,20 +2,20 @@ pageClass: rule-details sidebarDepth: 0 title: vue/define-macros-order -description: enforce order of `defineEmits` and `defineProps` compiler macros +description: enforce order of compiler macros (`defineProps`, `defineEmits`, etc.) since: v8.7.0 --- # vue/define-macros-order -> enforce order of `defineEmits` and `defineProps` compiler macros +> enforce order of compiler macros (`defineProps`, `defineEmits`, etc.) - :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule. - :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). ## :book: Rule Details -This rule reports the `defineProps` and `defineEmits` compiler macros when they are not the first statements in ` +``` + + + + + +```vue + + +``` + + + ### `{ "defineExposeLast": true }` diff --git a/docs/rules/index.md b/docs/rules/index.md index 113ff416b..16a195125 100644 --- a/docs/rules/index.md +++ b/docs/rules/index.md @@ -213,7 +213,7 @@ For example: | [vue/component-options-name-casing](./component-options-name-casing.md) | enforce the casing of component name in `components` options | :wrench::bulb: | :hammer: | | [vue/custom-event-name-casing](./custom-event-name-casing.md) | enforce specific casing for custom event name | | :hammer: | | [vue/define-emits-declaration](./define-emits-declaration.md) | enforce declaration style of `defineEmits` | | :hammer: | -| [vue/define-macros-order](./define-macros-order.md) | enforce order of `defineEmits` and `defineProps` compiler macros | :wrench::bulb: | :lipstick: | +| [vue/define-macros-order](./define-macros-order.md) | enforce order of compiler macros (`defineProps`, `defineEmits`, etc.) | :wrench::bulb: | :lipstick: | | [vue/define-props-declaration](./define-props-declaration.md) | enforce declaration style of `defineProps` | | :hammer: | | [vue/enforce-style-attribute](./enforce-style-attribute.md) | enforce or forbid the use of the `scoped` and `module` attributes in SFC top level style tags | | :hammer: | | [vue/html-button-has-type](./html-button-has-type.md) | disallow usage of button without an explicit type attribute | | :hammer: | diff --git a/lib/rules/define-macros-order.js b/lib/rules/define-macros-order.js index f24c58af7..920b5c294 100644 --- a/lib/rules/define-macros-order.js +++ b/lib/rules/define-macros-order.js @@ -11,13 +11,15 @@ const MACROS_PROPS = 'defineProps' const MACROS_OPTIONS = 'defineOptions' const MACROS_SLOTS = 'defineSlots' const MACROS_MODEL = 'defineModel' -const ORDER_SCHEMA = [ +const MACROS_EXPOSE = 'defineExpose' +const KNOWN_MACROS = new Set([ MACROS_EMITS, MACROS_PROPS, MACROS_OPTIONS, MACROS_SLOTS, - MACROS_MODEL -] + MACROS_MODEL, + MACROS_EXPOSE +]) const DEFAULT_ORDER = [MACROS_PROPS, MACROS_EMITS] /** @@ -109,6 +111,12 @@ function create(context) { /** @type {ASTNode} */ let defineExposeNode + if (order.includes(MACROS_EXPOSE) && defineExposeLast) { + throw new Error( + "`defineExpose` macro can't be in the `order` array if `defineExposeLast` is true." + ) + } + return utils.compositingVisitors( utils.defineScriptSetupVisitor(context, { onDefinePropsExit(node) { @@ -130,6 +138,20 @@ function create(context) { }, onDefineExposeExit(node) { defineExposeNode = getDefineMacrosStatement(node) + }, + + /** @param {CallExpression} node */ + 'Program > ExpressionStatement > CallExpression, Program > VariableDeclaration > VariableDeclarator > CallExpression'( + node + ) { + if ( + node.callee && + node.callee.type === 'Identifier' && + order.includes(node.callee.name) && + !KNOWN_MACROS.has(node.callee.name) + ) { + macrosNodes.set(node.callee.name, [getDefineMacrosStatement(node)]) + } } }), { @@ -333,7 +355,7 @@ module.exports = { type: 'layout', docs: { description: - 'enforce order of `defineEmits` and `defineProps` compiler macros', + 'enforce order of compiler macros (`defineProps`, `defineEmits`, etc.)', categories: undefined, url: 'https://eslint.vuejs.org/rules/define-macros-order.html' }, @@ -346,7 +368,8 @@ module.exports = { order: { type: 'array', items: { - enum: ORDER_SCHEMA + type: 'string', + minLength: 1 }, uniqueItems: true, additionalItems: false diff --git a/tests/lib/rules/define-macros-order.js b/tests/lib/rules/define-macros-order.js index 3aa2e434d..72b359866 100644 --- a/tests/lib/rules/define-macros-order.js +++ b/tests/lib/rules/define-macros-order.js @@ -195,6 +195,65 @@ tester.run('define-macros-order', rule, { } ] }, + { + filename: 'test.vue', + code: ` + + `, + options: [ + { + order: ['definePage', 'defineModel', 'defineEmits'] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [ + { + order: ['definePage', 'defineModel', 'defineEmits'] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [ + { + order: ['definePage', 'defineModel', 'defineEmits'] + } + ] + }, { filename: 'test.vue', code: ` @@ -254,6 +313,22 @@ tester.run('define-macros-order', rule, { order: ['defineModel', 'defineSlots'] } ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [ + { + order: ['definePage', 'defineModel'] + } + ] } ], invalid: [ @@ -382,6 +457,40 @@ tester.run('define-macros-order', rule, { } ] }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + options: [{ order: ['definePage', 'defineProps'] }], + errors: [ + { + message: message('definePage'), + line: 8 + } + ] + }, { filename: 'test.vue', code: ` @@ -425,6 +534,61 @@ tester.run('define-macros-order', rule, { } ] }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + options: [ + { order: ['definePage', 'defineCustom', 'defineProps', 'defineEmits'] } + ], + languageOptions: { + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } + }, + errors: [ + { + message: message('definePage'), + line: 15 + } + ] + }, { filename: 'test.vue', code: ` @@ -537,6 +701,25 @@ tester.run('define-macros-order', rule, { } ] }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + options: [{ order: ['definePage', 'defineProps'] }], + errors: [ + { + message: message('definePage'), + line: 3 + } + ] + }, { filename: 'test.vue', code: ` @@ -633,6 +816,54 @@ tester.run('define-macros-order', rule, { } ] }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + options: [{ order: ['defineCustom', 'definePage'] }], + errors: [ + { + message: message('defineCustom'), + line: 5 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + options: [{ order: ['defineCustom', 'definePage'] }], + errors: [ + { + message: message('defineCustom'), + line: 5 + } + ] + }, { filename: 'test.vue', code: ` @@ -895,6 +1126,44 @@ tester.run('define-macros-order', rule, { } ] }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + options: [ + { + order: [ + 'defineSomething', + 'defineCustom', + 'defineModel', + 'defineOptions', + 'definePage' + ] + } + ], + errors: [ + { + message: message('defineSomething'), + line: 5 + } + ] + }, { filename: 'test.vue', code: `