<NME> index.js
<BEF> ADDFILE
<MSG> Add property-no-unknown rule

<DFF> @@ -0,0 +1,75 @@
+import {
+  isString,
+  isArray,
+  isBoolean,
+} from "lodash"
+import {
+  isStandardSyntaxProperty,
+  isCustomProperty,
+  report,
+  ruleMessages,
+  validateOptions,
+} from "../../utils"
+import { all as properties } from "known-css-properties"
+
+export const ruleName = "property-no-unknown"
+
+export const messages = ruleMessages(ruleName, {
+  rejected: (property) => `Unexpected unknown property "${property}"`,
+})
+
+const isPropertyIgnored = (prop, ignore) => {
+  if (isArray(ignore)) {
+    return ignore.indexOf(prop) > -1
+  }
+
+  if (isString(ignore)) {
+    return prop === ignore
+  }
+
+  return false
+}
+
+const isPropertyValid = (prop) => {
+  return properties.indexOf(prop) > -1
+}
+
+const validate = (result, options) => {
+  return function (node) {
+    const prop = node.prop
+
+    if (!isStandardSyntaxProperty(prop)) { return }
+    if (isCustomProperty(prop)) { return }
+    if (isPropertyIgnored(prop, options.ignoreProperties)) { return }
+    if (isPropertyValid(prop)) { return }
+
+    report({
+      message: messages.rejected(node.prop),
+      node,
+      result,
+      ruleName,
+    })
+  }
+}
+
+export default function (enabled, options) {
+  return (root, result) => {
+    const validOptions = validateOptions(result, ruleName, {
+      actual: enabled,
+      possible: isBoolean,
+    }, {
+      actual: options,
+      possible: {
+        ignoreProperties: [isString],
+      },
+      optional: true,
+    })
+
+    if (!validOptions) { return }
+    if (!enabled) { return }
+    if (!options) { options = {} }
+
+    root.walkDecls(validate(result, options))
+  }
+}
+

