123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- /**
- * @fileoverview Require or prevent a new line after jsx elements and expressions.
- * @author Johnny Zabala
- * @author Joseph Stiles
- */
- 'use strict';
- const docsUrl = require('../util/docsUrl');
- const report = require('../util/report');
- // ------------------------------------------------------------------------------
- // Rule Definition
- // ------------------------------------------------------------------------------
- const messages = {
- require: 'JSX element should start in a new line',
- prevent: 'JSX element should not start in a new line',
- allowMultilines: 'Multiline JSX elements should start in a new line',
- };
- function isMultilined(node) {
- return node.loc.start.line !== node.loc.end.line;
- }
- module.exports = {
- meta: {
- docs: {
- description: 'Require or prevent a new line after jsx elements and expressions.',
- category: 'Stylistic Issues',
- recommended: false,
- url: docsUrl('jsx-newline'),
- },
- fixable: 'code',
- messages,
- schema: [
- {
- type: 'object',
- properties: {
- prevent: {
- default: false,
- type: 'boolean',
- },
- allowMultilines: {
- default: false,
- type: 'boolean',
- },
- },
- additionalProperties: false,
- if: {
- properties: {
- allowMultilines: {
- const: true,
- },
- },
- },
- then: {
- properties: {
- prevent: {
- const: true,
- },
- },
- required: [
- 'prevent',
- ],
- },
- },
- ],
- },
- create(context) {
- const jsxElementParents = new Set();
- const sourceCode = context.getSourceCode();
- function isBlockCommentInCurlyBraces(element) {
- const elementRawValue = sourceCode.getText(element);
- return /^\s*{\/\*/.test(elementRawValue);
- }
- function isNonBlockComment(element) {
- return !isBlockCommentInCurlyBraces(element) && (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer');
- }
- return {
- 'Program:exit'() {
- jsxElementParents.forEach((parent) => {
- parent.children.forEach((element, index, elements) => {
- if (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer') {
- const configuration = context.options[0] || {};
- const prevent = configuration.prevent || false;
- const allowMultilines = configuration.allowMultilines || false;
- const firstAdjacentSibling = elements[index + 1];
- const secondAdjacentSibling = elements[index + 2];
- const hasSibling = firstAdjacentSibling
- && secondAdjacentSibling
- && (firstAdjacentSibling.type === 'Literal' || firstAdjacentSibling.type === 'JSXText');
- if (!hasSibling) return;
- // Check adjacent sibling has the proper amount of newlines
- const isWithoutNewLine = !/\n\s*\n/.test(firstAdjacentSibling.value);
- if (isBlockCommentInCurlyBraces(element)) return;
- if (
- allowMultilines
- && (
- isMultilined(element)
- || isMultilined(elements.slice(index + 2).find(isNonBlockComment))
- )
- ) {
- if (!isWithoutNewLine) return;
- const regex = /(\n)(?!.*\1)/g;
- const replacement = '\n\n';
- const messageId = 'allowMultilines';
- report(context, messages[messageId], messageId, {
- node: secondAdjacentSibling,
- fix(fixer) {
- return fixer.replaceText(
- firstAdjacentSibling,
- sourceCode.getText(firstAdjacentSibling)
- .replace(regex, replacement)
- );
- },
- });
- return;
- }
- if (isWithoutNewLine === prevent) return;
- const messageId = prevent
- ? 'prevent'
- : 'require';
- const regex = prevent
- ? /(\n\n)(?!.*\1)/g
- : /(\n)(?!.*\1)/g;
- const replacement = prevent
- ? '\n'
- : '\n\n';
- report(context, messages[messageId], messageId, {
- node: secondAdjacentSibling,
- fix(fixer) {
- return fixer.replaceText(
- firstAdjacentSibling,
- // double or remove the last newline
- sourceCode.getText(firstAdjacentSibling)
- .replace(regex, replacement)
- );
- },
- });
- }
- });
- });
- },
- ':matches(JSXElement, JSXFragment) > :matches(JSXElement, JSXExpressionContainer)': (node) => {
- jsxElementParents.add(node.parent);
- },
- };
- },
- };
|