Linting
Code linting is a way to increase code quality by detecting common programming errors and ensure that certain best practices are followed.
As a result the code is likely to contain fewer bugs and be easier to read. Detecting these simple errors before a manual code review also makes reviewing the code more effective since the reviewer can focus on more complex issues.
Linters work by parsing and inspecting the source code, and then analysing the syntax and structure. If any code violates the rules defined by the linter a warning is shown, explaining what part of the code has a potential issue and why.
Linting can be done both within some code editors themselves and/or it can be built into your automated processes such as gulp.
HTML
The following are htmllint rules that can be used against html code produced for Aviva.
{
"attr-bans": false,
"attr-no-dup": true,
"attr-no-unsafe-char": true,
"attr-req-value": true,
"attr-validate": true,
"class-no-dup": true,
"class-style": "bem",
"doctype-html5": true,
"fig-req-figcaption": true,
"focusable-tabindex-style": true,
"head-req-title": true,
"head-valid-content-model": true,
"html-req-lang": true,
"html-valid-content-model": true,
"id-class-style": false,
"id-no-dup": true,
"img-req-alt": "allownull",
"img-req-src": true,
"indent-style": "tabs",
"input-radio-req-name": true,
"input-req-label": true,
"label-req-for": true,
"lang-style": false,
"line-end-style": false,
"line-no-trailing-whitespace": true,
"raw-ignore-regex": "(([ \t]*({{[^{}]+}}|{%[^{}]+%}))|([ \t]*>script[^]*?>\/script<)|([ \t]*(>style[^]*?>\/style<))|( playsinline))+",
"spec-char-escape": true,
"table-req-header": true,
"tag-bans": ["b", "i"],
"tag-close": true,
"tag-name-lowercase": true,
"tag-name-match": true,
"tag-self-close": "always",
"title-max-len": false,
"title-no-dup": true
}
For more information about the htmllint rules used see the htmllint options documentation.
CSS
The following are styleLint rules (for v.9.X of stylelint) that can be used against CSS styling produced for Aviva.
{
'plugins': [
'stylelint-order'
],
'rules' : {
'color-no-invalid-hex' : true,
'font-family-no-duplicate-names' : true,
'font-family-no-missing-generic-family-keyword' : true,
'function-calc-no-invalid' : true,
'function-calc-no-unspaced-operator' : true,
'function-linear-gradient-no-nonstandard-direction' : true,
'string-no-newline' : true,
'unit-no-unknown' : true,
'property-no-unknown' : [ true, {
checkPrefixed : true,
ignoreProperties : [ 'scrollbar-3dlight-color' ]
}],
'keyframe-declaration-no-important' : true,
'declaration-block-no-duplicate-properties' : [ true, {
ignore : [ 'consecutive-duplicates-with-different-values' ]
}],
'declaration-block-no-shorthand-property-overrides' : true,
'block-no-empty' : true,
'selector-pseudo-class-no-unknown' : true,
'selector-pseudo-element-no-unknown' : true,
'selector-type-no-unknown' : true,
'media-feature-name-no-unknown' : true,
'at-rule-no-unknown' : true,
'comment-no-empty' : true,
'no-duplicate-at-import-rules' : true,
'no-duplicate-selectors' : true,
'no-empty-source' : true,
'no-extra-semicolons' : true,
'no-invalid-double-slash-comments' : true,
'color-named' : 'never',
'function-url-no-scheme-relative' : true,
'function-url-scheme-blacklist' : [ '/^http:/' ],
'keyframes-name-pattern' : '([a-z]+-)?[a-z0-9-]+',
'number-max-precision' : 6,
'time-min-milliseconds' : 10,
'shorthand-property-no-redundant-values' : true,
'value-no-vendor-prefix' : true,
'custom-property-pattern' : '([a-z]+-)?[a-z0-9-]+',
'property-no-vendor-prefix' : [ true, {
ignoreProperties: [ 'appearance', 'text-size-adjust', 'column-break-inside' ]
}],
'declaration-block-no-redundant-longhand-properties' : true,
'declaration-no-important' : true,
'declaration-block-single-line-max-declarations' : 1,
'selector-class-pattern' : '([a-z]+-)?[a-z0-9-]+((_{2}|-{2})?[a-z0-9-]+)?(-{2}[a-z0-9-]+)?[a-z0-9]',
'selector-id-pattern' : '[a-z0-9^]+[-]?[a-z0-9^]+',
'selector-max-attribute' : 2,
'selector-max-class' : 7,
'selector-max-combinators' : 7,
'selector-max-compound-selectors' : 7,
'selector-max-empty-lines' : 0,
'selector-max-id' : 2,
'selector-max-pseudo-class' : 3,
'selector-max-specificity' : ['5,5,5'],
'selector-max-type' : 5,
'selector-max-universal' : 1,
'selector-no-vendor-prefix' : [ true, {
ignoreSelectors: [ '/.*-input-placeholder/', '/.*-placeholder/', '/.*-selection/', '/.*-fullscreen/' ]
}],
'media-feature-name-no-vendor-prefix' : true,
'at-rule-no-vendor-prefix' : true,
'max-nesting-depth' : 0,
'no-unknown-animations' : true,
'color-hex-case' : 'lower',
'color-hex-length' : 'short',
'font-family-name-quotes' : 'always-unless-keyword',
'font-weight-notation' : 'named-where-possible',
'function-comma-newline-after' : 'never-multi-line',
'function-comma-newline-before' : 'never-multi-line',
'function-comma-space-after' : 'always',
'function-comma-space-before' : 'never',
'function-max-empty-lines' : 0,
'function-name-case' : 'lower',
'function-parentheses-newline-inside' : 'never-multi-line',
'function-parentheses-space-inside' : 'never',
'function-url-quotes' : 'always',
'function-whitespace-after' : 'always',
'number-leading-zero' : 'never',
'number-no-trailing-zeros' : true,
'string-quotes' : 'single',
'length-zero-no-unit' : true,
'unit-case' : 'lower',
'value-keyword-case' : [ 'lower', {
ignoreKeywords: [ 'BlinkMacSystemFont' ],
ignoreProperties: [ 'unicode-range' ]
}],
'value-list-comma-newline-after' : 'never-multi-line',
'value-list-comma-newline-before' : 'never-multi-line',
'value-list-comma-space-after' : 'always',
'value-list-comma-space-before' : 'never',
'value-list-max-empty-lines' : 0,
'property-case' : 'lower',
'declaration-bang-space-after' : 'never',
'declaration-bang-space-before' : 'always',
'declaration-colon-newline-after' : 'always-multi-line',
'declaration-colon-space-after' : 'always-single-line',
'declaration-colon-space-before' : 'never',
'declaration-empty-line-before' : 'never',
'declaration-block-semicolon-newline-after' : 'always',
'declaration-block-semicolon-newline-before' : 'never-multi-line',
'declaration-block-semicolon-space-after' : 'always-single-line',
'declaration-block-semicolon-space-before' : 'never',
'declaration-block-trailing-semicolon' : 'always',
'block-closing-brace-empty-line-before' : 'never',
'block-closing-brace-newline-after' : 'always',
'block-closing-brace-newline-before' : 'always',
'block-closing-brace-space-after' : 'always-single-line',
'block-closing-brace-space-before' : 'always-single-line',
'block-opening-brace-newline-after' : 'always',
'block-opening-brace-space-after' : 'always-single-line',
'block-opening-brace-space-before' : 'always',
'selector-attribute-brackets-space-inside' : 'never',
'selector-attribute-operator-space-after' : 'always',
'selector-attribute-operator-space-before' : 'always',
'selector-attribute-quotes' : 'always',
'selector-combinator-space-after' : 'always',
'selector-combinator-space-before' : 'always',
'selector-descendant-combinator-no-non-space' : true,
'selector-pseudo-class-case' : 'lower',
'selector-pseudo-class-parentheses-space-inside' : 'never',
'selector-pseudo-element-case' : 'lower',
'selector-pseudo-element-colon-notation' : 'single',
'selector-type-case' : 'lower',
'selector-list-comma-newline-after' : 'always',
'selector-list-comma-newline-before' : 'never-multi-line',
'selector-list-comma-space-after' : 'always-single-line',
'selector-list-comma-space-before' : 'never',
'media-feature-colon-space-after' : 'always',
'media-feature-colon-space-before' : 'never',
'media-feature-name-case' : 'lower',
'media-feature-parentheses-space-inside' : 'never',
'media-feature-range-operator-space-after' : 'always',
'media-feature-range-operator-space-before' : 'always',
'media-query-list-comma-newline-after' : 'never-multi-line',
'media-query-list-comma-newline-before' : 'never-multi-line',
'media-query-list-comma-space-after' : 'always',
'media-query-list-comma-space-before' : 'never',
'at-rule-empty-line-before' : [ 'always', {
ignoreAtRules: [ 'import' ]
}],
'at-rule-name-case' : 'lower',
'at-rule-name-newline-after' : 'always-multi-line',
'at-rule-name-space-after' : 'always',
'at-rule-semicolon-newline-after' : 'always',
'at-rule-semicolon-space-before' : 'never',
'comment-empty-line-before' : [ 'always', {
ignore: [ 'after-comment', 'stylelint-commands' ]
}],
'comment-whitespace-inside' : 'always',
'indentation' : [ 'tab', {
'indentClosingBrace' : false
}],
'max-empty-lines' : 3,
'max-line-length' : [ 100, {
ignore: [ 'non-comments' ]
}],
'no-eol-whitespace' : true,
'no-missing-end-of-source-newline' : true,
'no-empty-first-line' : true,
'order/properties-alphabetical-order': true
}
}
For more information about the styleLint rules used see the styleLint user documentation.
'no-duplicate-selectors'
should remain enabled during development but may require removing for monitored builds as there are some genuine cases where duplicate selectors are required ie. to maintain compatibility with older versions of IE newer CSS features such as flex may need to be set in a duplicate selector.
JavaScript
The following are ESLint rules that can be used against JavaScript code produced for Aviva.
{
'env' : {
'browser' : true,
'amd' : true,
'jquery' : true
},
'globals' : {
'Modernizr' : false,
'FRAMEWORK' : true,
'locale' : true,
's' : true
},
'extends' : 'eslint:recommended',
'rules' : {
'for-direction' : 'error',
'getter-return' : [
'error',
{
allowImplicit : true
}
],
'no-await-in-loop' : 'error',
'no-extra-parens' : [
'off',
'all',
{
conditionalAssign : false,
returnAssign : false,
nestedBinaryExpressions : false,
ignoreJSX : 'none',
enforceForArrowConditionals : false
}
],
'no-async-promise-executor' : 'error',
'no-compare-neg-zero' : 'error',
'no-cond-assign' : [ 'error', 'always' ],
'no-console' : 'error',
'no-constant-condition' : [
'error',
{
checkLoops : true
}
],
'no-control-regex' : 'error',
'no-debugger' : 'error',
'no-dupe-args' : 'error',
'no-duplicate-case' : 'error',
'no-empty' : [
'error',
{
allowEmptyCatch : false
}
],
'no-empty-character-class' : 'error',
'no-ex-assign' : 'error',
'no-extra-boolean-cast' : 'error',
'no-extra-semi' : 'error',
'no-func-assign' : 'error',
'no-inner-declarations' : [ 'error', 'functions' ],
'no-invalid-regexp': [
'error',
{
allowConstructorFlags : []
}
],
'no-irregular-whitespace': [
'error',
{
skipStrings : true,
skipComments : true,
skipRegExps : true,
skipTemplates : true
}
],
'no-misleading-character-class' : 'error',
'no-obj-calls' : 'error',
'no-prototype-builtins' : 'error',
'no-regex-spaces' : 'error',
'no-sparse-arrays' : 'error',
'no-template-curly-in-string' : 'error',
'no-unexpected-multiline' : 'error',
'no-unreachable' : 'error',
'no-unsafe-finally' : 'error',
'no-unsafe-negation' : 'error',
'require-atomic-updates' : 'error',
'use-isnan' : 'error',
'valid-typeof' : [
'error',
{
requireStringLiterals : true
}
],
'accessor-pairs' : 'error',
'array-callback-return' : [
'error',
{
allowImplicit : true
}
],
'block-scoped-var' : 'error',
'class-methods-use-this' : 'error',
'complexity' : [ 'error', {
'max' : 5
}],
'consistent-return' : 'error',
'curly' : 'error',
'default-case' : [
'off',
{
commentPattern : '/^no default$/i'
}
],
'dot-location' : [ 'error', 'property' ],
'dot-notation' : 'error',
'eqeqeq' : 'error',
'guard-for-in' : 'error',
'max-classes-per-file' : [ 'off', 1 ],
'no-alert' : 'error',
'no-caller' : 'error',
'no-case-declarations' : 'error',
'no-div-regex' : 'error',
'no-else-return' : 'error',
'no-empty-function' : 'error',
'no-empty-pattern' : 'error',
'no-eq-null' : 'error',
'no-eval' : 'error',
'no-extend-native' : 'error',
'no-extra-bind' : 'error',
'no-extra-label' : 'error',
'no-fallthrough' : [
'error',
{
commentPattern : 'break[\\s\\w]*omitted'
}
],
'no-floating-decimal' : 'error',
'no-global-assign': 'error',
'no-implicit-coercion' : 'error',
'no-implicit-globals' : 'error',
'no-implied-eval' : 'error',
'no-invalid-this' : 'off',
'no-iterator' : 'error',
'no-labels' : 'error',
'no-lone-blocks' : 'error',
'no-loop-func' : 'error',
'no-magic-numbers' : [
'off',
{
ignore : [],
ignoreArrayIndexes : false,
enforceConst : false,
detectObjects : false
}
],
'no-multi-spaces' : [
'error',
{
ignoreEOLComments : false,
exceptions : {}
}
],
'no-multi-str' : 'error',
'no-new' : 'error',
'no-new-func' : 'error',
'no-new-wrappers' : 'error',
'no-octal' : 'error',
'no-octal-escape' : 'error',
'no-param-reassign' : 'error',
'no-proto' : 'error',
'no-redeclare' : [
'error',
{
builtinGlobals : false
}
],
'no-restricted-properties' : [
'error',
{
object : 'arguments',
property : 'callee',
message : 'arguments.callee is deprecated'
},
{
object : 'global',
property : 'isFinite',
message : 'Please use Number.isFinite instead'
},
{
object : 'self',
property : 'isFinite',
message : 'Please use Number.isFinite instead'
},
{
object : 'window',
property : 'isFinite',
message : 'Please use Number.isFinite instead'
},
{
object : 'global',
property : 'isNaN',
message : 'Please use Number.isNaN instead'
},
{
object : 'self',
property : 'isNaN',
message : 'Please use Number.isNaN instead'
},
{
object : 'window',
property : 'isNaN',
message : 'Please use Number.isNaN instead'
},
{
property : '__defineGetter__',
message : 'Please use Object.defineProperty instead.'
},
{
property : '__defineSetter__',
message : 'Please use Object.defineProperty instead.'
},
{
object : 'Math',
property : 'pow',
message : 'Use the exponentiation operator (**) instead.'
}
],
'no-return-assign' : 'error',
'no-return-await' : 'error',
'no-script-url' : 'error',
'no-self-assign' : [
'error',
{
props : true
}
],
'no-self-compare' : 'error',
'no-sequences' : 'error',
'no-throw-literal' : 'error',
'no-unmodified-loop-condition' : 'error',
'no-unused-expressions' : 'error',
'no-useless-call' : 'error',
'no-useless-concat' : 'error',
'no-useless-escape' : 'error',
'no-useless-return' : 'error',
'no-void' : 'error',
'no-warning-comments' : [
'off',
{
terms : [],
location : 'start'
}
],
'no-with' : 'error',
'prefer-promise-reject-errors' : 'error',
'radix' : 'error',
'require-await' : 'off',
'require-unicode-regexp' : 'off',
'vars-on-top' : 'error',
'wrap-iife' : [ 'error', 'inside' ],
'yoda' : 'error',
'strict' : 'error',
'init-declarations' : [ 'off', 'always' ],
'no-delete-var' : 'error',
'no-label-var' : 'error',
'no-restricted-globals' : [
'error',
'addEventListener',
'blur',
'close',
'closed',
'confirm',
'defaultStatus',
'event',
'external',
'defaultstatus',
'find',
'focus',
'frameElement',
'frames',
'history',
'innerHeight',
'innerWidth',
'isFinite',
'isNaN',
'length',
'location',
'locationbar',
'menubar',
'moveBy',
'moveTo',
'name',
'onblur',
'onerror',
'onfocus',
'onload',
'onresize',
'onunload',
'open',
'opener',
'opera',
'outerHeight',
'outerWidth',
'pageXOffset',
'pageYOffset',
'parent',
'print',
'removeEventListener',
'resizeBy',
'resizeTo',
'screen',
'screenLeft',
'screenTop',
'screenX',
'screenY',
'scroll',
'scrollbars',
'scrollBy',
'scrollTo',
'scrollX',
'scrollY',
'self',
'status',
'statusbar',
'stop',
'toolbar',
'top'
],
'no-shadow' : 'error',
'no-shadow-restricted-names' : 'error',
'no-undef' : [
'error',
{
typeof : false
}
],
'no-undef-init' : 'error',
'no-undefined' : 'off',
'no-unused-vars' : [
'error',
{
vars : 'all',
args : 'after-used',
ignoreRestSiblings : true
},
],
'no-use-before-define' : 'error',
'array-bracket-newline' : [ 'error', {
'multiline' : true
}],
'array-bracket-spacing' : [ 'error', 'always' ],
'array-element-newline' : [ 'error', {
'multiline' : true
}],
'block-spacing' : 'error',
'brace-style' : [ 'error', '1tbs' ],
'camelcase' : [
'error',
{
properties : 'never'
}
],
'capitalized-comments' : 'error',
'comma-dangle' : 'error',
'comma-spacing' : [ 'error', {
'before' : false,
'after' : true
}],
'comma-style' : 'error',
'computed-property-spacing' : [ 'error', 'always' ],
'consistent-this' : [ 'off', 'that' ],
'eol-last' : 'error',
'func-call-spacing' : 'error',
'func-name-matching' : [
'error',
{
considerPropertyDescriptor : true
}
],
'func-names': [ 'error', 'as-needed' ],
'func-style' : 'error',
'function-paren-newline' : 'error',
'id-blacklist' : 'off',
'id-length' : [
'off',
{
min : 2,
max : Infinity,
properties : 'always',
exceptions : []
}
],
'id-match' : [
'off',
'^[a-z]+([A-Z][a-z]+)*$',
{
properties : true,
onlyDeclarations : false
}
],
'implicit-arrow-linebreak' : 'error',
'indent' : [ 'error', 'tab' ],
'jsx-quotes' : [ 'off', 'prefer-double' ],
'key-spacing' : [ 'error', {
'beforeColon' : true,
'afterColon' : true
}],
'keyword-spacing' : 'error',
'line-comment-position' : [
'off',
{
position : 'above'
}
],
'linebreak-style' : [ 'off', 'unix' ],
'lines-around-comment' : [
'off',
{
beforeBlockComment : true,
afterBlockComment : true,
beforeLineComment : true,
afterLineComment : true,
allowBlockStart : true,
allowBlockEnd : true,
allowObjectStart : true,
allowObjectEnd : true,
allowArrayStart : true,
allowArrayEnd : true,
allowClassStart : true,
allowClassEnd : true,
applyDefaultIgnorePatterns : true,
ignorePattern : ''
}
],
'lines-between-class-members' : [
'error',
'always',
{
exceptAfterSingleLine : false
}
],
'max-depth' : [ 'error', {
'max' : 5
}],
'max-len' : [ 'error', 200 ],
'max-lines' : [ 'error', {
'max' : 1500
}],
'max-lines-per-function': [
'error',
{
max: 50,
skipBlankLines: true,
skipComments: true,
IIFEs: false,
},
],
'max-nested-callbacks' : 'error',
'max-params' : [ 'error', {
'max' : 5
}],
'max-statements' : 'off',
'max-statements-per-line' : 'error',
'multiline-comment-style' : [ 'error', 'starred-block' ],
'multiline-ternary' : [ 'error', 'never' ],
'new-cap' : 'error',
'new-parens' : 'error',
'newline-per-chained-call' : 'error',
'no-array-constructor' : 'error',
'no-bitwise' : 'error',
'no-continue' : 'error',
'no-lonely-if' : 'error',
'no-mixed-operators' : 'error',
'no-mixed-spaces-and-tabs' : 'error',
'no-multi-assign' : 'error',
'no-multiple-empty-lines' : 'error',
'no-negated-condition' : 'error',
'no-nested-ternary' : 'error',
'no-new-object' : 'error',
'no-plusplus' : [ 'error', {
'allowForLoopAfterthoughts' : true
}],
'no-restricted-syntax' : [
'error',
{
selector: 'ForInStatement',
message: 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.'
},
{
selector: 'ForOfStatement',
message: 'iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favour of array iterations.'
},
{
selector: 'LabeledStatement',
message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.'
},
{
selector: 'WithStatement',
message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.'
}
],
'no-tabs' : 'off',
'no-ternary' : 'off',
'no-trailing-spaces' : 'error',
'no-underscore-dangle' : 'error',
'no-unneeded-ternary' : 'error',
'no-whitespace-before-property' : 'error',
'nonblock-statement-body-position' : 'error',
'object-curly-newline' :'error',
'object-curly-spacing' : [ 'error', 'always' ],
'object-property-newline' : 'error',
'one-var' : 'off',
'one-var-declaration-per-line' : 'error',
'operator-assignment' : 'error',
'operator-linebreak' : 'error',
'padded-blocks' : [ 'error', 'never' ],
'padding-line-between-statements' : 'error',
'prefer-object-spread' : 'error',
'quote-props' : [
'error',
'as-needed',
{
keywords: false,
unnecessary: true,
numbers: false
}
],
'quotes' : [ 'error', 'single' ],
'semi' : 'error',
'semi-spacing' : 'error',
'semi-style' : 'error',
'sort-keys' : [
'off',
'asc',
{
caseSensitive : false,
natural : true
}
],
'sort-vars' : [
'off',
{
ignoreCase : false
}
],
'space-before-blocks' : 'error',
'space-before-function-paren' : [ 'error', 'never' ],
'space-in-parens' : [ 'error', 'always', {
'exceptions' : [ '{}' ]
}],
'space-infix-ops' : [ 'error', {
'int32Hint' : false
}],
'space-unary-ops' : 'error',
'spaced-comment' : 'error',
'switch-colon-spacing' : 'error',
'template-tag-spacing' : 'error',
'unicode-bom' : 'error',
'wrap-regex' : 'error'
}
}
For more information about the ESLint rules used see the ESLint documentation.
Rules currently under review
The rule set is under review and will be updated regularly to improve the consistency and quality of the code being produced. The following are a list of rules that are under review for change/addition:
- max-len
- one-var
- padding-line-between-statements
The code styling rules will also be under review.