From e5cf2460a10e8fdf4faa22e6154e1ff16711fae0 Mon Sep 17 00:00:00 2001 From: William Bowling Date: Fri, 5 Oct 2018 12:04:36 +1000 Subject: [PATCH 1/2] add a colonSpace rule to enfoce a space after a colon --- .stylintrc | 1 + src/checks/colonSpace.js | 49 ++++++++++++++++++++++ src/checks/index.js | 1 + src/core/config.js | 2 + test/test.js | 91 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+) create mode 100644 src/checks/colonSpace.js diff --git a/.stylintrc b/.stylintrc index ac020941..ba4f0e0f 100644 --- a/.stylintrc +++ b/.stylintrc @@ -3,6 +3,7 @@ "blocks": false, "brackets": false, "colons": "always", + "colonSpace": "always", "colors": { "expect": "always" }, diff --git a/src/checks/colonSpace.js b/src/checks/colonSpace.js new file mode 100644 index 00000000..41000d20 --- /dev/null +++ b/src/checks/colonSpace.js @@ -0,0 +1,49 @@ +'use strict' + +var validJSON = require( '../data/valid.json' ) +// we only want to check colons on properties/values +var ignoreRe = /( ^[&$=#>.]|\.[a-zA-Z]|#[a-zA-Z]| \+ | , | = | ~ | > | &| {|}|\(|if|for(?!\w)|else|return|@block|@media|@import|@extend|@require|,$)/m + +/** + * @description if set to always, enforces spaces after colons. if set to never, disallows spaces + * @param {string} [line] curr line being linted + * @param {string} [origLine] curr line before being stripped + * @returns {boolean} true if space missing, false if not + */ +var colonSpace = function( line, origLine ) { + if ( ignoreRe.test( origLine ) || this.state.context === 0 || origLine.indexOf( ':' ) === -1 ) { return } + + var hasSpace + var hasPseudo = false + var hasScope = false + var arr = this.splitAndStrip( new RegExp( /\s/ ), origLine ) + + if ( this.state.conf === 'always' || this.state.conf === 'never' ) { + // check for pseudo selector + hasPseudo = validJSON.pseudo.some( function( val ) { + return origLine.indexOf( val ) !== -1 + } ) + + // check for scope selector + hasScope = validJSON.scope.some( function( val ) { + return origLine.indexOf( val ) !== -1 + } ) + + if ( !hasPseudo && !hasScope ) { + hasSpace = arr.length > 1 && arr[0].indexOf( ':' ) === arr[0].length - 1 + } + } + + // if spaces should be follow commas, but there is no space on the line + if ( this.state.conf === 'always' && hasSpace === false ) { + this.msg( 'colons must be followed by a space for readability', origLine.indexOf( ':' ) ) + } + // if spaces should not be followed by a comma, but there are spaces anyway + else if ( this.state.conf === 'never' && hasSpace === true ) { + this.msg( 'spaces after colons are not allowed', origLine.indexOf( ':' ) ) + } + + return hasSpace +} + +module.exports = colonSpace diff --git a/src/checks/index.js b/src/checks/index.js index 6a11e24b..5ae7a612 100644 --- a/src/checks/index.js +++ b/src/checks/index.js @@ -6,6 +6,7 @@ var linterMethods = stampit().methods( { blocks: require( './blocks' ), brackets: require( './brackets' ), colons: require( './colons' ), + colonSpace: require( './colonSpace' ), colors: require( './colors' ), commaSpace: require( './commaSpace' ), commentSpace: require( './commentSpace' ), diff --git a/src/core/config.js b/src/core/config.js index a64a35bb..53f5b688 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -9,6 +9,8 @@ var config = { brackets: 'never', // enforce or disallow colons colons: 'always', + // check for space after colons + colonSpace: 'always', // check for hex colors used without variables colors: 'always', // check for spaces after commas (0, 0, 0, .18) diff --git a/test/test.js b/test/test.js index f2205a2a..052205d4 100644 --- a/test/test.js +++ b/test/test.js @@ -736,6 +736,97 @@ describe( 'Linter Style Checks: ', function() { } ) } ) + describe( 'colon space: prefer margin: 0 over margin:0', function() { + var colonSpaceTest = lint.colonSpace.bind( app ) + + beforeEach( function() { + app.state.conf = 'always' + } ) + + it( 'false if no space is found', function() { + app.state.context = 1 + assert.equal( false, colonSpaceTest( '', 'margin:0 auto' ) ) + assert.equal( false, colonSpaceTest( '', 'margin:0' ) ) + } ) + + + it( 'true if space is found', function() { + app.state.context = 1 + assert.equal( true, colonSpaceTest( '', 'margin: 0 auto' ) ) + assert.equal( true, colonSpaceTest( '', 'margin: 0' ) ) + assert.equal( true, colonSpaceTest( '', 'grid-template-areas: "icon label"' ) ) + } ) + + it( 'undefined if syntax or css selector', function() { + assert.equal( undefined, colonSpaceTest( '', '#id' ) ) + assert.equal( undefined, colonSpaceTest( '', '$.some-class' ) ) + assert.equal( undefined, colonSpaceTest( '', '> child selector' ) ) + assert.equal( undefined, colonSpaceTest( '', '.class-name' ) ) + assert.equal( undefined, colonSpaceTest( '', 'for ( 0..9 )' ) ) + assert.equal( undefined, colonSpaceTest( '', '@media $med' ) ) + assert.equal( undefined, colonSpaceTest( '', '@extend $med' ) ) + assert.equal( undefined, colonSpaceTest( '', '@extends $med' ) ) + assert.equal( undefined, colonSpaceTest( '', '@import _some-file' ) ) + assert.equal( undefined, colonSpaceTest( '', '.class-name, #id-name' ) ) + assert.equal( undefined, colonSpaceTest( '', '.class-name + #id-name' ) ) + assert.equal( undefined, colonSpaceTest( '', 'p ~ ul' ) ) + assert.equal( undefined, colonSpaceTest( '', 'p > ul' ) ) + assert.equal( undefined, colonSpaceTest( '', 'if ( $var == 50px )' ) ) + assert.equal( undefined, colonSpaceTest( '', 'hash = {' ) ) + assert.equal( undefined, colonSpaceTest( '', '}' ) ) + assert.equal( undefined, colonSpaceTest( '', '.class-name a' ) ) + assert.equal( undefined, colonSpaceTest( '', '&.class-name a' ) ) + assert.equal( undefined, colonSpaceTest( '', '&:active' ) ) + assert.equal( undefined, colonSpaceTest( '', 'return: $value' ) ) + assert.equal( undefined, colonSpaceTest( '', 'return $value' ) ) + assert.equal( undefined, colonSpaceTest( '', '@media screen and (max-width: 1183px)' ) ) + } ) + } ) + + describe( 'colon space: prefer margin:0 over margin: 0', function() { + var colonSpaceTest = lint.colonSpace.bind( app ) + + beforeEach( function() { + app.state.conf = 'never' + } ) + + it( 'true if no space is found', function() { + app.state.context = 1 + assert.equal( false, colonSpaceTest( '', 'margin:0 auto' ) ) + assert.equal( false, colonSpaceTest( '', 'margin:0' ) ) + } ) + + it( 'undefined if no colon', function() { + app.state.context = 1 + assert.equal( undefined, colonSpaceTest( '', 'margin 0 auto' ) ) + } ) + + it( 'undefined if syntax or css selector', function() { + assert.equal( undefined, colonSpaceTest( '', '#id' ) ) + assert.equal( undefined, colonSpaceTest( '', '$.some-class' ) ) + assert.equal( undefined, colonSpaceTest( '', '> child selector' ) ) + assert.equal( undefined, colonSpaceTest( '', '.class-name' ) ) + assert.equal( undefined, colonSpaceTest( '', 'for ( 0..9 )' ) ) + assert.equal( undefined, colonSpaceTest( '', '@media $med' ) ) + assert.equal( undefined, colonSpaceTest( '', '@extend $med' ) ) + assert.equal( undefined, colonSpaceTest( '', '@extends $med' ) ) + assert.equal( undefined, colonSpaceTest( '', '@import _some-file' ) ) + assert.equal( undefined, colonSpaceTest( '', '.class-name, #id-name' ) ) + assert.equal( undefined, colonSpaceTest( '', '.class-name + #id-name' ) ) + assert.equal( undefined, colonSpaceTest( '', 'p ~ ul' ) ) + assert.equal( undefined, colonSpaceTest( '', 'p > ul' ) ) + assert.equal( undefined, colonSpaceTest( '', 'if ( $var == 50px )' ) ) + assert.equal( undefined, colonSpaceTest( '', 'hash = {' ) ) + assert.equal( undefined, colonSpaceTest( '', '}' ) ) + assert.equal( undefined, colonSpaceTest( '', '.class-name a' ) ) + assert.equal( undefined, colonSpaceTest( '', '&.class-name a' ) ) + assert.equal( undefined, colonSpaceTest( '', '&:active' ) ) + assert.equal( undefined, colonSpaceTest( '', 'return: $value' ) ) + assert.equal( undefined, colonSpaceTest( '', 'return $value' ) ) + assert.equal( undefined, colonSpaceTest( '', '@media screen and (max-width: 1183px)' ) ) + } ) + } ) + describe( 'colors', function() { var colorsTest = lint.colors.bind( app ) From 681497f37bcb95b4985940aee146a30511113014 Mon Sep 17 00:00:00 2001 From: William Bowling Date: Fri, 5 Oct 2018 13:02:41 +1000 Subject: [PATCH 2/2] update readme for colonSpace --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 763fec6b..4b06d0eb 100644 --- a/README.md +++ b/README.md @@ -377,6 +377,12 @@ Example if 'always': prefer `margin: 0` over `margin 0` Example if 'never: prefer `margin 0` over `margin: 0` +### colonSpace ( default: 'always', 'always' || 'never' || false ) +Enforce or disallow spaces after colons + +Example if always: prefer `margin: 0` over `margin:0` + +Example if never: prefer `margin:0` over `margin: 0` ### colors ( default: 'always' || false ) When 'always', enforce variables when defining hex values