{'commit': 'diff --git a/package.json b/package.json\nindex db522cca..9c09cc99 100644\n--- a/package.json\n+++ b/package.json\n@@ -70,6 +70,7 @@\n     "coveralls": "^2.11.9",\n     "eslint": "3.0.0",\n     "eslint-config-stylelint": "^3.0.0",\n+    "known-css-properties": "^0.0.2",\n     "npm-run-all": "^2.1.0",\n     "npmpub": "^3.0.1",\n     "nyc": "^7.0.0",\ndiff --git a/src/rules/index.js b/src/rules/index.js\nindex f72dadd6..501f58d3 100644\n--- a/src/rules/index.js\n+++ b/src/rules/index.js\n@@ -101,6 +101,7 @@ import numberZeroLengthNoUnit from "./number-zero-length-no-unit"\n import propertyBlacklist from "./property-blacklist"\n import propertyCase from "./property-case"\n import propertyNoVendorPrefix from "./property-no-vendor-prefix"\n+import propertyNoUnknown from "./property-no-unknown"\n import propertyUnitBlacklist from "./property-unit-blacklist"\n import propertyUnitWhitelist from "./property-unit-whitelist"\n import propertyValueBlacklist from "./property-value-blacklist"\n@@ -262,6 +263,7 @@ export default {\n   "property-blacklist": propertyBlacklist,\n   "property-case": propertyCase,\n   "property-no-vendor-prefix": propertyNoVendorPrefix,\n+  "property-no-unknown": propertyNoUnknown,\n   "property-unit-blacklist": propertyUnitBlacklist,\n   "property-unit-whitelist": propertyUnitWhitelist,\n   "property-value-blacklist": propertyValueBlacklist,\ndiff --git a/src/rules/property-no-unknown/README.md b/src/rules/property-no-unknown/README.md\nnew file mode 100644\nindex 00000000..8246a8c0\n--- /dev/null\n+++ b/src/rules/property-no-unknown/README.md\n@@ -0,0 +1,119 @@\n+# property-no-unknown\n+\n+Disallow unknown property.\n+\n+```css\n+.foo { heigth: 100%; }\n+/**    ↑\n+ *  These properties */\n+```\n+\n+This rule considers properties defined in the [CSS Specifications and browser specific properties](https://github.com/betit/known-css-properties#source) to be known.\n+\n+## Options\n+\n+### `true`\n+\n+The following patterns are considered warnings:\n+\n+```css\n+.foo {\n+  colr: blue;\n+}\n+```\n+\n+```css\n+.foo {\n+  -moz-align-self: center;\n+}\n+```\n+\n+The following patterns are *not* considered warnings:\n+\n+```css\n+.foo {\n+  color: green;\n+}\n+```\n+\n+```css\n+.foo {\n+  fill: black;\n+}\n+```\n+\n+```css\n+.foo {\n+  -webkit-align-self: center;\n+}\n+```\n+\n+```css\n+.foo {\n+  align-self: center;\n+}\n+```\n+\n+```css\n+.foo {\n+  --bg-color: white;\n+}\n+```\n+\n+```css\n+.foo {\n+  $bgColor: white;\n+}\n+```\n+\n+```css\n+.foo {\n+  @bgColor: white;\n+}\n+```\n+\n+```css\n+.foo {\n+  #{$prop}-color: black;\n+}\n+```\n+\n+```css\n+.foo {\n+  @{prop}-color: black;\n+}\n+```\n+\n+## Optional options\n+\n+### `ignoreProperties: ["array", "of", "properties"]`\n+\n+Allow unknown properties.\n+\n+Given:\n+\n+```js\n+["-moz-overflow-scrolling"]\n+```\n+\n+The following patterns are considered warnings:\n+\n+```css\n+.foo {\n+  overflow-scrolling: auto;\n+}\n+```\n+\n+The following patterns are *not* considered warnings:\n+\n+```css\n+.foo {\n+  -webkit-overflow-scrolling: auto;\n+}\n+```\n+\n+```css\n+.foo {\n+  -moz-overflow-scrolling: auto;\n+}\n+```\ndiff --git a/src/rules/property-no-unknown/__tests__/index.js b/src/rules/property-no-unknown/__tests__/index.js\nnew file mode 100644\nindex 00000000..cf2f6205\n--- /dev/null\n+++ b/src/rules/property-no-unknown/__tests__/index.js\n@@ -0,0 +1,81 @@\n+import { testRule } from "../../../testUtils"\n+import rules from "../../../rules"\n+import { ruleName, messages } from ".."\n+\n+const rule = rules[ruleName]\n+\n+testRule(rule, {\n+  ruleName,\n+  config: [true],\n+\n+  accept: [ {\n+    code: ".foo { color: green; }",\n+  }, {\n+    code: ".foo { fill: black; }",\n+  }, {\n+    code: ".foo { -webkit-align-self: center; }",\n+  }, {\n+    code: ".foo { align-self: center; }",\n+  }, {\n+    code: ".foo { --bg-color: white; }",\n+    description: "ignore standard CSS variables",\n+  } ],\n+\n+  reject: [ {\n+    code: ".foo { colr: blue; }",\n+    message: messages.rejected("colr"),\n+    line: 1,\n+    column: 8,\n+  }, {\n+    code: ".foo { -moz-align-self: center; }",\n+    message: messages.rejected("-moz-align-self"),\n+    line: 1,\n+    column: 8,\n+  } ],\n+})\n+\n+testRule(rule, {\n+  ruleName,\n+  syntax: "scss",\n+  config: [true],\n+\n+  accept: [ {\n+    code: ".foo { $bgColor: white; }",\n+    description: "ignore SCSS variables",\n+  }, {\n+    code: ".foo { #{$prop}: black; }",\n+    description: "ignore property interpolation",\n+  } ],\n+})\n+\n+testRule(rule, {\n+  ruleName,\n+  syntax: "less",\n+  config: [true],\n+\n+  accept: [ {\n+    code: ".foo { @bgColor: white; }",\n+    description: "ignore LESS variables",\n+  }, {\n+    code: ".foo { @{prop}: black; }",\n+    description: "ignore property interpolation",\n+  } ],\n+})\n+\n+testRule(rule, {\n+  ruleName,\n+  config: [ true, { ignoreProperties: ["-moz-overflow-scrolling"] } ],\n+\n+  accept: [ {\n+    code: ".foo { -webkit-overflow-scrolling: auto; }",\n+  }, {\n+    code: ".foo { -moz-overflow-scrolling: auto; }",\n+  } ],\n+\n+  reject: [{\n+    code: ".foo { overflow-scrolling: auto; }",\n+    message: messages.rejected("overflow-scrolling"),\n+    line: 1,\n+    column: 8,\n+  }],\n+})\ndiff --git a/src/rules/property-no-unknown/index.js b/src/rules/property-no-unknown/index.js\nnew file mode 100644\nindex 00000000..b9ee14c2\n--- /dev/null\n+++ b/src/rules/property-no-unknown/index.js\n@@ -0,0 +1,75 @@\n+import {\n+  isString,\n+  isArray,\n+  isBoolean,\n+} from "lodash"\n+import {\n+  isStandardSyntaxProperty,\n+  isCustomProperty,\n+  report,\n+  ruleMessages,\n+  validateOptions,\n+} from "../../utils"\n+import { all as properties } from "known-css-properties"\n+\n+export const ruleName = "property-no-unknown"\n+\n+export const messages = ruleMessages(ruleName, {\n+  rejected: (property) => `Unexpected unknown property "${property}"`,\n+})\n+\n+const isPropertyIgnored = (prop, ignore) => {\n+  if (isArray(ignore)) {\n+    return ignore.indexOf(prop) > -1\n+  }\n+\n+  if (isString(ignore)) {\n+    return prop === ignore\n+  }\n+\n+  return false\n+}\n+\n+const isPropertyValid = (prop) => {\n+  return properties.indexOf(prop) > -1\n+}\n+\n+const validate = (result, options) => {\n+  return function (node) {\n+    const prop = node.prop\n+\n+    if (!isStandardSyntaxProperty(prop)) { return }\n+    if (isCustomProperty(prop)) { return }\n+    if (isPropertyIgnored(prop, options.ignoreProperties)) { return }\n+    if (isPropertyValid(prop)) { return }\n+\n+    report({\n+      message: messages.rejected(node.prop),\n+      node,\n+      result,\n+      ruleName,\n+    })\n+  }\n+}\n+\n+export default function (enabled, options) {\n+  return (root, result) => {\n+    const validOptions = validateOptions(result, ruleName, {\n+      actual: enabled,\n+      possible: isBoolean,\n+    }, {\n+      actual: options,\n+      possible: {\n+        ignoreProperties: [isString],\n+      },\n+      optional: true,\n+    })\n+\n+    if (!validOptions) { return }\n+    if (!enabled) { return }\n+    if (!options) { options = {} }\n+\n+    root.walkDecls(validate(result, options))\n+  }\n+}\n+\n', 'commit_subject': 'Add property-no-unknown rule', 'repo_name': 'stylelint/stylelint', 'committer': {'name': 'Vio', 'email': '33b2a10dedfd91e5b05c12234d80200788ee33f0@mavrix.es', 'time_sec': '1468235370', 'tz_offset': '120', 'date': {'seconds': '1468235370'}}, 'commit_tree': '93dd4d356d5c24349f1a5b9d9f82c81aa8ac2965', 'commit_message': 'Add property-no-unknown rule\n', 'language_name': 'TypeScript', 'license': 'mit', 'hunks': '@@ -0,0 +1,75 @@\n+import {\n+  isString,\n+  isArray,\n+  isBoolean,\n+} from "lodash"\n+import {\n+  isStandardSyntaxProperty,\n+  isCustomProperty,\n+  report,\n+  ruleMessages,\n+  validateOptions,\n+} from "../../utils"\n+import { all as properties } from "known-css-properties"\n+\n+export const ruleName = "property-no-unknown"\n+\n+export const messages = ruleMessages(ruleName, {\n+  rejected: (property) => `Unexpected unknown property "${property}"`,\n+})\n+\n+const isPropertyIgnored = (prop, ignore) => {\n+  if (isArray(ignore)) {\n+    return ignore.indexOf(prop) > -1\n+  }\n+\n+  if (isString(ignore)) {\n+    return prop === ignore\n+  }\n+\n+  return false\n+}\n+\n+const isPropertyValid = (prop) => {\n+  return properties.indexOf(prop) > -1\n+}\n+\n+const validate = (result, options) => {\n+  return function (node) {\n+    const prop = node.prop\n+\n+    if (!isStandardSyntaxProperty(prop)) { return }\n+    if (isCustomProperty(prop)) { return }\n+    if (isPropertyIgnored(prop, options.ignoreProperties)) { return }\n+    if (isPropertyValid(prop)) { return }\n+\n+    report({\n+      message: messages.rejected(node.prop),\n+      node,\n+      result,\n+      ruleName,\n+    })\n+  }\n+}\n+\n+export default function (enabled, options) {\n+  return (root, result) => {\n+    const validOptions = validateOptions(result, ruleName, {\n+      actual: enabled,\n+      possible: isBoolean,\n+    }, {\n+      actual: options,\n+      possible: {\n+        ignoreProperties: [isString],\n+      },\n+      optional: true,\n+    })\n+\n+    if (!validOptions) { return }\n+    if (!enabled) { return }\n+    if (!options) { options = {} }\n+\n+    root.walkDecls(validate(result, options))\n+  }\n+}\n+\n', 'addition_count': 75, 'deletion_count': 0, 'src_file': '/dev/null', 'tgt_file': 'b/src/rules/property-no-unknown/index.js', 'file_extension': '.js', 'before_file': 'ADDFILE', 'message': 'Add property-no-unknown rule\n', 'author_name': 'Vio', 'lang': 'js'}