From 2c6fd62414dfec6bc98f41d86e816fad026d8d96 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Wed, 13 Apr 2022 19:22:09 +0900 Subject: [PATCH] Add no-non-optimized-style-attributes rule --- README.md | 1 + docs/rules.md | 1 + .../no-non-optimized-style-attributes.md | 110 ++++++++++++++++++ .../no-non-optimized-style-attributes.ts | 75 ++++++++++++ src/utils/rules.ts | 2 + .../invalid/comment01-errors.json | 7 ++ .../invalid/comment01-input.svelte | 5 + .../invalid/key01-errors.json | 7 ++ .../invalid/key01-input.svelte | 6 + .../invalid/test01-errors.json | 22 ++++ .../invalid/test01-input.svelte | 21 ++++ .../valid/test01-input.svelte | 14 +++ .../no-non-optimized-style-attributes.ts | 16 +++ 13 files changed, 287 insertions(+) create mode 100644 docs/rules/no-non-optimized-style-attributes.md create mode 100644 src/rules/no-non-optimized-style-attributes.ts create mode 100644 tests/fixtures/rules/no-non-optimized-style-attributes/invalid/comment01-errors.json create mode 100644 tests/fixtures/rules/no-non-optimized-style-attributes/invalid/comment01-input.svelte create mode 100644 tests/fixtures/rules/no-non-optimized-style-attributes/invalid/key01-errors.json create mode 100644 tests/fixtures/rules/no-non-optimized-style-attributes/invalid/key01-input.svelte create mode 100644 tests/fixtures/rules/no-non-optimized-style-attributes/invalid/test01-errors.json create mode 100644 tests/fixtures/rules/no-non-optimized-style-attributes/invalid/test01-input.svelte create mode 100644 tests/fixtures/rules/no-non-optimized-style-attributes/valid/test01-input.svelte create mode 100644 tests/src/rules/no-non-optimized-style-attributes.ts diff --git a/README.md b/README.md index 79bdd4d1f..183e00a43 100644 --- a/README.md +++ b/README.md @@ -268,6 +268,7 @@ These rules relate to better ways of doing things to help you avoid problems: |:--------|:------------|:---| | [@ota-meshi/svelte/button-has-type](https://ota-meshi.github.io/eslint-plugin-svelte/rules/button-has-type/) | disallow usage of button without an explicit type attribute | | | [@ota-meshi/svelte/no-at-debug-tags](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/) | disallow the use of `{@debug}` | :star: | +| [@ota-meshi/svelte/no-non-optimized-style-attributes](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-non-optimized-style-attributes/) | disallow style attributes that cannot be optimized | | | [@ota-meshi/svelte/no-unused-svelte-ignore](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: | | [@ota-meshi/svelte/no-useless-mustaches](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :wrench: | diff --git a/docs/rules.md b/docs/rules.md index bf3c03d4a..de13cdce5 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -41,6 +41,7 @@ These rules relate to better ways of doing things to help you avoid problems: |:--------|:------------|:---| | [@ota-meshi/svelte/button-has-type](./rules/button-has-type.md) | disallow usage of button without an explicit type attribute | | | [@ota-meshi/svelte/no-at-debug-tags](./rules/no-at-debug-tags.md) | disallow the use of `{@debug}` | :star: | +| [@ota-meshi/svelte/no-non-optimized-style-attributes](./rules/no-non-optimized-style-attributes.md) | disallow style attributes that cannot be optimized | | | [@ota-meshi/svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md) | disallow unused svelte-ignore comments | :star: | | [@ota-meshi/svelte/no-useless-mustaches](./rules/no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: | diff --git a/docs/rules/no-non-optimized-style-attributes.md b/docs/rules/no-non-optimized-style-attributes.md new file mode 100644 index 000000000..b9883dbcf --- /dev/null +++ b/docs/rules/no-non-optimized-style-attributes.md @@ -0,0 +1,110 @@ +--- +pageClass: "rule-details" +sidebarDepth: 0 +title: "@ota-meshi/svelte/no-non-optimized-style-attributes" +description: "disallow style attributes that cannot be optimized" +--- + +# @ota-meshi/svelte/no-non-optimized-style-attributes + +> disallow style attributes that cannot be optimized + +- :exclamation: **_This rule has not been released yet._** + +## :book: Rule Details + +This rule reports `style` attributes written in a format that cannot be optimized. + +Svelte parses the content written in the style attribute and tries to optimize it. (See [https://github.com/sveltejs/svelte/pull/810](https://github.com/sveltejs/svelte/pull/810)) +If Svelte can be successfully optimized, Svelte can minimize the number of re-renders. + +e.g. + +template: + +```html +
+``` + +compiled: + +```js +div.style.setProperty("font-size", "12px") // font-size style is not updated once it is initially set. +div.style.setProperty("color", color) // color style is updated only when color variable is updated. +div.style.setProperty("transform", `translate(${x}px, ${y}px)`) // transform style is updated only when x, or y variables is updated. +``` + +However, if the optimization fails, it will be re-rendered triggered by the update of all variables described in the style attribute. + +e.g. + +template: + +```html + + +
+``` + +compiled: + +```js +// If any of variables color, x, or y are updated, all styles will be updated. +div.setAttribute( + "style", + `font-size: 12px; color: ${color}; ${/* transformStyle */ ctx[0]}`, +) +``` + +Examples: + + + + + +```svelte + + + +
+
+ + +
+
+ +
+
+``` + + + +## :wrench: Options + +Nothing. + +## :mag: Implementation + +- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/no-non-optimized-style-attributes.ts) +- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/no-non-optimized-style-attributes.ts) diff --git a/src/rules/no-non-optimized-style-attributes.ts b/src/rules/no-non-optimized-style-attributes.ts new file mode 100644 index 000000000..045ebbffa --- /dev/null +++ b/src/rules/no-non-optimized-style-attributes.ts @@ -0,0 +1,75 @@ +import { createRule } from "../utils" +import { parseStyleAttributeValue } from "../utils/css-utils" + +export default createRule("no-non-optimized-style-attributes", { + meta: { + docs: { + description: "disallow style attributes that cannot be optimized", + category: "Best Practices", + recommended: false, + }, + schema: [], + messages: { + shorthand: + "It cannot be optimized because style attribute is specified using shorthand.", + comment: "It cannot be optimized because contains comments.", + interpolationKey: + "It cannot be optimized because property of style declaration contain interpolation.", + complex: "It cannot be optimized because too complex.", + }, + type: "suggestion", + }, + create(context) { + return { + SvelteShorthandAttribute(node) { + if (node.key.name !== "style") { + return + } + + context.report({ + node, + messageId: "shorthand", + }) + }, + SvelteAttribute(node) { + if (node.key.name !== "style") { + return + } + const root = parseStyleAttributeValue(node, context) + if (!root) { + return + } + + for (const child of root.nodes) { + if (child.type === "decl") { + if (child.unsafe) { + context.report({ + node, + loc: child.loc, + messageId: "complex", + }) + } else if (child.prop.name.includes("{")) { + context.report({ + node, + loc: child.prop.loc, + messageId: "interpolationKey", + }) + } + } else if (child.type === "comment") { + context.report({ + node, + loc: child.loc, + messageId: "comment", + }) + } else { + context.report({ + node, + loc: child.loc, + messageId: "complex", + }) + } + } + }, + } + }, +}) diff --git a/src/utils/rules.ts b/src/utils/rules.ts index 93be165fb..b512ab795 100644 --- a/src/utils/rules.ts +++ b/src/utils/rules.ts @@ -13,6 +13,7 @@ import noDupeElseIfBlocks from "../rules/no-dupe-else-if-blocks" import noDupeStyleProperties from "../rules/no-dupe-style-properties" import noDynamicSlotName from "../rules/no-dynamic-slot-name" import noInnerDeclarations from "../rules/no-inner-declarations" +import noNonOptimizedStyleAttributes from "../rules/no-non-optimized-style-attributes" import noNotFunctionHandler from "../rules/no-not-function-handler" import noObjectInTextMustaches from "../rules/no-object-in-text-mustaches" import noShorthandStylePropertyOverrides from "../rules/no-shorthand-style-property-overrides" @@ -43,6 +44,7 @@ export const rules = [ noDupeStyleProperties, noDynamicSlotName, noInnerDeclarations, + noNonOptimizedStyleAttributes, noNotFunctionHandler, noObjectInTextMustaches, noShorthandStylePropertyOverrides, diff --git a/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/comment01-errors.json b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/comment01-errors.json new file mode 100644 index 000000000..24654c280 --- /dev/null +++ b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/comment01-errors.json @@ -0,0 +1,7 @@ +[ + { + "message": "It cannot be optimized because contains comments.", + "line": 5, + "column": 30 + } +] diff --git a/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/comment01-input.svelte b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/comment01-input.svelte new file mode 100644 index 000000000..4243c324b --- /dev/null +++ b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/comment01-input.svelte @@ -0,0 +1,5 @@ + + +
diff --git a/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/key01-errors.json b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/key01-errors.json new file mode 100644 index 000000000..c5e24908b --- /dev/null +++ b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/key01-errors.json @@ -0,0 +1,7 @@ +[ + { + "message": "It cannot be optimized because property of style declaration contain interpolation.", + "line": 6, + "column": 30 + } +] diff --git a/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/key01-input.svelte b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/key01-input.svelte new file mode 100644 index 000000000..7b7d29c42 --- /dev/null +++ b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/key01-input.svelte @@ -0,0 +1,6 @@ + + +
diff --git a/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/test01-errors.json b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/test01-errors.json new file mode 100644 index 000000000..77abc442a --- /dev/null +++ b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/test01-errors.json @@ -0,0 +1,22 @@ +[ + { + "message": "It cannot be optimized because too complex.", + "line": 14, + "column": 12 + }, + { + "message": "It cannot be optimized because too complex.", + "line": 17, + "column": 46 + }, + { + "message": "It cannot be optimized because contains comments.", + "line": 20, + "column": 30 + }, + { + "message": "It cannot be optimized because property of style declaration contain interpolation.", + "line": 21, + "column": 30 + } +] diff --git a/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/test01-input.svelte b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/test01-input.svelte new file mode 100644 index 000000000..3a691c653 --- /dev/null +++ b/tests/fixtures/rules/no-non-optimized-style-attributes/invalid/test01-input.svelte @@ -0,0 +1,21 @@ + + + +
+
+ + +
+
+ +
+
diff --git a/tests/fixtures/rules/no-non-optimized-style-attributes/valid/test01-input.svelte b/tests/fixtures/rules/no-non-optimized-style-attributes/valid/test01-input.svelte new file mode 100644 index 000000000..495962567 --- /dev/null +++ b/tests/fixtures/rules/no-non-optimized-style-attributes/valid/test01-input.svelte @@ -0,0 +1,14 @@ + + + +
+ + +
diff --git a/tests/src/rules/no-non-optimized-style-attributes.ts b/tests/src/rules/no-non-optimized-style-attributes.ts new file mode 100644 index 000000000..925204716 --- /dev/null +++ b/tests/src/rules/no-non-optimized-style-attributes.ts @@ -0,0 +1,16 @@ +import { RuleTester } from "eslint" +import rule from "../../../src/rules/no-non-optimized-style-attributes" +import { loadTestCases } from "../../utils/utils" + +const tester = new RuleTester({ + parserOptions: { + ecmaVersion: 2020, + sourceType: "module", + }, +}) + +tester.run( + "no-non-optimized-style-attributes", + rule as any, + loadTestCases("no-non-optimized-style-attributes"), +)