diff --git a/src/rules/at-if-no-null/README.md b/src/rules/at-if-no-null/README.md new file mode 100644 index 00000000..66a1edba --- /dev/null +++ b/src/rules/at-if-no-null/README.md @@ -0,0 +1,44 @@ +# at-if-no-null + +Check for equality to null is unnecessarily explicit since `null` is falsey in Sass. + +```scss +a { + @if $x == null {} +/** ↑ ↑ + * == or != null is unncessary */ +} +``` + +## Options + +true + +### `true` + +The following patterns are considered warnings: +```scss +a { + @if $x == null {} +} +``` + +```scss +a { + @if $x != null {} +} +``` + +The following patterns are *not* considered warnings: + +```scss +a { + @if $x {} +} +``` + +```scss +a { + @if not $x {} +} +``` diff --git a/src/rules/at-if-no-null/__tests__/index.js b/src/rules/at-if-no-null/__tests__/index.js new file mode 100644 index 00000000..6609f4b6 --- /dev/null +++ b/src/rules/at-if-no-null/__tests__/index.js @@ -0,0 +1,47 @@ +import rule, { ruleName, messages } from ".."; + +testRule(rule, { + ruleName, + config: [true], + syntax: "scss", + + accept: [ + { + code: `a { + @if $x {} + }`, + description: "does not use the != null format" + }, + { + code: `a { + @if not $x {} + }`, + description: "does not use the == null format" + }, + { + code: `a { + @if $x != null and $x > 1 {} + }`, + description: "does not use the == null format" + } + ], + + reject: [ + { + code: `a { + @if $x == null {} + }`, + description: "uses the == null format", + message: messages.equals_null, + line: 2 + }, + { + code: `a { + @if $x != null {} + }`, + description: "uses the != null format", + message: messages.not_equals_null, + line: 2 + } + ] +}); diff --git a/src/rules/at-if-no-null/index.js b/src/rules/at-if-no-null/index.js new file mode 100644 index 00000000..62d9c6b6 --- /dev/null +++ b/src/rules/at-if-no-null/index.js @@ -0,0 +1,49 @@ +import { namespace } from "../../utils"; +import { utils } from "stylelint"; + +export const ruleName = namespace("at-if-no-null"); + +export const messages = utils.ruleMessages(ruleName, { + equals_null: "Expected @if not statement rather than @if statement == null", + not_equals_null: "Expected @if statement rather than @if statement != null" +}); + +export default function(expectation) { + return (root, result) => { + const validOptions = utils.validateOptions(result, ruleName, { + actual: expectation + }); + + if (!validOptions) { + return; + } + + root.walkAtRules(atrule => { + // Do nothing if it's not an @if + if (atrule.name !== "if") { + return; + } + + // If rule != null and (expr), skip + if (atrule.params.match(/\(?[ \t]*.* != null and .*\)?/)) { + return; + } + + if (atrule.params.match(/\(?[ \t]*.* == null[ \t]*\)?/)) { + utils.report({ + message: messages.equals_null, + node: atrule, + result, + ruleName + }); + } else if (atrule.params.match(/\(?[ \t]*.* != null[ \t]*\)?/)) { + utils.report({ + message: messages.not_equals_null, + node: atrule, + result, + ruleName + }); + } + }); + }; +} diff --git a/src/rules/index.js b/src/rules/index.js index 5d264e1c..71817c9f 100644 --- a/src/rules/index.js +++ b/src/rules/index.js @@ -8,6 +8,7 @@ import atFunctionParenthesesSpaceBefore from "./at-function-parentheses-space-be import atFunctionPattern from "./at-function-pattern"; import atIfClosingBraceNewlineAfter from "./at-if-closing-brace-newline-after"; import atIfClosingBraceSpaceAfter from "./at-if-closing-brace-space-after"; +import atIfNoNull from "./at-if-no-null"; import atImportNoPartialLeadingUnderscore from "./at-import-no-partial-leading-underscore"; import atImportPartialExtensionBlacklist from "./at-import-partial-extension-blacklist"; import atImportPartialExtensionWhitelist from "./at-import-partial-extension-whitelist"; @@ -57,6 +58,7 @@ export default { "at-function-pattern": atFunctionPattern, "at-if-closing-brace-newline-after": atIfClosingBraceNewlineAfter, "at-if-closing-brace-space-after": atIfClosingBraceSpaceAfter, + "at-if-no-null": atIfNoNull, "at-import-no-partial-leading-underscore": atImportNoPartialLeadingUnderscore, "at-import-partial-extension-blacklist": atImportPartialExtensionBlacklist, "at-import-partial-extension-whitelist": atImportPartialExtensionWhitelist,