From 493e23ab211532f65711204d444c63f7f1b8b9f8 Mon Sep 17 00:00:00 2001 From: etherealm13 Date: Mon, 22 Jul 2024 22:56:44 +0530 Subject: [PATCH 1/3] [New] `padding-lines-between-tags`: add rule and test cases --- README.md | 84 +++++----- docs/rules/padding-lines-between-tags.md | 152 ++++++++++++++++++ lib/rules/index.js | 1 + lib/rules/padding-lines-between-tags.js | 189 +++++++++++++++++++++++ 4 files changed, 381 insertions(+), 45 deletions(-) create mode 100644 docs/rules/padding-lines-between-tags.md create mode 100644 lib/rules/padding-lines-between-tags.js diff --git a/README.md b/README.md index e77a809efe..1a13ff5d99 100644 --- a/README.md +++ b/README.md @@ -34,47 +34,47 @@ You should also specify settings that will be shared across all the plugin rules ```json5 { - "settings": { - "react": { - "createClass": "createReactClass", // Regex for Component Factory to use, - // default to "createReactClass" - "pragma": "React", // Pragma to use, default to "React" - "fragment": "Fragment", // Fragment to use (may be a property of ), default to "Fragment" - "version": "detect", // React version. "detect" automatically picks the version you have installed. - // You can also use `16.0`, `16.3`, etc, if you want to override the detected value. - // Defaults to the "defaultVersion" setting and warns if missing, and to "detect" in the future - "defaultVersion": "", // Default React version to use when the version you have installed cannot be detected. - // If not provided, defaults to the latest React version. - "flowVersion": "0.53" // Flow version + settings: { + react: { + createClass: 'createReactClass', // Regex for Component Factory to use, + // default to "createReactClass" + pragma: 'React', // Pragma to use, default to "React" + fragment: 'Fragment', // Fragment to use (may be a property of ), default to "Fragment" + version: 'detect', // React version. "detect" automatically picks the version you have installed. + // You can also use `16.0`, `16.3`, etc, if you want to override the detected value. + // Defaults to the "defaultVersion" setting and warns if missing, and to "detect" in the future + defaultVersion: '', // Default React version to use when the version you have installed cannot be detected. + // If not provided, defaults to the latest React version. + flowVersion: '0.53', // Flow version }, - "propWrapperFunctions": [ - // The names of any function used to wrap propTypes, e.g. `forbidExtraProps`. If this isn't set, any propTypes wrapped in a function will be skipped. - "forbidExtraProps", - {"property": "freeze", "object": "Object"}, - {"property": "myFavoriteWrapper"}, - // for rules that check exact prop wrappers - {"property": "forbidExtraProps", "exact": true} + propWrapperFunctions: [ + // The names of any function used to wrap propTypes, e.g. `forbidExtraProps`. If this isn't set, any propTypes wrapped in a function will be skipped. + 'forbidExtraProps', + { property: 'freeze', object: 'Object' }, + { property: 'myFavoriteWrapper' }, + // for rules that check exact prop wrappers + { property: 'forbidExtraProps', exact: true }, ], - "componentWrapperFunctions": [ - // The name of any function used to wrap components, e.g. Mobx `observer` function. If this isn't set, components wrapped by these functions will be skipped. - "observer", // `property` - {"property": "styled"}, // `object` is optional - {"property": "observer", "object": "Mobx"}, - {"property": "observer", "object": ""} // sets `object` to whatever value `settings.react.pragma` is set to + componentWrapperFunctions: [ + // The name of any function used to wrap components, e.g. Mobx `observer` function. If this isn't set, components wrapped by these functions will be skipped. + 'observer', // `property` + { property: 'styled' }, // `object` is optional + { property: 'observer', object: 'Mobx' }, + { property: 'observer', object: '' }, // sets `object` to whatever value `settings.react.pragma` is set to ], - "formComponents": [ + formComponents: [ // Components used as alternatives to
for forms, eg. - "CustomForm", - {"name": "SimpleForm", "formAttribute": "endpoint"}, - {"name": "Form", "formAttribute": ["registerEndpoint", "loginEndpoint"]}, // allows specifying multiple properties if necessary + 'CustomForm', + { name: 'SimpleForm', formAttribute: 'endpoint' }, + { name: 'Form', formAttribute: ['registerEndpoint', 'loginEndpoint'] }, // allows specifying multiple properties if necessary ], - "linkComponents": [ + linkComponents: [ // Components used as alternatives to for linking, eg. - "Hyperlink", - {"name": "MyLink", "linkAttribute": "to"}, - {"name": "Link", "linkAttribute": ["to", "href"]}, // allows specifying multiple properties if necessary - ] - } + 'Hyperlink', + { name: 'MyLink', linkAttribute: 'to' }, + { name: 'Link', linkAttribute: ['to', 'href'] }, // allows specifying multiple properties if necessary + ], + }, } ``` @@ -84,9 +84,7 @@ Add "react" to the plugins section. ```json { - "plugins": [ - "react" - ] + "plugins": ["react"] } ``` @@ -136,9 +134,7 @@ This pairs well with the `eslint:all` rule. ```json { - "plugins": [ - "react" - ], + "plugins": ["react"], "extends": ["eslint:all", "plugin:react/all"] } ``` @@ -205,6 +201,7 @@ Refer to the [official docs](https://eslint.org/docs/latest/user-guide/configuri The schema of the `settings.react` object would be identical to that of what's already described above in the legacy config section. + ### Flat Configs This plugin exports 3 flat configs: @@ -375,6 +372,7 @@ module.exports = [ | [no-unused-prop-types](docs/rules/no-unused-prop-types.md) | Disallow definitions of unused propTypes | | | | | | | [no-unused-state](docs/rules/no-unused-state.md) | Disallow definitions of unused state | | | | | | | [no-will-update-set-state](docs/rules/no-will-update-set-state.md) | Disallow usage of setState in componentWillUpdate | | | | | | +| [padding-lines-between-tags](docs/rules/padding-lines-between-tags.md) | Enforce no padding lines between tags for React Components | | | 🔧 | | | | [prefer-es6-class](docs/rules/prefer-es6-class.md) | Enforce ES5 or ES6 class for React Components | | | | | | | [prefer-exact-props](docs/rules/prefer-exact-props.md) | Prefer exact proptype definitions | | | | | | | [prefer-read-only-props](docs/rules/prefer-read-only-props.md) | Enforce that props are read-only | | | 🔧 | | | @@ -407,15 +405,11 @@ module.exports = [ [npm-url]: https://npmjs.org/package/eslint-plugin-react [npm-image]: https://img.shields.io/npm/v/eslint-plugin-react.svg - [status-url]: https://github.com/jsx-eslint/eslint-plugin-react/pulse [status-image]: https://img.shields.io/github/last-commit/jsx-eslint/eslint-plugin-react.svg - [tidelift-url]: https://tidelift.com/subscription/pkg/npm-eslint-plugin-react?utm_source=npm-eslint-plugin-react&utm_medium=referral&utm_campaign=readme [tidelift-image]: https://tidelift.com/badges/package/npm/eslint-plugin-react?style=flat - [package-url]: https://npmjs.org/package/eslint-plugin-react [npm-version-svg]: https://versionbadg.es/jsx-eslint/eslint-plugin-react.svg - [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/jsx-eslint/eslint-plugin-react [actions-url]: https://github.com/jsx-eslint/eslint-plugin-react/actions diff --git a/docs/rules/padding-lines-between-tags.md b/docs/rules/padding-lines-between-tags.md new file mode 100644 index 0000000000..42e2c0067c --- /dev/null +++ b/docs/rules/padding-lines-between-tags.md @@ -0,0 +1,152 @@ +# Enforce no padding lines between tags for React Components (`react/padding-lines-between-tags`) + +🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). + + + +Require or disallow newlines between sibling tags in React. + +## Rule Details Options + +```json +{ + "padding-line-between-tags": [ + "error", + [{ "blankLine": "always", "prev": "*", "next": "*" }] + ] +} +``` + +This rule requires blank lines between each sibling HTML tag by default. + +A configuration is an object which has 3 properties; `blankLine`, `prev` and `next`. For example, `{ blankLine: "always", prev: "br", next: "div" }` means “one or more blank lines are required between a `br` tag and a `div` tag.” You can supply any number of configurations. If a tag pair matches multiple configurations, the last matched configuration will be used. + +- `blankLine` is one of the following: + - `always` requires one or more blank lines. + - `never` disallows blank lines. + - `consistent` requires or disallows a blank line based on the first sibling element. +- `prev` any tag name without brackets. +- `next` any tag name without brackets. + +### Disallow blank lines between all tags + +`{ blankLine: 'never', prev: '*', next: '*' }` + + + +```react + +
+
+
+
+
+
+ +``` + + + +### Require newlines after `
` + +`{ blankLine: 'always', prev: 'br', next: '*' }` + + + +```react + +
+
    +
  • +
  • +
    + +
  • +
  • +
+
+
+``` + +
+ +### Require newlines between `
` and `` + +`{ blankLine: 'always', prev: 'br', next: 'img' }` + + + +```react + +
+
    +
  • +
  • +
  • +
  • +
    + +
  • +
  • +
+
+
+``` + +```react [Fixed] + +
+
    +
  • +
  • + +
  • +
  • +
    + + +
  • +
  • +
+
+
+``` + +
+ +### Require consistent newlines + +`{ blankLine: 'consistent', prev: '*', next: '*' }` + + + +```react + +
+
    +
  • +
  • +
  • +
+ +
+ +
+
+ +``` + + + +## When Not To Use It + +If you are not using React. diff --git a/lib/rules/index.js b/lib/rules/index.js index 11a4475ba2..2b17db2401 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -89,6 +89,7 @@ module.exports = { 'no-unused-state': require('./no-unused-state'), 'no-object-type-as-default-prop': require('./no-object-type-as-default-prop'), 'no-will-update-set-state': require('./no-will-update-set-state'), + 'padding-lines-between-tags': require('./padding-lines-between-tags'), 'prefer-es6-class': require('./prefer-es6-class'), 'prefer-exact-props': require('./prefer-exact-props'), 'prefer-read-only-props': require('./prefer-read-only-props'), diff --git a/lib/rules/padding-lines-between-tags.js b/lib/rules/padding-lines-between-tags.js new file mode 100644 index 0000000000..c3fbc2583a --- /dev/null +++ b/lib/rules/padding-lines-between-tags.js @@ -0,0 +1,189 @@ +/** + * @fileoverview Enforce no padding lines between tags for React Components + * @author Alankar Anand + * Based on https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/padding-line-between-tags.js + * https://github.com/jsx-eslint/eslint-plugin-react/issues/3554 + */ + +'use strict'; + +const docsUrl = require('../util/docsUrl'); + +/** + * Split the source code into multiple lines based on the line delimiters. + * Copied from padding-line-between-blocks + * @param {string} text Source code as a string. + * @returns {string[]} Array of source code lines. + */ +const messages = { + never: 'Unexpected blank line before this tag.', + always: 'Expected blank line before this tag.', +}; + +function splitLines(text) { + return text.split(/\r\n|[\r\n\u2028\u2029]/gu); +} + +function insertNewLine(context, tag, sibling, lineDifference) { + const endTag = tag.closingElement || tag.openingElement; + + if (lineDifference === 1) { + context.report({ + messageId: 'always', + loc: sibling && sibling.loc, + // @ts-ignore + fix(fixer) { + return fixer.insertTextAfter(tag, '\n'); + }, + }); + } else if (lineDifference === 0) { + context.report({ + messageId: 'always', + loc: sibling && sibling.loc, + // @ts-ignore + fix(fixer) { + const lastSpaces = /** @type {RegExpExecArray} */ ( + /^\s*/.exec(context.getSourceCode().lines[endTag.loc.start.line - 1]) + )[0]; + + return fixer.insertTextAfter(endTag, `\n\n${lastSpaces}`); + }, + }); + } +} + +function removeExcessLines(context, endTag, sibling, lineDifference) { + if (lineDifference > 1) { + let hasOnlyTextBetween = true; + for ( + let i = endTag.loc && endTag.loc.start.line; + i < sibling.loc.start.line - 1 && hasOnlyTextBetween; + i++ + ) { + hasOnlyTextBetween = !/^\s*$/.test(context.getSourceCode().lines[i]); + } + if (!hasOnlyTextBetween) { + context.report({ + messageId: 'never', + loc: sibling && sibling.loc, + // @ts-ignore + fix(fixer) { + const start = endTag.range[1]; + const end = sibling.range[0]; + const paddingText = context.getSourceCode().text.slice(start, end); + const textBetween = splitLines(paddingText); + let newTextBetween = `\n${textBetween.pop()}`; + for (let i = textBetween.length - 1; i >= 0; i--) { + if (!/^\s*$/.test(textBetween[i])) { + newTextBetween = `${i === 0 ? '' : '\n'}${ + textBetween[i] + }${newTextBetween}`; + } + } + return fixer.replaceTextRange([start, end], `${newTextBetween}`); + }, + }); + } + } +} + +function checkNewLine(context, configureList) { + const firstConsistentBlankLines = new Map(); + + const reverseConfigureList = [...configureList].reverse(); + + return (node) => { + if (!node.parent.parent) { + return; + } + + const endTag = node.closingElement || node.openingElement; + + if (!node.parent.children) { + return; + } + const lowerSiblings = node.parent.children + .filter( + (element) => element.type === 'JSXElement' && element.range !== node.range + ) + .filter((sibling) => sibling.range[0] - endTag.range[1] >= 0); + + if (lowerSiblings.length === 0) { + return; + } + const closestSibling = lowerSiblings[0]; + + const lineDifference = closestSibling.loc.start.line - endTag.loc.end.line; + + const configure = reverseConfigureList.find( + (config) => (config.prev === '*' + || node.openingElement.name.name === config.prev) + && (config.next === '*' + || closestSibling.openingElement.name.name === config.next) + ); + + if (!configure) { + return; + } + + let blankLine = configure.blankLine; + + if (blankLine === 'consistent') { + const firstConsistentBlankLine = firstConsistentBlankLines.get( + node.parent + ); + if (firstConsistentBlankLine == null) { + firstConsistentBlankLines.set( + node.parent, + lineDifference > 1 ? 'always' : 'never' + ); + return; + } + blankLine = firstConsistentBlankLine; + } + + if (blankLine === 'always') { + insertNewLine(context, node, closestSibling, lineDifference); + } else { + removeExcessLines(context, endTag, closestSibling, lineDifference); + } + }; +} + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce no padding lines between tags for React Components', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('padding-lines-between-tags'), + }, + fixable: 'code', + messages, + schema: [ + { + type: 'array', + items: { + type: 'object', + properties: { + blankLine: { enum: ['always', 'never', 'consistent'] }, + prev: { type: 'string' }, + next: { type: 'string' }, + }, + additionalProperties: false, + required: ['blankLine', 'prev', 'next'], + }, + }, + ], + }, + + create(context) { + const configureList = context.options[0] || [ + { blankLine: 'always', prev: '*', next: '*' }, + ]; + return { + JSXElement: checkNewLine(context, configureList), + }; + }, +}; From 580b3038b44c04d85b959cd9097b0f7138998f9c Mon Sep 17 00:00:00 2001 From: etherealm13 Date: Sat, 27 Jul 2024 00:22:15 +0530 Subject: [PATCH 2/3] resolved pr comments related to docs and rule --- README.md | 83 +++++++++++++----------- docs/rules/padding-lines-between-tags.md | 39 ++--------- lib/rules/padding-lines-between-tags.js | 36 +++++----- 3 files changed, 69 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 1a13ff5d99..56e7c3f670 100644 --- a/README.md +++ b/README.md @@ -34,47 +34,47 @@ You should also specify settings that will be shared across all the plugin rules ```json5 { - settings: { - react: { - createClass: 'createReactClass', // Regex for Component Factory to use, - // default to "createReactClass" - pragma: 'React', // Pragma to use, default to "React" - fragment: 'Fragment', // Fragment to use (may be a property of ), default to "Fragment" - version: 'detect', // React version. "detect" automatically picks the version you have installed. - // You can also use `16.0`, `16.3`, etc, if you want to override the detected value. - // Defaults to the "defaultVersion" setting and warns if missing, and to "detect" in the future - defaultVersion: '', // Default React version to use when the version you have installed cannot be detected. - // If not provided, defaults to the latest React version. - flowVersion: '0.53', // Flow version + "settings": { + "react": { + "createClass": "createReactClass", // Regex for Component Factory to use, + // default to "createReactClass" + "pragma": "React", // Pragma to use, default to "React" + "fragment": "Fragment", // Fragment to use (may be a property of ), default to "Fragment" + "version": "detect", // React version. "detect" automatically picks the version you have installed. + // You can also use `16.0`, `16.3`, etc, if you want to override the detected value. + // Defaults to the "defaultVersion" setting and warns if missing, and to "detect" in the future + "defaultVersion": "", // Default React version to use when the version you have installed cannot be detected. + // If not provided, defaults to the latest React version. + "flowVersion": "0.53" // Flow version }, - propWrapperFunctions: [ - // The names of any function used to wrap propTypes, e.g. `forbidExtraProps`. If this isn't set, any propTypes wrapped in a function will be skipped. - 'forbidExtraProps', - { property: 'freeze', object: 'Object' }, - { property: 'myFavoriteWrapper' }, - // for rules that check exact prop wrappers - { property: 'forbidExtraProps', exact: true }, + "propWrapperFunctions": [ + // The names of any function used to wrap propTypes, e.g. `forbidExtraProps`. If this isn't set, any propTypes wrapped in a function will be skipped. + "forbidExtraProps", + {"property": "freeze", "object": "Object"}, + {"property": "myFavoriteWrapper"}, + // for rules that check exact prop wrappers + {"property": "forbidExtraProps", "exact": true} ], - componentWrapperFunctions: [ - // The name of any function used to wrap components, e.g. Mobx `observer` function. If this isn't set, components wrapped by these functions will be skipped. - 'observer', // `property` - { property: 'styled' }, // `object` is optional - { property: 'observer', object: 'Mobx' }, - { property: 'observer', object: '' }, // sets `object` to whatever value `settings.react.pragma` is set to + "componentWrapperFunctions": [ + // The name of any function used to wrap components, e.g. Mobx `observer` function. If this isn't set, components wrapped by these functions will be skipped. + "observer", // `property` + {"property": "styled"}, // `object` is optional + {"property": "observer", "object": "Mobx"}, + {"property": "observer", "object": ""} // sets `object` to whatever value `settings.react.pragma` is set to ], - formComponents: [ + "formComponents": [ // Components used as alternatives to for forms, eg. - 'CustomForm', - { name: 'SimpleForm', formAttribute: 'endpoint' }, - { name: 'Form', formAttribute: ['registerEndpoint', 'loginEndpoint'] }, // allows specifying multiple properties if necessary + "CustomForm", + {"name": "SimpleForm", "formAttribute": "endpoint"}, + {"name": "Form", "formAttribute": ["registerEndpoint", "loginEndpoint"]}, // allows specifying multiple properties if necessary ], - linkComponents: [ + "linkComponents": [ // Components used as alternatives to
for linking, eg. - 'Hyperlink', - { name: 'MyLink', linkAttribute: 'to' }, - { name: 'Link', linkAttribute: ['to', 'href'] }, // allows specifying multiple properties if necessary - ], - }, + "Hyperlink", + {"name": "MyLink", "linkAttribute": "to"}, + {"name": "Link", "linkAttribute": ["to", "href"]}, // allows specifying multiple properties if necessary + ] + } } ``` @@ -84,7 +84,9 @@ Add "react" to the plugins section. ```json { - "plugins": ["react"] + "plugins": [ + "react" + ] } ``` @@ -134,7 +136,9 @@ This pairs well with the `eslint:all` rule. ```json { - "plugins": ["react"], + "plugins": [ + "react" + ], "extends": ["eslint:all", "plugin:react/all"] } ``` @@ -201,7 +205,6 @@ Refer to the [official docs](https://eslint.org/docs/latest/user-guide/configuri The schema of the `settings.react` object would be identical to that of what's already described above in the legacy config section. - ### Flat Configs This plugin exports 3 flat configs: @@ -405,11 +408,15 @@ module.exports = [ [npm-url]: https://npmjs.org/package/eslint-plugin-react [npm-image]: https://img.shields.io/npm/v/eslint-plugin-react.svg + [status-url]: https://github.com/jsx-eslint/eslint-plugin-react/pulse [status-image]: https://img.shields.io/github/last-commit/jsx-eslint/eslint-plugin-react.svg + [tidelift-url]: https://tidelift.com/subscription/pkg/npm-eslint-plugin-react?utm_source=npm-eslint-plugin-react&utm_medium=referral&utm_campaign=readme [tidelift-image]: https://tidelift.com/badges/package/npm/eslint-plugin-react?style=flat + [package-url]: https://npmjs.org/package/eslint-plugin-react [npm-version-svg]: https://versionbadg.es/jsx-eslint/eslint-plugin-react.svg + [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/jsx-eslint/eslint-plugin-react [actions-url]: https://github.com/jsx-eslint/eslint-plugin-react/actions diff --git a/docs/rules/padding-lines-between-tags.md b/docs/rules/padding-lines-between-tags.md index 42e2c0067c..f25fed53f1 100644 --- a/docs/rules/padding-lines-between-tags.md +++ b/docs/rules/padding-lines-between-tags.md @@ -19,7 +19,7 @@ Require or disallow newlines between sibling tags in React. This rule requires blank lines between each sibling HTML tag by default. -A configuration is an object which has 3 properties; `blankLine`, `prev` and `next`. For example, `{ blankLine: "always", prev: "br", next: "div" }` means “one or more blank lines are required between a `br` tag and a `div` tag.” You can supply any number of configurations. If a tag pair matches multiple configurations, the last matched configuration will be used. +A configuration is an object which has 3 properties; `blankLine`, `prev`, and `next`. For example, `{ blankLine: "always", prev: "br", next: "div" }` means “one or more blank lines are required between a `br` tag and a `div` tag.” You can supply any number of configurations. If a tag pair matches multiple configurations, the last matched configuration will be used. - `blankLine` is one of the following: - `always` requires one or more blank lines. @@ -32,11 +32,7 @@ A configuration is an object which has 3 properties; `blankLine`, `prev` and `ne `{ blankLine: 'never', prev: '*', next: '*' }` - - -```react +```jsx
@@ -47,17 +43,11 @@ A configuration is an object which has 3 properties; `blankLine`, `prev` and `ne ``` - - ### Require newlines after `
` `{ blankLine: 'always', prev: 'br', next: '*' }` - - -```react +```jsx
    @@ -72,18 +62,11 @@ A configuration is an object which has 3 properties; `blankLine`, `prev` and `ne ``` - - ### Require newlines between `
    ` and `` `{ blankLine: 'always', prev: 'br', next: 'img' }` - - -```react +```jsx
      @@ -100,7 +83,7 @@ A configuration is an object which has 3 properties; `blankLine`, `prev` and `ne ``` -```react [Fixed] +```jsx [Fixed]
        @@ -119,17 +102,11 @@ A configuration is an object which has 3 properties; `blankLine`, `prev` and `ne ``` - - ### Require consistent newlines `{ blankLine: 'consistent', prev: '*', next: '*' }` - - -```react +```jsx
          @@ -145,8 +122,6 @@ A configuration is an object which has 3 properties; `blankLine`, `prev` and `ne ``` - - ## When Not To Use It -If you are not using React. +If you are not using jsx. diff --git a/lib/rules/padding-lines-between-tags.js b/lib/rules/padding-lines-between-tags.js index c3fbc2583a..2bba3bf678 100644 --- a/lib/rules/padding-lines-between-tags.js +++ b/lib/rules/padding-lines-between-tags.js @@ -8,6 +8,8 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); /** * Split the source code into multiple lines based on the line delimiters. @@ -15,21 +17,17 @@ const docsUrl = require('../util/docsUrl'); * @param {string} text Source code as a string. * @returns {string[]} Array of source code lines. */ -const messages = { - never: 'Unexpected blank line before this tag.', - always: 'Expected blank line before this tag.', -}; function splitLines(text) { return text.split(/\r\n|[\r\n\u2028\u2029]/gu); } -function insertNewLine(context, tag, sibling, lineDifference) { +function insertNewLine(tag, sibling, lineDifference) { const endTag = tag.closingElement || tag.openingElement; if (lineDifference === 1) { - context.report({ - messageId: 'always', + report({ + message: 'Unexpected blank line before {{endTag}}.', loc: sibling && sibling.loc, // @ts-ignore fix(fixer) { @@ -37,13 +35,13 @@ function insertNewLine(context, tag, sibling, lineDifference) { }, }); } else if (lineDifference === 0) { - context.report({ - messageId: 'always', + report({ + message: 'Expected blank line before {{endTag}}.', loc: sibling && sibling.loc, // @ts-ignore fix(fixer) { const lastSpaces = /** @type {RegExpExecArray} */ ( - /^\s*/.exec(context.getSourceCode().lines[endTag.loc.start.line - 1]) + /^\s*/.exec(eslintUtil.getSourceCode().lines[endTag.loc.start.line - 1]) )[0]; return fixer.insertTextAfter(endTag, `\n\n${lastSpaces}`); @@ -52,7 +50,7 @@ function insertNewLine(context, tag, sibling, lineDifference) { } } -function removeExcessLines(context, endTag, sibling, lineDifference) { +function removeExcessLines(endTag, sibling, lineDifference) { if (lineDifference > 1) { let hasOnlyTextBetween = true; for ( @@ -60,17 +58,17 @@ function removeExcessLines(context, endTag, sibling, lineDifference) { i < sibling.loc.start.line - 1 && hasOnlyTextBetween; i++ ) { - hasOnlyTextBetween = !/^\s*$/.test(context.getSourceCode().lines[i]); + hasOnlyTextBetween = !/^\s*$/.test(eslintUtil.getSourceCode().lines[i]); } if (!hasOnlyTextBetween) { - context.report({ + report({ messageId: 'never', loc: sibling && sibling.loc, // @ts-ignore fix(fixer) { const start = endTag.range[1]; const end = sibling.range[0]; - const paddingText = context.getSourceCode().text.slice(start, end); + const paddingText = eslintUtil.getSourceCode().text.slice(start, end); const textBetween = splitLines(paddingText); let newTextBetween = `\n${textBetween.pop()}`; for (let i = textBetween.length - 1; i >= 0; i--) { @@ -87,7 +85,7 @@ function removeExcessLines(context, endTag, sibling, lineDifference) { } } -function checkNewLine(context, configureList) { +function checkNewLine(configureList) { const firstConsistentBlankLines = new Map(); const reverseConfigureList = [...configureList].reverse(); @@ -143,15 +141,16 @@ function checkNewLine(context, configureList) { } if (blankLine === 'always') { - insertNewLine(context, node, closestSibling, lineDifference); + insertNewLine(node, closestSibling, lineDifference); } else { - removeExcessLines(context, endTag, closestSibling, lineDifference); + removeExcessLines(endTag, closestSibling, lineDifference); } }; } /** @type {import('eslint').Rule.RuleModule} */ module.exports = { + // eslint-disable-next-line eslint-plugin/prefer-message-ids meta: { docs: { description: 'Enforce no padding lines between tags for React Components', @@ -160,7 +159,6 @@ module.exports = { url: docsUrl('padding-lines-between-tags'), }, fixable: 'code', - messages, schema: [ { type: 'array', @@ -183,7 +181,7 @@ module.exports = { { blankLine: 'always', prev: '*', next: '*' }, ]; return { - JSXElement: checkNewLine(context, configureList), + JSXElement: checkNewLine(configureList), }; }, }; From 1c0268438ea8c28fe90fddfc741fe268cd192922 Mon Sep 17 00:00:00 2001 From: etherealm13 Date: Sat, 27 Jul 2024 00:26:48 +0530 Subject: [PATCH 3/3] resolved pr comments: updated from spread to concat --- lib/rules/padding-lines-between-tags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/padding-lines-between-tags.js b/lib/rules/padding-lines-between-tags.js index 2bba3bf678..a979d988bb 100644 --- a/lib/rules/padding-lines-between-tags.js +++ b/lib/rules/padding-lines-between-tags.js @@ -88,7 +88,7 @@ function removeExcessLines(endTag, sibling, lineDifference) { function checkNewLine(configureList) { const firstConsistentBlankLines = new Map(); - const reverseConfigureList = [...configureList].reverse(); + const reverseConfigureList = [].concat(configureList).reverse(); return (node) => { if (!node.parent.parent) {