Skip to content

Commit

Permalink
Merge pull request #25 from davidtheclark/strict-default
Browse files Browse the repository at this point in the history
Make strict mode default and clean up test files; fixes #24 and #23
  • Loading branch information
davidtheclark committed Mar 26, 2015
2 parents f724516 + 96ae59b commit 4cb6db8
Show file tree
Hide file tree
Showing 25 changed files with 182 additions and 208 deletions.
29 changes: 13 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,15 @@ npm install postcss-bem-linter

**Default mode**:

* Only allow selectors that *begin* with a selector sequence matching the defined convention
(ignoring sequences are combinators).
* Only allow selectors sequences that match the defined convention.
* Only allow custom-property names that *begin* with the defined `ComponentName`.
* The `:root` selector can only contain custom-properties.
* The `:root` cannot be combined with other selectors.

**Strict mode**:
**Weak mode**:

* All the tests in "default mode".
* Disallow selector sequences *after combinators* that do not match the
defined convention. (The convention for sequences after combinators can be the same as
or different from that for initial sequences.)
* While *initial* selector sequences (before combinators) must match the defined convention,
sequences *after* combinators are not held to any standard.

## Use

Expand Down Expand Up @@ -86,11 +83,11 @@ You can define a custom pattern by passing an object with the following properti
component name and return a regular expression. `initial` returns a description of valid
initial selector sequences — those occurring at the beginning of a selector, before any
combinators. `combined` returns a description of valid selector sequences allowed *after* combinators.
Two things to note: In non-strict mode, *any* combined sequences are accepted.
And if you do not specify a combined pattern, in strict mode it is assumed that combined
Two things to note: If you do not specify a combined pattern, it is assumed that combined
sequences must match the same pattern as initial sequences.
And in weak mode, *any* combined sequences are accepted.
- `utilities`: A regular expression describing valid utility selectors. This will be use
if the stylesheet uses `/** @define utilities */`, as explained below.
if the stylesheet uses `/** @define utilities */`, as explained below.

So you might call the plugin in any of the following ways:

Expand Down Expand Up @@ -137,8 +134,8 @@ are defining either a named component or utilities, using either
`/** @define ComponentName */` or `/** @define utilities */` in the first line
of the file.

Strict mode is turned on by adding `; use strict` to this definition,
e.g. `/** @define ComponentName; use strict */`.
Weak mode is turned on by adding `; weak` to this definition,
e.g. `/** @define ComponentName; weak */`.

```css
/** @define MyComponent */
Expand All @@ -149,21 +146,21 @@ e.g. `/** @define ComponentName; use strict */`.

.MyComponent {}

.MyComponent .other {}
.MyComponent-other {}
```

Strict mode:
Weak mode:

```css
/** @define MyComponent; use strict */
/** @define MyComponent; weak */

:root {
--MyComponent-property: value;
}

.MyComponent {}

.MyComponent-other {}
.MyComponent .other {}
```

Utilities:
Expand Down
6 changes: 3 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var presetPatterns = require('./lib/preset-patterns');

module.exports = conformance;

var RE_DIRECTIVE = /\*\s*@define ([-_a-zA-Z0-9]+)\s*(?:;\s*(use strict))?\s*/;
var RE_DIRECTIVE = /\*\s*@define ([-_a-zA-Z0-9]+)\s*(?:;\s*(weak))?\s*/;
var UTILITIES_IDENT = 'utilities';

