react-carousel-query
Version:
A infinite carousel component made with react that handles the pagination for you.
143 lines (120 loc) • 3.77 kB
JavaScript
/**
* @fileoverview Validate closing tag location in JSX
* @author Ross Solomon
*/
;
const repeat = require('string.prototype.repeat');
const has = require('hasown');
const astUtil = require('../util/ast');
const docsUrl = require('../util/docsUrl');
const getSourceCode = require('../util/eslint').getSourceCode;
const report = require('../util/report');
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
const messages = {
onOwnLine: 'Closing tag of a multiline JSX expression must be on its own line.',
matchIndent: 'Expected closing tag to match indentation of opening.',
alignWithOpening: 'Expected closing tag to be aligned with the line containing the opening tag',
};
const defaultOption = 'tag-aligned';
const optionMessageMap = {
'tag-aligned': 'matchIndent',
'line-aligned': 'alignWithOpening',
};
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
docs: {
description: 'Enforce closing tag location for multiline JSX',
category: 'Stylistic Issues',
recommended: false,
url: docsUrl('jsx-closing-tag-location'),
},
fixable: 'whitespace',
messages,
schema: [{
anyOf: [
{
enum: ['tag-aligned', 'line-aligned'],
},
{
type: 'object',
properties: {
location: {
enum: ['tag-aligned', 'line-aligned'],
},
},
additionalProperties: false,
},
],
}],
},
create(context) {
const config = context.options[0];
let option = defaultOption;
if (typeof config === 'string') {
option = config;
} else if (typeof config === 'object') {
if (has(config, 'location')) {
option = config.location;
}
}
function getIndentation(openingStartOfLine, opening) {
if (option === 'line-aligned') return openingStartOfLine.column;
if (option === 'tag-aligned') return opening.loc.start.column;
}
function handleClosingElement(node) {
if (!node.parent) {
return;
}
const sourceCode = getSourceCode(context);
const opening = node.parent.openingElement || node.parent.openingFragment;
const openingLoc = sourceCode.getFirstToken(opening).loc.start;
const openingLine = sourceCode.lines[openingLoc.line - 1];
const openingStartOfLine = {
column: /^\s*/.exec(openingLine)[0].length,
line: openingLoc.line,
};
if (opening.loc.start.line === node.loc.start.line) {
return;
}
if (
opening.loc.start.column === node.loc.start.column
&& option === 'tag-aligned'
) {
return;
}
if (
openingStartOfLine.column === node.loc.start.column
&& option === 'line-aligned'
) {
return;
}
const messageId = astUtil.isNodeFirstInLine(context, node)
? optionMessageMap[option]
: 'onOwnLine';
report(context, messages[messageId], messageId, {
node,
loc: node.loc,
fix(fixer) {
const indent = repeat(
' ',
getIndentation(openingStartOfLine, opening)
);
if (astUtil.isNodeFirstInLine(context, node)) {
return fixer.replaceTextRange(
[node.range[0] - node.loc.start.column, node.range[0]],
indent
);
}
return fixer.insertTextBefore(node, `\n${indent}`);
},
});
}
return {
JSXClosingElement: handleClosingElement,
JSXClosingFragment: handleClosingElement,
};
},
};