Socket
Socket
Sign inDemoInstall

jscs

Package Overview
Dependencies
Maintainers
4
Versions
95
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jscs - npm Package Compare versions

Comparing version 1.11.3 to 1.12.0

CHANGELOG.md

98

lib/checker.js

@@ -38,12 +38,31 @@ var vowFs = require('vow-fs');

Checker.prototype.checkFile = function(path) {
if (!this._configuration.isFileExcluded(path)) {
return vowFs.read(path, 'utf8').then(function(data) {
return this.checkString(data, path);
}, this);
if (this._configuration.isFileExcluded(path)) {
return Vow.resolve(null);
}
return Vow.resolve(null);
return vowFs.read(path, 'utf8').then(function(data) {
return this.checkString(data, path);
}, this);
};
/**
* Fixes single file.
*
* @param {String} path
* @returns {Promise * Errors}
*/
Checker.prototype.fixFile = function(path) {
if (this._configuration.isFileExcluded(path)) {
return Vow.resolve(null);
}
return vowFs.read(path, 'utf8').then(function(data) {
var result = this.fixString(data, path);
return vowFs.write(path, result.output).then(function() {
return result.errors;
});
}, this);
};
/**
* Checks directory recursively.

@@ -55,2 +74,33 @@ *

Checker.prototype.checkDirectory = function(path) {
return this._processDirectory(path, this.checkFile.bind(this));
};
/**
* Checks directory or file.
*
* @param {String} path
* @returns {Promise * Error[]}
*/
Checker.prototype.checkPath = function(path) {
return this._processPath(path, this.checkFile.bind(this));
};
/**
* Fixes directory or file.
*
* @param {String} path
* @returns {Promise * Error[]}
*/
Checker.prototype.fixPath = function(path) {
return this._processPath(path, this.fixFile.bind(this));
};
/**
* Processes directory recursively.
*
* @param {String} path
* @param {Function} fileHandler
* @returns {Promise * Error[]}
*/
Checker.prototype._processDirectory = function(path, fileHandler) {
return vowFs.listDir(path).then(function(filenames) {

@@ -66,3 +116,3 @@ return Vow.all(filenames.map(function(filename) {

if (stat.isDirectory()) {
return this.checkDirectory(fullname);
return this._processDirectory(fullname, fileHandler);
}

@@ -74,3 +124,3 @@

return Vow.when(this.checkFile(fullname)).then(function(errors) {
return Vow.when(fileHandler(fullname)).then(function(errors) {
if (errors) {

@@ -90,8 +140,9 @@ return errors;

/**
* Checks directory or file.
* Processes directory or file.
*
* @param {String} path
* @returns {Error[]}
* @param {Function} fileHandler
* @returns {Promise * Error[]}
*/
Checker.prototype.checkPath = function(path) {
Checker.prototype._processPath = function(path, fileHandler) {
path = path.replace(/\/$/, '');

@@ -107,6 +158,6 @@ var _this = this;

if (stat.isDirectory()) {
return _this.checkDirectory(path);
return _this._processDirectory(path, fileHandler);
}
return _this.checkFile(path).then(function(errors) {
return fileHandler(path).then(function(errors) {
if (errors) {

@@ -128,5 +179,22 @@ return [errors];

Checker.prototype.checkStdin = function() {
return this._processStdin(this.checkString.bind(this));
};
/**
* Fixes stdin input
*
* @returns {Promise}
*/
Checker.prototype.fixStdin = function() {
return this._processStdin(this.fixString.bind(this));
};
/**
*
* @param {Function} stdinHandler
* @returns {Promise}
*/
Checker.prototype._processStdin = function(stdinHandler) {
var stdInput = [];
var deferred = Vow.defer();
var _this = this;

@@ -140,5 +208,3 @@ process.stdin.setEncoding('utf8');

process.stdin.on('end', function() {
var errors = _this.checkString(stdInput.join(''));
deferred.resolve(errors);
deferred.resolve(stdinHandler(stdInput.join('')));
});

@@ -145,0 +211,0 @@

14

lib/cli-config.js

@@ -9,3 +9,3 @@ /**

var stripJSONComments = require('strip-json-comments');
var supportsColor = require('supports-color');
var supportsColor = require('chalk').supportsColor;
var glob = require('glob');

@@ -24,5 +24,5 @@

options = Object.create(options || {});
options = Object.create(options);
options.maxDepth = 1;
options.cwd = path.resolve(options.cwd || '.');
options.cwd = path.resolve(options.cwd);

@@ -34,3 +34,3 @@ do {

if (configPath) {
return !fn || fn(path.join(options.cwd, configPath));
return fn(path.join(options.cwd, configPath));
}

@@ -92,3 +92,3 @@ })[0];

* @param {String} [cwd = process.cwd()] - directory path which will be joined with config argument
* @return {Object}
* @return {Object|undefined}
*/

@@ -168,5 +168,5 @@ exports.load = function(config, cwd) {

return {
path: writerPath,
writer: writer
path: writerPath,
writer: writer
};
};

@@ -117,2 +117,11 @@ /**

if (program.fix) {
return {
promise: checker.fixStdin().then(function(result) {
process.stdout.write(result.output);
}),
checker: checker
};
}
checkerPromise = checker.checkStdin().then(function(errors) {

@@ -125,3 +134,4 @@ return [errors];

if (args.length) {
checkerPromise = Vow.all(args.map(checker.checkPath, checker)).then(function(results) {
var method = program.fix ? checker.fixPath : checker.checkPath;
checkerPromise = Vow.all(args.map(method, checker)).then(function(results) {
return [].concat.apply([], results);

@@ -128,0 +138,0 @@ });

@@ -567,2 +567,3 @@ var assert = require('assert');

this.registerRule(require('../rules/disallow-multiple-line-strings'));
this.registerRule(require('../rules/disallow-multiple-spaces'));
this.registerRule(require('../rules/validate-line-breaks'));

@@ -597,2 +598,5 @@ this.registerRule(require('../rules/validate-quote-marks'));

this.registerRule(require('../rules/disallow-padding-newlines-after-blocks'));
this.registerRule(require('../rules/require-padding-newlines-after-blocks'));
this.registerRule(require('../rules/disallow-padding-newlines-in-blocks'));

@@ -608,5 +612,10 @@ this.registerRule(require('../rules/require-padding-newlines-in-blocks'));

this.registerRule(require('../rules/disallow-padding-newlines-before-line-comments'));
this.registerRule(require('../rules/require-padding-newlines-before-line-comments'));
this.registerRule(require('../rules/disallow-trailing-comma'));
this.registerRule(require('../rules/require-trailing-comma'));
this.registerRule(require('../rules/require-dollar-before-jquery-assignment'));
this.registerRule(require('../rules/disallow-comma-before-line-break'));

@@ -670,3 +679,5 @@ this.registerRule(require('../rules/require-comma-before-line-break'));

this.registerRule(require('../rules/require-line-break-after-variable-assignment'));
this.registerRule(require('../rules/require-padding-newline-after-variable-declaration'));
this.registerRule(require('../rules/require-semicolons'));
this.registerRule(require('../rules/disallow-semicolons'));

@@ -676,3 +687,6 @@

this.registerRule(require('../rules/disallow-spaces-in-for-statement'));
this.registerRule(require('../rules/disallow-keywords-in-comments'));
this.registerRule(require('../rules/disallow-identifier-names'));
};

@@ -702,5 +716,11 @@

// https://github.com/felixge/node-style-guide#nodejs-style-guide
this.registerPreset('node-style-guide', require('../../presets/node-style-guide.json'));
// https://www.mediawiki.org/wiki/Manual:Coding_conventions/JavaScript
this.registerPreset('wikimedia', require('../../presets/wikimedia.json'));
// https://make.wordpress.org/core/handbook/coding-standards/javascript/
this.registerPreset('wordpress', require('../../presets/wordpress.json'));
// https://github.com/yandex/codestyle/blob/master/javascript.md

@@ -707,0 +727,0 @@ this.registerPreset('yandex', require('../../presets/yandex.json'));

@@ -6,3 +6,3 @@ var fs = require('fs');

var prompt = require('prompt');
var colors = require('colors');
var chalk = require('chalk');
var assign = require('lodash.assign');

@@ -25,3 +25,3 @@

{
name: colors.green('Please choose a preset number:'),
name: chalk.green('Please choose a preset number:'),
require: true,

@@ -78,6 +78,5 @@ pattern: /\d+/

var errorList = statsForPresets[presetIndex].errors;
errorList = getUniqueErrorNames(errorList);
var errorStats = getErrorsByRuleName(statsForPresets[presetIndex].errors);
var violatedRuleCount = errorList.length;
var violatedRuleCount = Object.keys(errorStats).length;

@@ -88,6 +87,6 @@ if (!violatedRuleCount) { return this._config; }

var errorPrompts = generateRuleHandlingPrompts(errorList);
var errorPrompts = generateRuleHandlingPrompts(errorStats);
return this._getUserViolationChoices(errorPrompts)
.then(this._handleViolatedRules.bind(this, errorPrompts, errorList))
.then(this._handleViolatedRules.bind(this, errorPrompts))
.then(function() {

@@ -136,3 +135,3 @@ return this._config;

statsForPresets.forEach(function(presetStats, idx) {
table.push([idx + 1, presetStats.name, presetStats.sum]);
table.push([idx + 1, presetStats.name, presetStats.sum, getUniqueErrorNames(presetStats.errors).length]);
});

@@ -174,9 +173,8 @@

*/
Generator.prototype._handleViolatedRules = function(errorPrompts, errorList, choices) {
errorPrompts.forEach(function(errorPrompt, idx) {
var associatedRuleName = errorList[idx];
Generator.prototype._handleViolatedRules = function(errorPrompts, choices) {
errorPrompts.forEach(function(errorPrompt) {
var userChoice = choices[errorPrompt.name];
if (userChoice.toLowerCase() === 'e') {
this._config[associatedRuleName] = null;
this._config[errorPrompt.associatedRuleName] = null;
}

@@ -212,9 +210,22 @@ }, this);

* @private
* @param {String[]} violatedRuleNames
* @param {Object} errors
* @return {Object[]}
*/
function generateRuleHandlingPrompts(violatedRuleNames) {
function generateRuleHandlingPrompts(errors) {
// Generate list of rule names, sorted by violation count (descending)
var violatedRuleNames = Object.keys(errors);
violatedRuleNames.sort(function(a, b) {
return errors[b].violations - errors[a].violations;
});
return violatedRuleNames.map(function(ruleName) {
var violationCount = errors[ruleName].violations;
var fileCount = Object.keys(errors[ruleName].files).length;
var prompt = assign({}, prompts[1]);
prompt.name = colors.green(ruleName) + ': ' + prompt.name;
prompt.associatedRuleName = ruleName;
prompt.name = chalk.green(ruleName) +
' (' + violationCount + ' violation' + (violationCount > 1 ? 's' : '') +
' in ' + fileCount + ' file' + (fileCount > 1 ? 's' : '') + '):\n ' +
prompt.name;
return prompt;

@@ -226,2 +237,23 @@ });

* @private
* @param {Object[]} errorList
* @return {Object}
*/
function getErrorsByRuleName(errorsList) {
var errors = {};
errorsList.forEach(function(error) {
var rulename = error.rule;
errors[rulename] = errors[rulename] || {
files: {},
violations: 0
};
errors[rulename].violations += 1;
errors[rulename].files[error.filename] = true;
});
return errors;
}
/**
* @private
* @param {Object[]} errorsList

@@ -258,3 +290,3 @@ * @return {String[]}

},
head: ['', 'Preset', '#Errors']
head: ['', 'Preset', '#Errors', '#Rules']
});

@@ -261,0 +293,0 @@ }

var assert = require('assert');
var colors = require('colors');
var chalk = require('chalk');
var TokenAssert = require('./token-assert');

@@ -69,3 +69,4 @@

line: errorInfo.line,
column: errorInfo.column
column: errorInfo.column,
fixed: errorInfo.fixed
});

@@ -157,3 +158,3 @@ },

/**
/**
* Sets the current rule so that errors are aware

@@ -179,5 +180,5 @@ * of which rule triggered them.

function formatErrorMessage(message, filename, colorize) {
return (colorize ? colors.bold(message) : message) +
return (colorize ? chalk.bold(message) : message) +
' at ' +
(colorize ? colors.green(filename) : filename) + ' :';
(colorize ? chalk.green(filename) : filename) + ' :';
}

@@ -214,3 +215,3 @@

var lineNumber = prependSpaces((n + 1).toString(), 5) + ' |';
return ' ' + (colorize ? colors.grey(lineNumber) : lineNumber) + line;
return ' ' + (colorize ? chalk.grey(lineNumber) : lineNumber) + line;
}

@@ -228,5 +229,5 @@

var res = (new Array(column + 9)).join('-') + '^';
return colorize ? colors.grey(res) : res;
return colorize ? chalk.grey(res) : res;
}
module.exports = Errors;

@@ -21,3 +21,3 @@ var treeIterator = require('./tree-iterator');

this._source = source;
this._tree = tree || {tokens: []};
this._tree = tree || {tokens: [], comments: []};

@@ -27,40 +27,19 @@ this._es3 = options.es3 || false;

this._lineBreaks = null;
this._lines = source.split(/\r\n|\r|\n/);
this._tokenRangeStartIndex = null;
this._tokenRangeEndIndex = null;
var index = this._index = {};
var _this = this;
this._buildTokenIndex();
this._tree.tokens = this._fixEs6Tokens(this._tree.tokens);
this._tokens = this._buildTokenList(this._tree.tokens, this._tree.comments);
this._addEOFToken();
this._applyWhitespaceData(this._tokens, source);
this.iterate(function(node, parentNode, parentCollection) {
var type = node.type;
var tokenIndexes = this._buildTokenIndex(this._tokens);
this._tokenRangeStartIndex = tokenIndexes.tokenRangeStartIndex;
this._tokenRangeEndIndex = tokenIndexes.tokenRangeEndIndex;
this._tokensByLineIndex = tokenIndexes.tokensByLineIndex;
node.parentNode = parentNode;
node.parentCollection = parentCollection;
(index[type] || (index[type] = [])).push(node);
this._index = this._buildNodeIndex();
this._fixEsprimaIdentifiers();
// Temporary fix (i hope) for esprima tokenizer
// (https://code.google.com/p/esprima/issues/detail?id=481)
// Fixes #83, #180
switch (type) {
case 'Property':
convertKeywordToIdentifierIfRequired(node.key);
break;
case 'MemberExpression':
convertKeywordToIdentifierIfRequired(node.property);
break;
}
});
this._buildDisabledRuleIndex();
// Part of temporary esprima fix.
function convertKeywordToIdentifierIfRequired(node) {
var tokenPos = _this.getTokenPosByRangeStart(node.range[0]);
var token = _this._tree.tokens[tokenPos];
if (token.type === 'Keyword') {
token.type = 'Identifier';
}
}
};

@@ -70,3 +49,28 @@

/**
* Returns the first line break character encountered in the file.
* Assumes LF if the file is only one line.
*
* @returns {String}
*/
getLineBreakStyle: function() {
var lineBreaks = this.getLineBreaks();
return lineBreaks.length ? lineBreaks[0] : '\n';
},
/**
* Returns all line break characters from the file.
*
* @returns {String[]}
*/
getLineBreaks: function() {
if (this._lineBreaks === null) {
this._lineBreaks = this._source.match(/\r\n|\r|\n/g) || [];
}
return this._lineBreaks;
},
/**
* Builds an index of disabled rules by starting line for error suppression.
*
* @private
*/

@@ -76,3 +80,3 @@ _buildDisabledRuleIndex: function() {

var comments = this.getComments() || [];
var comments = this.getComments();
var commentRe = /(jscs\s*:\s*(en|dis)able)(.*)/;

@@ -123,2 +127,3 @@

* @param {Number} line the line the comment appears on
* @private
*/

@@ -145,74 +150,35 @@ _addToDisabledRuleIndex: function(enabled, rulesStr, line) {

* Builds token index by starting pos for futher navigation.
*
* @param {Object[]} tokens
* @returns {{tokenRangeStartIndex: {}, tokenRangeEndIndex: {}}}
* @private
*/
_buildTokenIndex: function() {
// Temporary fix (i hope) for esprima tokenizer, which results
// in duplicate tokens on `export default function() {}`
// (https://code.google.com/p/esprima/issues/detail?id=631)
if (this.getDialect() === 'es6') {
var tokenHash = {};
this._tree.tokens = this._tree.tokens.filter(function(token) {
var hashKey = token.range[0] + '_' + token.range[1];
var isDuplicate = tokenHash[hashKey];
tokenHash[hashKey] = true;
return !isDuplicate;
});
}
var tokens = this._tree.tokens;
_buildTokenIndex: function(tokens) {
var tokenRangeStartIndex = {};
var tokenRangeEndIndex = {};
var tokensByLineIndex = {};
for (var i = 0, l = tokens.length; i < l; i++) {
tokenRangeStartIndex[tokens[i].range[0]] = i;
tokenRangeEndIndex[tokens[i].range[1]] = i;
tokens[i]._tokenIndex = i;
}
this._tokenRangeStartIndex = tokenRangeStartIndex;
this._tokenRangeEndIndex = tokenRangeEndIndex;
},
/**
* Builds comments index by starting pos for futher navigation.
*/
_buildCommentIndex: function() {
var comments = this.getComments();
var tokens = this.getTokens();
var tokenIndex = 0;
var tokensLength = tokens.length;
var tokenCommentsBeforeIndex = this._tokenCommentsBeforeIndex = {};
var tokenCommentsAfterIndex = this._tokenCommentsAfterIndex = {};
var partialComments = [];
for (var i = 0, l = comments.length; i < l && comments[i].range[1] <= tokens[0].range[0]; i++) {
partialComments.push(comments[i]);
}
if (partialComments.length) {
tokenCommentsBeforeIndex[0] = partialComments;
partialComments = [];
}
for (; i < l; i++) {
var startPos = comments[i].range[0];
if (partialComments.length) {
tokenCommentsAfterIndex[tokenIndex] = partialComments;
tokenCommentsBeforeIndex[tokenIndex + 1] = partialComments;
partialComments = [];
var token = tokens[i];
// tokens by range
tokenRangeStartIndex[token.range[0]] = i;
tokenRangeEndIndex[token.range[1]] = i;
// tokens by line
var lineNumber = token.loc.start.line;
if (!tokensByLineIndex[lineNumber]) {
tokensByLineIndex[lineNumber] = [];
}
while (tokenIndex + 1 < tokensLength && tokens[tokenIndex + 1].range[1] <= startPos) {
tokenIndex++;
}
partialComments.push(comments[i]);
tokensByLineIndex[lineNumber].push(token);
token._tokenIndex = i;
}
if (partialComments.length) {
tokenCommentsAfterIndex[tokenIndex] = partialComments;
tokenCommentsBeforeIndex[tokenIndex + 1] = partialComments;
}
return {
tokenRangeStartIndex: tokenRangeStartIndex,
tokenRangeEndIndex: tokenRangeEndIndex,
tokensByLineIndex: tokensByLineIndex
};
},
/**
* Returns token position using range start from the index.
*
* @returns {Object}
*/
getTokenPosByRangeStart: function(start) {
return this._tokenRangeStartIndex[start];
},
/**
* Returns token using range start from the index.

@@ -224,4 +190,5 @@ *

var tokenIndex = this._tokenRangeStartIndex[start];
return tokenIndex === undefined ? undefined : this._tree.tokens[tokenIndex];
return tokenIndex === undefined ? undefined : this._tokens[tokenIndex];
},
/**

@@ -234,4 +201,5 @@ * Returns token using range end from the index.

var tokenIndex = this._tokenRangeEndIndex[end];
return tokenIndex === undefined ? undefined : this._tree.tokens[tokenIndex];
return tokenIndex === undefined ? undefined : this._tokens[tokenIndex];
},
/**

@@ -246,2 +214,3 @@ * Returns the first token for the node from the AST.

},
/**

@@ -256,12 +225,48 @@ * Returns the last token for the node from the AST.

},
/**
* Returns the first token for the file.
*
* @returns {Object}
*/
getFirstToken: function() {
return this._tokens[0];
},
/**
* Returns the last token for the file.
*
* @returns {Object}
*/
getLastToken: function() {
return this._tokens[this._tokens.length - 1];
},
/**
* Returns the first token before the given.
*
* @param {Object} token
* @param {Object} [options]
* @param {Boolean} [options.includeComments=false]
* @returns {Object|undefined}
*/
getPrevToken: function(token) {
getPrevToken: function(token, options) {
var index = token._tokenIndex - 1;
return index >= 0 ? this._tree.tokens[index] : undefined;
if (index < 0) {
return undefined;
}
if (options && options.includeComments) {
return this._tokens[index];
}
do {
if (!this._tokens[index].isComment) {
return this._tokens[index];
}
} while (--index >= 0);
return undefined;
},
/**

@@ -271,8 +276,24 @@ * Returns the first token after the given.

* @param {Object} token
* @param {Object} [options]
* @param {Boolean} [options.includeComments=false]
* @returns {Object|undefined}
*/
getNextToken: function(token) {
getNextToken: function(token, options) {
var index = token._tokenIndex + 1;
return index < this._tree.tokens.length ? this._tree.tokens[index] : undefined;
if (index >= this._tokens.length) {
return undefined;
}
if (options && options.includeComments) {
return this._tokens[index];
}
do {
if (!this._tokens[index].isComment) {
return this._tokens[index];
}
} while (++index < this._tokens.length);
},
/**

@@ -296,2 +317,3 @@ * Returns the first token before the given which matches type (and value).

},
/**

@@ -315,2 +337,3 @@ * Returns the first token after the given which matches type (and value).

},
/**

@@ -326,2 +349,3 @@ * Returns the first token before the given which matches type (and value).

},
/**

@@ -337,2 +361,3 @@ * Returns the first token after the given which matches type (and value).

},
/**

@@ -348,2 +373,3 @@ * Iterates through the token tree using tree iterator.

},
/**

@@ -366,2 +392,3 @@ * Returns node by its range position

},
/**

@@ -387,2 +414,3 @@ * Returns nodes by type(s) from earlier built index.

},
/**

@@ -398,2 +426,3 @@ * Iterates nodes by type(s) from earlier built index.

},
/**

@@ -419,2 +448,3 @@ * Iterates tokens by type(s) from the token array.

},
/**

@@ -440,28 +470,55 @@ * Iterates token by value from the token array.

},
/**
* Returns comment node directly followed by a token
* Iterates tokens by type and value(s) from the token array.
* Calls passed function for every matched token.
*
* @param {Object} token
* @returns {Object|undefined}
* @param {String} type
* @param {String|String[]} value
* @param {Function} cb
*/
getCommentAfterToken: function(token) {
if (!this._tokenCommentsAfterIndex) {
this._buildCommentIndex();
}
var comments = this._tokenCommentsAfterIndex[token._tokenIndex];
return comments ? comments[0] : undefined;
iterateTokensByTypeAndValue: function(type, value, cb) {
var values = (typeof value === 'string') ? [value] : value;
var valueIndex = {};
values.forEach(function(type) {
valueIndex[type] = true;
});
this.getTokens().forEach(function(token, index, tokens) {
if (token.type === type && valueIndex[token.value]) {
cb(token, index, tokens);
}
});
},
/**
* Returns comment node directly followed by a token
* Returns first token for the specified line.
* Line numbers start with 1.
*
* @param {Object} token
* @param {Number} lineNumber
* @param {Object} [options]
* @param {Boolean} [options.includeComments]
* @returns {Object|undefined}
*/
getCommentBeforeToken: function(token) {
if (!this._tokenCommentsBeforeIndex) {
this._buildCommentIndex();
getFirstTokenOnLine: function(lineNumber, options) {
var tokensByLine = this._tokensByLineIndex[lineNumber];
if (!tokensByLine) {
return undefined;
}
var comments = this._tokenCommentsBeforeIndex[token._tokenIndex];
return comments ? comments[comments.length - 1] : undefined;
if (options && options.includeComments) {
return tokensByLine[0];
}
for (var i = 0; i < tokensByLine.length; i++) {
var token = tokensByLine[i];
if (!token.isComment) {
return token;
}
}
return undefined;
},
/**

@@ -483,2 +540,3 @@ * Returns which dialect of JS this file supports.

},
/**

@@ -492,2 +550,3 @@ * Returns string representing contents of the file.

},
/**

@@ -501,2 +560,3 @@ * Returns token tree, built using esprima.

},
/**

@@ -508,4 +568,5 @@ * Returns token list, built using esprima.

getTokens: function() {
return this._tree.tokens;
return this._tokens;
},
/**

@@ -517,2 +578,3 @@ * Returns comment token list, built using esprima.

},
/**

@@ -526,2 +588,3 @@ * Returns source filename for this object representation.

},
/**

@@ -535,10 +598,9 @@ * Returns array of source lines for the file.

},
/**
* Returns array of source lines for the file with comments removed.
* Will report erroneous trailing tokens in multiline comments if an error reporter is provided.
*
* @param {Errors} [errors=null] errors
* @returns {Array}
*/
getLinesWithCommentsRemoved: function(errors) {
getLinesWithCommentsRemoved: function() {
var lines = this.getLines().concat();

@@ -561,17 +623,240 @@

lines[x] = lines[x].substring(endCol);
}
});
if (errors && lines[x] !== '') {
errors.add(
'Multiline comments should not have tokens on its ending line',
x + 1,
endCol
);
}
return lines;
},
/**
* Renders JS-file sources using token list.
*
* @returns {String}
*/
render: function() {
var result = '';
// For-loop for maximal speed.
for (var i = 0; i < this._tokens.length; i++) {
var token = this._tokens[i];
result += token.whitespaceBefore;
switch (token.type) {
// Line-comment: // ...
case 'Line':
result += '//' + token.value;
break;
// Block-comment: /* ... */
case 'Block':
result += '/*' + token.value + '*/';
break;
default:
result += token.value;
}
}
return result;
},
/**
* Builds token list using both code tokens and comment-tokens.
*
* @returns {Object[]}
* @private
*/
_buildTokenList: function(codeTokens, commentTokens) {
var result = [];
var codeQueue = codeTokens.concat();
var commentQueue = commentTokens.concat();
while (codeQueue.length > 0 || commentQueue.length > 0) {
if (codeQueue.length > 0 && (!commentQueue.length || commentQueue[0].range[0] > codeQueue[0].range[0])) {
result.push(codeQueue.shift());
} else {
var commentToken = commentQueue.shift();
commentToken.isComment = true;
result.push(commentToken);
}
}
return result;
},
/**
* Adds JSCS-specific EOF (end of file) token.
*
* @private
*/
_addEOFToken: function() {
var loc = {
line: this._lines.length,
column: this._lines[this._lines.length - 1].length
};
this._tokens.push({
type: 'EOF',
value: '',
range: [this._source.length, this._source.length + 1],
loc: {start: loc, end: loc}
});
},
return lines;
/**
* Applies whitespace information to the token list.
*
* @param {Object[]} tokens
* @param {String} source
* @private
*/
_applyWhitespaceData: function(tokens, source) {
var prevPos = 0;
// For-loop for maximal speed.
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
var rangeStart = token.range[0];
var whitespace;
if (rangeStart === prevPos) {
whitespace = '';
} else {
whitespace = source.substring(prevPos, rangeStart);
}
token.whitespaceBefore = whitespace;
prevPos = token.range[1];
}
},
/**
* Temporary fix (I hope) for esprima tokenizer, which results
* in duplicate tokens on `export default function() {}`
* (https://code.google.com/p/esprima/issues/detail?id=631)
*
* @param {Object[]} tokens
* @returns {Object[]}
* @private
*/
_fixEs6Tokens: function(tokens) {
if (this.getDialect() !== 'es6') {
return tokens;
}
var tokenHash = {};
return tokens.filter(function(token) {
var hashKey = token.range[0] + '_' + token.range[1];
var isDuplicate = tokenHash[hashKey];
tokenHash[hashKey] = true;
return !isDuplicate;
});
},
/**
* Builds node index using node type as the key.
*
* @returns {Object}
* @private
*/
_buildNodeIndex: function() {
var index = {};
this.iterate(function(node, parentNode, parentCollection) {
var type = node.type;
node.parentNode = parentNode;
node.parentCollection = parentCollection;
(index[type] || (index[type] = [])).push(node);
});
return index;
},
/**
* Temporary fix (I hope) for esprima tokenizer
* (https://code.google.com/p/esprima/issues/detail?id=481)
* Fixes #83, #180
* @private
*/
_fixEsprimaIdentifiers: function() {
var _this = this;
this.iterateNodesByType(['Property', 'MemberExpression'], function(node) {
switch (node.type) {
case 'Property':
convertKeywordToIdentifierIfRequired(node.key);
break;
case 'MemberExpression':
convertKeywordToIdentifierIfRequired(node.property);
break;
}
});
function convertKeywordToIdentifierIfRequired(node) {
var token = _this.getTokenByRangeStart(node.range[0]);
if (token.type === 'Keyword') {
token.type = 'Identifier';
}
}
}
};
/**
* Parses a JS-file.
*
* @param {String} source
* @param {Object} esprima
* @param {Object} [esprimaOptions]
* @returns {Object}
*/
JsFile.parse = function(source, esprima, esprimaOptions) {
var finalEsprimaOptions = {
tolerant: true
};
if (esprimaOptions) {
for (var key in esprimaOptions) {
finalEsprimaOptions[key] = esprimaOptions[key];
}
}
// Set required options
finalEsprimaOptions.loc = true;
finalEsprimaOptions.range = true;
finalEsprimaOptions.comment = true;
finalEsprimaOptions.tokens = true;
finalEsprimaOptions.sourceType = 'module';
var hashbang = source.indexOf('#!') === 0;
var tree;
// Convert bin annotation to a comment
if (hashbang) {
source = '//' + source.substr(2);
}
var instrumentationData = {};
var hasInstrumentationData = false;
// Process special case code like iOS instrumentation imports: `#import 'abc.js';`
source = source.replace(/^#!?[^\n]+\n/gm, function(str, pos) {
hasInstrumentationData = true;
instrumentationData[pos] = str.substring(0, str.length - 1);
return '//' + str.slice(2);
});
tree = esprima.parse(source, finalEsprimaOptions);
// Change the bin annotation comment
if (hashbang) {
tree.comments[0].type = 'Hashbang';
tree.comments[0].value = '#!' + tree.comments[0].value;
}
if (hasInstrumentationData) {
tree.comments.forEach(function(token) {
var rangeStart = token.range[0];
if (instrumentationData.hasOwnProperty(rangeStart)) {
token.type = 'InstrumentationDirective';
token.value = instrumentationData[rangeStart];
}
});
}
return tree;
};
module.exports = JsFile;

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -45,6 +45,6 @@ * #### Example

module.exports.prototype = {
configure: function(disallowAnonymousFunctions) {
configure: function(options) {
assert(
disallowAnonymousFunctions === true,
'disallowAnonymousFunctions option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -60,3 +60,3 @@ },

if (node.id === null) {
errors.add('Anonymous functions needs to be named', node.loc.start);
errors.add('Anonymous functions need to be named', node.loc.start);
}

@@ -63,0 +63,0 @@ });

@@ -45,6 +45,6 @@ /**

module.exports.prototype = {
configure: function(disallowCapitalizedComments) {
configure: function(options) {
assert(
disallowCapitalizedComments === true,
'disallowCapitalizedComments option requires a value of true or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -61,3 +61,3 @@ },

file.getComments().forEach(function(comment) {
file.iterateTokensByType(['Line', 'Block'], function(comment) {
var stripped = comment.value.replace(/[\n\s\*]/g, '');

@@ -64,0 +64,0 @@ var firstChar = stripped[0];

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -43,11 +43,7 @@ * JSHint: [`laxcomma`](http://www.jshint.com/docs/options/#laxcomma)

configure: function(disallowCommaBeforeLineBreak) {
configure: function(options) {
assert(
typeof disallowCommaBeforeLineBreak === 'boolean',
'disallowCommaBeforeLineBreak option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
disallowCommaBeforeLineBreak === true,
'disallowCommaBeforeLineBreak option requires true value or should be removed'
);
},

@@ -60,10 +56,8 @@

check: function(file, errors) {
file.iterateTokensByType('Punctuator', function(token) {
if (token.value === ',') {
errors.assert.sameLine({
token: token,
nextToken: file.getNextToken(token),
message: 'Commas should be placed on new line'
});
}
file.iterateTokensByTypeAndValue('Punctuator', ',', function(token) {
errors.assert.sameLine({
token: token,
nextToken: file.getNextToken(token),
message: 'Commas should be placed on new line'
});
});

@@ -70,0 +64,0 @@ }

/**
* Disallows curly braces after statements.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -6,0 +6,0 @@ * Values: Array of quoted keywords or `true` to disallow curly braces after the following keywords:

@@ -11,3 +11,3 @@ /**

*
* Type: `Boolean` or `Object`
* Types: `Boolean` or `Object`
*

@@ -24,3 +24,3 @@ * Values:

* ```js
* "disallowDanglingUnderscores": { allExcept: ["_exception"] }
* "disallowDanglingUnderscores": { "allExcept": ["_exception"] }
* ```

@@ -63,3 +63,3 @@ *

// verify first item in `allExcept` property in object (if it's an object)
// verify first item in `allExcept` property in object (if it's an object)
assert(

@@ -69,3 +69,3 @@ typeof identifiers !== 'object' ||

typeof identifiers.allExcept[0] === 'string',
'Property `allExcept` in requireSpaceAfterLineComment should be an array of strings'
'Property `allExcept` in ' + this.getOptionName() + ' should be an array of strings'
);

@@ -72,0 +72,0 @@

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -37,11 +37,7 @@ * JSHint: [`noempty`](http://jshint.com/docs/options/#noempty)

configure: function(disallowEmptyBlocks) {
configure: function(options) {
assert(
typeof disallowEmptyBlocks === 'boolean',
'disallowEmptyBlocks option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
disallowEmptyBlocks === true,
'disallowEmptyBlocks option requires true value or should be removed'
);
},

@@ -48,0 +44,0 @@

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -45,6 +45,6 @@ * #### Example

module.exports.prototype = {
configure: function(disallowFunctionDeclarations) {
configure: function(options) {
assert(
disallowFunctionDeclarations === true,
'disallowFunctionDeclarations option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -51,0 +51,0 @@ },

@@ -40,3 +40,3 @@ /**

configure: function(types) {
assert(Array.isArray(types), 'disallowImplicitTypeConversion option requires array value');
assert(Array.isArray(types), this.getOptionName() + ' option requires array value');
this._typeIndex = {};

@@ -43,0 +43,0 @@ for (var i = 0, l = types.length; i < l; i++) {

/**
* Disallows keywords in your comments, such as TODO or FIXME
*
* Type: `Boolean` or `String` or `Array`
* Types: `Boolean`, `String` or `Array`
*

@@ -64,3 +64,3 @@ * Values:

module.exports.prototype = {
configure: function(disallowKeywordsInComments) {
configure: function(keywords) {
this._message = 'Comments cannot contain the following keywords: ';

@@ -70,7 +70,7 @@ this._keywords = ['todo', 'fixme'];

switch (true) {
case Array.isArray(disallowKeywordsInComments):
case Array.isArray(keywords):
// use the array of strings provided to build RegExp pattern
this._keywords = disallowKeywordsInComments;
this._keywords = keywords;
/* falls through */
case disallowKeywordsInComments:
case keywords:
// use default keywords

@@ -80,9 +80,9 @@ this._message += this._keywords.join(', ');

break;
case typeof disallowKeywordsInComments === 'string':
case typeof keywords === 'string':
// use string passed in as the RegExp pattern
this._message = 'Comments cannot contain keywords based on the expression you provided';
this._keywordRegEx = new RegExp(disallowKeywordsInComments, 'gi');
this._keywordRegEx = new RegExp(keywords, 'gi');
break;
default:
assert(false, 'disallowKeywordsInComments option requires a value of true, a string or an array');
assert(false, this.getOptionName() + ' option requires a true value, a string or an array');
}

@@ -96,3 +96,3 @@ },

check: function(file, errors) {
file.getComments().forEach(function(comment) {
file.iterateTokensByType(['Line', 'Block'], function(comment) {
getCommentErrors(comment, this._keywordRegEx).forEach(function(errorObj) {

@@ -103,2 +103,2 @@ errors.add(this._message, errorObj);

}
};
};

@@ -43,7 +43,4 @@ /**

configure: function(keywords) {
assert(Array.isArray(keywords), 'disallowKeywordsOnNewLine option requires array value');
this._keywordIndex = {};
for (var i = 0, l = keywords.length; i < l; i++) {
this._keywordIndex[keywords[i]] = true;
}
assert(Array.isArray(keywords), this.getOptionName() + ' option requires array value');
this._keywords = keywords;
},

@@ -56,19 +53,15 @@

check: function(file, errors) {
var keywordIndex = this._keywordIndex;
file.iterateTokensByTypeAndValue('Keyword', this._keywords, function(token) {
var prevToken = file.getPrevToken(token);
file.iterateTokensByType('Keyword', function(token) {
if (keywordIndex[token.value]) {
var prevToken = file.getPrevToken(token);
// Special case for #905, even though it contradicts rule meaning,
// it makes more sense that way.
if (token.value === 'else' && prevToken.value !== '}') {
return;
}
// Special case for #905, even though it contradicts rule meaning,
// it makes more sense that way.
if (token.value === 'else' && prevToken.value !== '}') {
return;
}
errors.assert.sameLine({
token: file.getPrevToken(token),
nextToken: token
});
}
errors.assert.sameLine({
token: prevToken,
nextToken: token
});
});

@@ -75,0 +68,0 @@ }

@@ -30,7 +30,4 @@ /**

configure: function(keywords) {
assert(Array.isArray(keywords), 'disallowKeywords option requires array value');
this._keywordIndex = {};
for (var i = 0, l = keywords.length; i < l; i++) {
this._keywordIndex[keywords[i]] = true;
}
assert(Array.isArray(keywords), this.getOptionName() + ' option requires array value');
this._keywords = keywords;
},

@@ -43,11 +40,7 @@

check: function(file, errors) {
var keywordIndex = this._keywordIndex;
file.iterateTokensByType('Keyword', function(token) {
if (keywordIndex[token.value]) {
errors.add(
'Illegal keyword: ' + token.value,
token.loc.start
);
}
file.iterateTokensByTypeAndValue('Keyword', this._keywords, function(token) {
errors.add(
'Illegal keyword: ' + token.value,
token.loc.start
);
});

@@ -54,0 +47,0 @@ }

@@ -5,3 +5,3 @@ /**

*
* Type: `Boolean` or `String`
* Types: `Boolean` or `String`
*

@@ -59,9 +59,9 @@ * Values: `true` or `"smart"`

configure: function(disallowMixedSpacesAndTabs) {
configure: function(options) {
assert(
disallowMixedSpacesAndTabs === true || disallowMixedSpacesAndTabs === 'smart',
'disallowMixedSpacesAndTabs option requires true or "smart" value'
options === true || options === 'smart',
this.getOptionName() + ' option requires a true value or "smart"'
);
this._disallowMixedSpacesAndTabs = disallowMixedSpacesAndTabs;
this._options = options;
},

@@ -74,3 +74,3 @@

check: function(file, errors) {
var test = this._disallowMixedSpacesAndTabs === true ?
var test = this._options === true ?
(/ \t|\t [^\*]|\t $/) :

@@ -77,0 +77,0 @@ (/ \t/);

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -37,11 +37,7 @@ * #### Example

configure: function(disallowMultipleLineBreaks) {
configure: function(options) {
assert(
typeof disallowMultipleLineBreaks === 'boolean',
'disallowMultipleLineBreaks option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
disallowMultipleLineBreaks === true,
'disallowMultipleLineBreaks option requires true value or should be removed'
);
},

@@ -54,12 +50,18 @@

check: function(file, errors) {
var lines = file.getLines();
for (var i = 1, l = lines.length; i < l; i++) {
var line = lines[i];
if (line === '' && lines[i - 1] === '') {
while (++i < l && lines[i] === '') {}
errors.add('Multiple line break', i - 1, 0);
// Iterate over all tokens (including comments)
file.getTokens().forEach(function(token, index, tokens) {
// If there are no trailing tokens, exit early
var nextToken = tokens[index + 1];
if (!nextToken) {
return;
}
}
errors.assert.linesBetween({
token: token,
nextToken: nextToken,
atMost: 2,
});
});
}
};

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -37,11 +37,7 @@ * JSHint: [`multistr`](http://www.jshint.com/docs/options/#multistr)

configure: function(disallowMultipleLineStrings) {
configure: function(options) {
assert(
typeof disallowMultipleLineStrings === 'boolean',
'disallowMultipleLineStrings option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
disallowMultipleLineStrings === true,
'disallowMultipleLineStrings option requires true value or should be removed'
);
},

@@ -48,0 +44,0 @@

/**
* Disallows multiple `var` declaration (except for-loop).
*
* Type: `Boolean` or `String`
* Types: `Boolean` or `String`
*

@@ -60,12 +60,12 @@ * Values:

configure: function(disallowMultipleVarDecl) {
configure: function(options) {
assert(
disallowMultipleVarDecl === true ||
disallowMultipleVarDecl === 'strict' ||
disallowMultipleVarDecl === 'exceptUndefined',
'disallowMultipleVarDecl option requires true, "strict", or "exceptUndefined" value'
options === true ||
options === 'strict' ||
options === 'exceptUndefined',
this.getOptionName() + ' option requires a true value, "strict", or "exceptUndefined"'
);
this.strictMode = disallowMultipleVarDecl === 'strict';
this.exceptUndefined = disallowMultipleVarDecl === 'exceptUndefined';
this.strictMode = options === 'strict';
this.exceptUndefined = options === 'exceptUndefined';
},

@@ -72,0 +72,0 @@

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -78,11 +78,7 @@ * #### Example

module.exports.prototype = {
configure: function(disallowNewlineBeforeBlockStatements) {
configure: function(options) {
assert(
typeof disallowNewlineBeforeBlockStatements === 'boolean',
'disallowNewlineBeforeBlockStatements option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
disallowNewlineBeforeBlockStatements === true,
'disallowNewlineBeforeBlockStatements option requires true value or should be removed'
);
},

@@ -89,0 +85,0 @@

/**
* Requires putting certain operators on the next line rather than on the current line before a line break.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -43,3 +43,3 @@ * Values: Array of operators to apply to or `true`

assert(Array.isArray(operators) || operators === true,
'disallowOperatorBeforeLineBreak option requires array or true value');
this.getOptionName() + ' option requires array or true value');

@@ -57,20 +57,10 @@ if (operators === true) {

check: function(file, errors) {
var tokens = file.getTokens();
var lastToken;
var operators = this._operators;
tokens.forEach(function(token) {
if (lastToken) {
if (lastToken.type === 'Punctuator' && operators.indexOf(lastToken.value) > -1) {
if (lastToken.loc.end.line < token.loc.end.line) {
errors.add(
'Operator needs to either be on the same line or after a line break.',
token.loc.start
);
}
}
}
lastToken = token;
file.iterateTokensByTypeAndValue('Punctuator', this._operators, function(token) {
errors.assert.sameLine({
token: token,
nextToken: file.getNextToken(token),
message: 'Operator needs to either be on the same line or after a line break.'
});
});
}
};
/**
* Disallow an empty line above the specified keywords.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -75,3 +75,3 @@ * Values: Array of quoted types or `true` to disallow padding new lines after all of the keywords below.

assert(Array.isArray(keywords) || keywords === true,
'disallowPaddingNewlinesBeforeKeywords option requires array or true value');
this.getOptionName() + ' option requires array or true value');

@@ -82,6 +82,3 @@ if (keywords === true) {

this._keywordIndex = {};
for (var i = 0, l = keywords.length; i < l; i++) {
this._keywordIndex[keywords[i]] = true;
}
this._keywords = keywords;
},

@@ -94,18 +91,11 @@

check: function(file, errors) {
var keywordIndex = this._keywordIndex;
file.iterateTokensByType('Keyword', function(token) {
if (keywordIndex[token.value]) {
var prevToken = file.getPrevToken(token);
if (prevToken && token.loc.start.line - prevToken.loc.end.line > 1) {
errors.add(
'Keyword `' + token.value + '` should not have an empty line above it',
token.loc.start.line,
token.loc.start.column
);
}
}
file.iterateTokensByTypeAndValue('Keyword', this._keywords, function(token) {
errors.assert.linesBetween({
token: file.getPrevToken(token),
nextToken: token,
atMost: 1,
message: 'Keyword `' + token.value + '` should not have an empty line above it'
});
});
}
};

@@ -6,3 +6,3 @@ /**

*
* Values: `true` validates all non-empty blocks.
* Value: `true` validates all non-empty blocks.
*

@@ -42,6 +42,6 @@ * #### Example

configure: function(disallowPaddingNewlinesInBlocks) {
configure: function(options) {
assert(
disallowPaddingNewlinesInBlocks === true,
'disallowPaddingNewlinesInBlocks option requires the value true or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -55,22 +55,20 @@ },

check: function(file, errors) {
var lines = file.getLines();
file.iterateNodesByType('BlockStatement', function(node) {
var openingBracket = file.getFirstNodeToken(node);
var startLine = openingBracket.loc.start.line;
errors.assert.linesBetween({
token: openingBracket,
nextToken: file.getNextToken(openingBracket, {includeComments: true}),
atMost: 1,
message: 'Expected no padding newline after opening curly brace'
});
var closingBracket = file.getLastNodeToken(node);
var closingLine = closingBracket.loc.start.line;
if (startLine === closingLine) {
return;
}
if (lines[startLine] === '') {
errors.add('Expected no padding newline after opening curly brace', openingBracket.loc.end);
}
if (lines[closingLine - 2] === '') {
errors.add('Expected no padding newline before closing curly brace', closingBracket.loc.start);
}
errors.assert.linesBetween({
token: file.getPrevToken(closingBracket, {includeComments: true}),
nextToken: closingBracket,
atMost: 1,
message: 'Expected no padding newline before closing curly brace'
});
});

@@ -77,0 +75,0 @@ }

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -44,8 +44,4 @@ * #### Example

assert(
typeof value === 'boolean',
'disallowPaddingNewLinesInObjects option requires boolean value'
);
assert(
value === true,
'disallowPaddingNewLinesInObjects option requires true value or should be removed'
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -67,12 +63,15 @@ },

if (openingBracket.loc.end.line !== nextToken.loc.start.line) {
errors.add('Illegal newline after opening curly brace', nextToken.loc.start);
}
errors.assert.sameLine({
token: openingBracket,
nextToken: nextToken,
message: 'Illegal newline after opening curly brace'
});
var closingBracket = file.getLastNodeToken(node);
var prevToken = file.getPrevToken(closingBracket);
if (closingBracket.loc.start.line !== prevToken.loc.end.line) {
errors.add('Illegal newline before closing curly brace', closingBracket.loc.start);
}
errors.assert.sameLine({
token: file.getPrevToken(closingBracket),
nextToken: closingBracket,
message: 'Illegal newline before closing curly brace'
});
});

@@ -79,0 +78,0 @@ }

/**
* Disallows quoted keys in object if possible.
*
* Type: `String` or `Boolean`
* Types: `String` or `Boolean`
*

@@ -44,9 +44,9 @@ * Values:

configure: function(disallowQuotedKeysInObjects) {
configure: function(options) {
assert(
disallowQuotedKeysInObjects === true || disallowQuotedKeysInObjects === 'allButReserved',
this.getOptionName() + ' options should be true or "allButReserved" value'
options === true || options === 'allButReserved',
this.getOptionName() + ' option requires a true value or "allButReserved"'
);
this._mode = disallowQuotedKeysInObjects;
this._mode = options;
},

@@ -53,0 +53,0 @@

@@ -34,6 +34,6 @@ /**

module.exports.prototype = {
configure: function(disallowSemicolons) {
configure: function(options) {
assert(
disallowSemicolons === true,
'disallowSemicolons option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -47,13 +47,10 @@ },

check: function(file, errors) {
file.getTokens()
.filter(function(token) {
return token.type === 'Punctuator' && token.value === ';';
})
.forEach(function(token) {
var nextToken = file.getNextToken(token);
if (!nextToken || nextToken.loc.end.line > token.loc.end.line) {
errors.add('semicolons are disallowed at the end of a line.', token.loc.end);
}
});
file.iterateTokensByTypeAndValue('Punctuator', ';', function(token) {
var nextToken = file.getNextToken(token);
// do not use assertions here as this is not yet autofixable
if (nextToken.type === 'EOF' || nextToken.loc.end.line > token.loc.end.line) {
errors.add('semicolons are disallowed at the end of a line.', token.loc.end);
}
});
}
};
/**
* Requires sticking binary operators to the right.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -50,3 +50,3 @@ * Values: Array of quoted operators or `true` to disallow space after all possible binary operators

Array.isArray(operators) || isTrue,
'disallowSpaceAfterBinaryOperators option requires array or true value'
this.getOptionName() + ' option requires array or true value'
);

@@ -73,10 +73,8 @@

if (operators[',']) {
file.iterateTokensByType('Punctuator', function(token) {
if (token.value === ',') {
errors.assert.noWhitespaceBetween({
token: token,
nextToken: file.getNextToken(token),
message: 'Operator , should stick to following expression'
});
}
file.iterateTokensByTypeAndValue('Punctuator', ',', function(token) {
errors.assert.noWhitespaceBetween({
token: token,
nextToken: file.getNextToken(token),
message: 'Operator , should stick to following expression'
});
});

@@ -83,0 +81,0 @@ }

/**
* Disallows space after keyword.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -50,3 +50,3 @@ * Values: Array of quoted keywords or `true` to disallow spaces after all possible keywords.

Array.isArray(keywords) || keywords === true,
'disallowSpaceAfterKeywords option requires array or true value'
this.getOptionName() + ' option requires array or true value'
);

@@ -58,6 +58,3 @@

this._keywordIndex = {};
for (var i = 0, l = keywords.length; i < l; i++) {
this._keywordIndex[keywords[i]] = true;
}
this._keywords = keywords;
},

@@ -70,11 +67,7 @@

check: function(file, errors) {
var keywordIndex = this._keywordIndex;
file.iterateTokensByType('Keyword', function(token) {
if (keywordIndex[token.value]) {
errors.assert.noWhitespaceBetween({
token: token,
nextToken: file.getNextToken(token)
});
}
file.iterateTokensByTypeAndValue('Keyword', this._keywords, function(token) {
errors.assert.noWhitespaceBetween({
token: token,
nextToken: file.getNextToken(token)
});
});

@@ -81,0 +74,0 @@ }

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -35,6 +35,6 @@ * #### Example

configure: function(disallowSpaceAfterLineComment) {
configure: function(options) {
assert(
disallowSpaceAfterLineComment === true,
'disallowSpaceAfterLineComment option requires the value true'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -48,10 +48,6 @@ },

check: function(file, errors) {
var comments = file.getComments();
comments.forEach(function(comment) {
if (comment.type === 'Line') {
var value = comment.value;
if (value.length > 0 && value[0] === ' ') {
errors.add('Illegal space after line comment', comment.loc.start);
}
file.iterateTokensByType('Line', function(comment) {
var value = comment.value;
if (value.length > 0 && value[0] === ' ') {
errors.add('Illegal space after line comment', comment.loc.start);
}

@@ -58,0 +54,0 @@ });

/**
* Disallows space after object keys.
*
* Type: `Boolean`
* Types: `Boolean` or `String`
*

@@ -6,0 +6,0 @@ * Values:

/**
* Requires sticking unary operators to the right.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -6,0 +6,0 @@ * Values: Array of quoted operators or `true` to disallow space after prefix for all unary operators

/**
* Requires sticking binary operators to the left.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -51,3 +51,3 @@ * Values: Array of quoted operators or `true` to disallow space before all possible binary operators

Array.isArray(operators) || isTrue,
'disallowSpaceBeforeBinaryOperators option requires array or true value'
this.getOptionName() + ' option requires array or true value'
);

@@ -74,10 +74,8 @@

if (operators[',']) {
file.iterateTokensByType('Punctuator', function(token) {
if (token.value === ',') {
errors.assert.noWhitespaceBetween({
token: file.getPrevToken(token),
nextToken: token,
message: 'Operator , should stick to previous expression'
});
}
file.iterateTokensByTypeAndValue('Punctuator', ',', function(token) {
errors.assert.noWhitespaceBetween({
token: file.getPrevToken(token, {includeComments: true}),
nextToken: token,
message: 'Operator , should stick to previous expression'
});
});

@@ -110,3 +108,3 @@ }

var prevToken = file.getPrevToken(operatorToken);
var prevToken = file.getPrevToken(operatorToken, {includeComments: true});

@@ -113,0 +111,0 @@ if (operators[operator]) {

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -57,11 +57,7 @@ * #### Example

module.exports.prototype = {
configure: function(disallowSpaceBeforeBlockStatements) {
configure: function(options) {
assert(
typeof disallowSpaceBeforeBlockStatements === 'boolean',
'disallowSpaceBeforeBlockStatements option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
disallowSpaceBeforeBlockStatements === true,
'disallowSpaceBeforeBlockStatements option requires true value or should be removed'
);
},

@@ -68,0 +64,0 @@

/**
* Disallows space before keyword.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -45,3 +45,3 @@ * Values: Array of quoted keywords or `true` to disallow spaces before all possible keywords.

Array.isArray(keywords) || keywords === true,
'disallowSpaceBeforeKeywords option requires array or true value');
this.getOptionName() + ' option requires array or true value');

@@ -52,6 +52,3 @@ if (keywords === true) {

this._keywordIndex = {};
for (var i = 0, l = keywords.length; i < l; i++) {
this._keywordIndex[keywords[i]] = true;
}
this._keywords = keywords;
},

@@ -64,20 +61,14 @@

check: function(file, errors) {
var keywordIndex = this._keywordIndex;
file.iterateTokensByTypeAndValue('Keyword', this._keywords, function(token) {
var prevToken = file.getPrevToken(token, {includeComments: true});
if (!prevToken || prevToken.isComment) {
return;
}
file.iterateTokensByType(['Keyword'], function(token) {
if (keywordIndex[token.value]) {
var prevToken = file.getPrevToken(token);
if (!prevToken) {
return;
}
prevToken = file.getCommentBeforeToken(token) || prevToken;
if (prevToken.type !== 'Keyword' && prevToken.value !== ';') {
errors.assert.noWhitespaceBetween({
token: prevToken,
nextToken: token,
message: 'Illegal space before "' + token.value + '" keyword'
});
}
if (prevToken.type !== 'Keyword' && prevToken.value !== ';') {
errors.assert.noWhitespaceBetween({
token: prevToken,
nextToken: token,
message: 'Illegal space before "' + token.value + '" keyword'
});
}

@@ -84,0 +75,0 @@ });

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -34,3 +34,3 @@ * #### Example

disallow === true,
this.getOptionName() + ' option requires true value or should be removed'
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -37,0 +37,0 @@ },

/**
* Requires sticking unary operators to the left.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -6,0 +6,0 @@ * Values: Array of quoted operators or `true` to disallow space before postfix for all unary operators

@@ -33,11 +33,7 @@ /**

configure: function(disallowSpaceBetweenArguments) {
configure: function(options) {
assert(
typeof disallowSpaceBetweenArguments === 'boolean',
this.getOptionName() + ' option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
disallowSpaceBetweenArguments === true,
this.getOptionName() + ' option requires true value or should be removed'
);
},

@@ -44,0 +40,0 @@

@@ -47,3 +47,3 @@ /**

typeof options === 'object',
'disallowSpacesInAnonymousFunctionExpression option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -54,3 +54,3 @@

options.beforeOpeningRoundBrace === true,
'disallowSpacesInAnonymousFunctionExpression.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -63,3 +63,3 @@ );

options.beforeOpeningCurlyBrace === true,
'disallowSpacesInAnonymousFunctionExpression.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -71,3 +71,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'disallowSpacesInAnonymousFunctionExpression must have beforeOpeningCurlyBrace ' +
this.getOptionName() + ' must have beforeOpeningCurlyBrace ' +
' or beforeOpeningRoundBrace property'

@@ -97,21 +97,22 @@ );

// anonymous function expressions only
if (!node.id) {
if (node.id) {
return;
}
if (beforeOpeningRoundBrace) {
var functionToken = file.getFirstNodeToken(node);
errors.assert.noWhitespaceBetween({
token: functionToken,
nextToken: file.getNextToken(functionToken),
message: 'Illegal space before opening round brace',
});
}
if (beforeOpeningRoundBrace) {
var functionToken = file.getFirstNodeToken(node);
errors.assert.noWhitespaceBetween({
token: functionToken,
nextToken: file.getNextToken(functionToken),
message: 'Illegal space before opening round brace',
});
}
if (beforeOpeningCurlyBrace) {
var bodyToken = file.getFirstNodeToken(node.body);
errors.assert.noWhitespaceBetween({
token: file.getPrevToken(bodyToken),
nextToken: bodyToken,
message: 'Illegal space before opening curly brace',
});
}
if (beforeOpeningCurlyBrace) {
var bodyToken = file.getFirstNodeToken(node.body);
errors.assert.noWhitespaceBetween({
token: file.getPrevToken(bodyToken),
nextToken: bodyToken,
message: 'Illegal space before opening curly brace',
});
}

@@ -118,0 +119,0 @@ });

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -33,6 +33,6 @@ * #### Example

module.exports.prototype = {
configure: function(requireSpacesInCallExpression) {
configure: function(options) {
assert(
requireSpacesInCallExpression === true,
'disallowSpacesInCallExpression option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -39,0 +39,0 @@ },

/**
* Disallows space before and/or after `?` or `:` in conditional expressions.
*
* Type: `Object` or `Boolean`
* Types: `Object` or `Boolean`
*

@@ -72,3 +72,3 @@ * Values: `"afterTest"`, `"beforeConsequent"`, `"afterConsequent"`, `"beforeAlternate"` as child properties,

typeof options === 'object',
optionName + ' option must be an object or boolean true'
optionName + ' option requires a true value or an object'
);

@@ -117,8 +117,8 @@

token = file.getPrevToken(questionMarkToken);
if (token.loc.end.column !== questionMarkToken.loc.start.column) {
errors.add(
'Illegal space after test',
test.loc.end
);
}
errors.assert.noWhitespaceBetween({
token: token,
nextToken: questionMarkToken,
message: 'Illegal space after test'
});
}

@@ -128,8 +128,8 @@

token = file.getNextToken(questionMarkToken);
if (token.loc.start.column !== questionMarkToken.loc.end.column) {
errors.add(
'Illegal space before consequent',
consequent.loc.start
);
}
errors.assert.noWhitespaceBetween({
token: questionMarkToken,
nextToken: token,
message: 'Illegal space before consequent'
});
}

@@ -139,8 +139,8 @@

token = file.getPrevToken(colonToken);
if (token.loc.end.column !== colonToken.loc.start.column) {
errors.add(
'Illegal space after consequent',
consequent.loc.end
);
}
errors.assert.noWhitespaceBetween({
token: token,
nextToken: colonToken,
message: 'Illegal space after consequent'
});
}

@@ -150,8 +150,7 @@

token = file.getNextToken(colonToken);
if (token.loc.start.column !== colonToken.loc.end.column) {
errors.add(
'Illegal space before alternate',
alternate.loc.start
);
}
errors.assert.noWhitespaceBetween({
token: colonToken,
nextToken: token,
message: 'Illegal space before alternate'
});
}

@@ -158,0 +157,0 @@ }.bind(this));

@@ -6,3 +6,3 @@ /**

*
* Values: `true` to disallow spaces in between for statement.
* Value: `true` to disallow spaces in between for statement.
*

@@ -49,6 +49,6 @@ * #### Example

module.exports.prototype = {
configure: function(disallowSpacesInForStatement) {
configure: function(options) {
assert(
disallowSpacesInForStatement === true,
'disallowSpacesInForStatement option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -55,0 +55,0 @@ },

@@ -27,4 +27,5 @@ /**

* ```js
* function a() {}
* function a (){}
* function a () {}
* function a (){}
* ```

@@ -41,3 +42,3 @@ */

typeof options === 'object',
'disallowSpacesInFunctionDeclaration option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -48,3 +49,3 @@

options.beforeOpeningRoundBrace === true,
'disallowSpacesInFunctionDeclaration.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -57,3 +58,3 @@ );

options.beforeOpeningCurlyBrace === true,
'disallowSpacesInFunctionDeclaration.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -65,3 +66,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'disallowSpacesInFunctionDeclaration must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
this.getOptionName() + ' must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
);

@@ -68,0 +69,0 @@

@@ -29,4 +29,8 @@ /**

* ```js
* var x = function() {};
* var x = function (){};
* var x = function () {};
* var x = function a() {};
* var x = function a (){};
* var x = function a () {};
* ```

@@ -43,3 +47,3 @@ */

typeof options === 'object',
'disallowSpacesInFunctionExpression option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -50,3 +54,3 @@

options.beforeOpeningRoundBrace === true,
'disallowSpacesInFunctionExpression.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -59,3 +63,3 @@ );

options.beforeOpeningCurlyBrace === true,
'disallowSpacesInFunctionExpression.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -67,3 +71,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'disallowSpacesInFunctionExpression must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
this.getOptionName() + ' must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
);

@@ -70,0 +74,0 @@

@@ -5,3 +5,3 @@ /**

* Disallows space before `()` or `{}` in function expressions (both [named](#disallowspacesinnamedfunctionexpression)
* and [anonymous](#disallowspacesinanonymousfunctionexpression)).
* and [anonymous](#disallowspacesinanonymousfunctionexpression)) and function declarations.
*

@@ -16,3 +16,3 @@ * Type: `Object`

* ```js
* "disallowSpacesInFunctionExpression": {
* "disallowSpacesInFunction": {
* "beforeOpeningRoundBrace": true,

@@ -28,2 +28,3 @@ * "beforeOpeningCurlyBrace": true

* var x = function a(){};
* function a(){}
* ```

@@ -34,4 +35,11 @@ *

* ```js
* var x = function() {};
* var x = function (){};
* var x = function () {};
* var x = function a() {};
* var x = function a (){};
* var x = function a () {};
* function a() {}
* function a (){}
* function a () {}
* ```

@@ -48,3 +56,3 @@ */

typeof options === 'object',
'disallowSpacesInFunction option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -55,3 +63,3 @@

options.beforeOpeningRoundBrace === true,
'disallowSpacesInFunction.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -64,3 +72,3 @@ );

options.beforeOpeningCurlyBrace === true,
'disallowSpacesInFunction.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -72,3 +80,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'disallowSpacesInFunction must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
this.getOptionName() + ' must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
);

@@ -75,0 +83,0 @@

@@ -27,4 +27,5 @@ /**

* ```js
* var x = function a() {};
* var x = function a (){};
* var x = function a () {};
* var x = function a (){};
* ```

@@ -41,3 +42,3 @@ */

typeof options === 'object',
'disallowSpacesInNamedFunctionExpression option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -48,3 +49,3 @@

options.beforeOpeningRoundBrace === true,
'disallowSpacesInNamedFunctionExpression.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -57,3 +58,3 @@ );

options.beforeOpeningCurlyBrace === true,
'disallowSpacesInNamedFunctionExpression.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -65,3 +66,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'disallowSpacesInNamedFunctionExpression must have beforeOpeningCurlyBrace ' +
this.getOptionName() + ' must have beforeOpeningCurlyBrace ' +
'or beforeOpeningRoundBrace property'

@@ -68,0 +69,0 @@ );

/**
* Disallows space after opening array square bracket and before closing.
*
* Type: `Boolean` or `String`
* Types: `Boolean`, `String` or `Object`
*

@@ -62,3 +62,3 @@ * Values: `"all"` or `true` for strict mode, `"nested"` (*deprecated* use `"allExcept": [ "[", "]" ]`)

var error = 'disallowSpacesInsideArrayBrackets rule' +
var error = this.getOptionName() + ' rule' +
' requires string value "all" or "nested" or object';

@@ -100,5 +100,5 @@

var openBracket = file.getFirstNodeToken(node);
var afterOpen = file.getNextToken(openBracket);
var afterOpen = file.getNextToken(openBracket, {includeComments: true});
var closeBracket = file.getLastNodeToken(node);
var beforeClose = file.getPrevToken(closeBracket);
var beforeClose = file.getPrevToken(closeBracket, {includeComments: true});

@@ -114,4 +114,3 @@ // Skip for empty array brackets

nextToken: afterOpen,
spaces: 1,
message: 'One space required after opening bracket'
message: 'Illegal space after opening bracket'
});

@@ -124,4 +123,3 @@ }

nextToken: closeBracket,
spaces: 1,
message: 'One space required before closing bracket'
message: 'Illegal space before closing bracket'
});

@@ -128,0 +126,0 @@ }

/**
* Disallows space after opening square bracket and before closing.
*
* Type: `Boolean` or `Object`
* Types: `Boolean` or `Object`
*

@@ -50,3 +50,3 @@ * Values: `true` for strict mode, or `"allExcept": [ "[", "]" ]`

var error = 'disallowSpacesInsideBrackets rule requires string value true or object';
var error = this.getOptionName() + ' rule requires string value true or object';

@@ -53,0 +53,0 @@ if (isObject) {

/**
* Disallows space after opening object curly brace and before closing.
*
* Type: `Object`, `Boolean` or `String`
* Types: `Object`, `Boolean` or `String`
*

@@ -59,3 +59,3 @@ * Values: `"all"` or `true` for strict mode, `"nested"` (*deprecated* use `"allExcept": ['}']`)

var error = 'disallowSpacesInsideObjectBrackets rule' +
var error = this.getOptionName() + ' rule' +
' requires string "all" or "nested", true value or object';

@@ -62,0 +62,0 @@

/**
* Disallows space after opening round bracket and before closing.
*
* Type: `Object` or `Boolean`
* Types: `Object` or `Boolean`
*
* Values: `true` or Object with either `"only"` with array of tokens
* Values: Either `true` or Object with `"only"` property as an array of tokens
*

@@ -47,3 +47,3 @@ * #### Example

var error = 'disallowSpacesInsideParentheses option requires' +
var error = this.getOptionName() + ' option requires' +
' true or object value with "only" properties ';

@@ -81,10 +81,4 @@

function isCommentInRange(start, end) {
return file.getComments().some(function(comment) {
return start <= comment.range[0] && end >= comment.range[1];
});
}
file.iterateTokenByValue('(', function(token) {
var nextToken = file.getNextToken(token);
var nextToken = file.getNextToken(token, {includeComments: true});
var value = nextToken.value;

@@ -96,11 +90,11 @@

if (token.range[1] !== nextToken.range[0] &&
token.loc.end.line === nextToken.loc.start.line &&
!isCommentInRange(token.range[1], nextToken.range[0])) {
errors.add('Illegal space after opening round bracket', token.loc.end);
}
errors.assert.noWhitespaceBetween({
token: token,
nextToken: nextToken,
message: 'Illegal space after opening round bracket'
});
});
file.iterateTokenByValue(')', function(token) {
var prevToken = file.getPrevToken(token);
var prevToken = file.getPrevToken(token, {includeComments: true});
var value = prevToken.value;

@@ -121,7 +115,7 @@

if (prevToken.range[1] !== token.range[0] &&
prevToken.loc.end.line === token.loc.start.line &&
!isCommentInRange(prevToken.range[1], token.range[0])) {
errors.add('Illegal space before closing round bracket', prevToken.loc.end);
}
errors.assert.noWhitespaceBetween({
token: prevToken,
nextToken: token,
message: 'Illegal space before closing round bracket'
});
});

@@ -128,0 +122,0 @@ }

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -37,11 +37,7 @@ * JSHint: [`es3`](http://jshint.com/docs/options/#es3)

module.exports.prototype = {
configure: function(disallowTrailingComma) {
configure: function(options) {
assert(
typeof disallowTrailingComma === 'boolean',
'disallowTrailingComma option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
disallowTrailingComma === true,
'disallowTrailingComma option requires true value or should be removed'
);
},

@@ -48,0 +44,0 @@

/**
* Requires all lines to end on a non-whitespace character
*
* Type: `Boolean` or `String`
* Types: `Boolean` or `String`
*

@@ -53,8 +53,8 @@ * Values:

configure: function(disallowTrailingWhitespace) {
configure: function(options) {
assert(
disallowTrailingWhitespace === true || disallowTrailingWhitespace === 'ignoreEmptyLines',
'disallowTrailingWhitespace option requires true value or "ignoreEmptyLines" string'
options === true || options === 'ignoreEmptyLines',
this.getOptionName() + ' option requires a true value or "ignoreEmptyLines"'
);
this._ignoreEmptyLines = disallowTrailingWhitespace === 'ignoreEmptyLines';
this._ignoreEmptyLines = options === 'ignoreEmptyLines';
},

@@ -61,0 +61,0 @@

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -38,11 +38,7 @@ * #### Example

configure: function(disallowYodaConditions) {
configure: function(options) {
assert(
typeof disallowYodaConditions === 'boolean',
'disallowYodaConditions option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
disallowYodaConditions === true,
'disallowYodaConditions option requires true value or should be removed'
);
this._operatorIndex = {

@@ -49,0 +45,0 @@ '==': true,

/**
* Requires all lines to be at most the number of characters specified
*
* Type: `Integer` or `Object`
* Types: `Integer` or `Object`
*

@@ -51,3 +51,3 @@ * Values:

typeof maximumLineLength.value === 'number',
'maximumLineLength option requires the "value" property to be defined'
this.getOptionName() + ' option requires the "value" property to be defined'
);

@@ -68,3 +68,3 @@

typeof maximumLineLength === 'number',
'maximumLineLength option requires number value or options object'
this.getOptionName() + ' option requires number value or options object'
);

@@ -99,3 +99,3 @@

if (this._allowUrlComments) {
file.getComments().forEach(function(comment) {
file.iterateTokensByType(['Line', 'Block'], function(comment) {
for (var i = comment.loc.start.line; i <= comment.loc.end.line; i++) {

@@ -102,0 +102,0 @@ lines[i - 1] = lines[i - 1].replace(/(http|https|ftp):\/\/[^\s$]+/, '');

@@ -93,5 +93,8 @@ /**

var colon = file.getNextToken(keyToken);
if (colon.loc.start.column !== maxKeyEndPos + 1) {
errors.add('Alignment required', colon.loc.start);
}
errors.assert.spacesBetween({
token: keyToken,
nextToken: colon,
exactly: maxKeyEndPos - keyToken.loc.end.column + 1,
message: 'Alignment required'
});
});

@@ -98,0 +101,0 @@ });

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -45,6 +45,6 @@ * #### Example

module.exports.prototype = {
configure: function(requireAnonymousFunctions) {
configure: function(options) {
assert(
requireAnonymousFunctions === true,
'requireAnonymousFunctions option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -51,0 +51,0 @@ },

/**
* Requires blocks to begin and end with a newline
*
* Type: `Boolean` or `Integer`
* Types: `Boolean` or `Integer`
*

@@ -54,9 +54,9 @@ * Values: `true` validates all non-empty blocks,

configure: function(requireBlocksOnNewline) {
configure: function(options) {
assert(
requireBlocksOnNewline === true || typeof requireBlocksOnNewline === 'number',
'requireBlocksOnNewline option requires the value true or an Integer'
options === true || typeof options === 'number',
this.getOptionName() + ' option requires the value true or an Integer'
);
this._minStatements = requireBlocksOnNewline === true ? 0 : requireBlocksOnNewline;
this._minStatements = options === true ? 0 : options;
},

@@ -79,5 +79,7 @@

if (openingBracket.loc.start.line === nextToken.loc.start.line) {
errors.add('Missing newline after opening curly brace', openingBracket.loc.end);
}
errors.assert.differentLine({
token: openingBracket,
nextToken: nextToken,
message: 'Missing newline after opening curly brace'
});

@@ -87,5 +89,7 @@ var closingBracket = file.getLastNodeToken(node);

if (closingBracket.loc.start.line === prevToken.loc.start.line) {
errors.add('Missing newline before closing curly brace', prevToken.loc.end);
}
errors.assert.differentLine({
token: prevToken,
nextToken: closingBracket,
message: 'Missing newline before closing curly brace'
});
});

@@ -92,0 +96,0 @@ }

/**
* Requires identifiers to be camelCased or UPPERCASE_WITH_UNDERSCORES
*
* Type: `Boolean` or `String`
* Types: `Boolean` or `String`
*

@@ -62,9 +62,9 @@ * Values: `true` or `"ignoreProperties"`

configure: function(requireCamelCase) {
configure: function(options) {
assert(
requireCamelCase === true || requireCamelCase === 'ignoreProperties',
'requireCamelCaseOrUpperCaseIdentifiers option requires true value or `ignoreProperties` string'
options === true || options === 'ignoreProperties',
this.getOptionName() + ' option requires a true value or `ignoreProperties`'
);
this._ignoreProperties = (requireCamelCase === 'ignoreProperties');
this._ignoreProperties = (options === 'ignoreProperties');
},

@@ -71,0 +71,0 @@

/**
* Requires the first alphabetical character of a comment to be uppercase, unless it is part of a multi-line textblock.
*
* Type: `Boolean`
* By default, the prefix for inline comments `jscs` is ignored.
*
* Value: `true`
* Types: `Boolean` or `Object`
*
* Values:
* - `true`
* - `Object`:
* - `allExcept`: array of quoted exceptions
*
* #### Example

@@ -48,2 +53,40 @@ *

* ```
*
* ```js
* "requireCapitalizedComments": { allExcept: ["jshint"] }
* ```
*
* Valid:
*
* ```
* function sayHello() {
* \/* jshint: -W071 *\/
*
* // I can now say hello in lots of statements, if I like.
* return "Hello";
* }
* ```
*
* * Invalid:
*
* ```
* function sayHello() {
* \/* jshint: -W071 *\/
*
* // i can now say hello in lots of statements, if I like.
* return "Hello";
* }
* ```
*
* * Invalid:
*
* ```
* function sayHello() {
* \/* istanbul ignore next *\/
*
* // I'd like to ignore this statement in coverage reports.
* return "Hello";
* }
* ```
*
*/

@@ -56,7 +99,34 @@

module.exports.prototype = {
configure: function(requireCapitalizedComments) {
configure: function(options) {
// except comments that begin with `jscs`, since these are used to
// selectively enable/disable rules within a file
this._exceptions = {
'jscs': true
};
var optionName = this.getOptionName();
var isObject = typeof options === 'object';
assert(
requireCapitalizedComments === true,
'requireCapitalizedComments option requires a value of true or should be removed'
options === true ||
isObject,
optionName + ' option requires a true value ' +
'or an object with String[] `allExcept` property'
);
if (isObject) {
var exceptions = options.allExcept;
// verify items in `allExcept` property in object are string values
assert(
Array.isArray(exceptions) &&
exceptions.every(function(el) { return typeof el === 'string'; }),
'Property `allExcept` in ' + optionName + ' should be an array of strings'
);
for (var i = 0, l = exceptions.length; i < l; i++) {
this._exceptions[exceptions[i]] = true;
}
}
},

@@ -70,2 +140,3 @@

var inTextBlock = null;
var exceptions = this._exceptions;

@@ -75,3 +146,11 @@ var letterPattern = require('../../patterns/L');

file.getComments().forEach(function(comment) {
file.iterateTokensByType(['Line', 'Block'], function(comment) {
// strip leading whitespace and any asterisks
// split on whitespace and colons
var splitComment = comment.value.replace(/(^\s+|[\*])/g, '').split(/[\s\:]/g);
if (exceptions[splitComment[0]]) {
return;
}
var stripped = comment.value.replace(/[\n\s\*]/g, '');

@@ -78,0 +157,0 @@ var firstChar = stripped[0];

/**
* Requires constructors to be capitalized (except for `this`)
*
* Type: `Boolean` or `Object`
* Types: `Boolean` or `Object`
*

@@ -39,10 +39,10 @@ * Values: `true` or Object with `allExcept` Array of quoted identifiers which are exempted

module.exports.prototype = {
configure: function(requireCapitalizedConstructors) {
configure: function(options) {
assert(
requireCapitalizedConstructors === true || Array.isArray(requireCapitalizedConstructors.allExcept),
'requireCapitalizedConstructors option requires object of exceptions or true value'
options === true || Array.isArray(options.allExcept),
this.getOptionName() + ' option requires a true value or an object of exceptions'
);
this._allowedConstructors = {};
var allExcept = requireCapitalizedConstructors.allExcept;
var allExcept = options.allExcept;
if (allExcept) {

@@ -49,0 +49,0 @@ for (var i = 0, l = allExcept.length; i < l; i++) {

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -43,11 +43,7 @@ * JSHint: [`laxcomma`](http://www.jshint.com/docs/options/#laxcomma)

configure: function(requireCommaBeforeLineBreak) {
configure: function(options) {
assert(
typeof requireCommaBeforeLineBreak === 'boolean',
'requireCommaBeforeLineBreak option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
requireCommaBeforeLineBreak === true,
'requireCommaBeforeLineBreak option requires true value or should be removed'
);
},

@@ -60,10 +56,8 @@

check: function(file, errors) {
file.iterateTokensByType('Punctuator', function(token) {
if (token.value === ',') {
errors.assert.sameLine({
token: file.getPrevToken(token),
nextToken: token,
message: 'Commas should not be placed on new line'
});
}
file.iterateTokensByTypeAndValue('Punctuator', ',', function(token) {
errors.assert.sameLine({
token: file.getPrevToken(token),
nextToken: token,
message: 'Commas should not be placed on new line'
});
});

@@ -70,0 +64,0 @@ }

/**
* Requires curly braces after statements.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -51,3 +51,3 @@ * Values: Array of quoted keywords or `true` to require curly braces after the following keywords:

Array.isArray(statementTypes) || statementTypes === true,
'requireCurlyBraces option requires array or true value'
this.getOptionName() + ' option requires array or true value'
);

@@ -54,0 +54,0 @@

/**
* Requires member expressions to use dot notation when possible
*
* Type: `Boolean` or `String`
* Types: `Boolean` or `String`
*

@@ -83,8 +83,8 @@ * Values:

configure: function(requireDotNotation) {
configure: function(options) {
assert(
requireDotNotation === true || requireDotNotation === 'except_snake_case',
'requireDotNotation option requires true value or "except_snake_case" string'
options === true || options === 'except_snake_case',
this.getOptionName() + ' option requires a true value or "except_snake_case"'
);
this._exceptSnakeCase = requireDotNotation === 'except_snake_case';
this._exceptSnakeCase = options === 'except_snake_case';
},

@@ -91,0 +91,0 @@

@@ -11,3 +11,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -60,6 +60,6 @@ * #### Example

module.exports.prototype = {
configure: function(requireFunctionDeclarations) {
configure: function(options) {
assert(
requireFunctionDeclarations === true,
'requireFunctionDeclarations option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -66,0 +66,0 @@ },

@@ -43,7 +43,4 @@ /**

configure: function(keywords) {
assert(Array.isArray(keywords), 'requireKeywordsOnNewLine option requires array value');
this._keywordIndex = {};
for (var i = 0, l = keywords.length; i < l; i++) {
this._keywordIndex[keywords[i]] = true;
}
assert(Array.isArray(keywords), this.getOptionName() + ' option requires array value');
this._keywords = keywords;
},

@@ -56,11 +53,7 @@

check: function(file, errors) {
var keywordIndex = this._keywordIndex;
file.iterateTokensByType('Keyword', function(token) {
if (keywordIndex[token.value]) {
errors.assert.differentLine({
token: file.getPrevToken(token),
nextToken: token
});
}
file.iterateTokensByTypeAndValue('Keyword', this._keywords, function(token) {
errors.assert.differentLine({
token: file.getPrevToken(token),
nextToken: token
});
});

@@ -67,0 +60,0 @@ }

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -47,11 +47,7 @@ * #### Example

configure: function(requireLineBreakAfterVariableAssignment) {
configure: function(options) {
assert(
typeof requireLineBreakAfterVariableAssignment === 'boolean',
'requireLineFeedAtFileEnd option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
requireLineBreakAfterVariableAssignment === true,
'requireLineFeedAtFileEnd option requires true value or should be removed'
);
},

@@ -65,15 +61,20 @@

var lastDeclaration;
file.iterate(function(node) {
file.iterateNodesByType('VariableDeclaration', function(node) {
if (node.parentNode.type === 'ForStatement' ||
node.parentNode.type === 'ForInStatement' ||
node.parentNode.type === 'ForOfStatement') {
return;
}
if (node && node.type === 'VariableDeclaration') {
for (var i = 0; i < node.declarations.length; i++) {
var thisDeclaration = node.declarations[i];
if (thisDeclaration.parentNode.kind === 'var') {
if (lastDeclaration && lastDeclaration.init &&
thisDeclaration.loc.start.line === lastDeclaration.loc.end.line) {
errors.add('Variable assignments should be followed by new line',
thisDeclaration.loc.start.line, thisDeclaration.loc.start.column);
}
lastDeclaration = thisDeclaration;
for (var i = 0; i < node.declarations.length; i++) {
var thisDeclaration = node.declarations[i];
if (thisDeclaration.parentNode.kind === 'var') {
if (lastDeclaration && lastDeclaration.init) {
errors.assert.differentLine({
token: lastDeclaration,
nextToken: thisDeclaration,
message: 'Variable assignments should be followed by new line'
});
}
lastDeclaration = thisDeclaration;
}

@@ -80,0 +81,0 @@ }

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -22,11 +22,7 @@ * #### Example

configure: function(requireLineFeedAtFileEnd) {
configure: function(options) {
assert(
typeof requireLineFeedAtFileEnd === 'boolean',
'requireLineFeedAtFileEnd option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
requireLineFeedAtFileEnd === true,
'requireLineFeedAtFileEnd option requires true value or should be removed'
);
},

@@ -39,8 +35,11 @@

check: function(file, errors) {
var lines = file.getLines();
if (lines[lines.length - 1] !== '') {
errors.add('Missing line feed at file end', lines.length, 0);
}
var lastToken = file.getLastToken();
var prevToken = file.getPrevToken(lastToken, {includeComments: true});
errors.assert.differentLine({
token: prevToken,
nextToken: lastToken,
message: 'Missing line feed at file end'
});
}
};
/**
* Requires multiple `var` declaration.
*
* Type: `Boolean` or `String`
* Types: `Boolean` or `String`
*

@@ -100,14 +100,9 @@ * Values: `true` or `"onevar"`

module.exports.prototype = {
configure: function(requireMultipleVarDecl) {
configure: function(options) {
assert(
typeof requireMultipleVarDecl === 'boolean' ||
typeof requireMultipleVarDecl === 'string',
'requireMultipleVarDecl option requires boolean or string'
options === true || options === 'onevar',
this.getOptionName() + ' option requires a true value or `onevar`'
);
assert(
requireMultipleVarDecl === true || requireMultipleVarDecl === 'onevar',
'requireMultipleVarDecl option requires true value or `onevar` string'
);
this._check = typeof requireMultipleVarDecl === 'string' ? onevar : consecutive;
this._check = typeof options === 'string' ? onevar : consecutive;
},

@@ -114,0 +109,0 @@

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -78,11 +78,7 @@ * #### Example

module.exports.prototype = {
configure: function(requireNewlineBeforeBlockStatements) {
configure: function(options) {
assert(
typeof requireNewlineBeforeBlockStatements === 'boolean',
'requireNewlineBeforeBlockStatements option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
requireNewlineBeforeBlockStatements === true,
'requireNewlineBeforeBlockStatements option requires true value or should be removed'
);
},

@@ -89,0 +85,0 @@

/**
* Requires operators to appear before line breaks and not after.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -61,3 +61,3 @@ * Values: Array of quoted operators or `true` to require all possible binary operators to appear before line breaks

Array.isArray(operators) || isTrue,
'requireOperatorBeforeLineBreak option requires array value or true value'
this.getOptionName() + ' option requires array value or true value'
);

@@ -64,0 +64,0 @@

/**
* Requires an empty line above the specified keywords unless the keyword is the first expression in a block.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -73,3 +73,3 @@ * Values: Array of quoted types or `true` to require padding new lines before all of the keywords below.

assert(Array.isArray(keywords) || keywords === true,
'requirePaddingNewlinesBeforeKeywords option requires array or true value');
this.getOptionName() + ' option requires array or true value');

@@ -80,6 +80,3 @@ if (keywords === true) {

this._keywordIndex = {};
for (var i = 0, l = keywords.length; i < l; i++) {
this._keywordIndex[keywords[i]] = true;
}
this._keywords = keywords;
},

@@ -93,29 +90,27 @@

var excludedTokens = [':', ',', '(', '='];
var keywordIndex = this._keywordIndex;
file.iterateTokensByType('Keyword', function(token) {
if (keywordIndex[token.value]) {
var prevToken = file.getPrevToken(token);
file.iterateTokensByTypeAndValue('Keyword', this._keywords, function(token) {
var prevToken = file.getPrevToken(token);
// Handle special case of 'else if' construct.
if (token.value === 'if' && prevToken && prevToken.value === 'else') {
return;
// Handling for special cases.
} else if (prevToken && excludedTokens.indexOf(prevToken.value) > -1) {
return;
}
// Handle special case of 'else if' construct.
if (token.value === 'if' && prevToken && prevToken.value === 'else') {
return;
// Handling for special cases.
} else if (prevToken && excludedTokens.indexOf(prevToken.value) > -1) {
return;
}
// Handle all other cases
// The { character is there to handle the case of a matching token which happens to be the first
// statement in a block
// The ) character is there to handle the case of `if (...) matchingKeyword` in which case
// requiring padding would break the statement
if (prevToken && prevToken.value !== '{' && prevToken.value !== ')' &&
token.loc.start.line - prevToken.loc.end.line < 2) {
errors.add(
'Keyword `' + token.value + '` should have an empty line above it',
token.loc.start.line,
token.loc.start.column
);
}
// Handle all other cases
// The { character is there to handle the case of a matching token which happens to be the first
// statement in a block
// The ) character is there to handle the case of `if (...) matchingKeyword` in which case
// requiring padding would break the statement
if (prevToken && prevToken.value !== '{' && prevToken.value !== ')') {
errors.assert.linesBetween({
token: prevToken,
nextToken: token,
atLeast: 2,
message: 'Keyword `' + token.value + '` should have an empty line above it'
});
}

@@ -122,0 +117,0 @@ });

/**
* Requires blocks to begin and end with 2 newlines
*
* Type: `Boolean` or `Integer`
* Types: `Boolean` or `Integer`
*

@@ -68,9 +68,9 @@ * Values: `true` validates all non-empty blocks,

configure: function(requirePaddingNewlinesInBlocks) {
configure: function(options) {
assert(
requirePaddingNewlinesInBlocks === true || typeof requirePaddingNewlinesInBlocks === 'number',
'requirePaddingNewlinesInBlocks option requires the value true or an Integer'
options === true || typeof options === 'number',
this.getOptionName() + ' option requires the value true or an Integer'
);
this._minStatements = requirePaddingNewlinesInBlocks === true ? 0 : requirePaddingNewlinesInBlocks;
this._minStatements = options === true ? 0 : options;
},

@@ -91,14 +91,18 @@

var openingBracket = file.getFirstNodeToken(node);
var nextToken = file.getCommentAfterToken(openingBracket) || file.getNextToken(openingBracket);
if (nextToken.loc.start.line - openingBracket.loc.start.line < 2) {
errors.add('Expected a padding newline after opening curly brace', openingBracket.loc.end);
}
errors.assert.linesBetween({
token: openingBracket,
nextToken: file.getNextToken(openingBracket, {includeComments: true}),
atLeast: 2,
message: 'Expected a padding newline after opening curly brace'
});
var closingBracket = file.getLastNodeToken(node);
var prevToken = file.getCommentBeforeToken(closingBracket) || file.getPrevToken(closingBracket);
if (closingBracket.loc.start.line - prevToken.loc.start.line < 2) {
errors.add('Expected a padding newline before closing curly brace', prevToken.loc.end);
}
errors.assert.linesBetween({
token: file.getPrevToken(closingBracket, {includeComments: true}),
nextToken: closingBracket,
atLeast: 2,
message: 'Expected a padding newline before closing curly brace'
});
});

@@ -105,0 +109,0 @@ }

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -44,8 +44,4 @@ * #### Example

assert(
typeof value === 'boolean',
'requirePaddingNewLinesInObjects option requires boolean value'
);
assert(
value === true,
'requirePaddingNewLinesInObjects option requires true value or should be removed'
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -67,12 +63,15 @@ },

if (openingBracket.loc.end.line === nextToken.loc.start.line) {
errors.add('Missing newline after opening curly brace', nextToken.loc.start);
}
errors.assert.differentLine({
token: openingBracket,
nextToken: nextToken,
message: 'Missing newline after opening curly brace'
});
var closingBracket = file.getLastNodeToken(node);
var prevToken = file.getPrevToken(closingBracket);
if (closingBracket.loc.start.line === prevToken.loc.end.line) {
errors.add('Missing newline before closing curly brace', closingBracket.loc.start);
}
errors.assert.differentLine({
token: file.getPrevToken(closingBracket),
nextToken: closingBracket,
message: 'Missing newline before closing curly brace'
});
});

@@ -79,0 +78,0 @@ }

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -44,11 +44,7 @@ * JSHint: [`immed`](http://www.jshint.com/docs/options/#immed)

configure: function(requireParenthesesAroundIIFE) {
configure: function(options) {
assert(
typeof requireParenthesesAroundIIFE === 'boolean',
'requireParenthesesAroundIIFE option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
requireParenthesesAroundIIFE === true,
'requireParenthesesAroundIIFE option requires true value or should be removed'
);
},

@@ -55,0 +51,0 @@

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -12,3 +12,3 @@ * #### Example

* ```js
* "requiresQuotedKeysInObjects": true
* "requireQuotedKeysInObjects": true
* ```

@@ -35,6 +35,6 @@ *

configure: function(requireQuotedKeysInObjects) {
configure: function(options) {
assert(
requireQuotedKeysInObjects === true,
this.getOptionName() + ' option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -41,0 +41,0 @@ },

/**
* Disallows sticking binary operators to the right.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -51,3 +51,3 @@ * Values: Array of quoted operators or `true` to require space after all possible binary operators

Array.isArray(operators) || isTrue,
'requireSpaceAfterBinaryOperators option requires array or true value'
this.getOptionName() + ' option requires array or true value'
);

@@ -74,10 +74,8 @@

if (operators[',']) {
file.iterateTokensByType('Punctuator', function(token) {
if (token.value === ',') {
errors.assert.whitespaceBetween({
token: token,
nextToken: file.getNextToken(token),
message: 'Operator , should not stick to following expression'
});
}
file.iterateTokensByTypeAndValue('Punctuator', ',', function(token) {
errors.assert.whitespaceBetween({
token: token,
nextToken: file.getNextToken(token),
message: 'Operator , should not stick to following expression'
});
});

@@ -84,0 +82,0 @@ }

/**
* Requires space after keyword.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -45,7 +45,2 @@ * Values: Array of quoted keywords or `true` to require all of the keywords below to have a space afterward.

var assert = require('assert');
var util = require('util');
var texts = [
'Missing space after "%s" keyword',
'Should be one space instead of %d, after "%s" keyword'
];

@@ -61,3 +56,3 @@ var defaultKeywords = require('../utils').spacedKeywords;

Array.isArray(keywords) || keywords === true,
'requireSpaceAfterKeywords option requires array or true value');
this.getOptionName() + ' option requires array or true value');

@@ -68,6 +63,3 @@ if (keywords === true) {

this._keywordIndex = {};
for (var i = 0, l = keywords.length; i < l; i++) {
this._keywordIndex[keywords[i]] = true;
}
this._keywords = keywords;
},

@@ -80,25 +72,15 @@

check: function(file, errors) {
var keywordIndex = this._keywordIndex;
file.iterateTokensByTypeAndValue('Keyword', this._keywords, function(token) {
var nextToken = file.getNextToken(token, {includeComments: true});
file.iterateTokensByType(['Keyword'], function(token) {
if (keywordIndex[token.value]) {
var nextToken = file.getCommentAfterToken(token) || file.getNextToken(token);
var diff = nextToken.range[0] - token.range[1];
if (nextToken.type === 'Punctuator' && nextToken.value === ';') {
return;
}
if (nextToken.loc.end.line === token.loc.start.line &&
diff !== 1
) {
if (nextToken.type !== 'Punctuator' || nextToken.value !== ';') {
errors.add(
util.format.apply(null,
diff === 0 ?
[texts[0], token.value] :
[texts[1], diff, token.value]
),
nextToken.loc.start.line,
nextToken.loc.start.column
);
}
}
}
errors.assert.spacesBetween({
token: token,
nextToken: nextToken,
exactly: 1,
message: 'One space required after "' + token.value + '" keyword'
});
});

@@ -105,0 +87,0 @@ }

/**
* Requires that a line comment (`//`) be followed by a space.
*
* Type: `Boolean` or `Object` or `String`
* Types: `Boolean`, `Object` or `String`
*
* Values:
* - `true`
* - `"allowSlash"` (*deprecated* use `"except": ["/"]`) allows `/// ` format
* - `"allowSlash"` (*deprecated* use `"allExcept": ["/"]`) allows `/// ` format
* - `Object`:

@@ -40,8 +40,8 @@ * - `allExcept`: array of allowed strings before space `//(here) `

configure: function(requireSpaceAfterLineComment) {
configure: function(options) {
assert(
requireSpaceAfterLineComment === true ||
requireSpaceAfterLineComment === 'allowSlash' ||
typeof requireSpaceAfterLineComment === 'object',
'requireSpaceAfterLineComment option requires the value `true` ' +
options === true ||
options === 'allowSlash' ||
typeof options === 'object',
this.getOptionName() + ' option requires a true value ' +
'or an object with String[] `allExcept` property'

@@ -52,6 +52,6 @@ );

assert(
typeof requireSpaceAfterLineComment !== 'object' ||
Array.isArray(requireSpaceAfterLineComment.allExcept) &&
typeof requireSpaceAfterLineComment.allExcept[0] === 'string',
'Property `allExcept` in requireSpaceAfterLineComment should be an array of strings'
typeof options !== 'object' ||
Array.isArray(options.allExcept) &&
typeof options.allExcept[0] === 'string',
'Property `allExcept` in ' + this.getOptionName() + ' should be an array of strings'
);

@@ -62,4 +62,4 @@

// need to drop allowSlash support in 2.0. Fixes #697
this._allExcept = requireSpaceAfterLineComment === 'allowSlash' ? ['/'] :
requireSpaceAfterLineComment.allExcept || [];
this._allExcept = options === 'allowSlash' ? ['/'] :
options.allExcept || [];
},

@@ -72,26 +72,23 @@

check: function(file, errors) {
var comments = file.getComments();
var allExcept = this._allExcept;
comments.forEach(function(comment) {
if (comment.type === 'Line') {
var value = comment.value;
file.iterateTokensByType('Line', function(comment) {
var value = comment.value;
// cutout exceptions
allExcept.forEach(function(el) {
if (value.indexOf(el) === 0) {
value = value.substr(el.length);
}
});
if (value.length === 0) {
return;
// cutout exceptions
allExcept.forEach(function(el) {
if (value.indexOf(el) === 0) {
value = value.substr(el.length);
}
});
if (value[0] !== ' ') {
errors.add('Missing space after line comment', comment.loc.start);
}
if (value.length === 0) {
return;
}
if (value[0] !== ' ') {
errors.add('Missing space after line comment', comment.loc.start);
}
});
}
};

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -31,6 +31,6 @@ * #### Example

configure: function(requireSpaceAfterObjectKeys) {
configure: function(options) {
assert(
requireSpaceAfterObjectKeys === true,
this.getOptionName() + ' option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -37,0 +37,0 @@ },

/**
* Disallows sticking unary operators to the right.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -6,0 +6,0 @@ * Values: Array of quoted operators or `true` to require space after prefix for all unary operators

/**
* Disallows sticking binary operators to the left.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -55,3 +55,3 @@ * Values: Array of quoted operators or `true` to require space before all possible binary operators

Array.isArray(operators) || isTrue,
'requireSpaceBeforeBinaryOperators option requires array or true value'
this.getOptionName() + ' option requires array or true value'
);

@@ -78,10 +78,8 @@

if (operators[',']) {
file.iterateTokensByType('Punctuator', function(token) {
if (token.value === ',') {
errors.assert.whitespaceBetween({
token: file.getPrevToken(token),
nextToken: token,
message: 'Operator , should not stick to preceding expression'
});
}
file.iterateTokensByTypeAndValue('Punctuator', ',', function(token) {
errors.assert.whitespaceBetween({
token: file.getPrevToken(token),
nextToken: token,
message: 'Operator , should not stick to preceding expression'
});
});

@@ -88,0 +86,0 @@ }

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -57,11 +57,7 @@ * #### Example

module.exports.prototype = {
configure: function(requireSpaceBeforeBlockStatements) {
configure: function(options) {
assert(
typeof requireSpaceBeforeBlockStatements === 'boolean',
'requireSpaceBeforeBlockStatements option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
requireSpaceBeforeBlockStatements === true,
'requireSpaceBeforeBlockStatements option requires true value or should be removed'
);
},

@@ -77,6 +73,6 @@

errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: file.getPrevToken(first),
nextToken: first,
spaces: 1,
exactly: 1,
disallowNewLine: true,

@@ -83,0 +79,0 @@ message: 'One space required before opening brace for block expressions'

/**
* Requires space before keyword.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -36,7 +36,2 @@ * Values: Array of quoted keywords or `true` to require all possible keywords to have a preceding space.

var assert = require('assert');
var util = require('util');
var texts = [
'Missing space before "%s" keyword',
'Should be one space instead of %d, before "%s" keyword'
];

@@ -52,3 +47,3 @@ var defaultKeywords = require('../utils').spacedKeywords;

Array.isArray(keywords) || keywords === true,
'requireSpaceAfterKeywords option requires array or true value');
this.getOptionName() + ' option requires array or true value');

@@ -59,6 +54,3 @@ if (keywords === true) {

this._keywordIndex = {};
for (var i = 0, l = keywords.length; i < l; i++) {
this._keywordIndex[keywords[i]] = true;
}
this._keywords = keywords;
},

@@ -71,28 +63,14 @@

check: function(file, errors) {
var keywordIndex = this._keywordIndex;
file.iterateTokensByTypeAndValue('Keyword', this._keywords, function(token) {
var prevToken = file.getPrevToken(token, {includeComments: true});
if (!prevToken || prevToken.isComment) {
return;
}
file.iterateTokensByType(['Keyword'], function(token) {
if (keywordIndex[token.value]) {
var prevToken = file.getPrevToken(token);
if (!prevToken) {
return;
}
prevToken = file.getCommentBeforeToken(token) || prevToken;
var diff = token.range[0] - prevToken.range[1];
if (prevToken.loc.end.line === token.loc.start.line && diff !== 1) {
if (prevToken.type !== 'Punctuator' || prevToken.value !== ';') {
errors.add(
util.format.apply(null,
diff === 0 ?
[texts[0], token.value] :
[texts[1], diff, token.value]
),
token.loc.start.line,
token.loc.start.column
);
}
}
if (prevToken.type !== 'Punctuator' || prevToken.value !== ';') {
errors.assert.whitespaceBetween({
token: prevToken,
nextToken: token,
message: 'Missing space before "' + token.value + '" keyword'
});
}

@@ -99,0 +77,0 @@ });

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -31,6 +31,6 @@ * #### Example

configure: function(disallow) {
configure: function(options) {
assert(
disallow === true,
this.getOptionName() + ' option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -37,0 +37,0 @@ },

/**
* Disallows sticking unary operators to the left.
*
* Type: `Array` or `Boolean`
* Types: `Array` or `Boolean`
*

@@ -6,0 +6,0 @@ * Values: Array of quoted operators or `true` to require space before postfix for all unary operators

@@ -33,11 +33,7 @@ /**

configure: function(requireSpaceBetweenArguments) {
configure: function(options) {
assert(
typeof requireSpaceBetweenArguments === 'boolean',
this.getOptionName() + ' option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
requireSpaceBetweenArguments === true,
this.getOptionName() + ' option requires true value or should be removed'
);
},

@@ -44,0 +40,0 @@

@@ -47,3 +47,3 @@ /**

typeof options === 'object',
'requireSpacesInAnonymousFunctionExpression option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -54,3 +54,3 @@

options.beforeOpeningRoundBrace === true,
'requireSpacesInAnonymousFunctionExpression.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -63,3 +63,3 @@ );

options.beforeOpeningCurlyBrace === true,
'requireSpacesInAnonymousFunctionExpression.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -71,3 +71,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'requireSpacesInAnonymousFunctionExpression must have beforeOpeningCurlyBrace ' +
this.getOptionName() + ' must have beforeOpeningCurlyBrace ' +
' or beforeOpeningRoundBrace property'

@@ -74,0 +74,0 @@ );

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -33,6 +33,6 @@ * #### Example

module.exports.prototype = {
configure: function(requireSpacesInCallExpression) {
configure: function(options) {
assert(
requireSpacesInCallExpression === true,
'requireSpacesInCallExpression option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -39,0 +39,0 @@ },

/**
* Requires space before and/or after `?` or `:` in conditional expressions.
*
* Type: `Object` or `Boolean`
* Types: `Object` or `Boolean`
*

@@ -62,3 +62,3 @@ * Values: `"afterTest"`, `"beforeConsequent"`, `"afterConsequent"`, `"beforeAlternate"` as child properties,

typeof options === 'object',
optionName + ' option must be an object or boolean true'
optionName + ' option requires a true value or an object'
);

@@ -95,3 +95,2 @@

file.iterateNodesByType(['ConditionalExpression'], function(node) {
var test = node.test;
var consequent = node.consequent;

@@ -107,8 +106,7 @@ var alternate = node.alternate;

token = file.getPrevToken(questionMarkToken);
if (token.loc.end.column === questionMarkToken.loc.start.column) {
errors.add(
'Missing space after test',
test.loc.end
);
}
errors.assert.whitespaceBetween({
token: token,
nextToken: questionMarkToken,
message: 'Missing space after test'
});
}

@@ -118,8 +116,7 @@

token = file.getNextToken(questionMarkToken);
if (token.loc.start.column === questionMarkToken.loc.end.column) {
errors.add(
'Missing space before consequent',
consequent.loc.start
);
}
errors.assert.whitespaceBetween({
token: questionMarkToken,
nextToken: token,
message: 'Missing space before consequent'
});
}

@@ -129,8 +126,7 @@

token = file.getPrevToken(colonToken);
if (token.loc.end.column === colonToken.loc.start.column) {
errors.add(
'Missing space after consequent',
consequent.loc.end
);
}
errors.assert.whitespaceBetween({
token: token,
nextToken: colonToken,
message: 'Missing space after consequent'
});
}

@@ -140,8 +136,7 @@

token = file.getNextToken(colonToken);
if (token.loc.start.column === colonToken.loc.end.column) {
errors.add(
'Missing space before alternate',
alternate.loc.start
);
}
errors.assert.whitespaceBetween({
token: colonToken,
nextToken: token,
message: 'Missing space before alternate'
});
}

@@ -148,0 +143,0 @@ }.bind(this));

@@ -6,3 +6,3 @@ /**

*
* Values: `true` to requires spaces inbetween for statement.
* Value: `true` to requires spaces inbetween for statement.
*

@@ -49,6 +49,6 @@ * #### Example

module.exports.prototype = {
configure: function(requireSpacesInForStatement) {
configure: function(options) {
assert(
requireSpacesInForStatement === true,
'requireSpacesInForStatement option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -65,6 +65,6 @@ },

var testToken = file.getFirstNodeToken(node.test);
errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: file.getPrevToken(testToken),
nextToken: testToken,
spaces: 1,
exactly: 1,
message: 'One space required after semicolon'

@@ -75,6 +75,6 @@ });

var updateToken = file.getFirstNodeToken(node.update);
errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: file.getPrevToken(updateToken),
nextToken: updateToken,
spaces: 1,
exactly: 1,
message: 'One space required after semicolon'

@@ -81,0 +81,0 @@ });

@@ -29,2 +29,3 @@ /**

* function a (){}
* function a(){}
* ```

@@ -41,3 +42,3 @@ */

typeof options === 'object',
'requireSpacesInFunctionDeclaration option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -48,3 +49,3 @@

options.beforeOpeningRoundBrace === true,
'requireSpacesInFunctionDeclaration.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -57,3 +58,3 @@ );

options.beforeOpeningCurlyBrace === true,
'requireSpacesInFunctionDeclaration.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -65,3 +66,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'requireSpacesInFunctionDeclaration must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
this.getOptionName() + ' must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
);

@@ -68,0 +69,0 @@

@@ -30,2 +30,6 @@ /**

* var x = function() {};
* var x = function (){};
* var x = function(){};
* var x = function a() {};
* var x = function a (){};
* var x = function a(){};

@@ -43,3 +47,3 @@ * ```

typeof options === 'object',
'requireSpacesInFunctionExpression option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -50,3 +54,3 @@

options.beforeOpeningRoundBrace === true,
'requireSpacesInFunctionExpression.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -59,3 +63,3 @@ );

options.beforeOpeningCurlyBrace === true,
'requireSpacesInFunctionExpression.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -67,3 +71,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'requireSpacesInFunctionExpression must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
this.getOptionName() + ' must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
);

@@ -70,0 +74,0 @@

@@ -5,3 +5,3 @@ /**

* Requires space before `()` or `{}` in function expressions (both [named](#requirespacesinnamedfunctionexpression)
* and [anonymous](#requirespacesinanonymousfunctionexpression)).
* and [anonymous](#requirespacesinanonymousfunctionexpression)) and function declarations.
*

@@ -16,3 +16,3 @@ * Type: `Object`

* ```js
* "requireSpacesInFunctionExpression": {
* "requireSpacesInFunction": {
* "beforeOpeningRoundBrace": true,

@@ -28,2 +28,3 @@ * "beforeOpeningCurlyBrace": true

* var x = function a () {};
* function a () {}
* ```

@@ -35,3 +36,10 @@ *

* var x = function() {};
* var x = function (){};
* var x = function(){};
* var x = function a() {};
* var x = function a (){};
* var x = function a(){};
* function a() {}
* function a (){}
* function a(){}
* ```

@@ -48,3 +56,3 @@ */

typeof options === 'object',
'requireSpacesInFunction option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -55,3 +63,3 @@

options.beforeOpeningRoundBrace === true,
'requireSpacesInFunction.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -64,3 +72,3 @@ );

options.beforeOpeningCurlyBrace === true,
'requireSpacesInFunction.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -72,3 +80,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'requireSpacesInFunction must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
this.getOptionName() + ' must have beforeOpeningCurlyBrace or beforeOpeningRoundBrace property'
);

@@ -75,0 +83,0 @@

@@ -28,2 +28,3 @@ /**

* var x = function a() {};
* var x = function a (){};
* var x = function a(){};

@@ -41,3 +42,3 @@ * ```

typeof options === 'object',
'requireSpacesInNamedFunctionExpression option must be the object'
this.getOptionName() + ' option must be the object'
);

@@ -48,3 +49,3 @@

options.beforeOpeningRoundBrace === true,
'requireSpacesInNamedFunctionExpression.beforeOpeningRoundBrace ' +
this.getOptionName() + '.beforeOpeningRoundBrace ' +
'property requires true value or should be removed'

@@ -57,3 +58,3 @@ );

options.beforeOpeningCurlyBrace === true,
'requireSpacesInNamedFunctionExpression.beforeOpeningCurlyBrace ' +
this.getOptionName() + '.beforeOpeningCurlyBrace ' +
'property requires true value or should be removed'

@@ -65,3 +66,3 @@ );

options.beforeOpeningCurlyBrace || options.beforeOpeningRoundBrace,
'requireSpacesInNamedFunctionExpression must have beforeOpeningCurlyBrace ' +
this.getOptionName() + ' must have beforeOpeningCurlyBrace ' +
'or beforeOpeningRoundBrace property'

@@ -68,0 +69,0 @@ );

/**
* Requires space after opening array square bracket and before closing.
*
* Type: `String` or `Object`
* Types: `String` or `Object`
*

@@ -61,3 +61,3 @@ * Values: `"all"` for strict mode, `"allButNested"` (*deprecated* use `"allExcept": [ "[", "]"]`)

var error = 'requireSpacesInsideArrayBrackets rule' +
var error = this.getOptionName() + ' rule' +
' requires string value "all" or "allButNested" or object';

@@ -99,5 +99,5 @@

var openBracket = file.getFirstNodeToken(node);
var afterOpen = file.getNextToken(openBracket);
var afterOpen = file.getNextToken(openBracket, {includeComments: true});
var closeBracket = file.getLastNodeToken(node);
var beforeClose = file.getPrevToken(closeBracket);
var beforeClose = file.getPrevToken(closeBracket, {includeComments: true});

@@ -110,6 +110,6 @@ // Skip for empty array brackets

if (!(afterOpen.value in exceptions)) {
errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: openBracket,
nextToken: afterOpen,
spaces: 1,
exactly: 1,
message: 'One space required after opening bracket'

@@ -120,6 +120,6 @@ });

if (!(beforeClose.value in exceptions)) {
errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: beforeClose,
nextToken: closeBracket,
spaces: 1,
exactly: 1,
message: 'One space required before closing bracket'

@@ -126,0 +126,0 @@ });

/**
* Requires space after opening square bracket and before closing.
*
* Type: `Boolean` or `Object`
* Types: `Boolean` or `Object`
*

@@ -50,3 +50,3 @@ * Values: `true` for strict mode, or `"allExcept": [ "[", "]"]`

var error = 'requireSpacesInsideBrackets rule requires string value true or object';
var error = this.getOptionName() + ' rule requires string value true or object';

@@ -88,6 +88,6 @@ if (isObject) {

errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: token,
nextToken: nextToken,
spaces: 1,
exactly: 1,
message: 'One space required after opening bracket'

@@ -110,6 +110,6 @@ });

errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: prevToken,
nextToken: token,
spaces: 1,
exactly: 1,
message: 'One space required before closing bracket'

@@ -116,0 +116,0 @@ });

/**
* Requires space after opening object curly brace and before closing.
*
* Type: `Object` or `String`
* Types: `Object` or `String`
*

@@ -60,3 +60,3 @@ * Values: `"all"` for strict mode, `"allButNested"` (*deprecated* use `"allExcept": ['}']`)

var error = 'requireSpacesInsideObjectBrackets rule' +
var error = this.getOptionName() + ' rule' +
' requires string value \'all\' or \'allButNested\' or object';

@@ -105,6 +105,6 @@

errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: openingBracket,
nextToken: nextToken,
spaces: 1,
exactly: 1,
message: 'One space required after opening curly brace'

@@ -120,6 +120,6 @@ });

errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: prevToken,
nextToken: closingBracket,
spaces: 1,
exactly: 1,
message: 'One space required before closing curly brace'

@@ -126,0 +126,0 @@ });

/**
* Requires space after opening round bracket and before closing.
*
* Type: `Object` or `String`
* Types: `Object` or `String`
*

@@ -57,3 +57,3 @@ * Values: `"all"` for strict mode, `"allButNested"`

var error = 'requireSpacesInsideParentheses rule' +
var error = this.getOptionName() + ' rule' +
' requires string value \'all\' or \'allButNested\' or object';

@@ -98,10 +98,4 @@

function isComment(position) {
return file.getComments().some(function(comment) {
return position >= comment.range[0] && position < comment.range[1];
});
}
file.iterateTokenByValue('(', function(token) {
var nextToken = file.getNextToken(token);
var nextToken = file.getNextToken(token, {includeComments: true});
var value = nextToken.value;

@@ -118,13 +112,11 @@

if (
(token.range[1] === nextToken.range[0] &&
token.loc.end.line === nextToken.loc.start.line) ||
isComment(token.range[1])
) {
errors.add('Missing space after opening round bracket', token.loc.end);
}
errors.assert.whitespaceBetween({
token: token,
nextToken: nextToken,
message: 'Missing space after opening round bracket'
});
});
file.iterateTokenByValue(')', function(token) {
var prevToken = file.getPrevToken(token);
var prevToken = file.getPrevToken(token, {includeComments: true});
var value = prevToken.value;

@@ -148,13 +140,9 @@

if (
(token.range[0] === prevToken.range[1] &&
token.loc.end.line === prevToken.loc.start.line) ||
isComment(token.range[0] - 1)
) {
errors.add('Missing space before closing round bracket',
token.loc.end.line,
token.loc.end.column - 2);
}
errors.assert.whitespaceBetween({
token: prevToken,
nextToken: token,
message: 'Missing space before closing round bracket'
});
});
}
};
/**
* Requires an extra comma following the final element of an array or object literal.
*
* Type: `Boolean` or `Object`
* Types: `Boolean` or `Object`
*

@@ -53,16 +53,16 @@ * Values:

module.exports.prototype = {
configure: function(requireTrailingComma) {
configure: function(options) {
if (typeof requireTrailingComma === 'object') {
if ('ignoreSingleValue' in requireTrailingComma) {
if (typeof options === 'object') {
if ('ignoreSingleValue' in options) {
assert(
requireTrailingComma.ignoreSingleValue === true,
'requireTrailingComma option ignoreSingleValue requires true value or should be removed'
options.ignoreSingleValue === true,
this.getOptionName() + ' option ignoreSingleValue requires true value or should be removed'
);
this._ignoreSingleValue = true;
}
if ('ignoreSingleLine' in requireTrailingComma) {
if ('ignoreSingleLine' in options) {
assert(
requireTrailingComma.ignoreSingleLine === true,
'requireTrailingComma option ignoreSingleLine requires true value or should be removed'
options.ignoreSingleLine === true,
this.getOptionName() + ' option ignoreSingleLine requires true value or should be removed'
);

@@ -73,4 +73,4 @@ this._ignoreSingleLine = true;

assert(
requireTrailingComma === true,
'requireTrailingComma option requires true value or should be removed'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);

@@ -77,0 +77,0 @@ }

@@ -6,3 +6,3 @@ /**

*
* Values: `true`
* Value: `true`
*

@@ -37,11 +37,7 @@ * #### Example

configure: function(requireYodaConditions) {
configure: function(options) {
assert(
typeof requireYodaConditions === 'boolean',
'requireYodaConditions option requires boolean value'
options === true,
this.getOptionName() + ' option requires a true value or should be removed'
);
assert(
requireYodaConditions === true,
'requireYodaConditions option requires true value or should be removed'
);
this._operatorIndex = {

@@ -48,0 +44,0 @@ '==': true,

/**
* Option to check `var that = this` expressions
*
* Type: `Array` or `String`
* Types: `Array` or `String`
*

@@ -36,3 +36,3 @@ * Values: String value used for context local declaration

Array.isArray(keywords) || typeof keywords === 'string',
'safeContextKeyword option requires string or array value'
this.getOptionName() + ' option requires string or array value'
);

@@ -39,0 +39,0 @@

/**
* Validates indentation for switch statements and block statements
*
* Type: `Integer` or `String` or `Object`
* Types: `Integer`, `String` or `Object`
*

@@ -117,20 +117,20 @@ * Values:

configure: function(validateIndentation) {
configure: function(options) {
this._includeEmptyLines = false;
if (typeof validateIndentation === 'object') {
this._includeEmptyLines = (validateIndentation.includeEmptyLines === true);
validateIndentation = validateIndentation.value;
if (typeof options === 'object') {
this._includeEmptyLines = (options.includeEmptyLines === true);
options = options.value;
}
assert(
validateIndentation === '\t' ||
(typeof validateIndentation === 'number' && validateIndentation > 0),
'validateIndentation option requires a positive number of spaces or "\\t"' +
options === '\t' ||
(typeof options === 'number' && options > 0),
this.getOptionName() + ' option requires a positive number of spaces or "\\t"' +
' or options object with "value" property'
);
if (typeof validateIndentation === 'number') {
if (typeof options === 'number') {
this._indentChar = ' ';
this._indentSize = validateIndentation;
this._indentSize = options;
} else {

@@ -197,6 +197,5 @@ this._indentChar = '\t';

function markAlternateBlockStatement(node, property) {
var child = node[property];
if (child && child.type === 'BlockStatement') {
markCheck(child);
function markKeyword(node) {
if (node) {
markCheck(file.getPrevToken(file.getFirstNodeToken(node)));
}

@@ -251,7 +250,7 @@ }

function getIndentationFromLine(i) {
function getIndentationFromLine(line) {
var rNotIndentChar = new RegExp('[^' + indentChar + ']');
var firstContent = lines[i].search(rNotIndentChar);
var firstContent = line.search(rNotIndentChar);
if (firstContent === -1) {
firstContent = lines[i].length;
firstContent = line.length;
}

@@ -262,17 +261,40 @@ return firstContent;

function checkIndentations() {
var lineAugment = 0;
linesToCheck.forEach(function(line, i) {
var actualIndentation = getIndentationFromLine(i);
var lineNumber = i + 1;
var actualIndentation = line.indentation;
var expectedIndentation = getExpectedIndentation(line, actualIndentation);
// do not augment this line considering this line changes indentation
if (line.pop.length || line.push.length) {
lineAugment = 0;
}
if (line.check) {
if (actualIndentation !== expectedIndentation) {
errors.add(
'Expected indentation of ' + expectedIndentation + ' characters',
i + 1,
expectedIndentation
);
// correct the indentation so that future lines
// can be validated appropriately
actualIndentation = expectedIndentation;
}
errors.assert.indentation({
lineNumber: lineNumber,
actual: actualIndentation,
expected: expectedIndentation,
indentChar: indentChar
});
// for multiline statements, we need move subsequent lines over the correct
// number of spaces to match the change made to the first line of the statement.
lineAugment = expectedIndentation - actualIndentation;
// correct the indentation so that future lines can be validated appropriately
actualIndentation = expectedIndentation;
} else if (!line.empty) {
// in the case that we moved a previous line over a certain number spaces,
// we need to move this line over as well, but technically, it's not an error
errors.assert.indentation({
lineNumber: lineNumber,
actual: actualIndentation,
// Avoid going negative in the case that a previous line was overindented,
// and now outdenting a line that is already at column zero.
expected: Math.max(actualIndentation + lineAugment, 0),
indentChar: indentChar,
silent: true
});
}

@@ -327,3 +349,3 @@

line.pushAltLine.forEach(function(altLine) {
expected.push(getIndentationFromLine(altLine) + (indentSize * indents));
expected.push(linesToCheck[altLine].indentation + (indentSize * indents));
});

@@ -401,9 +423,37 @@ }

file.iterateNodesByType('ObjectExpression', function(node) {
if (!isMultiline(node)) {
return;
}
var children = getChildren(node);
// only check objects that have children and that look like they are trying to adhere
// to an indentation strategy, i.e. objects that have curly braces on their own lines.
if (!children.length || node.loc.start.line === children[0].loc.start.line ||
node.loc.end.line === children[children.length - 1].loc.end.line) {
return;
}
markChildren(node);
markPop(node, 1);
markPush(node, 1);
markEndCheck(node);
markPushAlt(node);
});
file.iterateNodesByType('IfStatement', function(node) {
markAlternateBlockStatement(node, 'alternate');
markKeyword(node.alternate);
});
file.iterateNodesByType('TryStatement', function(node) {
markAlternateBlockStatement(node, 'handler');
markAlternateBlockStatement(node, 'finalizer');
if (!isMultiline(node)) {
return;
}
var handler = node.handlers && node.handlers.length ? node.handlers[0] : node.handler;
if (handler) {
markCheck(handler);
}
markKeyword(node.finalizer);
});

@@ -455,8 +505,39 @@

if (_this._includeEmptyLines) {
file.getLines().forEach(function(line, i) {
if (line.match(/^\s*$/)) {
linesToCheck[i].check = true;
linesToCheck.forEach(function(line) {
if (line.empty) {
line.check = true;
}
});
}
// starting from the bottom, which allows back to back comments to be checked, mark comments
file.getComments().concat().reverse().forEach(function(node) {
var startLine = node.loc.start.line;
var firstToken = file.getFirstTokenOnLine(startLine, {includeComments: true});
var nextToken = file.getNextToken(firstToken, {includeComments: true});
var nextStartLine = nextToken.loc.start.line;
var nextLine = linesToCheck[nextStartLine - 1];
// ignore if not the only token on the line, or not right above another checked line
if (firstToken !== node || startLine === nextStartLine || !nextLine.check) {
return;
}
// ignore if next line is a case statement, which is kind of hacky, but avoids
// additional complexity for what qualifies as an outdent
if (nextToken && nextToken.type === 'Keyword' &&
(nextToken.value === 'case' || nextToken.value === 'default')) {
return;
}
// ignore if above a line that both introduces and ends an ident,
// which catches cases like a comment above an `else if`, but not nested ifs.
if (nextLine.push.length && nextLine.pop.length) {
return;
}
markCheck(node);
});
}

@@ -471,5 +552,4 @@

var lines = file.getLinesWithCommentsRemoved(errors);
var indentStack = [0];
var linesToCheck = lines.map(function() {
var linesToCheck = file.getLines().map(function(line) {
return {

@@ -479,3 +559,5 @@ push: [],

pop: [],
check: false
check: false,
indentation: getIndentationFromLine(line),
empty: line.match(/^\s*$/)
};

@@ -482,0 +564,0 @@ });

@@ -8,3 +8,3 @@ var assert = require('assert');

configure: function(options) {
assert(typeof options === 'object', 'validateJSDoc option requires object value');
assert(typeof options === 'object', this.getOptionName() + ' option requires object value');
this._options = options;

@@ -11,0 +11,0 @@ },

@@ -36,3 +36,3 @@ /**

typeof options === 'string' || typeof options === 'object',
'validateLineBreaks option requires string or object value'
this.getOptionName() + ' option requires string or object value'
);

@@ -64,13 +64,10 @@

var lineBreaks = file.getSource().match(/\r\n|\r|\n/g);
for (var i = 0, len = lineBreaks.length; i < len; i++) {
if (lineBreaks[i] !== this._allowedLineBreak) {
file.getLineBreaks().some(function(lineBreak, i) {
if (lineBreak !== this._allowedLineBreak) {
errors.add('Invalid line break', i + 1, lines[i].length);
if (this._reportOncePerFile) {
break;
}
return this._reportOncePerFile;
}
}
}, this);
}
};

@@ -38,9 +38,9 @@ /**

configure: function(validateParameterSeparator) {
configure: function(options) {
assert(
typeof validateParameterSeparator === 'string' && /^[ ]?,[ ]?$/.test(validateParameterSeparator),
'validateParameterSpacing option requires string value containing only a comma and optional spaces'
typeof options === 'string' && /^[ ]?,[ ]?$/.test(options),
this.getOptionName() + ' option requires string value containing only a comma and optional spaces'
);
this._separator = validateParameterSeparator;
this._separator = options;
},

@@ -68,6 +68,6 @@

if (whitespaceBeforeComma) {
errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: prevParamToken,
nextToken: punctuatorToken,
spaces: 1,
exactly: 1,
message: 'One space required after function parameter \'' + prevParamToken.value + '\''

@@ -86,6 +86,6 @@ });

if (whitespaceAfterComma) {
errors.assert.whitespaceBetween({
errors.assert.spacesBetween({
token: punctuatorToken,
nextToken: nextParamToken,
spaces: 1,
exactly: 1,
message: 'One space required before function parameter \'' + nextParamToken.value + '\''

@@ -92,0 +92,0 @@ });

/**
* Requires all quote marks to be either the supplied value, or consistent if `true`
*
* Type: `String` or `Object`
* Types: `Boolean`, `String` or `Object`
*

@@ -69,3 +69,3 @@ * Values:

typeof quoteMark.escape === 'boolean' && quoteMark.mark !== undefined,
'validateQuoteMarks option requires the "escape" and "mark" property to be defined'
this.getOptionName() + ' option requires the "escape" and "mark" property to be defined'
);

@@ -78,3 +78,3 @@ this._allowEscape = quoteMark.escape;

quoteMark === '"' || quoteMark === '\'' || quoteMark === true,
'validateQuoteMarks option requires \'"\', "\'", or boolean true'
this.getOptionName() + ' option requires \'"\', "\'", or boolean true'
);

@@ -81,0 +81,0 @@

@@ -7,2 +7,4 @@ var defaultEsprima = require('esprima');

var MAX_FIX_ATTEMPTS = 5;
/**

@@ -80,42 +82,112 @@ * Starts Code Style checking process.

/**
* Parses source code into an AST
* Checks file provided with a string.
* @param {String} source
* @param {String} [filename='input']
* @returns {Errors}
*/
checkString: function(source, filename) {
filename = filename || 'input';
var sourceTree;
var parseError;
try {
sourceTree = JsFile.parse(source, this._esprima, this._configuration.getEsprimaOptions());
} catch (e) {
parseError = e;
}
var file = this._createJsFileInstance(filename, source, sourceTree);
var errors = new Errors(file, this._verbose);
if (this._maxErrorsExceeded) {
return errors;
}
if (parseError) {
this._addParseError(errors, parseError);
return errors;
}
this._checkJsFile(file, errors);
return errors;
},
/**
* Checks a file specified using JsFile instance.
* Fills Errors instance with validation errors.
*
* @param {String} str the source code to parse
* @return {Object} the output AST
* @throws Error on invalid source code.
* @param {JsFile} file
* @param {Errors} errors
* @private
*/
_parse: function(str) {
var hashbang = str.indexOf('#!') === 0;
var tree;
_checkJsFile: function(file, errors) {
if (this._maxErrorsExceeded) {
return;
}
// Convert bin annotation to a comment
if (hashbang) {
str = '//' + str.substr(2);
var errorFilter = this._configuration.getErrorFilter();
this._configuredRules.forEach(function(rule) {
errors.setCurrentRule(rule.getOptionName());
rule.check(file, errors);
}, this);
this._configuration.getUnsupportedRuleNames().forEach(function(rulename) {
errors.add('Unsupported rule: ' + rulename, 1, 0);
});
// sort errors list to show errors as they appear in source
errors.getErrorList().sort(function(a, b) {
return (a.line - b.line) || (a.column - b.column);
});
if (errorFilter) {
errors.filter(errorFilter);
}
// Strip special case code like iOS instrumentation imports: `#import 'abc.js';`
str = str.replace(/^#!?[^\n]+\n/gm, '');
if (this._maxErrorsEnabled()) {
this._maxErrorsExceeded = this._errorsFound + errors.getErrorCount() > this._maxErrors;
errors.stripErrorList(Math.max(0, this._maxErrors - this._errorsFound));
}
var esprimaOptions = {
tolerant: true
};
var esprimaOptionsFromConfig = this._configuration.getEsprimaOptions();
for (var key in esprimaOptionsFromConfig) {
esprimaOptions[key] = esprimaOptionsFromConfig[key];
this._errorsFound += errors.getErrorCount();
},
/**
* Adds parse error to the error list.
*
* @param {Errors} errors
* @param {Error} parseError
* @private
*/
_addParseError: function(errors, parseError) {
if (this._maxErrorsExceeded) {
return;
}
// Set required options
esprimaOptions.loc = true;
esprimaOptions.range = true;
esprimaOptions.comment = true;
esprimaOptions.tokens = true;
esprimaOptions.sourceType = 'module';
tree = this._esprima.parse(str, esprimaOptions);
errors.setCurrentRule('parseError');
errors.add(parseError.description, parseError.lineNumber, parseError.column);
// Remove the bin annotation comment
if (hashbang) {
tree.comments = tree.comments.slice(1);
if (this._maxErrorsEnabled()) {
this._errorsFound += 1;
this._maxErrorsExceeded = this._errorsFound >= this._maxErrors;
}
},
return tree;
/**
* Creates configured JsFile instance.
*
* @param {String} filename
* @param {String} source
* @param {Object} sourceTree
* @private
*/
_createJsFileInstance: function(filename, source, sourceTree) {
return new JsFile(filename, source, sourceTree, {
es3: this._configuration.isES3Enabled(),
es6: this._configuration.isESNextEnabled()
});
},

@@ -125,14 +197,18 @@

* Checks file provided with a string.
* @param {String} str
* @param {String} filename
* @returns {Errors}
* @param {String} source
* @param {String} [filename='input']
* @returns {{output: String, errors: Errors}}
*/
checkString: function(str, filename) {
fixString: function(source, filename) {
if (this._maxErrorsEnabled()) {
throw new Error('Cannot autofix when `maxError` option is enabled');
}
filename = filename || 'input';
var tree;
var sourceTree;
var parseError;
try {
tree = this._parse(str);
sourceTree = JsFile.parse(source, this._esprima, this._configuration.getEsprimaOptions());
} catch (e) {

@@ -142,47 +218,43 @@ parseError = e;

var file = new JsFile(filename, str, tree, {
es3: this._configuration.isES3Enabled(),
es6: this._configuration.isESNextEnabled()
});
if (parseError) {
var parseErrors = new Errors(this._createJsFileInstance(filename, source, sourceTree), this._verbose);
this._addParseError(parseErrors, parseError);
return {output: source, errors: parseErrors};
} else {
var attempt = 0;
var errors;
var file;
var errors = new Errors(file, this._verbose);
var errorFilter = this._configuration.getErrorFilter();
do {
file = this._createJsFileInstance(filename, source, sourceTree);
errors = new Errors(file, this._verbose);
if (!this._maxErrorsExceeded) {
if (parseError) {
errors.setCurrentRule('parseError');
errors.add(parseError.description, parseError.lineNumber, parseError.column);
// Changes to current sources are made in rules through assertions.
this._checkJsFile(file, errors);
return errors;
}
var hasFixes = errors.getErrorList().some(function(err) {
return err.fixed;
});
this._configuredRules.forEach(function(rule) {
errors.setCurrentRule(rule.getOptionName());
rule.check(file, errors);
}, this);
if (!hasFixes) {
break;
}
this._configuration.getUnsupportedRuleNames().forEach(function(rulename) {
errors.add('Unsupported rule: ' + rulename, 1, 0);
});
source = file.render();
sourceTree = JsFile.parse(source, this._esprima, this._configuration.getEsprimaOptions());
// sort errors list to show errors as they appear in source
errors.getErrorList().sort(function(a, b) {
return (a.line - b.line) || (a.column - b.column);
});
attempt++;
} while (attempt < MAX_FIX_ATTEMPTS);
if (errorFilter) {
errors.filter(errorFilter);
}
if (this._maxErrors !== null && !isNaN(this._maxErrors)) {
if (!this._maxErrorsExceeded) {
this._maxErrorsExceeded = this._errorsFound + errors.getErrorCount() > this._maxErrors;
}
errors.stripErrorList(Math.max(0, this._maxErrors - this._errorsFound));
}
this._errorsFound += errors.getErrorCount();
return {output: source, errors: errors};
}
},
return errors;
/**
* Returns `true` if max erros limit is enabled.
*
* @returns {Boolean}
*/
_maxErrorsEnabled: function() {
return this._maxErrors !== null && !isNaN(this._maxErrors);
},

@@ -189,0 +261,0 @@

@@ -27,25 +27,4 @@ var utils = require('util');

TokenAssert.prototype.whitespaceBetween = function(options) {
var token = options.token;
var nextToken = options.nextToken;
if (options.hasOwnProperty('spaces')) {
var spaces = options.spaces;
if (nextToken.loc.start.line === token.loc.end.line &&
(nextToken.loc.start.column - token.loc.end.column) !== spaces
) {
this.emit('error', {
message: options.message ||
spaces + ' spaces required between ' + token.value + ' and ' + nextToken.value,
line: token.loc.end.line,
column: token.loc.end.column
});
}
} else {
if (nextToken.range[0] === token.range[1]) {
this.emit('error', {
message: options.message || 'Missing space between ' + token.value + ' and ' + nextToken.value,
line: token.loc.end.line,
column: token.loc.end.column
});
}
}
options.atLeast = 1;
this.spacesBetween(options);
};

@@ -62,17 +41,8 @@

TokenAssert.prototype.noWhitespaceBetween = function(options) {
var token = options.token;
var nextToken = options.nextToken;
if (nextToken.range[0] !== token.range[1] &&
(options.disallowNewLine || token.loc.end.line === nextToken.loc.start.line)
) {
this.emit('error', {
message: options.message || 'Unexpected whitespace between ' + token.value + ' and ' + nextToken.value,
line: token.loc.end.line,
column: token.loc.end.column
});
}
options.exactly = 0;
this.spacesBetween(options);
};
/**
* Requires tokens to be on the same line.
* Requires to have the whitespace between specified tokens with the provided options.
*

@@ -82,6 +52,13 @@ * @param {Object} options.token

* @param {String} [options.message]
* @param {Object} [options.atLeast] At least how many spaces the tokens are apart
* @param {Object} [options.atMost] At most how many spaces the tokens are apart
* @param {Object} [options.exactly] Exactly how many spaces the tokens are apart
* @param {Boolean} [options.disallowNewLine=false]
*/
TokenAssert.prototype.sameLine = function(options) {
TokenAssert.prototype.spacesBetween = function(options) {
var token = options.token;
var nextToken = options.nextToken;
var atLeast = options.atLeast;
var atMost = options.atMost;
var exactly = options.exactly;

@@ -92,8 +69,49 @@ if (!token || !nextToken) {

if (token.loc.end.line !== nextToken.loc.start.line) {
this._validateOptions(options);
if (!options.disallowNewLine && token.loc.end.line !== nextToken.loc.start.line) {
return;
}
// Only attempt to remove or add lines if there are no comments between the two nodes
// as this prevents accidentally moving a valid token onto a line comment ed line
var fixed = this._file.getNextToken(options.token, {includeComments: true}) === nextToken;
var emitError = function(countPrefix, spaceCount) {
if (fixed) {
nextToken.whitespaceBefore = new Array(spaceCount + 1).join(' ');
}
var msgPostfix = token.value + ' and ' + nextToken.value;
if (!options.message) {
if (exactly === 0) {
// support noWhitespaceBetween
options.message = 'Unexpected whitespace between ' + msgPostfix;
} else if (exactly !== undefined) {
// support whitespaceBetween (spaces option)
options.message = spaceCount + ' spaces required between ' + msgPostfix;
} else if (atLeast === 1 && atMost === undefined) {
// support whitespaceBetween (no spaces option)
options.message = 'Missing space between ' + msgPostfix;
} else {
options.message = countPrefix + ' ' + spaceCount + ' spaces required between ' + msgPostfix;
}
}
this.emit('error', {
message: options.message || token.value + ' and ' + nextToken.value + ' should be on the same line',
message: options.message,
line: token.loc.end.line,
column: token.loc.end.column
column: token.loc.end.column,
fixed: fixed
});
}.bind(this);
var spacesBetween = Math.abs(nextToken.range[0] - token.range[1]);
if (atLeast !== undefined && spacesBetween < atLeast) {
emitError('at least', atLeast);
} else if (atMost !== undefined && spacesBetween > atMost) {
emitError('at most', atMost);
} else if (exactly !== undefined && spacesBetween !== exactly) {
emitError('exactly', exactly);
}

@@ -103,2 +121,127 @@ };

/**
* Requires the specified line to have the expected indentation.
*
* @param {Number} options.lineNumber
* @param {Number} options.actual
* @param {Number} options.expected
* @param {String} options.indentChar
* @param {Boolean} [options.silent] if true, will suppress error emission but still fix whitespace
*/
TokenAssert.prototype.indentation = function(options) {
var lineNumber = options.lineNumber;
var actual = options.actual;
var expected = options.expected;
var indentChar = options.indentChar;
if (actual === expected) {
return;
}
if (!options.silent) {
this.emit('error', {
message: 'Expected indentation of ' + expected + ' characters',
line: lineNumber,
column: expected,
fixed: true
});
}
var token = this._file.getFirstTokenOnLine(lineNumber, {includeComments: true});
var newWhitespace = (new Array(expected + 1)).join(indentChar);
if (!token) {
this._setEmptyLineIndentation(lineNumber, newWhitespace);
return;
}
this._updateWhitespaceByLine(token, function(lines) {
lines[lines.length - 1] = newWhitespace;
return lines;
});
if (token.isComment) {
this._updateCommentWhitespace(token, indentChar, actual, expected);
}
};
/**
* Updates the whitespace of a line by passing split lines to a callback function
* for editing.
*
* @param {Number} lineNumber
* @param {Function} callback
*/
TokenAssert.prototype._updateWhitespaceByLine = function(token, callback) {
var lineBreak = this._file.getLineBreakStyle();
var lines = token.whitespaceBefore.split(/\r\n|\r|\n/);
lines = callback(lines);
token.whitespaceBefore = lines.join(lineBreak);
};
/**
* Updates the whitespace of a line by passing split lines to a callback function
* for editing.
*
* @param {Number} lineNumber
* @param {Function} callback
*/
TokenAssert.prototype._updateCommentWhitespace = function(token, indentChar, actual, expected) {
var difference = expected - actual;
var tokenLines = token.value.split(/\r\n|\r|\n/);
var i = 1;
if (difference >= 0) {
var lineWhitespace = (new Array(difference + 1)).join(indentChar);
for (; i < tokenLines.length; i++) {
tokenLines[i] = tokenLines[i] === '' ? '' : lineWhitespace + tokenLines[i];
}
} else {
for (; i < tokenLines.length; i++) {
tokenLines[i] = tokenLines[i].substring(-difference);
}
}
token.value = tokenLines.join(this._file.getLineBreakStyle());
};
/**
* Fixes the indentation of a line that has no tokens on it
*
* @param {Number} lineNumber
* @param {String} newWhitespace
*/
TokenAssert.prototype._setEmptyLineIndentation = function(lineNumber, newWhitespace) {
var token;
do {
token = this._file.getFirstTokenOnLine(++lineNumber, {includeComments: true});
} while (!token);
this._updateWhitespaceByLine(token, function(lines) {
if (lines[0] !== '') {
lines[0] = newWhitespace;
}
for (var i = 1; i < lines.length; i++) {
lines[i] = newWhitespace;
}
return lines;
});
};
/**
* Requires tokens to be on the same line.
*
* @param {Object} options.token
* @param {Object} options.nextToken
* @param {String} [options.message]
*/
TokenAssert.prototype.sameLine = function(options) {
options.exactly = 0;
this.linesBetween(options);
};
/**
* Requires tokens to be on different lines.

@@ -111,4 +254,24 @@ *

TokenAssert.prototype.differentLine = function(options) {
options.atLeast = 1;
this.linesBetween(options);
};
/**
* Requires tokens to have a certain amount of lines between them.
* Set at least one of atLeast or atMost OR set exactly.
*
* @param {Object} options.token
* @param {Object} options.nextToken
* @param {Object} [options.message]
* @param {Object} [options.atLeast] At least how many lines the tokens are apart
* @param {Object} [options.atMost] At most how many lines the tokens are apart
* @param {Object} [options.exactly] Exactly how many lines the tokens are apart
*/
TokenAssert.prototype.linesBetween = function(options) {
var token = options.token;
var nextToken = options.nextToken;
var atLeast = options.atLeast;
var atMost = options.atMost;
var exactly = options.exactly;

@@ -119,8 +282,44 @@ if (!token || !nextToken) {

if (token.loc.end.line === nextToken.loc.start.line) {
this._validateOptions(options);
// Only attempt to remove or add lines if there are no comments between the two nodes
// as this prevents accidentally moving a valid token onto a line comment ed line
var fixed = this._file.getNextToken(options.token, {includeComments: true}) === nextToken;
var linesBetween = Math.abs(token.loc.end.line - nextToken.loc.start.line);
var emitError = function(countPrefix, lineCount) {
var msgPrefix = token.value + ' and ' + nextToken.value;
if (!options.message) {
if (exactly === 0) {
// support sameLine
options.message = msgPrefix + ' should be on the same line';
} else if (atLeast === 1 && atMost === undefined) {
// support differentLine
options.message = msgPrefix + ' should be on different lines';
} else {
// support linesBetween
options.message = msgPrefix + ' should have ' + countPrefix + ' ' + lineCount + ' line(s) between them';
}
}
if (fixed) {
this._augmentLineCount(nextToken, lineCount);
}
this.emit('error', {
message: options.message || token.value + ' and ' + nextToken.value + ' should be on different lines',
message: options.message,
line: token.loc.end.line,
column: token.loc.end.column
column: token.loc.end.column,
fixed: fixed
});
}.bind(this);
if (atLeast !== undefined && linesBetween < atLeast) {
emitError('at least', atLeast);
} else if (atMost !== undefined && linesBetween > atMost) {
emitError('at most', atMost);
} else if (exactly !== undefined && linesBetween !== exactly) {
emitError('exactly', exactly);
}

@@ -130,2 +329,70 @@ };

/**
* Throws errors if atLeast, atMost, and exactly options don't mix together properly or
* if the tokens provided are equivalent.
*
* @param {Object} options.token
* @param {Object} options.nextToken
* @param {Object} [options.atLeast] At least how many spaces the tokens are apart
* @param {Object} [options.atMost] At most how many spaces the tokens are apart
* @param {Object} [options.exactly] Exactly how many spaces the tokens are apart
* @throws {Error} If the options are non-sensical
*/
TokenAssert.prototype._validateOptions = function(options) {
var token = options.token;
var nextToken = options.nextToken;
var atLeast = options.atLeast;
var atMost = options.atMost;
var exactly = options.exactly;
if (token === nextToken) {
throw new Error('You cannot specify the same token as both token and nextToken');
}
if (atLeast === undefined &&
atMost === undefined &&
exactly === undefined) {
throw new Error('You must specify at least one option');
}
if (exactly !== undefined && (atLeast !== undefined || atMost !== undefined)) {
throw new Error('You cannot specify atLeast or atMost with exactly');
}
if (atLeast !== undefined && atMost !== undefined && atMost < atLeast) {
throw new Error('atLeast and atMost are in conflict');
}
};
/**
* Augments token whitespace to contain the correct number of newlines while preserving indentation
*
* @param {Object} token
* @param {Number} lineCount
*/
TokenAssert.prototype._augmentLineCount = function(token, lineCount) {
if (lineCount === 0) {
token.whitespaceBefore = ' ';
return;
}
this._updateWhitespaceByLine(token, function(lines) {
var currentLineCount = lines.length;
var lastLine = lines[lines.length - 1];
if (currentLineCount <= lineCount) {
// add additional lines that maintain the same indentation as the former last line
for (; currentLineCount <= lineCount; currentLineCount++) {
lines[lines.length - 1] = '';
lines.push(lastLine);
}
} else {
// remove lines and then ensure that the new last line maintains the previous indentation
lines = lines.slice(0, lineCount + 1);
lines[lines.length - 1] = lastLine;
}
return lines;
});
};
/**
* Requires specific token before given.

@@ -132,0 +399,0 @@ *

@@ -35,2 +35,13 @@ var estraverse = require('estraverse');

keys: {
JSXIdentifier: [],
JSXNamespacedName: ['namespace', 'name'],
JSXMemberExpression: ['object', 'property'],
JSXEmptyExpression: [],
JSXExpressionContainer: ['expression'],
JSXElement: ['openingElement', 'closingElement', 'children'],
JSXClosingElement: ['name'],
JSXOpeningElement: ['name', 'attributes'],
JSXAttribute: ['name', 'value'],
JSXSpreadAttribute: ['argument'],
JSXText: null,
XJSIdentifier: [],

@@ -37,0 +48,0 @@ XJSNamespacedName: ['namespace', 'name'],

@@ -5,3 +5,3 @@ {

"name": "jscs",
"version": "1.11.3",
"version": "1.12.0",
"main": "lib/checker",

@@ -70,10 +70,10 @@ "homepage": "https://github.com/jscs-dev/node-jscs",

"dependencies": {
"chalk": "~1.0.0",
"cli-table": "~0.3.1",
"colors": "~1.0.3",
"commander": "~2.6.0",
"esprima": "~1.2.4",
"esprima-harmony-jscs": "1.1.0-tolerate-import",
"estraverse": "~1.9.1",
"esprima": "^1.2.5",
"esprima-harmony-jscs": "1.1.0-templates",
"estraverse": "^1.9.3",
"exit": "~0.1.2",
"glob": "~4.3.5",
"glob": "^5.0.1",
"lodash.assign": "~3.0.0",

@@ -83,18 +83,17 @@ "minimatch": "~2.0.1",

"strip-json-comments": "~1.0.2",
"supports-color": "~1.2.0",
"vow": "~0.4.8",
"vow-fs": "~0.3.4",
"xmlbuilder": "~2.5.0"
"xmlbuilder": "^2.6.1"
},
"devDependencies": {
"browserify": "~8.1.3",
"browserify": "^9.0.3",
"coveralls": "~2.11.2",
"has-ansi": "~1.0.1",
"jshint": "~2.6.0",
"mocha": "~2.1.0",
"mocha": "^2.2.0",
"regenerate": "~1.2.1",
"rewire": "~2.1.5",
"sinon": "~1.12.2",
"rewire": "^2.3.1",
"sinon": "^1.13.0",
"unicode-7.0.0": "~0.1.5",
"unit-coverage": "~3.3.0",
"unit-coverage": "^3.4.0",
"xml2js": "~0.4.4"

@@ -114,3 +113,3 @@ },

"-t",
"test/**/*.js",
"test/specs/**/*.js",
"-S",

@@ -121,3 +120,3 @@ "relative",

"-O",
"tests=test"
"tests=test/specs"
]

@@ -124,0 +123,0 @@ },

@@ -60,2 +60,6 @@ {

],
"requirePaddingNewLinesBeforeLineComments": {
"allExcept": "firstAfterCurly"
},
"requirePaddingNewLinesAfterBlocks": true,
"safeContextKeyword": "_this",

@@ -62,0 +66,0 @@ "validateLineBreaks": "LF",

@@ -65,3 +65,4 @@ {

"disallowNewlineBeforeBlockStatements": true,
"disallowMultipleLineStrings": true
"disallowMultipleLineStrings": true,
"requireSpaceBeforeObjectValues": true
}

@@ -13,3 +13,2 @@ {

"requireParenthesesAroundIIFE": true,
"requireMultipleVarDecl": "onevar",
"requireCommaBeforeLineBreak": true,

@@ -68,2 +67,3 @@ "requireCamelCaseOrUpperCaseIdentifiers": true,

},
"requirePaddingNewLinesBeforeLineComments": true,
"validateLineBreaks": "LF",

@@ -70,0 +70,0 @@

@@ -98,3 +98,6 @@ {

"disallowTrailingWhitespace": true,
"validateLineBreaks": "LF"
"validateIndentation": 4,
"validateLineBreaks": "LF",
"requireSemicolons": true
}

@@ -35,2 +35,3 @@ [![Build Status](https://travis-ci.org/jscs-dev/node-jscs.svg?branch=master)](https://travis-ci.org/jscs-dev/node-jscs)

* [Famous](http://famo.us/)
* [less.js](http://lesscss.org/)
* [Goodvidio](http://goodvid.io/)

Sorry, the diff of this file is not supported yet

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