From 1b44ec6084ecffb13c25896b4df2c28be0c3f116 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Fri, 31 May 2019 18:41:13 -0700 Subject: [PATCH 1/4] Ensure all map keys are quoted --- src/rules/index.js | 2 + src/rules/map-keys-always-quoted/README.md | 26 ++++++ .../map-keys-always-quoted/__tests__/index.js | 28 +++++++ src/rules/map-keys-always-quoted/index.js | 83 +++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 src/rules/map-keys-always-quoted/README.md create mode 100644 src/rules/map-keys-always-quoted/__tests__/index.js create mode 100644 src/rules/map-keys-always-quoted/index.js diff --git a/src/rules/index.js b/src/rules/index.js index 73a1bf75..db37cf18 100644 --- a/src/rules/index.js +++ b/src/rules/index.js @@ -33,6 +33,7 @@ import functionNoQuotedStrings from "./function-quote-no-quoted-strings-inside"; import functionNoUnquotedStrings from "./function-unquote-no-unquoted-strings-inside"; import mediaFeatureValueDollarVariable from "./media-feature-value-dollar-variable"; import noDollarVariables from "./no-dollar-variables"; +import mapKeysAlwaysQuoted from "./map-keys-always-quoted"; import noDuplicateDollarVariables from "./no-duplicate-dollar-variables"; import operatorNoNewlineAfter from "./operator-no-newline-after"; import operatorNoNewlineBefore from "./operator-no-newline-before"; @@ -76,6 +77,7 @@ export default { "double-slash-comment-whitespace-inside": doubleSlashCommentWhitespaceInside, "function-quote-no-quoted-strings-inside": functionNoQuotedStrings, "function-unquote-no-unquoted-strings-inside": functionNoUnquotedStrings, + "map-keys-always-quoted": mapKeysAlwaysQuoted, "media-feature-value-dollar-variable": mediaFeatureValueDollarVariable, "no-dollar-variables": noDollarVariables, "no-duplicate-dollar-variables": noDuplicateDollarVariables, diff --git a/src/rules/map-keys-always-quoted/README.md b/src/rules/map-keys-always-quoted/README.md new file mode 100644 index 00000000..d698d989 --- /dev/null +++ b/src/rules/map-keys-always-quoted/README.md @@ -0,0 +1,26 @@ +# map-keys-always-quoted + +All map keys should be quoted. + +```scss +$test: (Helvetica: 14px, Arial: 25px); + /** ↑ ↑ + * These words should be quoted. + */ +``` + +## Options + +### `true` + +The following patterns are considered violations: + +```scss +$test: (Helvetica: 14px, Arial: 25px); +``` + +The following patterns are _not_ considered violations: + +```scss +$test: ("foo": 14px, "bar": 25px); +``` diff --git a/src/rules/map-keys-always-quoted/__tests__/index.js b/src/rules/map-keys-always-quoted/__tests__/index.js new file mode 100644 index 00000000..cd3925e9 --- /dev/null +++ b/src/rules/map-keys-always-quoted/__tests__/index.js @@ -0,0 +1,28 @@ +import rule, { ruleName, messages } from ".."; + +testRule(rule, { + ruleName, + config: [true], + syntax: "scss", + + accept: [ + { + code: ` + $test: ("foo": 14px, "bar": 25px); + `, + description: "accepts strings without quotes" + } + ], + + reject: [ + { + code: ` + $test: (Helvetica: 25px, Arial: 50px) + `, + message: messages.rejected, + description: + "does not accept variables representing strings that are quoted.", + location: 1 + } + ] +}); diff --git a/src/rules/map-keys-always-quoted/index.js b/src/rules/map-keys-always-quoted/index.js new file mode 100644 index 00000000..10eaf55c --- /dev/null +++ b/src/rules/map-keys-always-quoted/index.js @@ -0,0 +1,83 @@ +import { utils } from "stylelint"; +import { namespace } from "../../utils"; +import valueParser from "postcss-value-parser"; + +export const ruleName = namespace("map-keys-always-quoted"); + +export const messages = utils.ruleMessages(ruleName, { + rejected: "Quote function used with an already-quoted string" +}); + +function rule(primary) { + return (root, result) => { + const validOptions = utils.validateOptions(result, ruleName, { + actual: primary + }); + + if (!validOptions) { + return; + } + + root.walkDecls(decl => { + if (decl.prop[0] !== "$") { + return; + } + + valueParser(decl.value).walk(node => { + if ( + node.type === "function" && + node.value === "" && + isMap(node.nodes) + ) { + // Identify all of the map-keys and see if they're strings (not words). + const mapKeys = returnMapKeys(node.nodes); + + mapKeys.forEach(map_key => { + if (map_key.type === "word") { + utils.report({ + message: messages.rejected, + node: decl, + result, + ruleName + }); + } + }); + } + }); + }); + }; +} + +// Takes in a list of map nodes and identifies if they are a map. +// A map is identified by the pattern: [string/word colon(div) anything comma(div) ...] +function isMap(nodes) { + if (nodes.length < 4) { + return false; + } + + if (nodes[0].type !== "word" && nodes[0].type !== "string") { + return false; + } + + if (nodes[1].value !== ":") { + return false; + } + + if (nodes[3].value !== ",") { + return false; + } + + return true; +} + +function returnMapKeys(array) { + const new_array = []; + + for (let i = 0; i < array.length; i += 4) { + new_array.push(array[i]); + } + + return new_array; +} + +export default rule; From 7eddfaba9030477959cd2ec80aa91fe623fee591 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Mon, 1 Jul 2019 11:32:51 -0700 Subject: [PATCH 2/4] rename --- src/rules/index.js | 4 ++-- .../{map-keys-always-quoted => map-keys-quotes}/README.md | 2 +- .../__tests__/index.js | 0 .../{map-keys-always-quoted => map-keys-quotes}/index.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/rules/{map-keys-always-quoted => map-keys-quotes}/README.md (93%) rename src/rules/{map-keys-always-quoted => map-keys-quotes}/__tests__/index.js (100%) rename src/rules/{map-keys-always-quoted => map-keys-quotes}/index.js (96%) diff --git a/src/rules/index.js b/src/rules/index.js index db37cf18..25cee4cc 100644 --- a/src/rules/index.js +++ b/src/rules/index.js @@ -33,7 +33,7 @@ import functionNoQuotedStrings from "./function-quote-no-quoted-strings-inside"; import functionNoUnquotedStrings from "./function-unquote-no-unquoted-strings-inside"; import mediaFeatureValueDollarVariable from "./media-feature-value-dollar-variable"; import noDollarVariables from "./no-dollar-variables"; -import mapKeysAlwaysQuoted from "./map-keys-always-quoted"; +import mapKeysQuotes from "./map-keys-quotes"; import noDuplicateDollarVariables from "./no-duplicate-dollar-variables"; import operatorNoNewlineAfter from "./operator-no-newline-after"; import operatorNoNewlineBefore from "./operator-no-newline-before"; @@ -77,7 +77,7 @@ export default { "double-slash-comment-whitespace-inside": doubleSlashCommentWhitespaceInside, "function-quote-no-quoted-strings-inside": functionNoQuotedStrings, "function-unquote-no-unquoted-strings-inside": functionNoUnquotedStrings, - "map-keys-always-quoted": mapKeysAlwaysQuoted, + "map-keys-quotes": mapKeysQuotes, "media-feature-value-dollar-variable": mediaFeatureValueDollarVariable, "no-dollar-variables": noDollarVariables, "no-duplicate-dollar-variables": noDuplicateDollarVariables, diff --git a/src/rules/map-keys-always-quoted/README.md b/src/rules/map-keys-quotes/README.md similarity index 93% rename from src/rules/map-keys-always-quoted/README.md rename to src/rules/map-keys-quotes/README.md index d698d989..e58b1753 100644 --- a/src/rules/map-keys-always-quoted/README.md +++ b/src/rules/map-keys-quotes/README.md @@ -1,4 +1,4 @@ -# map-keys-always-quoted +# map-keys-quotes All map keys should be quoted. diff --git a/src/rules/map-keys-always-quoted/__tests__/index.js b/src/rules/map-keys-quotes/__tests__/index.js similarity index 100% rename from src/rules/map-keys-always-quoted/__tests__/index.js rename to src/rules/map-keys-quotes/__tests__/index.js diff --git a/src/rules/map-keys-always-quoted/index.js b/src/rules/map-keys-quotes/index.js similarity index 96% rename from src/rules/map-keys-always-quoted/index.js rename to src/rules/map-keys-quotes/index.js index 10eaf55c..134f889a 100644 --- a/src/rules/map-keys-always-quoted/index.js +++ b/src/rules/map-keys-quotes/index.js @@ -2,7 +2,7 @@ import { utils } from "stylelint"; import { namespace } from "../../utils"; import valueParser from "postcss-value-parser"; -export const ruleName = namespace("map-keys-always-quoted"); +export const ruleName = namespace("map-keys-quotes"); export const messages = utils.ruleMessages(ruleName, { rejected: "Quote function used with an already-quoted string" From 815ca1c7547eb1ff54f5f76521bfee4dd9a99e54 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Mon, 1 Jul 2019 11:35:29 -0700 Subject: [PATCH 3/4] changed messaging --- src/rules/map-keys-quotes/__tests__/index.js | 2 +- src/rules/map-keys-quotes/index.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rules/map-keys-quotes/__tests__/index.js b/src/rules/map-keys-quotes/__tests__/index.js index cd3925e9..2336d50c 100644 --- a/src/rules/map-keys-quotes/__tests__/index.js +++ b/src/rules/map-keys-quotes/__tests__/index.js @@ -19,7 +19,7 @@ testRule(rule, { code: ` $test: (Helvetica: 25px, Arial: 50px) `, - message: messages.rejected, + message: messages.expected, description: "does not accept variables representing strings that are quoted.", location: 1 diff --git a/src/rules/map-keys-quotes/index.js b/src/rules/map-keys-quotes/index.js index 134f889a..8410b23a 100644 --- a/src/rules/map-keys-quotes/index.js +++ b/src/rules/map-keys-quotes/index.js @@ -5,7 +5,7 @@ import valueParser from "postcss-value-parser"; export const ruleName = namespace("map-keys-quotes"); export const messages = utils.ruleMessages(ruleName, { - rejected: "Quote function used with an already-quoted string" + expected: "Expected keys in map to be quoted." }); function rule(primary) { @@ -35,7 +35,7 @@ function rule(primary) { mapKeys.forEach(map_key => { if (map_key.type === "word") { utils.report({ - message: messages.rejected, + message: messages.expected, node: decl, result, ruleName From 8628f6495efd5ba8db2adc6f2d6249867d67c314 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Tue, 2 Jul 2019 10:35:49 -0700 Subject: [PATCH 4/4] using always instead of true/false --- src/rules/map-keys-quotes/README.md | 2 +- src/rules/map-keys-quotes/__tests__/index.js | 2 +- src/rules/map-keys-quotes/index.js | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rules/map-keys-quotes/README.md b/src/rules/map-keys-quotes/README.md index e58b1753..d59cbd88 100644 --- a/src/rules/map-keys-quotes/README.md +++ b/src/rules/map-keys-quotes/README.md @@ -11,7 +11,7 @@ $test: (Helvetica: 14px, Arial: 25px); ## Options -### `true` +### `always` The following patterns are considered violations: diff --git a/src/rules/map-keys-quotes/__tests__/index.js b/src/rules/map-keys-quotes/__tests__/index.js index 2336d50c..3c5ec988 100644 --- a/src/rules/map-keys-quotes/__tests__/index.js +++ b/src/rules/map-keys-quotes/__tests__/index.js @@ -2,7 +2,7 @@ import rule, { ruleName, messages } from ".."; testRule(rule, { ruleName, - config: [true], + config: ["always"], syntax: "scss", accept: [ diff --git a/src/rules/map-keys-quotes/index.js b/src/rules/map-keys-quotes/index.js index 8410b23a..cd08ff61 100644 --- a/src/rules/map-keys-quotes/index.js +++ b/src/rules/map-keys-quotes/index.js @@ -11,7 +11,8 @@ export const messages = utils.ruleMessages(ruleName, { function rule(primary) { return (root, result) => { const validOptions = utils.validateOptions(result, ruleName, { - actual: primary + actual: primary, + possible: ["always"] }); if (!validOptions) {