/**
Expand Down Expand Up @@ -56,14 +56,14 @@ function conformance(patterns, opts) {
);
}

var isStrict = initialComment.match(RE_DIRECTIVE)[2] === 'use strict';
var weakMode = initialComment.match(RE_DIRECTIVE)[2] === 'weak';

validateRules(styles);
if (isUtilities) {
validateUtilities(styles, patterns.utilities);
} else {
validateSelectors(
styles, defined, isStrict, patterns.selectors, opts
styles, defined, weakMode, patterns.selectors, opts
);
}
validateCustomProperties(styles, defined);
Expand Down
18 changes: 8 additions & 10 deletions lib/is-valid-selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ module.exports = isValidSelector;
/**
* @param {String} selector - The selector to test
* @param {String} componentName - The component's name
* @param {Boolean} isStrictMode - Whether to use strict mode
* @param {Boolean} weakMode
* @param {String|SelectorPattern} [pattern="suit"] - Either a string
* identifying a pre-defined SelectorPattern to use, or a custom
* SelectorPattern, as described above
* @param {Object} [opts] - Options to pass to the pattern functions
* @returns {Boolean}
*/
function isValidSelector(selector, componentName, isStrictMode, pattern, opts) {
function isValidSelector(selector, componentName, weakMode, pattern, opts) {

// Don't bother with :root
if (selector === ':root') { return true; }
Expand All @@ -58,16 +58,14 @@ function isValidSelector(selector, componentName, isStrictMode, pattern, opts) {
// Error if an acceptable initialPattern does not begin the selector
if (!initialPattern.test(sequences[0])) { return false; }

// In strict mode, error if combined simple selectors do not match the
// Unless in weak mode, error if combined simple selectors do not match the
// combinedPattern
if (isStrictMode) {
return sequences.slice(1).every(function (combinedSequence) {
return initialPattern.test(combinedSequence) ||
combinedPattern.test(combinedSequence);
});
}
if (weakMode) { return true; }

return true;
return sequences.slice(1).every(function (combinedSequence) {
return initialPattern.test(combinedSequence) ||
combinedPattern.test(combinedSequence);
});
}

/**
Expand Down
6 changes: 3 additions & 3 deletions lib/validate-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ module.exports = validateSelectors;
/**
* @param {Object} styles
* @param {String} componentName
* @param {Boolean} strict
* @param {Boolean} weakMode
* @param {Object} pattern
* @param {Object} [opts]
*/
function validateSelectors(styles, componentName, strict, pattern, opts) {
function validateSelectors(styles, componentName, weakMode, pattern, opts) {
styles.eachRule(function (rule) {
if (rule.parent && rule.parent.name == 'keyframes') {
return;
Expand All @@ -28,7 +28,7 @@ function validateSelectors(styles, componentName, strict, pattern, opts) {

selectors.forEach(function (selector) {
// selectors must start with the componentName class, or be `:root`
if (!isValid(selector, componentName, strict, pattern, opts)) {
if (!isValid(selector, componentName, weakMode, pattern, opts)) {
throw rule.error('Invalid selector "' + selector + '". ');
}
});
Expand Down
6 changes: 3 additions & 3 deletions test/bem-pattern.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ var util = require('./test-util');
var assertFailure = util.assertFailure;

describe('using BEM pattern', function () {
it('accepts valid selectors in strict mode', function () {
util.assertSuccess(util.fixture('strict-bem-valid'), 'bem');
it('accepts valid selectors', function () {
util.assertSuccess(util.fixture('bem-valid'), 'bem');
});

describe('when given invalid selectors', function () {
var s = util.selectorTester('/** @define block; use strict */');
var s = util.selectorTester('/** @define block */');

// mirroring tests from
// https://github.com/bem/bem-naming/blob/master/test/original/validate.test.js
Expand Down
5 changes: 0 additions & 5 deletions test/fixtures/all-false-match.css

This file was deleted.

3 changes: 0 additions & 3 deletions test/fixtures/all-ignore.css

This file was deleted.

5 changes: 0 additions & 5 deletions test/fixtures/all-invalid-root-property.css

This file was deleted.

6 changes: 0 additions & 6 deletions test/fixtures/all-invalid-root-selector.css

This file was deleted.

5 changes: 0 additions & 5 deletions test/fixtures/all-invalid-root-vars.css

This file was deleted.

5 changes: 0 additions & 5 deletions test/fixtures/all-invalid-selector-component.css

This file was deleted.

7 changes: 0 additions & 7 deletions test/fixtures/all-invalid-selector-in-media-query.css

This file was deleted.

5 changes: 0 additions & 5 deletions test/fixtures/all-invalid-selector-tag.css

This file was deleted.

7 changes: 0 additions & 7 deletions test/fixtures/all-valid-selector-in-media-query.css

This file was deleted.

File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@
.foobar-Foo_foo
.foobar-Foo_foo:hover {}
.foobar-Foo_foo::after {}

@media all {
.f-Foo {}
}
File renamed without changes.
5 changes: 0 additions & 5 deletions test/fixtures/strict-invalid-selector.css

This file was deleted.

21 changes: 0 additions & 21 deletions test/fixtures/strict-valid-rules.css

This file was deleted.

48 changes: 48 additions & 0 deletions test/fixtures/suit-valid.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/** @define StrictValidRules */

.StrictValidRules {
color: green;
}

.StrictValidRules:focus {
color: green;
}

.StrictValidRules.is-active {
color: green;
}

.StrictValidRules .StrictValidRules--modifier {
color: green;
}

.StrictValidRules .StrictValidRules--modifier::after {
color: green;
}

.StrictValidRules {
color: green;
}

.StrictValidRules-child,
.StrictValidRules-child2 {
color: green;
}

.StrictValidRules-child:first-child:hover,
.StrictValidRules-child2:nth-child(3)::before {
color: green;
}

.StrictValidRules.is-active {
color: green;
}

@keyframes grow {
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}
32 changes: 0 additions & 32 deletions test/fixtures/valid-rules.css

This file was deleted.

36 changes: 36 additions & 0 deletions test/property-validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
var util = require('./test-util');
var assertSuccess = util.assertSuccess;
var assertFailure = util.assertFailure;
var fixture = util.fixture;

describe('property validation', function () {
it(
'accepts custom properties that begin with the component name',
function () {
assertSuccess(fixture('properties-valid'));
}
);

var invDef = '/** @define InvalidRootVars */';

it('accepts an empty root', function () {
assertSuccess(invDef + ':root {}');
});

it(
'rejects custom properties that do not being with the component name',
function () {
assertFailure(
invDef + ':root { --invalid-InvalidRootVars-color: green; }'
);
}
);

it('rejects invalid root properties', function () {
assertFailure(invDef + ':root { color: green; }');
});

it('rejects root selectors grouped with other selectors', function () {
assertFailure(invDef + ':root, .InvalidRootSelector {}');
});
});

0 comments on commit 4cb6db8

Please sign in to comment.