@croct/eslint-plugin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts ADDED
@@ -0,0 +1,266 @@
1
+ declare const configuration: {
2
+ rules: {
3
+ 'argument-spacing': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missing", never[], {
4
+ CallExpression: (node: import("@typescript-eslint/types/dist/ast-spec").CallExpression | import("@typescript-eslint/types/dist/ast-spec").NewExpression) => void;
5
+ NewExpression: (node: import("@typescript-eslint/types/dist/ast-spec").CallExpression | import("@typescript-eslint/types/dist/ast-spec").NewExpression) => void;
6
+ }>;
7
+ 'jsx-attribute-spacing': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missing", never[], {
8
+ JSXAttribute: (node: import("@typescript-eslint/types/dist/ast-spec").JSXAttribute) => void;
9
+ }>;
10
+ 'complex-expression-spacing': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missing", never[], {
11
+ ArrowFunctionExpression: (node: import("@typescript-eslint/types/dist/ast-spec").ArrowFunctionExpression) => void;
12
+ IfStatement: (node: import("@typescript-eslint/types/dist/ast-spec").IfStatement) => void;
13
+ }>;
14
+ };
15
+ configs: {
16
+ cypress: {
17
+ extends: string[];
18
+ plugins: string[];
19
+ rules: {
20
+ 'no-loop-func': number;
21
+ 'cypress/no-assigning-return-values': string;
22
+ 'cypress/no-unnecessary-waiting': string;
23
+ 'cypress/assertion-before-screenshot': string;
24
+ 'cypress/no-async-tests': string;
25
+ 'cypress/no-pause': string;
26
+ '@typescript-eslint/no-namespace': string;
27
+ };
28
+ };
29
+ react: {
30
+ extends: string[];
31
+ plugins: string[];
32
+ rules: {
33
+ '@croct/jsx-attribute-spacing': string;
34
+ 'react/jsx-wrap-multilines': string;
35
+ 'react/display-name': string;
36
+ 'react/jsx-newline': (string | {
37
+ prevent: boolean;
38
+ })[];
39
+ 'react/jsx-no-bind': (string | {
40
+ allowArrowFunctions: boolean;
41
+ allowBind: boolean;
42
+ allowFunctions: boolean;
43
+ })[];
44
+ 'react/no-unstable-nested-components': (string | {
45
+ allowAsProps: boolean;
46
+ })[];
47
+ 'react/jsx-no-useless-fragment': (string | {
48
+ allowExpressions: boolean;
49
+ })[];
50
+ 'react/function-component-definition': string;
51
+ 'testing-library/no-container': string;
52
+ 'testing-library/no-node-access': string;
53
+ 'testing-library/await-async-utils': string;
54
+ 'jsx-a11y/aria-role': (string | {
55
+ ignoreNonDOM: boolean;
56
+ })[];
57
+ 'react/jsx-uses-react': string;
58
+ 'react/react-in-jsx-scope': string;
59
+ 'react/jsx-one-expression-per-line': string;
60
+ 'react/prop-types': string;
61
+ 'react/require-default-props': string;
62
+ 'react/jsx-fragments': string[];
63
+ 'react-hooks/rules-of-hooks': string;
64
+ 'react-hooks/exhaustive-deps': string;
65
+ 'react/jsx-filename-extension': (number | {
66
+ extensions: string[];
67
+ })[];
68
+ 'react/jsx-indent': (string | number)[];
69
+ 'react/jsx-indent-props': string;
70
+ 'react/jsx-props-no-spreading': string;
71
+ 'no-restricted-imports': (string | {
72
+ name: string;
73
+ importNames: string[];
74
+ message: string;
75
+ })[];
76
+ 'import/order': (string | {
77
+ groups: string[];
78
+ pathGroups: {
79
+ pattern: string;
80
+ group: string;
81
+ position: string;
82
+ }[];
83
+ pathGroupsExcludedImportTypes: string[];
84
+ 'newlines-between': string;
85
+ alphabetize: {
86
+ order: string;
87
+ caseInsensitive: boolean;
88
+ };
89
+ })[];
90
+ };
91
+ };
92
+ typescript: {
93
+ extends: string[];
94
+ plugins: string[];
95
+ rules: {
96
+ '@typescript-eslint/array-type': (string | {
97
+ default: string;
98
+ })[];
99
+ '@typescript-eslint/prefer-as-const': string;
100
+ '@typescript-eslint/adjacent-overload-signatures': string;
101
+ '@typescript-eslint/type-annotation-spacing': string;
102
+ '@typescript-eslint/semi': string[];
103
+ '@typescript-eslint/strict-boolean-expressions': (string | {
104
+ allowString: boolean;
105
+ allowNumber: boolean;
106
+ allowNullableObject: boolean;
107
+ })[];
108
+ '@typescript-eslint/prefer-regexp-exec': string;
109
+ '@typescript-eslint/prefer-optional-chain': string;
110
+ 'no-shadow': string;
111
+ '@typescript-eslint/no-shadow': (string | {
112
+ ignoreTypeValueShadow: boolean;
113
+ ignoreFunctionTypeParameterNameValueShadow: boolean;
114
+ })[];
115
+ '@typescript-eslint/no-empty-interface': string;
116
+ '@typescript-eslint/explicit-member-accessibility': string[];
117
+ '@typescript-eslint/explicit-module-boundary-types': string;
118
+ '@typescript-eslint/explicit-function-return-type': string[];
119
+ '@typescript-eslint/no-explicit-any': string;
120
+ 'no-use-before-define': string;
121
+ '@typescript-eslint/no-use-before-define': string;
122
+ 'no-unused-expressions': string;
123
+ '@typescript-eslint/no-unused-expressions': string;
124
+ indent: (string | number | {
125
+ SwitchCase: number;
126
+ })[];
127
+ '@typescript-eslint/no-unused-vars': string;
128
+ 'no-unused-vars': string;
129
+ '@typescript-eslint/no-non-null-assertion': string;
130
+ 'object-curly-spacing': string;
131
+ '@typescript-eslint/object-curly-spacing': string;
132
+ '@typescript-eslint/member-delimiter-style': (string | {
133
+ multiline: {
134
+ delimiter: string;
135
+ requireLast: boolean;
136
+ };
137
+ singleline: {
138
+ delimiter: string;
139
+ requireLast: boolean;
140
+ };
141
+ overrides: {
142
+ interface: {
143
+ singleline: {
144
+ delimiter: string;
145
+ };
146
+ multiline: {
147
+ delimiter: string;
148
+ };
149
+ };
150
+ };
151
+ })[];
152
+ 'no-undef': string;
153
+ };
154
+ overrides: {
155
+ files: string[];
156
+ extends: string[];
157
+ plugins: string[];
158
+ rules: {
159
+ 'no-new-object': string;
160
+ };
161
+ env: {
162
+ jest: boolean;
163
+ };
164
+ }[];
165
+ };
166
+ javascript: {
167
+ extends: string[];
168
+ plugins: string[];
169
+ rules: {
170
+ '@croct/argument-spacing': string;
171
+ '@croct/complex-expression-spacing': string;
172
+ 'array-bracket-newline': string[];
173
+ 'multiline-ternary': string[];
174
+ 'no-undef-init': string;
175
+ 'jest/consistent-test-it': (string | {
176
+ fn: string;
177
+ })[];
178
+ 'jest/no-large-snapshots': string;
179
+ 'jest/prefer-expect-resolves': string;
180
+ 'jest/prefer-lowercase-title': (string | {
181
+ ignore: string[];
182
+ })[];
183
+ 'jest/prefer-spy-on': string;
184
+ 'jest/require-top-level-describe': string;
185
+ 'jest/prefer-to-contain': string;
186
+ 'jest/prefer-hooks-on-top': string;
187
+ 'jest/prefer-equality-matcher': string;
188
+ 'jest/no-test-return-statement': string;
189
+ 'function-call-argument-newline': string[];
190
+ 'no-underscore-dangle': string;
191
+ 'import/no-default-export': string;
192
+ 'array-element-newline': string[];
193
+ 'import-newlines/enforce': (string | {
194
+ items: number;
195
+ 'max-len': number;
196
+ })[];
197
+ 'no-smart-quotes/no-smart-quotes': string;
198
+ 'no-shadow': string;
199
+ 'import/prefer-default-export': string;
200
+ 'import/no-extraneous-dependencies': string;
201
+ 'no-use-before-define': string;
202
+ 'arrow-parens': string[];
203
+ 'class-methods-use-this': string;
204
+ 'consistent-return': string;
205
+ 'default-case': string;
206
+ 'import/extensions': string[];
207
+ 'import/no-unresolved': string;
208
+ indent: (string | number | {
209
+ SwitchCase: number;
210
+ })[];
211
+ 'linebreak-style': string[];
212
+ 'max-classes-per-file': string;
213
+ 'max-len': (string | {
214
+ code: number;
215
+ ignoreStrings: boolean;
216
+ })[];
217
+ 'no-await-in-loop': string;
218
+ 'no-bitwise': string;
219
+ 'no-continue': string;
220
+ 'no-multiple-empty-lines': (string | {
221
+ max: number;
222
+ maxEOF: number;
223
+ maxBOF: number;
224
+ })[];
225
+ 'no-plusplus': (string | {
226
+ allowForLoopAfterthoughts: boolean;
227
+ })[];
228
+ 'no-unused-expressions': string;
229
+ 'no-unused-vars': string;
230
+ 'no-restricted-syntax': string[];
231
+ 'object-curly-newline': (string | {
232
+ multiline: boolean;
233
+ consistent: boolean;
234
+ })[];
235
+ 'object-curly-spacing': string[];
236
+ 'require-await': string;
237
+ 'object-shorthand': string[];
238
+ 'padding-line-between-statements': (string | {
239
+ blankLine: string;
240
+ prev: string;
241
+ next: string[];
242
+ } | {
243
+ blankLine: string;
244
+ prev: string[];
245
+ next: string;
246
+ } | {
247
+ blankLine: string;
248
+ prev: string[];
249
+ next: string[];
250
+ })[];
251
+ };
252
+ overrides: {
253
+ files: string[];
254
+ extends: string[];
255
+ plugins: string[];
256
+ rules: {
257
+ 'no-new-object': string;
258
+ };
259
+ env: {
260
+ jest: boolean;
261
+ };
262
+ }[];
263
+ };
264
+ };
265
+ };
266
+ export = configuration;
package/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ const rules_1 = require("./rules");
3
+ const configs_1 = require("./configs");
4
+ const configuration = {
5
+ rules: rules_1.rules,
6
+ configs: configs_1.configs,
7
+ };
8
+ module.exports = configuration;
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@croct/eslint-plugin",
3
+ "version": "0.1.0",
4
+ "description": "ESLint rules and presets applied to all Croct JavaScript projects.",
5
+ "author": "",
6
+ "license": "UNLICENSED",
7
+ "main": "index.js",
8
+ "types": "index.d.ts",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/croct-tech/coding-standard-js.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/croct-tech/coding-standard-js/issues"
15
+ },
16
+ "homepage": "https://github.com/croct-tech/coding-standard-js",
17
+ "scripts": {
18
+ "lint": "eslint 'src/**/*.{ts,tsx}'",
19
+ "test": "jest -c jest.config.js --coverage",
20
+ "validate": "tsc --noEmit",
21
+ "build": "tsc -p tsconfig.build.json"
22
+ },
23
+ "keywords": [
24
+ "eslint",
25
+ "eslint-plugin",
26
+ "config",
27
+ "croct",
28
+ "javascript",
29
+ "styleguide",
30
+ "typescript",
31
+ "react",
32
+ "cypress"
33
+ ],
34
+ "dependencies": {
35
+ "@rushstack/eslint-patch": "^1.1",
36
+ "@typescript-eslint/eslint-plugin": "^5.10",
37
+ "@typescript-eslint/experimental-utils": "^5.10",
38
+ "eslint-config-airbnb": "^19.0",
39
+ "eslint-config-airbnb-base": "^15.0",
40
+ "eslint-plugin-cypress": "^2.12",
41
+ "eslint-plugin-import": "^2.25",
42
+ "eslint-plugin-import-newlines": "^1.1",
43
+ "eslint-plugin-jest": "^26.1",
44
+ "eslint-plugin-jest-dom": "^4.0",
45
+ "eslint-plugin-jsx-a11y": "^6.5",
46
+ "eslint-plugin-no-smart-quotes": "^1.3",
47
+ "eslint-plugin-react": "^7.28",
48
+ "eslint-plugin-react-hooks": "^4.3",
49
+ "eslint-plugin-testing-library": "^5.0"
50
+ },
51
+ "devDependencies": {
52
+ "@types/eslint": "^8.4",
53
+ "@types/jest": "^27.4",
54
+ "@typescript-eslint/parser": "^5.10",
55
+ "@typescript-eslint/types": "^5.10",
56
+ "@typescript-eslint/utils": "^5.10",
57
+ "eslint": "^8.8",
58
+ "eslint-plugin-eslint-plugin": "^4.1.0",
59
+ "eslint-plugin-self": "^1.2.1",
60
+ "jest": "^27.5",
61
+ "ts-jest": "^27.1",
62
+ "typescript": "^4.5"
63
+ },
64
+ "peerDependencies": {
65
+ "@typescript-eslint/parser": ">= 5",
66
+ "eslint": ">= 8"
67
+ }
68
+ }
@@ -0,0 +1,5 @@
1
+ import { TSESTree } from '@typescript-eslint/experimental-utils';
2
+ export declare const argumentSpacing: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missing", never[], {
3
+ CallExpression: (node: TSESTree.NewExpression | TSESTree.CallExpression) => void;
4
+ NewExpression: (node: TSESTree.NewExpression | TSESTree.CallExpression) => void;
5
+ }>;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.argumentSpacing = void 0;
4
+ const createRule_1 = require("../createRule");
5
+ exports.argumentSpacing = (0, createRule_1.createRule)({
6
+ name: 'argument-spacing',
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Enforces a surrounding line break before and after the argument list in multiline functional calls.',
11
+ recommended: 'error',
12
+ },
13
+ fixable: 'whitespace',
14
+ schema: [],
15
+ messages: {
16
+ missing: 'Missing new line.',
17
+ },
18
+ },
19
+ defaultOptions: [],
20
+ create: context => {
21
+ function check(node) {
22
+ const sourceCode = context.getSourceCode();
23
+ if (node.arguments.length === 0) {
24
+ return;
25
+ }
26
+ const lastArgument = node.arguments[node.arguments.length - 1];
27
+ const firstToken = sourceCode.getTokenBefore(node.arguments[0], {
28
+ filter: token => token.value === '(',
29
+ });
30
+ const lastToken = sourceCode.getTokenAfter(lastArgument, {
31
+ filter: token => token.value === ')',
32
+ });
33
+ if (firstToken.loc.start.line === lastToken.loc.end.line) {
34
+ return;
35
+ }
36
+ const lastArgumentFirstToken = sourceCode.getFirstToken(lastArgument);
37
+ if ((lastArgument.type !== 'ArrowFunctionExpression' || lastArgument.body.type === 'BlockStatement')
38
+ && firstToken.loc.start.line === lastArgumentFirstToken.loc.start.line) {
39
+ return;
40
+ }
41
+ const tokens = [
42
+ firstToken,
43
+ ...node.arguments,
44
+ lastToken,
45
+ ];
46
+ for (let i = 1; i < tokens.length; i++) {
47
+ const previousToken = tokens[i - 1];
48
+ const currentToken = tokens[i];
49
+ if (previousToken.loc.end.line === currentToken.loc.start.line) {
50
+ const tokenBefore = sourceCode.getTokenBefore(currentToken, { includeComments: true });
51
+ context.report({
52
+ node: node,
53
+ loc: {
54
+ start: tokenBefore.loc.end,
55
+ end: currentToken.loc.start,
56
+ },
57
+ messageId: 'missing',
58
+ fix: fixer => fixer.replaceTextRange([tokenBefore.range[1], currentToken.range[0]], '\n'),
59
+ });
60
+ }
61
+ }
62
+ }
63
+ return {
64
+ CallExpression: check,
65
+ NewExpression: check,
66
+ };
67
+ },
68
+ });
@@ -0,0 +1,5 @@
1
+ import { TSESTree } from '@typescript-eslint/experimental-utils';
2
+ export declare const complexExpressionSpacing: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missing", never[], {
3
+ ArrowFunctionExpression: (node: TSESTree.ArrowFunctionExpression) => void;
4
+ IfStatement: (node: TSESTree.IfStatement) => void;
5
+ }>;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.complexExpressionSpacing = void 0;
4
+ const createRule_1 = require("../createRule");
5
+ exports.complexExpressionSpacing = (0, createRule_1.createRule)({
6
+ name: 'complex-expression-spacing',
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Enforces a surrounding line break in complex expression.',
11
+ recommended: 'error',
12
+ },
13
+ fixable: 'whitespace',
14
+ schema: [],
15
+ messages: {
16
+ missing: 'Missing new line.',
17
+ },
18
+ },
19
+ defaultOptions: [],
20
+ create: context => {
21
+ const sourceCode = context.getSourceCode();
22
+ function check(node) {
23
+ const parentPreviousToken = sourceCode.getTokenBefore(node, {
24
+ filter: token => token.type === 'Punctuator',
25
+ });
26
+ const parentNextToken = sourceCode.getTokenAfter(node, {
27
+ filter: token => token.type === 'Punctuator',
28
+ });
29
+ if (parentPreviousToken.loc.end.line === parentNextToken.loc.start.line) {
30
+ return;
31
+ }
32
+ const firstToken = sourceCode.getFirstToken(node);
33
+ const lastToken = sourceCode.getLastToken(node);
34
+ const tokens = [
35
+ [parentPreviousToken, firstToken],
36
+ [lastToken, parentNextToken],
37
+ ];
38
+ for (const [previousToken, currentToken] of tokens) {
39
+ if (previousToken.loc.end.line !== currentToken.loc.start.line) {
40
+ continue;
41
+ }
42
+ const tokenBefore = sourceCode.getTokenBefore(currentToken, { includeComments: true });
43
+ context.report({
44
+ node: node,
45
+ loc: {
46
+ start: tokenBefore.loc.end,
47
+ end: currentToken.loc.start,
48
+ },
49
+ messageId: 'missing',
50
+ fix: fixer => fixer.replaceTextRange([tokenBefore.range[1], currentToken.range[0]], '\n'),
51
+ });
52
+ }
53
+ }
54
+ return {
55
+ ArrowFunctionExpression: (node) => {
56
+ const { body } = node;
57
+ if (body.type === 'ConditionalExpression') {
58
+ check(body);
59
+ }
60
+ },
61
+ IfStatement: (node) => {
62
+ check(node.test);
63
+ },
64
+ };
65
+ },
66
+ });
@@ -0,0 +1,2 @@
1
+ import { ESLintUtils } from '@typescript-eslint/experimental-utils';
2
+ export declare const createRule: <TOptions extends readonly unknown[], TMessageIds extends string, TRuleListener extends import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener = import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>({ name, meta, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<TOptions, TMessageIds, TRuleListener>>) => import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<TMessageIds, TOptions, TRuleListener>;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRule = void 0;
4
+ const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
5
+ exports.createRule = experimental_utils_1.ESLintUtils.RuleCreator(name => `https://github.com/croct-tech/coding-standard-js/tree/master/docs/${name}.md`);
@@ -0,0 +1,13 @@
1
+ export declare const rules: {
2
+ 'argument-spacing': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missing", never[], {
3
+ CallExpression: (node: import("@typescript-eslint/types/dist/ast-spec").CallExpression | import("@typescript-eslint/types/dist/ast-spec").NewExpression) => void;
4
+ NewExpression: (node: import("@typescript-eslint/types/dist/ast-spec").CallExpression | import("@typescript-eslint/types/dist/ast-spec").NewExpression) => void;
5
+ }>;
6
+ 'jsx-attribute-spacing': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missing", never[], {
7
+ JSXAttribute: (node: import("@typescript-eslint/types/dist/ast-spec").JSXAttribute) => void;
8
+ }>;
9
+ 'complex-expression-spacing': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missing", never[], {
10
+ ArrowFunctionExpression: (node: import("@typescript-eslint/types/dist/ast-spec").ArrowFunctionExpression) => void;
11
+ IfStatement: (node: import("@typescript-eslint/types/dist/ast-spec").IfStatement) => void;
12
+ }>;
13
+ };
package/rules/index.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rules = void 0;
4
+ const argument_spacing_1 = require("./argument-spacing");
5
+ const jsx_attribute_spacing_1 = require("./jsx-attribute-spacing");
6
+ const complex_expression_spacing_1 = require("./complex-expression-spacing");
7
+ exports.rules = {
8
+ 'argument-spacing': argument_spacing_1.argumentSpacing,
9
+ 'jsx-attribute-spacing': jsx_attribute_spacing_1.jsxAttributeSpacing,
10
+ 'complex-expression-spacing': complex_expression_spacing_1.complexExpressionSpacing,
11
+ };
@@ -0,0 +1,4 @@
1
+ import { TSESTree } from '@typescript-eslint/experimental-utils';
2
+ export declare const jsxAttributeSpacing: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missing", never[], {
3
+ JSXAttribute: (node: TSESTree.JSXAttribute) => void;
4
+ }>;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.jsxAttributeSpacing = void 0;
4
+ const createRule_1 = require("../createRule");
5
+ exports.jsxAttributeSpacing = (0, createRule_1.createRule)({
6
+ name: 'jsx-attribute-spacing',
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Enforces a surrounding line break in multiline JSX attributes.',
11
+ recommended: 'error',
12
+ },
13
+ fixable: 'whitespace',
14
+ schema: [],
15
+ messages: {
16
+ missing: 'Missing new line.',
17
+ },
18
+ },
19
+ defaultOptions: [],
20
+ create: context => {
21
+ const sourceCode = context.getSourceCode();
22
+ function check(node) {
23
+ const { value } = node;
24
+ if (value === null || value.type !== 'JSXExpressionContainer') {
25
+ return;
26
+ }
27
+ const firstToken = sourceCode.getFirstToken(value.expression);
28
+ const lastToken = sourceCode.getLastToken(value.expression);
29
+ if ((firstToken.type === 'Punctuator' && lastToken.type === 'Punctuator')
30
+ || (firstToken.loc.start.line === lastToken.loc.end.line)) {
31
+ return;
32
+ }
33
+ const leftBrace = sourceCode.getFirstToken(value);
34
+ const rightBrace = sourceCode.getLastToken(value);
35
+ const tokens = [
36
+ [leftBrace, firstToken],
37
+ [lastToken, rightBrace],
38
+ ];
39
+ for (const [previousToken, currentToken] of tokens) {
40
+ if (previousToken.loc.end.line !== currentToken.loc.start.line) {
41
+ continue;
42
+ }
43
+ const tokenBefore = sourceCode.getTokenBefore(currentToken, { includeComments: true });
44
+ context.report({
45
+ node: node,
46
+ loc: {
47
+ start: tokenBefore.loc.end,
48
+ end: currentToken.loc.start,
49
+ },
50
+ messageId: 'missing',
51
+ fix: fixer => fixer.replaceTextRange([tokenBefore.range[1], currentToken.range[0]], '\n'),
52
+ });
53
+ }
54
+ }
55
+ return {
56
+ JSXAttribute: check,
57
+ };
58
+ },
59
+ });