diff --git a/docs/rules/README.md b/docs/rules/README.md index 3af0a000e..6b8b2df5c 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -149,6 +149,7 @@ For example: | [vue/eqeqeq](./eqeqeq.md) | require the use of `===` and `!==` | :wrench: | | [vue/key-spacing](./key-spacing.md) | enforce consistent spacing between keys and values in object literal properties | :wrench: | | [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | | +| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: | | [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax | | | [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: | | [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | | diff --git a/docs/rules/no-boolean-default.md b/docs/rules/no-boolean-default.md new file mode 100644 index 000000000..74e53f92d --- /dev/null +++ b/docs/rules/no-boolean-default.md @@ -0,0 +1,49 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-boolean-default +description: disallow boolean defaults +--- +# vue/no-boolean-default +> disallow boolean defaults + +- :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. + +The rule prevents Boolean props from having a default value. + + +## :book: Rule Details +The rule is to enforce the HTML standard of always defaulting boolean attributes to false. + + + +```vue + +``` + + + +## :wrench: Options +- `'no-default'` (default) allows a prop definition object, but enforces that the `default` property not be defined. +- `'default-false'` enforces that the default can be set but must be set to `false`. + +```json + "vue/no-boolean-default": ["error", "no-default|default-false"] +``` + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-boolean-default.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-boolean-default.js) diff --git a/lib/index.js b/lib/index.js index 6a26fe061..a7627fcae 100644 --- a/lib/index.js +++ b/lib/index.js @@ -32,6 +32,7 @@ module.exports = { 'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'), 'name-property-casing': require('./rules/name-property-casing'), 'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'), + 'no-boolean-default': require('./rules/no-boolean-default'), 'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'), 'no-dupe-keys': require('./rules/no-dupe-keys'), 'no-duplicate-attributes': require('./rules/no-duplicate-attributes'), diff --git a/lib/rules/no-boolean-default.js b/lib/rules/no-boolean-default.js new file mode 100644 index 000000000..d0e3f36a2 --- /dev/null +++ b/lib/rules/no-boolean-default.js @@ -0,0 +1,92 @@ +/** + * @fileoverview Prevents boolean defaults from being set + * @author Hiroki Osame + */ +'use strict' + +const utils = require('../utils') + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +function isBooleanProp (prop) { + return ( + prop.type === 'Property' && + prop.key.type === 'Identifier' && + prop.key.name === 'type' && + prop.value.type === 'Identifier' && + prop.value.name === 'Boolean' + ) +} + +function getBooleanProps (props) { + return props + .filter(prop => ( + prop.value && + prop.value.properties && + prop.value.properties.find(isBooleanProp) + )) +} + +function getDefaultNode (propDef) { + return propDef.value.properties.find(p => { + return ( + p.type === 'Property' && + p.key.type === 'Identifier' && + p.key.name === 'default' + ) + }) +} + +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow boolean defaults', + category: undefined, + url: 'https://eslint.vuejs.org/rules/no-boolean-default.html' + }, + fixable: 'code', + schema: [ + { + enum: ['default-false', 'no-default'] + } + ] + }, + + create (context) { + return utils.executeOnVueComponent(context, (obj) => { + const props = utils.getComponentProps(obj) + const booleanProps = getBooleanProps(props) + + if (!booleanProps.length) return + + const booleanType = context.options[0] || 'no-default' + + booleanProps.forEach((propDef) => { + const defaultNode = getDefaultNode(propDef) + + switch (booleanType) { + case 'no-default': + if (defaultNode) { + context.report({ + node: defaultNode, + message: 'Boolean prop should not set a default (Vue defaults it to false).' + }) + } + break + + case 'default-false': + if (defaultNode.value.value !== false) { + context.report({ + node: defaultNode, + message: 'Boolean prop should be defaulted to false.' + }) + } + break + } + }) + }) + } +} diff --git a/tests/lib/rules/no-boolean-default.js b/tests/lib/rules/no-boolean-default.js new file mode 100644 index 000000000..9f87a3a34 --- /dev/null +++ b/tests/lib/rules/no-boolean-default.js @@ -0,0 +1,163 @@ +/** + * @fileoverview Prevents boolean defaults from being set + * @author Hiroki Osame + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +var rule = require('../../../lib/rules/no-boolean-default') + +var RuleTester = require('eslint').RuleTester + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +var ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module' + } +}) +ruleTester.run('no-boolean-default', rule, { + + valid: [ + { + filename: 'test.vue', + code: ` + export default { + props: { + enabled: Boolean + } + } + ` + }, + { + filename: 'test.vue', + code: ` + const props = {}; + export default { + props: { + ...props, + enabled: Boolean + } + } + ` + }, + { + filename: 'test.vue', + code: ` + const data = {}; + export default { + props: { + enabled: { + type: Boolean, + ...data + } + } + } + `, + options: ['no-default'] + }, + { + filename: 'test.vue', + code: ` + const data = {}; + export default { + props: { + enabled: { + type: Boolean, + default: false, + ...data + } + } + } + `, + options: ['default-false'] + }, + { + filename: 'test.vue', + code: ` + const data = {}; + export default { + props: { + enabled: data + } + } + ` + }, + { + filename: 'test.vue', + code: ` + const data = {}; + export default { + props: { + enabled: { + ...data + } + } + } + ` + } + ], + + invalid: [ + { + filename: 'test.vue', + code: ` + export default { + props: { + enabled: { + type: Boolean, + default: true, + } + } + } + `, + options: ['default-false'], + errors: [{ + message: 'Boolean prop should be defaulted to false.', + line: 6 + }] + }, + { + filename: 'test.vue', + code: ` + export default { + props: { + enabled: { + type: Boolean, + default: null, + } + } + } + `, + options: ['default-false'], + errors: [{ + message: 'Boolean prop should be defaulted to false.', + line: 6 + }] + }, + { + filename: 'test.vue', + code: ` + export default { + props: { + enabled: { + type: Boolean, + default: false, + } + } + } + `, + options: ['no-default'], + errors: [{ + message: 'Boolean prop should not set a default (Vue defaults it to false).', + line: 6 + }] + } + ] +})