Skip to content

Commit

Permalink
Add font-family-no-duplicate-names rule (stylelint#2020)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaidarenko authored and sergesemashko committed Mar 3, 2017
1 parent 0ba6142 commit bfe5049
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/user-guide/example-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ You might want to learn a little about [how rules are named and how they work to
"declaration-property-value-blacklist": {},
"declaration-property-value-whitelist": {},
"font-family-name-quotes": "always-where-required"|"always-where-recommended"|"always-unless-keyword",
"font-family-no-duplicate-names": true,
"font-weight-notation": "numeric"|"named",
"function-blacklist": string|[],
"function-calc-no-unspaced-operator": true,
Expand Down
1 change: 1 addition & 0 deletions docs/user-guide/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Here are all the rules within stylelint, grouped by the [*thing*](http://apps.wo
### Font family

- [`font-family-name-quotes`](../../src/rules/font-family-name-quotes/README.md): Specify whether or not quotation marks should be used around font family names.
- [`font-family-no-duplicate-names`](../../src/rules/font-family-no-duplicate-names/README.md): Disallow duplicate font family names.

### Font weight

Expand Down
47 changes: 47 additions & 0 deletions src/rules/font-family-no-duplicate-names/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# font-family-no-duplicate-names

Disallow duplicate font family names.

```css
a { font-family: serif, serif; }
/** ↑ ↑
* These font family names */
```

This rule checks the `font` and `font-family` properties.

This rule ignores `$sass`, `@less`, and `var(--custom-property)` variable syntaxes.

**Caveat:** This rule will stumble on *unquoted* multi-word font names and *unquoted* font names containing escape sequences. Wrap these font names in quotation marks, and everything should be fine.

## Options

### `true`

The following patterns are considered warnings:

```css
a { font-family: 'Times', Times, serif; }
```

```css
a { font: 1em "Arial", 'Arial', sans-serif; }
```

```css
a { font: normal 14px/32px -apple-system, BlinkMacSystemFont, sans-serif, sans-serif; }
```

The following patterns are *not* considered warnings:

```css
a { font-family: Times, serif; }
```

```css
a { font: 1em "Arial", "sans-serif", sans-serif; }
```

```css
a { font: normal 14px/32px -apple-system, BlinkMacSystemFont, sans-serif; }
```
58 changes: 58 additions & 0 deletions src/rules/font-family-no-duplicate-names/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
messages,
ruleName,
} from ".."
import rules from "../../../rules"
import { testRule } from "../../../testUtils"

const rule = rules[ruleName]

testRule(rule, {
ruleName,
config: [true],

accept: [ {
code: "a { font-family: \"Lucida Grande\", \"Arial\", sans-serif; }",
}, {
code: "a { font: 1em \"Lucida Grande\", \'Arial\', sans-serif; }",
}, {
code: "a { font: 1em \"Lucida Grande\", \'Arial\', \"sans-serif\", sans-serif; }",
}, {
code: "a { font-family: Times, serif; }",
}, {
code: "b { font: normal 14px/32px -apple-system, BlinkMacSystemFont, sans-serif; }",
} ],

reject: [ {
code: "a { font-family: \"Lucida Grande\", \'Arial\', sans-serif, sans-serif; }",
message: messages.rejected("sans-serif"),
line: 1,
column: 56,
}, {
code: "a { font-family: \'Arial\', \"Lucida Grande\", Arial, sans-serif; }",
message: messages.rejected("Arial"),
line: 1,
column: 44,
}, {
code: "a { fOnT-fAmIlY: \' Lucida Grande \', \"Lucida Grande\", sans-serif; }",
message: messages.rejected("Lucida Grande"),
line: 1,
column: 38,
}, {
code: "a { font-family: \'Times\', Times, \"serif\", serif; }",
message: messages.rejected("Times"),
line: 1,
column: 27,
}, {
code: "a { FONT: italic 300 16px/30px Arial, \" Arial\", serif; }",
message: messages.rejected("Arial"),
line: 1,
column: 39,
}, {
code: "b { font: normal 14px/32px -apple-system, BlinkMacSystemFont, sans-serif, sans-serif; }",
message: messages.rejected("sans-serif"),
line: 1,
column: 75,
} ],

})
66 changes: 66 additions & 0 deletions src/rules/font-family-no-duplicate-names/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
declarationValueIndex,
findFontFamily,
report,
ruleMessages,
validateOptions,
} from "../../utils"
import { fontFamilyKeywords } from "../../reference/keywordSets"

export const ruleName = "font-family-no-duplicate-names"

export const messages = ruleMessages(ruleName, {
rejected: (name) => `Unexpected duplicate name ${name}`,
})

const isFamilyNameKeyword = (node) => !node.quote && fontFamilyKeywords.has(node.value.toLowerCase())

export default function (actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) { return }

root.walkDecls(/^font(-family)?$/i, decl => {
const keywords = new Set()
const familyNames = new Set()

const fontFamilies = findFontFamily(decl.value)

if (fontFamilies.length === 0) { return }

fontFamilies.forEach(fontFamilyNode => {
if (isFamilyNameKeyword(fontFamilyNode)) {
const family = fontFamilyNode.value.toLowerCase()

if (keywords.has(family)) {
complain(messages.rejected(family), declarationValueIndex(decl) + fontFamilyNode.sourceIndex, decl)
return
}

keywords.add(family)
return
}

const family = fontFamilyNode.value.trim()

if (familyNames.has(family)) {
complain(messages.rejected(family), declarationValueIndex(decl) + fontFamilyNode.sourceIndex, decl)
return
}

familyNames.add(family)
})
})

function complain(message, index, decl) {
report({
result,
ruleName,
message,
node: decl,
index,
})
}
}
}

2 changes: 2 additions & 0 deletions src/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import declarationPropertyUnitWhitelist from "./declaration-property-unit-whitel
import declarationPropertyValueBlacklist from "./declaration-property-value-blacklist"
import declarationPropertyValueWhitelist from "./declaration-property-value-whitelist"
import fontFamilyNameQuotes from "./font-family-name-quotes"
import fontFamilyNoDuplicateNames from "./font-family-no-duplicate-names"
import fontWeightNotation from "./font-weight-notation"
import functionBlacklist from "./function-blacklist"
import functionCalcNoUnspacedOperator from "./function-calc-no-unspaced-operator"
Expand Down Expand Up @@ -227,6 +228,7 @@ export default {
"declaration-property-value-blacklist": declarationPropertyValueBlacklist,
"declaration-property-value-whitelist": declarationPropertyValueWhitelist,
"font-family-name-quotes": fontFamilyNameQuotes,
"font-family-no-duplicate-names": fontFamilyNoDuplicateNames,
"font-weight-notation": fontWeightNotation,
"function-blacklist": functionBlacklist,
"function-calc-no-unspaced-operator": functionCalcNoUnspacedOperator,
Expand Down

0 comments on commit bfe5049

Please sign in to comment.