diff --git a/.changeset/sixty-cougars-ring.md b/.changeset/sixty-cougars-ring.md new file mode 100644 index 00000000..a45bbedb --- /dev/null +++ b/.changeset/sixty-cougars-ring.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-yml": minor +--- + +feat: add `yml/no-trailing-zeros` rule diff --git a/README.md b/README.md index ce7a2e0e..0212b8ac 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ The rules with the following star :star: are included in the config. | [yml/no-empty-mapping-value](https://ota-meshi.github.io/eslint-plugin-yml/rules/no-empty-mapping-value.html) | disallow empty mapping values | | :star: | :star: | | [yml/no-empty-sequence-entry](https://ota-meshi.github.io/eslint-plugin-yml/rules/no-empty-sequence-entry.html) | disallow empty sequence entries | | :star: | :star: | | [yml/no-tab-indent](https://ota-meshi.github.io/eslint-plugin-yml/rules/no-tab-indent.html) | disallow tabs for indentation. | | :star: | :star: | +| [yml/no-trailing-zeros](https://ota-meshi.github.io/eslint-plugin-yml/rules/no-trailing-zeros.html) | disallow trailing zeros for floats | :wrench: | | | | [yml/plain-scalar](https://ota-meshi.github.io/eslint-plugin-yml/rules/plain-scalar.html) | require or disallow plain style scalar. | :wrench: | | :star: | | [yml/quotes](https://ota-meshi.github.io/eslint-plugin-yml/rules/quotes.html) | enforce the consistent use of either double, or single quotes | :wrench: | | :star: | | [yml/require-string-key](https://ota-meshi.github.io/eslint-plugin-yml/rules/require-string-key.html) | disallow mapping keys other than strings | | | | diff --git a/docs/rules/README.md b/docs/rules/README.md index 22a48bee..800eabfc 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -26,6 +26,7 @@ The rules with the following star :star: are included in the `plugin:yml/recomme | [yml/no-empty-mapping-value](./no-empty-mapping-value.md) | disallow empty mapping values | | :star: | :star: | | [yml/no-empty-sequence-entry](./no-empty-sequence-entry.md) | disallow empty sequence entries | | :star: | :star: | | [yml/no-tab-indent](./no-tab-indent.md) | disallow tabs for indentation. | | :star: | :star: | +| [yml/no-trailing-zeros](./no-trailing-zeros.md) | disallow trailing zeros for floats | :wrench: | | | | [yml/plain-scalar](./plain-scalar.md) | require or disallow plain style scalar. | :wrench: | | :star: | | [yml/quotes](./quotes.md) | enforce the consistent use of either double, or single quotes | :wrench: | | :star: | | [yml/require-string-key](./require-string-key.md) | disallow mapping keys other than strings | | | | diff --git a/docs/rules/no-trailing-zeros.md b/docs/rules/no-trailing-zeros.md new file mode 100644 index 00000000..3159823f --- /dev/null +++ b/docs/rules/no-trailing-zeros.md @@ -0,0 +1,43 @@ +--- +pageClass: "rule-details" +sidebarDepth: 0 +title: "yml/no-trailing-zeros" +description: "disallow trailing zeros for floats" +--- + +# yml/no-trailing-zeros + +> disallow trailing zeros for floats + +- :exclamation: **_This rule has not been released yet._** +- :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. + +## :book: Rule Details + +This rule enforces the removal of unnecessary trailing zeros from floats. + + + + + +```yaml +# eslint yml/no-trailing-zeros: 'error' + +# ✓ GOOD +"GOOD": 1.2 + +# ✗ BAD +'BAD': 1.20 +``` + + + +## :wrench: Options + +Nothing. + +## :mag: Implementation + +- [Rule source](https://github.com/ota-meshi/eslint-plugin-yml/blob/master/src/rules/no-trailing-zeros.ts) +- [Test source](https://github.com/ota-meshi/eslint-plugin-yml/blob/master/tests/src/rules/no-trailing-zeros.ts) +- [Test fixture sources](https://github.com/ota-meshi/eslint-plugin-yml/tree/master/tests/fixtures/rules/no-trailing-zeros) diff --git a/src/configs/prettier.ts b/src/configs/prettier.ts index 2267e337..349f7948 100644 --- a/src/configs/prettier.ts +++ b/src/configs/prettier.ts @@ -15,6 +15,7 @@ export = { "yml/indent": "off", "yml/key-spacing": "off", "yml/no-multiple-empty-lines": "off", + "yml/no-trailing-zeros": "off", "yml/quotes": "off", }, }; diff --git a/src/rules/no-trailing-zeros.ts b/src/rules/no-trailing-zeros.ts new file mode 100644 index 00000000..6e2d86b9 --- /dev/null +++ b/src/rules/no-trailing-zeros.ts @@ -0,0 +1,57 @@ +import type { AST } from "yaml-eslint-parser"; +import { createRule } from "../utils"; + +export default createRule("no-trailing-zeros", { + meta: { + docs: { + description: "disallow trailing zeros for floats", + categories: null, + extensionRule: false, + layout: true, + }, + fixable: "code", + schema: [], + messages: { + wrongZeros: "Trailing zeros are not allowed, fix to `{{fixed}}`.", + }, + type: "layout", + }, + create(context) { + if (!context.parserServices.isYAML) { + return {}; + } + + return { + YAMLScalar(node: AST.YAMLScalar) { + if (node.style !== "plain") { + return; + } else if (typeof node.value !== "number") { + return; + } else if (!node.strValue.endsWith("0")) { + return; + } + + const parts = node.strValue.split("."); + if (parts.length !== 2) { + return; + } + + while (parts[1].endsWith("0")) { + parts[1] = parts[1].slice(0, -1); + } + const fixed = parts[1] ? parts.join(".") : parts[0] || "0"; + + context.report({ + node, + messageId: "wrongZeros", + data: { + fixed, + }, + fix(fixer) { + return fixer.replaceText(node, fixed); + }, + }); + }, + }; + }, +}); diff --git a/src/utils/rules.ts b/src/utils/rules.ts index a5ef2728..167a4c10 100644 --- a/src/utils/rules.ts +++ b/src/utils/rules.ts @@ -19,6 +19,7 @@ import noEmptySequenceEntry from "../rules/no-empty-sequence-entry"; import noIrregularWhitespace from "../rules/no-irregular-whitespace"; import noMultipleEmptyLines from "../rules/no-multiple-empty-lines"; import noTabIndent from "../rules/no-tab-indent"; +import noTrailingZeros from "../rules/no-trailing-zeros"; import plainScalar from "../rules/plain-scalar"; import quotes from "../rules/quotes"; import requireStringKey from "../rules/require-string-key"; @@ -48,6 +49,7 @@ export const rules = [ noIrregularWhitespace, noMultipleEmptyLines, noTabIndent, + noTrailingZeros, plainScalar, quotes, requireStringKey, diff --git a/tests/fixtures/rules/no-trailing-zeros/invalid/errors.json b/tests/fixtures/rules/no-trailing-zeros/invalid/errors.json new file mode 100644 index 00000000..962796ff --- /dev/null +++ b/tests/fixtures/rules/no-trailing-zeros/invalid/errors.json @@ -0,0 +1,62 @@ +[ + { + "message": "Trailing zeros are not allowed, fix to `1.2`.", + "line": 4, + "column": 10 + }, + { + "message": "Trailing zeros are not allowed, fix to `.2`.", + "line": 6, + "column": 10 + }, + { + "message": "Trailing zeros are not allowed, fix to `1`.", + "line": 7, + "column": 10 + }, + { + "message": "Trailing zeros are not allowed, fix to `0`.", + "line": 8, + "column": 10 + }, + { + "message": "Trailing zeros are not allowed, fix to `+1`.", + "line": 9, + "column": 10 + }, + { + "message": "Trailing zeros are not allowed, fix to `-1`.", + "line": 10, + "column": 10 + }, + { + "message": "Trailing zeros are not allowed, fix to `1.2`.", + "line": 14, + "column": 12 + }, + { + "message": "Trailing zeros are not allowed, fix to `.2`.", + "line": 16, + "column": 12 + }, + { + "message": "Trailing zeros are not allowed, fix to `1`.", + "line": 17, + "column": 12 + }, + { + "message": "Trailing zeros are not allowed, fix to `0`.", + "line": 18, + "column": 12 + }, + { + "message": "Trailing zeros are not allowed, fix to `+1`.", + "line": 19, + "column": 12 + }, + { + "message": "Trailing zeros are not allowed, fix to `-1`.", + "line": 20, + "column": 12 + } +] diff --git a/tests/fixtures/rules/no-trailing-zeros/invalid/input.yml b/tests/fixtures/rules/no-trailing-zeros/invalid/input.yml new file mode 100644 index 00000000..79b7b6f6 --- /dev/null +++ b/tests/fixtures/rules/no-trailing-zeros/invalid/input.yml @@ -0,0 +1,20 @@ +# {"options": []} +- { + a: 1.23, + b: 1.20, + c: .23, + d: .20, + e: 1.0, + f: .0, + g: +1.0, + h: -1.0 + } +- + - "a": 1.23 + - "b": 1.20 + - "c": .23 + - "d": .20 + - "e": 1.0 + - "f": .0 + - "g": +1.0 + - "h": -1.0 diff --git a/tests/fixtures/rules/no-trailing-zeros/invalid/output.yml b/tests/fixtures/rules/no-trailing-zeros/invalid/output.yml new file mode 100644 index 00000000..2d6a8be0 --- /dev/null +++ b/tests/fixtures/rules/no-trailing-zeros/invalid/output.yml @@ -0,0 +1,20 @@ +# no-trailing-zeros/invalid/input.yml +- { + a: 1.23, + b: 1.2, + c: .23, + d: .2, + e: 1, + f: 0, + g: +1, + h: -1 + } +- + - "a": 1.23 + - "b": 1.2 + - "c": .23 + - "d": .2 + - "e": 1 + - "f": 0 + - "g": +1 + - "h": -1 diff --git a/tests/fixtures/rules/no-trailing-zeros/valid/input.yml b/tests/fixtures/rules/no-trailing-zeros/valid/input.yml new file mode 100644 index 00000000..539feec0 --- /dev/null +++ b/tests/fixtures/rules/no-trailing-zeros/valid/input.yml @@ -0,0 +1,20 @@ +# {"options": []} +- { + a: 1.23, + b: "1.20", + c: .23, + d: .2, + e: "1.0", + f: ".0", + g: "+1.0", + h: "-1.0" + } +- + - "a": 1.23 + - "b": "1.20" + - "c": .23 + - "d": .2 + - "e": "1.0" + - "f": ".0" + - "g": "+1.0" + - "h": "-1.0" diff --git a/tests/src/rules/no-trailing-zeros.ts b/tests/src/rules/no-trailing-zeros.ts new file mode 100644 index 00000000..31789409 --- /dev/null +++ b/tests/src/rules/no-trailing-zeros.ts @@ -0,0 +1,16 @@ +import { RuleTester } from "eslint"; +import rule from "../../../src/rules/no-trailing-zeros"; +import { loadTestCases } from "../../utils/utils"; + +const tester = new RuleTester({ + parser: require.resolve("yaml-eslint-parser"), + parserOptions: { + ecmaVersion: 2020, + }, +}); + +tester.run( + "no-trailing-zeros", + rule as any, + loadTestCases("no-trailing-zeros") +);