Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

bemlint

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bemlint - npm Package Compare versions

Comparing version 0.0.1 to 1.0.0

lib/cli-engine.js

330

lib/bemlint.js

@@ -10,245 +10,193 @@ /**

fs = require("fs"),
minify = require("jsonminify"),
debug = require("debug"),
glob = require("glob"),
shell = require("shelljs"),
bemNaming = require("bem-naming"),
lodash = require("lodash"),
EventEmitter = require("events").EventEmitter,
rules = require("./rules"),
attrClassRegex = /^(.*<.*?class="(.*?)".*)$/gim;
fs.existsSync = fs.existsSync || path.existsSync;
var defaultOptions = {
elem: '__',
mod: '_',
wordPattern: '[a-zA-Z0-9]+'
};
Array.prototype.islice = function(array, string) {
var index = array.indexOf(string);
return array.slice(0, index).concat(array.slice(index + 1));
}
/**
* Object that is responsible for verifying JavaScript text
* @name bemlint
*/
module.exports = (function() {
var api = Object.create(new EventEmitter()),
messages = [],
currentFilename = null,
sourceCode = null;
function BEMLintParser(options) {
options = Object.assign(Object.create(null), defaultOptions, options);
this.options = options;
bemNaming(options);
};
function runOnText (text, filePath) {
var match,
/**
* Resets the internal state of the object.
* @returns {void}
*/
api.reset = function() {
this.removeAllListeners();
messages = [];
while (match = attrClassRegex.exec(text)) {
var className = match[2],
lineNumber = getLineNumber(text, match[1]),
columnNumber = match[1].indexOf(match[2]),
message = getMessage(className);
if ( message ) {
messages.push({
line: lineNumber,
column: columnNumber,
message: message
});
}
}
var result = {
filePath: filePath,
messages: messages
sourceCode = null;
};
return result;
};
/**
* Verifies the text against the rules specified by the second argument.
* @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
* @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
* If this is not set, the filename will default to '<input>' in the rule context. If
* an object, then it has "filename", "saveState"
* @param {boolean} [saveState] Indicates if the state from the last run should be saved.
* Mostly useful for testing purposes.
* @returns {Object[]} The results as an array of messages or null if no messages.
*/
api.verify = function(textOrSourceCode, options) {
var text = (typeof textOrSourceCode === "string") ? textOrSourceCode : null;
BEMLintParser.prototype = {
currentFilename = options.filename;
constructor: BEMLintParser,
if (!options.saveState) {
this.reset();
}
executeOnFiles: function (files) {
var startTime = Date.now(),
results = [];
function runOnFile (file) {
var filePath = path.resolve(file);
debug("Processing " + filePath);
var text = fs.readFileSync(filePath, "utf8");
var result = runOnText(text, filePath);
results.push(result);
};
files.forEach(function(pattern) {
var file = path.resolve(pattern);
if (shell.test("-f", file)) {
runOnFile(fs.realpathSync(pattern), !shell.test("-d", file));
} else {
glob.sync(pattern, globOptions).forEach(function(globMatch) {
runOnFile(globMatch, false);
});
// only do this for text
if (text !== null) {
// there's no input, just exit here
if (text.trim().length === 0) {
return messages;
}
}
});
var messages = new Parser(text, options);
return {
results: results
};
return messages;
};
debug("Linting complete in: " + (Date.now() - startTime) + "ms");
}
};
return api;
}());
function getLineNumber(file, lineString) {
var limit = file.indexOf(lineString),
substring = file.substr(0, limit);
return substring.split("\n").length;
}
Array.prototype.isplice = function(array, index) {
Array.prototype.islice = function(array, string) {
var index = array.indexOf(string);
return array.slice(0, index).concat(array.slice(index + 1));
}
function Parser(text, options) {
function getMessage(string) {
var arrayClasses = string.split(/\s+/),
messages = [],
parser = {
valid: function() {
return bemNaming.validate(string) ? 'Valid BEM-naming notation' : 'Not valid BEM';
},
var match,
messages = [];
checkBlock: function(bemObj, classes, index) {
var message,
spliced = classes.slice();
/**
* Validates input in source with given rules
* @method validate
* @param {String} input
* @param {Array} source Array of classnames from html attribute
* @param {Integer} line line number
* @param {Integer} column column
* @param {Array} rulesToExclude List of rules to be excluded for validate
*/
function validate (input, source, line, column, rulesToExclude) {
var currentRules = lodash.assign({}, rules);
if ( spliced.length > 1 ) {
spliced = spliced.isplice(spliced, index);
if ( rulesToExclude ) {
for (var i in rules) {
if ( rulesToExclude.indexOf(i) >= 0 ) {
delete currentRules[i];
}
}
}
for (var i = spliced.length - 1; i >= 0; i--) {
var className = spliced[i],
currentbemObj = bemNaming.parse(className);
for (var i in currentRules) {
var message = rules[i](input, source);
if ( currentbemObj && currentbemObj.block == bemObj.block ) {
if ( bemNaming.isElem(className) ) {
message = 'Element of the same block in class';
break;
}
}
if ( message ) {
messages.push({
message: message,
line: line,
column: column,
rule: i
});
}
}
};
return message;
/**
* Checks --bp --bem-prefixes option specified
* @method checkPrefixes
* @param {String} string parsed from attr class name
* @return {Boolean}
*/
this.checkPrefixes = function(string) {
},
checkBlockMod: function(bemObj, classes, index) {
var message,
spliced = classes.slice();
if ( spliced.length > 1 ) {
spliced = spliced.isplice(spliced, index);
for (var i = spliced.length - 1; i >= 0; i--) {
var className = spliced[i],
currentbemObj = bemNaming.parse(className);
if ( currentbemObj && currentbemObj.block == bemObj.block ) {
if ( !bemNaming.isBlock(className) ) {
message = 'Using block_mod_val without Block';
} else {
message = '';
break;
}
} else {
message = 'Using block_mod_val without Block';
}
if ( options.bemPrefixes ) {
var valid = false;
for (var i = options.bemPrefixes.length - 1; i >= 0; i--) {
if ( new RegExp('^' + options.bemPrefixes[i]).test(string) ) {
valid = true;
break;
}
} else {
message = 'Using block__mod_val without Block';
}
return message;
return valid;
} else {
return true;
}
},
};
checkElement: function(bemObj, classes, index) {
var message,
spliced = classes.slice();
/**
* Validates every classname in attr with Rules
* @method validateString
* @param {String} classString whole `class` attr string
* @param {[type]} line line number
* @param {[type]} column column number
*/
this.validateString = function(classString, line, column) {
var arrayClasses = classString.split(/\s+/),
prevClass = '',
columnCounted = column,
excludedString = classString;
if ( spliced.length > 1 ) {
spliced = spliced.isplice(spliced, index);
for (var i = 0; i < arrayClasses.length; i++) {
var className = arrayClasses[i],
sliced = arrayClasses,
excludedString = excludedString.replace(className,'');
for (var i = spliced.length - 1; i >= 0; i--) {
var className = spliced[i],
currentbemObj = bemNaming.parse(className);
if ( currentbemObj ) {
if ( currentbemObj.block == bemObj.block ) {
if ( this.checkPrefixes(className) ) {
if ( bemObj.elem !== currentbemObj.elem ) {
message = 'Multiple Elements of the same Block';
}
if ( arrayClasses.length > 1 ) {
if ( bemObj.elem !== currentbemObj.elem ) {
message = 'Using elem__mod_val without Element';
}
sliced = arrayClasses.islice(arrayClasses, className);
if ( bemNaming.isBlock(currentbemObj) ) {
message = 'Element of the same block in class';
}
} else {
message = 'Using elem__mod_val without Element';
}
}
validate(className, sliced, line, columnCounted);
} else {
validate(className, sliced, line, columnCounted, ['same']);
}
} else {
if ( bemNaming.isElemMod(spliced[0])) {
message = 'Using elem__mod_val without Element';
}
}
return message;
},
columnCounted += className.length + excludedString.search(/\S/);
excludedString = excludedString.trim();
}
};
parse: function() {
var currentBlock = '';
for (var i = 0; i < arrayClasses.length; i++) {
var className = arrayClasses[i],
bemObj = bemNaming.parse(className);
while (match = attrClassRegex.exec(text)) {
var line = getLineNumber(text, match[1]),
classString = match[2],
column= match[1].indexOf(classString);
if ( bemObj && bemNaming.isBlock(className) ) {
messages[i] = parser.checkBlock(bemObj, arrayClasses, i);
}
this.validateString(classString, line, column + 1);
}
if ( bemObj && bemNaming.isBlockMod(className) ) {
messages[i] = parser.checkBlockMod(bemObj, arrayClasses, i);
}
if ( bemObj && bemObj.elem ) {
messages[i] = parser.checkElement(bemObj, arrayClasses, i);
}
return lodash.uniqWith(messages, lodash.isEqual);
};
}
return messages.join("");
},
};
return parser.parse();
function getLineNumber(file, lineString) {
var limit = file.indexOf(lineString),
substring = file.substr(0, limit);
return substring.split("\n").length;
}
module.exports = BEMLintParser;

@@ -22,3 +22,3 @@ /**

optionator = require("./options"),
BEMLintParser = require("./bemlint");
CLIEngine = require("./cli-engine");

@@ -121,3 +121,3 @@

parser = new BEMLintParser(currentOptions);
parser = new CLIEngine(currentOptions);

@@ -124,0 +124,0 @@ report = parser.executeOnFiles(files);

@@ -52,4 +52,12 @@ /**

{
option: "bem-prefixes",
alias: "bp",
type: "Array",
description: "Array of block names prefix to lint",
example: "['b-', 'l-', 'helper-']"
},
]
});
{
"name": "bemlint",
"version": "0.0.1",
"version": "1.0.0",
"description": "Linting module checks BEM-naming conventions in `class` attribute of the html files",

@@ -9,2 +9,6 @@ "main": "lib/bemlint.js",

},
"repository": {
"type": "git",
"url": "git://github.com/DesTincT/bemlint.git"
},
"keywords": [

@@ -20,7 +24,18 @@ "bem",

"debug": "^2.2.0",
"file-entry-cache": "^1.1.1",
"glob": "^7.0.0",
"jsonminify": "^0.2.3",
"lodash": "^4.5.1",
"optionator": "^0.8.1",
"shelljs": "^0.6.0"
"shelljs": "^0.6.0",
"ignore": "^2.2.19"
},
"bugs": {
"url": "https://github.com/DesTincT/bemlint/issues"
},
"homepage": "https://github.com/DesTincT/bemlint#readme",
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}

@@ -1,4 +0,18 @@

# bemlint
# bemlint (Beta-Schmeta)
This linter checks attribute `class` in accordance to [BEM naming](https://github.com/bem/bem-naming) conventions.
Based on code from [ESLint](https://github.com/eslint/eslint)
Not for production use for now.
## Installation
```
npm install -g bemlint
```
## Usage
```
bemlint test.html test2.html
```
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