Socket
Socket
Sign inDemoInstall

postcss-bem-linter

Package Overview
Dependencies
Maintainers
3
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

postcss-bem-linter - npm Package Compare versions

Comparing version 3.1.0 to 3.2.0

64

CHANGELOG.md

@@ -1,8 +0,16 @@

=== HEAD
### HEAD
=== 3.1.0 (October 11, 2017)
### 3.2.0 (January 07, 2019)
* Fix issue with with multiple components in same file
[#138](https://github.com/postcss/postcss-bem-linter/issues/138)
* Allow double dash modifier [#136](https://github.com/postcss/postcss-bem-linter/pull/136)
* Move to Prettier
* Switch to Yarn
### 3.1.0 (October 11, 2017)
* Allow both `/**` and `/*` for module definitions [#125](https://github.com/postcss/postcss-bem-linter/pull/125)
=== 3.0.0 (July 21, 2017)
### 3.0.0 (July 21, 2017)

@@ -16,27 +24,27 @@ * Update PostCSS to `^6.0.6` - Drops support for Node 0.12

=== 2.7.1 (June 06, 2017)
### 2.7.1 (June 06, 2017)
* Fix issue with nested rules when using LESS syntax [#114](https://github.com/postcss/postcss-bem-linter/pull/114)
=== 2.7.0 (February 28, 2017)
### 2.7.0 (February 28, 2017)
* Add implicitComponents and implicitUtilities option ([#93](https://github.com/postcss/postcss-bem-linter/pull/93))
=== 2.6.0 (September 26, 2016)
### 2.6.0 (September 26, 2016)
* Allow component selector patterns without `{componentName}` interpolation.
=== 2.5.1 (May 1, 2016)
### 2.5.1 (May 1, 2016)
* Add support for nested selectors in utility stylesheets.
=== 2.5.0 (April 27, 2016)
### 2.5.0 (April 27, 2016)
* Add support for nested selectors.
=== 2.4.1 (March 28, 2016)
### 2.4.1 (March 28, 2016)
* Fix preset `componentName` patterns.
=== 2.4.0 (March 3, 2016)
### 2.4.0 (March 3, 2016)

@@ -46,19 +54,19 @@ * Allow chaining modifier classes in SUIT pattern.

=== 2.3.3 (February 15, 2016)
### 2.3.3 (February 15, 2016)
* Skip nodes that do not have `source` properties.
=== 2.3.2 (February 14, 2016)
### 2.3.2 (February 14, 2016)
* Prevent failure if a PostCSS node lacks a `source` property.
=== 2.3.1 (February 9, 2016)
### 2.3.1 (February 9, 2016)
* Ensure that `@keyframes` selectors are always skipped.
=== 2.3.0 (November 24, 2015)
### 2.3.0 (November 24, 2015)
* Add ability to ignore custom properties via `postcss-bem-linter: ignore` comments and the `ignoreCustomProperties` pattern.
=== 2.2.0 (November 15, 2015)
### 2.2.0 (November 15, 2015)

@@ -69,7 +77,7 @@ * Allow attribute selectors in BEM preset pattern.

=== 2.1.0 (October 31, 2015)
### 2.1.0 (October 31, 2015)
* Support string patterns for everything: `componentName`, `componentSelectors` (with one description, and with `initial` and `combined`), `utilitySelectors`, and `ignoreSelectors` (with a single value or an array).
=== 2.0.0 (October 17, 2015)
### 2.0.0 (October 17, 2015)

@@ -79,7 +87,7 @@ * Add namespace option to BEM preset pattern.

=== 1.2.0 (October 12, 2015)
### 1.2.0 (October 12, 2015)
* Support array of patterns for `ignoreSelectors`.
=== 1.1.0 (September 18, 2015)
### 1.1.0 (September 18, 2015)

@@ -91,11 +99,11 @@ * Support selective overriding of a chosen preset's patterns.

=== 1.0.1 (August 30, 2015)
### 1.0.1 (August 30, 2015)
* Use PostCSS's improved warning API to provide more precise locations.
=== 1.0.0 (August 26, 2015)
### 1.0.0 (August 26, 2015)
* Upgrade to PostCSS 5.
=== 0.6.0 (August 8, 2015)
### 0.6.0 (August 8, 2015)

@@ -106,3 +114,3 @@ * Support multiple definitions per file.

=== 0.5.0 (August 5, 2015)
### 0.5.0 (August 5, 2015)

@@ -112,7 +120,7 @@ * Add alternate signature for designating preset and preset options.

=== 0.4.0 (June 23, 2015)
### 0.4.0 (June 23, 2015)
* Support `/* postcss-bem-linter: ignore */` comments.
=== 0.3.0 (May 23, 2015)
### 0.3.0 (May 23, 2015)

@@ -127,13 +135,13 @@ * Support BEM format and custom formats.

=== 0.2.0 (January 24, 2015)
### 0.2.0 (January 24, 2015)
* Use postcss 4.0.x API.
=== 0.1.1 (November 22, 2014)
### 0.1.1 (November 22, 2014)
* Skip `@keyframes` rules when validating selectors.
=== 0.1.0 (October 2, 2014)
### 0.1.0 (October 2, 2014)
* Initial release. Ported from rework-suit-conformance.
e1iv

@@ -17,4 +17,3 @@ 'use strict';

const END_DIRECTIVE = new RegExp(
'(?:\\*\\s*@end\\s*)|' +
'(?:\\s*postcss-bem-linter: end)\\s*'
'(?:\\*\\s*@end\\s*)|' + '(?:\\s*postcss-bem-linter: end)\\s*'
);

@@ -38,126 +37,148 @@ const UTILITIES_IDENT = 'utilities';

*/
module.exports = postcss.plugin('postcss-bem-linter', (primaryOptions, secondaryOptions) => {
const config = generateConfig(primaryOptions, secondaryOptions);
const patterns = config.patterns;
module.exports = postcss.plugin(
'postcss-bem-linter',
(primaryOptions, secondaryOptions) => {
const config = generateConfig(primaryOptions, secondaryOptions);
const patterns = config.patterns;
return (root, result) => {
const ranges = findRanges(root);
return (root, result) => {
const ranges = findRanges(root);
root.walkRules((rule) => {
if (rule.parent && rule.parent.name === 'keyframes') return;
if (!rule.source) return;
root.walkRules(rule => {
if (rule.parent && rule.parent.name === 'keyframes') return;
if (!rule.source) return;
const ruleStartLine = rule.source.start.line;
ranges.forEach((range) => {
if (ruleStartLine < range.start) return;
if (range.end && ruleStartLine > range.end) return;
checkRule(rule, range);
const ruleStartLine = rule.source.start.line;
ranges.forEach(range => {
if (ruleStartLine < range.start) return;
if (range.end && ruleStartLine > range.end) return;
checkRule(rule, range);
});
});
});
function checkRule(rule, range) {
if (range.defined === UTILITIES_IDENT) {
if (!patterns.utilitySelectors) {
function checkRule(rule, range) {
if (range.defined === UTILITIES_IDENT) {
if (!patterns.utilitySelectors) {
throw new Error(
'You tried to `@define utilities` but have not provided ' +
'a `utilitySelectors` pattern'
);
}
validateUtilities({
rule,
utilityPattern: toRegexp(patterns.utilitySelectors),
ignorePattern: toRegexp(patterns.ignoreSelectors),
result,
});
return;
}
if (!patterns.componentSelectors) {
throw new Error(
'You tried to `@define utilities` but have not provided ' +
'a `utilitySelectors` pattern'
'You tried to `@define` a component but have not provided ' +
'a `componentSelectors` pattern'
);
}
validateUtilities({
validateCustomProperties({
rule,
utilityPattern: toRegexp(patterns.utilitySelectors),
componentName: range.defined,
result,
ignorePattern: toRegexp(patterns.ignoreCustomProperties),
});
validateSelectors({
rule,
componentName: range.defined,
weakMode: range.weakMode,
selectorPattern: patterns.componentSelectors,
selectorPatternOptions: config.presetOptions,
ignorePattern: toRegexp(patterns.ignoreSelectors),
result,
});
return;
}
if (!patterns.componentSelectors) {
throw new Error(
'You tried to `@define` a component but have not provided ' +
'a `componentSelectors` pattern'
);
}
validateCustomProperties({
rule,
componentName: range.defined,
result,
ignorePattern: toRegexp(patterns.ignoreCustomProperties),
});
validateSelectors({
rule,
componentName: range.defined,
weakMode: range.weakMode,
selectorPattern: patterns.componentSelectors,
selectorPatternOptions: config.presetOptions,
ignorePattern: toRegexp(patterns.ignoreSelectors),
result,
});
}
function findRanges(root) {
const ranges = [];
function findRanges(root) {
const ranges = [];
if (root.source && root.source.input && root.source.input.file) {
const filename = root.source.input.file;
if (
checkImplicit.isImplicitUtilities(
config.implicitUtilities,
filename
)
) {
ranges.push({
defined: 'utilities',
start: 0,
weakMode: false,
});
} else if (
checkImplicit.isImplicitComponent(
config.implicitComponents,
filename
)
) {
let defined = stripUnderscore(
path.basename(filename).split('.')[0]
);
if (defined === 'index') {
defined = path.basename(path.join(filename, '..'));
}
if (root.source && root.source.input && root.source.input.file) {
const filename = root.source.input.file;
if (checkImplicit.isImplicitUtilities(config.implicitUtilities, filename)) {
ranges.push({
defined: 'utilities',
start: 0,
weakMode: false,
});
} else if (checkImplicit.isImplicitComponent(config.implicitComponents, filename)) {
let defined = stripUnderscore(path.basename(filename).split('.')[0]);
if (defined === 'index') {
defined = path.basename(path.join(filename, '..'));
if (
defined !== UTILITIES_IDENT &&
!toRegexp(config.componentNamePattern).test(defined)
) {
result.warn(
`Invalid component name from implicit conversion from filename ${filename}`
);
}
ranges.push({
defined,
start: 0,
weakMode: false,
});
}
}
if (defined !== UTILITIES_IDENT && !toRegexp(config.componentNamePattern).test(defined)) {
result.warn(
`Invalid component name from implicit conversion from filename ${filename}`
);
root.walkComments(comment => {
const commentStartLine = comment.source
? comment.source.start.line
: null;
if (!commentStartLine) return;
if (END_DIRECTIVE.test(comment.text)) {
endCurrentRange(commentStartLine);
return;
}
const directiveMatch = comment.text.match(DEFINE_DIRECTIVE);
if (!directiveMatch) return;
const defined = (directiveMatch[1] || directiveMatch[3]).trim();
if (
defined !== UTILITIES_IDENT &&
!toRegexp(config.componentNamePattern).test(defined)
) {
result.warn(`Invalid component name in definition /*${comment}*/`, {
node: comment,
});
}
endCurrentRange(commentStartLine);
ranges.push({
defined,
start: 0,
weakMode: false,
start: commentStartLine,
weakMode: directiveMatch[2] === WEAK_IDENT,
});
}
}
});
return ranges;
root.walkComments((comment) => {
const commentStartLine = (comment.source) ? comment.source.start.line : null;
if (!commentStartLine) return;
if (END_DIRECTIVE.test(comment.text)) {
endCurrentRange(commentStartLine);
return;
function endCurrentRange(line) {
if (!ranges.length) return;
const lastRange = ranges[ranges.length - 1];
if (lastRange.end) return;
lastRange.end = line;
}
const directiveMatch = comment.text.match(DEFINE_DIRECTIVE);
if (!directiveMatch) return;
const defined = (directiveMatch[1] || directiveMatch[3]).trim();
if (defined !== UTILITIES_IDENT && !toRegexp(config.componentNamePattern).test(defined)) {
result.warn(
`Invalid component name in definition /*${comment}*/`,
{node: comment}
);
}
endCurrentRange(commentStartLine);
ranges.push({
defined,
start: commentStartLine,
weakMode: directiveMatch[2] === WEAK_IDENT,
});
});
return ranges;
function endCurrentRange(line) {
if (!ranges.length) return;
const lastRange = ranges[ranges.length - 1];
if (lastRange.end) return;
lastRange.end = line;
}
}
};
});
};
}
);

@@ -51,6 +51,12 @@ 'use strict';

const implicitComponents =
getImplicitDefineValue('implicitComponents', primaryOptions, secondaryOptions);
const implicitUtilities =
getImplicitDefineValue('implicitUtilities', primaryOptions, secondaryOptions);
const implicitComponents = getImplicitDefineValue(
'implicitComponents',
primaryOptions,
secondaryOptions
);
const implicitUtilities = getImplicitDefineValue(
'implicitUtilities',
primaryOptions,
secondaryOptions
);

@@ -57,0 +63,0 @@ return {

@@ -18,7 +18,10 @@ 'use strict';

function hasOnlyExtends(node) {
return hasChildNodes(node) && node.every(child => child.type === 'atrule' && child.name === 'extend');
return (
hasChildNodes(node) &&
node.every(child => child.type === 'atrule' && child.name === 'extend')
);
}
function getComponentRootRule(node) {
while (node.root() !== node) {
while (node.root() !== node.parent) {
node = node.parent;

@@ -31,6 +34,8 @@ }

let selectors = [];
parent.walkRules((node) => {
parent.walkRules(node => {
// Only unwrap as far as the current rule being linted
if (node.selector !== rule.selector) {return;}
node.selectors.forEach((selector) => {
if (node.selector !== rule.selector) {
return;
}
node.selectors.forEach(selector => {
selectors = selectors.concat(resolveNestedSelector(selector, node));

@@ -37,0 +42,0 @@ });

@@ -16,7 +16,5 @@ 'use strict';

*/
module.exports = (selector) => {
module.exports = selector => {
const withoutPseudos = selector.split(':')[0];
return withoutPseudos
.split(/[\s>+~]/)
.filter(s => s !== '');
return withoutPseudos.split(/[\s>+~]/).filter(s => s !== '');
};

@@ -22,3 +22,6 @@ 'use strict';

function suitSelector(componentName, presetOptions) {
const ns = (presetOptions && presetOptions.namespace) ? `${presetOptions.namespace}-` : '';
const ns =
presetOptions && presetOptions.namespace
? `${presetOptions.namespace}-`
: '';
const WORD = '[a-z0-9][a-zA-Z0-9]*';

@@ -29,6 +32,3 @@ const descendant = `(?:-${WORD})?`;

const state = `(?:\\.is-${WORD})*`;
const body = descendant +
modifier +
attribute +
state;
const body = descendant + modifier + attribute + state;
return new RegExp(`^\\.${ns}${componentName}\\b${body}$`);

@@ -44,8 +44,11 @@ }

function bemSelector(block, presetOptions) {
const ns = (presetOptions && presetOptions.namespace) ? `${presetOptions.namespace}-` : '';
const ns =
presetOptions && presetOptions.namespace
? `${presetOptions.namespace}-`
: '';
const WORD = '[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*';
const element = `(?:__${WORD})?`;
const modifier = `(?:_${WORD}){0,2}`;
const modifier = `(?:(?:_|--)${WORD}){0,2}`;
const attribute = '(?:\\[.+\\])?';
return new RegExp(`^\\.${ns}${block}${element}${modifier}${attribute}$`);
}

@@ -9,6 +9,7 @@ 'use strict';

if (
previousNode
&& previousNode.type === 'comment'
&& previousNode.text === constants.IGNORE_COMMENT
) return true;
previousNode &&
previousNode.type === 'comment' &&
previousNode.text === constants.IGNORE_COMMENT
)
return true;

@@ -15,0 +16,0 @@ if (!patterns) return false;

@@ -5,9 +5,9 @@ 'use strict';

module.exports = (rule) => {
module.exports = rule => {
const previousNode = rule.prev();
return (
previousNode
&& previousNode.type === 'comment'
&& previousNode.text === constants.IGNORE_COMMENT
previousNode &&
previousNode.type === 'comment' &&
previousNode.text === constants.IGNORE_COMMENT
);
};
'use strict';
module.exports = (source) => {
module.exports = source => {
if (typeof source === 'string') {
const splitSource = source.split('{componentName}');
return (componentName) => {
return componentName => {
try {
return new RegExp(splitSource.length > 1 ? splitSource[0] + componentName + splitSource[1] : splitSource[0]);
return new RegExp(
splitSource.length > 1
? splitSource[0] + componentName + splitSource[1]
: splitSource[0]
);
} catch (e) {
throw new Error(`"${source}" does not produce a valid regular expression`);
throw new Error(
`"${source}" does not produce a valid regular expression`
);
}

@@ -12,0 +18,0 @@ };

'use strict';
module.exports = (source) => {
module.exports = source => {
if (Array.isArray(source)) {

@@ -5,0 +5,0 @@ return source.map(regexpify);

@@ -12,4 +12,4 @@ 'use strict';

*/
module.exports = (config) => {
config.rule.walkDecls((declaration) => {
module.exports = config => {
config.rule.walkDecls(declaration => {
const property = declaration.prop;

@@ -19,3 +19,4 @@

if (shouldIgnoreCustomProperty(property, declaration, config.ignorePattern)) return;
if (shouldIgnoreCustomProperty(property, declaration, config.ignorePattern))
return;

@@ -22,0 +23,0 @@ if (property.indexOf(`${config.componentName}-`) === 2) return;

@@ -19,15 +19,24 @@ 'use strict';

*/
module.exports = (config) => {
module.exports = config => {
if (shouldIgnoreRule(config.rule)) return;
const rule = config.rule;
const initialPattern = (config.selectorPattern.initial)
? toInterpoloatedRegexp(config.selectorPattern.initial)(config.componentName, config.selectorPatternOptions)
: toInterpoloatedRegexp(config.selectorPattern)(config.componentName, config.selectorPatternOptions);
const combinedPattern = (config.selectorPattern.combined)
? toInterpoloatedRegexp(config.selectorPattern.combined)(config.componentName, config.selectorPatternOptions)
const initialPattern = config.selectorPattern.initial
? toInterpoloatedRegexp(config.selectorPattern.initial)(
config.componentName,
config.selectorPatternOptions
)
: toInterpoloatedRegexp(config.selectorPattern)(
config.componentName,
config.selectorPatternOptions
);
const combinedPattern = config.selectorPattern.combined
? toInterpoloatedRegexp(config.selectorPattern.combined)(
config.componentName,
config.selectorPatternOptions
)
: toInterpoloatedRegexp(initialPattern);
const selectors = getSelectors(rule);
selectors.forEach((selector) => {
selectors.forEach(selector => {
// Don't bother with :root

@@ -41,13 +50,14 @@ if (selector === ':root') return;

sequence = allSequences[i];
if (config.ignorePattern && shouldIgnoreSelector(sequence, config.ignorePattern)) continue;
if (
config.ignorePattern &&
shouldIgnoreSelector(sequence, config.ignorePattern)
)
continue;
if (i === 0 && initialPattern.test(sequence)) continue;
if (i !== 0 && combinedPattern.test(sequence)) continue;
config.result.warn(
`Invalid component selector "${selector}"`,
{
node: rule,
word: selector,
}
);
config.result.warn(`Invalid component selector "${selector}"`, {
node: rule,
word: selector,
});
return;

@@ -54,0 +64,0 @@ }

@@ -15,18 +15,19 @@ 'use strict';

*/
module.exports = (config) => {
module.exports = config => {
if (shouldIgnoreRule(config.rule)) return;
const selectors = getSelectors(config.rule);
selectors.forEach((selector) => {
selectors.forEach(selector => {
const allSequences = listSequences(selector);
for (const sequence of allSequences) {
if (config.ignorePattern && shouldIgnoreSelector(sequence, config.ignorePattern)) continue;
if (
config.ignorePattern &&
shouldIgnoreSelector(sequence, config.ignorePattern)
)
continue;
if (config.utilityPattern.test(sequence)) continue;
config.result.warn(
`Invalid utility selector "${selector}"`,
{
node: config.rule,
word: selector,
}
);
config.result.warn(`Invalid utility selector "${selector}"`, {
node: config.rule,
word: selector,
});
return;

@@ -33,0 +34,0 @@ }

{
"name": "postcss-bem-linter",
"version": "3.1.0",
"version": "3.2.0",
"description": "A BEM linter for postcss",

@@ -15,8 +15,11 @@ "files": [

"devDependencies": {
"eslint": "^3.19.0",
"eslint-config-airbnb": "^15.0.2",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-prettier": "^2.6.0",
"eslint-plugin-react": "^7.1.0",
"jest": "^20.0.4"
"jest": "^22.4.3",
"prettier": "^1.11.1"
},

@@ -27,3 +30,3 @@ "scripts": {

"test": "jest",
"posttest": "npm run lint"
"posttest": "yarn run lint"
},

@@ -35,3 +38,4 @@ "jest": {

"test-util"
]
],
"testEnvironment": "node"
},

@@ -38,0 +42,0 @@ "license": "MIT",

@@ -21,3 +21,3 @@ # postcss-bem-linter

```
npm install postcss-bem-linter
npm install postcss-bem-linter --save-dev
```

@@ -111,3 +111,3 @@

```js
componentSelectors: function(componentName) {
componentSelectors(componentName) {
return new RegExp('^\\.ns-' + componentName + '(?:-[a-zA-Z]+)?$');

@@ -189,3 +189,3 @@ }

bemLinter('bem', { namespace: 'ydx' });
bemLinter({ present: 'bem', presetOptions: { namespace: 'ydx' }});
bemLinter({ preset: 'bem', presetOptions: { namespace: 'ydx' }});

@@ -202,3 +202,3 @@ // define a pattern for component names

bemLinter({
componentSelectors: function(componentName) {
componentSelectors(componentName) {
return new RegExp('^\\.' + componentName + '(?:-[a-z]+)?$');

@@ -215,6 +215,6 @@ }

componentSelectors: {
initial: function(componentName) {
initial(componentName) {
return new RegExp('^\\.' + componentName + '(?:-[a-z]+)?$');
},
combined: function(componentName) {
combined(componentName) {
return new RegExp('^\\.combined-' + componentName + '-[a-z]+$');

@@ -433,8 +433,8 @@ }

```js
var postcss = require('postcss');
var bemLinter = require('postcss-bem-linter');
var reporter = require('postcss-reporter');
const postcss = require('postcss');
const bemLinter = require('postcss-bem-linter');
const reporter = require('postcss-reporter');
files.forEach(function(file) {
var css = fs.readFileSync(file, 'utf-8');
files.forEach(file => {
const css = fs.readFileSync(file, 'utf-8');
postcss()

@@ -444,3 +444,3 @@ .use(bemLinter())

.process(css)
.then(function(result) { .. });
.then(result => { .. });
});

@@ -455,6 +455,6 @@ ```

Install the dependencies.
Install the dependencies. Requires [Yarn](https://yarnpkg.com/)
```
npm install
yarn
```

@@ -465,3 +465,3 @@

```
npm test
yarn test
```

@@ -472,3 +472,3 @@

```
npm start
yarn start
```
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc