react-scripts
Version:
Configuration and scripts for Create React App.
247 lines (228 loc) • 8.31 kB
JavaScript
/**
* @fileoverview Enforce or disallow spaces inside of curly braces in JSX attributes.
* @author Jamund Ferguson
* @author Brandyn Bennett
* @author Michael Ficarra
* @author Vignesh Anand
* @author Jamund Ferguson
* @author Yannick Croissant
* @author Erik Wendel
*/
'use strict';
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
var SPACING = {
always: 'always',
never: 'never'
};
var SPACING_VALUES = [SPACING.always, SPACING.never];
module.exports = function(context) {
var sourceCode = context.getSourceCode();
var spaced = context.options[0] === SPACING.always;
var multiline = context.options[1] ? context.options[1].allowMultiline : true;
var spacing = context.options[1] ? context.options[1].spacing || {} : {};
var defaultSpacing = spaced ? SPACING.always : SPACING.never;
var objectLiteralSpacing = spacing.objectLiterals || (spaced ? SPACING.always : SPACING.never);
// --------------------------------------------------------------------------
// Helpers
// --------------------------------------------------------------------------
/**
* Determines whether two adjacent tokens have a newline between them.
* @param {Object} left - The left token object.
* @param {Object} right - The right token object.
* @returns {boolean} Whether or not there is a newline between the tokens.
*/
function isMultiline(left, right) {
return left.loc.start.line !== right.loc.start.line;
}
/**
* Reports that there shouldn't be a newline after the first token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Token} token - The token to use for the report.
* @returns {void}
*/
function reportNoBeginningNewline(node, token) {
context.report({
node: node,
loc: token.loc.start,
message: 'There should be no newline after \'' + token.value + '\'',
fix: function(fixer) {
var nextToken = context.getSourceCode().getTokenAfter(token);
return fixer.replaceTextRange([token.range[1], nextToken.range[0]], spaced ? ' ' : '');
}
});
}
/**
* Reports that there shouldn't be a newline before the last token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Token} token - The token to use for the report.
* @returns {void}
*/
function reportNoEndingNewline(node, token) {
context.report({
node: node,
loc: token.loc.start,
message: 'There should be no newline before \'' + token.value + '\'',
fix: function(fixer) {
var previousToken = context.getSourceCode().getTokenBefore(token);
return fixer.replaceTextRange([previousToken.range[1], token.range[0]], spaced ? ' ' : '');
}
});
}
/**
* Reports that there shouldn't be a space after the first token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Token} token - The token to use for the report.
* @returns {void}
*/
function reportNoBeginningSpace(node, token) {
context.report({
node: node,
loc: token.loc.start,
message: 'There should be no space after \'' + token.value + '\'',
fix: function(fixer) {
var nextToken = context.getSourceCode().getTokenAfter(token);
return fixer.removeRange([token.range[1], nextToken.range[0]]);
}
});
}
/**
* Reports that there shouldn't be a space before the last token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Token} token - The token to use for the report.
* @returns {void}
*/
function reportNoEndingSpace(node, token) {
context.report({
node: node,
loc: token.loc.start,
message: 'There should be no space before \'' + token.value + '\'',
fix: function(fixer) {
var previousToken = context.getSourceCode().getTokenBefore(token);
return fixer.removeRange([previousToken.range[1], token.range[0]]);
}
});
}
/**
* Reports that there should be a space after the first token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Token} token - The token to use for the report.
* @returns {void}
*/
function reportRequiredBeginningSpace(node, token) {
context.report({
node: node,
loc: token.loc.start,
message: 'A space is required after \'' + token.value + '\'',
fix: function(fixer) {
return fixer.insertTextAfter(token, ' ');
}
});
}
/**
* Reports that there should be a space before the last token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Token} token - The token to use for the report.
* @returns {void}
*/
function reportRequiredEndingSpace(node, token) {
context.report({
node: node,
loc: token.loc.start,
message: 'A space is required before \'' + token.value + '\'',
fix: function(fixer) {
return fixer.insertTextBefore(token, ' ');
}
});
}
/**
* Determines if spacing in curly braces is valid.
* @param {ASTNode} node The AST node to check.
* @returns {void}
*/
function validateBraceSpacing(node) {
var first = context.getFirstToken(node);
var last = sourceCode.getLastToken(node);
var second = context.getTokenAfter(first);
var penultimate = sourceCode.getTokenBefore(last);
var isObjectLiteral = first.value === second.value;
if (isObjectLiteral) {
if (objectLiteralSpacing === SPACING.never) {
if (sourceCode.isSpaceBetweenTokens(first, second)) {
reportNoBeginningSpace(node, first);
} else if (!multiline && isMultiline(first, second)) {
reportNoBeginningNewline(node, first);
}
if (sourceCode.isSpaceBetweenTokens(penultimate, last)) {
reportNoEndingSpace(node, last);
} else if (!multiline && isMultiline(penultimate, last)) {
reportNoEndingNewline(node, last);
}
} else if (objectLiteralSpacing === SPACING.always) {
if (!sourceCode.isSpaceBetweenTokens(first, second)) {
reportRequiredBeginningSpace(node, first);
} else if (!multiline && isMultiline(first, second)) {
reportNoBeginningNewline(node, first);
}
if (!sourceCode.isSpaceBetweenTokens(penultimate, last)) {
reportRequiredEndingSpace(node, last);
} else if (!multiline && isMultiline(penultimate, last)) {
reportNoEndingNewline(node, last);
}
}
} else if (defaultSpacing === SPACING.always) {
if (!sourceCode.isSpaceBetweenTokens(first, second)) {
reportRequiredBeginningSpace(node, first);
} else if (!multiline && isMultiline(first, second)) {
reportNoBeginningNewline(node, first);
}
if (!sourceCode.isSpaceBetweenTokens(penultimate, last)) {
reportRequiredEndingSpace(node, last);
} else if (!multiline && isMultiline(penultimate, last)) {
reportNoEndingNewline(node, last);
}
} else if (defaultSpacing === SPACING.never) {
if (isMultiline(first, second)) {
if (!multiline) {
reportNoBeginningNewline(node, first);
}
} else if (sourceCode.isSpaceBetweenTokens(first, second)) {
reportNoBeginningSpace(node, first);
}
if (isMultiline(penultimate, last)) {
if (!multiline) {
reportNoEndingNewline(node, last);
}
} else if (sourceCode.isSpaceBetweenTokens(penultimate, last)) {
reportNoEndingSpace(node, last);
}
}
}
// --------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
return {
JSXExpressionContainer: validateBraceSpacing,
JSXSpreadAttribute: validateBraceSpacing
};
};
module.exports.schema = [{
enum: SPACING_VALUES
}, {
type: 'object',
properties: {
allowMultiline: {
type: 'boolean'
},
spacing: {
type: 'object',
properties: {
objectLiterals: {
enum: SPACING_VALUES
}
}
}
},
additionalProperties: false
}];