lint-rules-alvin 1.1.3 → 1.1.4

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.
@@ -1,7 +1,6 @@
1
1
  import { newlineBetweenImportsRule } from '../custom_rules/newline-between-imports.js';
2
2
  import { unnamedImportsLastRule } from '../custom_rules/unnamed-imports-last.js';
3
3
  import { jsxMultilinePropNewlineRule } from '../custom_rules/jsx-multiline-prop-newline.js';
4
- import { jsxNoSingleObjectCurlyNewlineRule } from '../custom_rules/jsx-no-single-object-curly-newline.js';
5
4
  import { maxChainPerLineRule } from '../custom_rules/max-chain-per-line.js';
6
5
  import { multilineParenNewlineRule } from '../custom_rules/multiline-paren-newline.js';
7
6
  import { multilineArrayAccessorNewlineRule } from '../custom_rules/multiline-array-accessor-newline.js';
@@ -19,7 +18,6 @@ export const custom = {
19
18
  'newline-between-imports': newlineBetweenImportsRule,
20
19
  'unnamed-imports-last': unnamedImportsLastRule,
21
20
  'jsx-multiline-prop-newline': jsxMultilinePropNewlineRule,
22
- 'jsx-no-single-object-curly-newline': jsxNoSingleObjectCurlyNewlineRule,
23
21
  'max-chain-per-line': maxChainPerLineRule,
24
22
  'multiline-paren-newline': multilineParenNewlineRule,
25
23
  'multiline-array-accessor-newline': multilineArrayAccessorNewlineRule,
@@ -33,8 +31,13 @@ export const custom = {
33
31
  { minItems: 2 }
34
32
  ],
35
33
  'custom/unnamed-imports-last': 'error',
36
- 'custom/jsx-multiline-prop-newline': 'error',
37
- 'custom/jsx-no-single-object-curly-newline': 'error',
34
+ 'custom/jsx-multiline-prop-newline': [
35
+ 'error',
36
+ {
37
+ enforceSingleLine: true,
38
+ allowSingleExpression: true
39
+ }
40
+ ],
38
41
  'custom/max-chain-per-line': [
39
42
  'error',
40
43
  {
@@ -5,108 +5,199 @@ export const jsxMultilinePropNewlineRule = {
5
5
  meta: {
6
6
  type: 'layout',
7
7
  docs: {
8
- description: 'Enforces a new line for the first prop if any prop\'s value is multiline.',
8
+ description: 'Enforces newline consistency for JSX properties.',
9
9
  category: 'Stylistic Issues',
10
10
  recommended: false
11
11
  },
12
12
  fixable: 'code',
13
- schema: [],
14
- messages: { error: 'The first property should be on a new line if any property\'s value is multiline.' }
13
+ schema: [
14
+ {
15
+ type: 'object',
16
+ properties: {
17
+ enforceSingleLine: {
18
+ type: 'boolean',
19
+ default: false
20
+ },
21
+ allowSingleExpression: {
22
+ type: 'boolean',
23
+ default: true
24
+ }
25
+ },
26
+ additionalProperties: false
27
+ }
28
+ ],
29
+ messages: {
30
+ collapse: 'This expression should not be surrounded by newlines.',
31
+ expand: 'This multiline expression should be surrounded by newlines.'
32
+ }
15
33
  },
16
-
17
34
  create(context) {
18
35
 
36
+ const {
37
+ allowSingleExpression = true,
38
+ enforceSingleLine = false
39
+ } = context.options[0] || {};
19
40
  const sourceCode = context.sourceCode;
20
41
 
21
- function isMultiline(node) {
22
-
23
- return node && node.loc && node
24
- .loc
25
- .start
26
- .line < node
27
- .loc
28
- .end
29
- .line;
30
-
31
- }
32
-
33
42
  return {
34
- JSXOpeningElement(node) {
43
+ JSXExpressionContainer(node) {
35
44
 
36
- if (node.attributes.length === 0) {
45
+ if (node.parent.type !== 'JSXAttribute' || node.expression.type === 'JSXEmptyExpression') {
37
46
 
38
47
  return;
39
48
 
40
49
  }
41
50
 
42
- const hasMultilineProp = node.attributes.some(
43
- (attr) => {
51
+ const openingBrace = sourceCode.getFirstToken(node);
52
+ const closingBrace = sourceCode.getLastToken(node);
53
+ const expression = node.expression;
54
+
55
+ const expressionIsMultiline = expression
56
+ .loc
57
+ .start
58
+ .line !== expression
59
+ .loc
60
+ .end
61
+ .line;
62
+
63
+ const hasNewlineAfter = openingBrace
64
+ .loc
65
+ .end
66
+ .line !== expression
67
+ .loc
68
+ .start
69
+ .line;
70
+ const hasNewlineBefore = expression
71
+ .loc
72
+ .end
73
+ .line !== closingBrace
74
+ .loc
75
+ .start
76
+ .line;
77
+
78
+ if (expressionIsMultiline) {
79
+
80
+ const isSingleObjectOrArray = expression.type === 'ObjectExpression' || expression.type === 'ArrayExpression';
81
+
82
+ if (allowSingleExpression && isSingleObjectOrArray) {
83
+
84
+ // Collapse newlines for single multiline Object/Array
85
+ if (hasNewlineAfter || hasNewlineBefore) {
86
+
87
+ context.report({
88
+ node,
89
+ messageId: 'collapse',
90
+ fix(fixer) {
91
+
92
+ const fixes = [];
93
+ if (hasNewlineAfter) {
94
+
95
+ fixes.push(
96
+ fixer.removeRange([
97
+ openingBrace.range[1],
98
+ expression.range[0]
99
+ ])
100
+ );
101
+
102
+ }
103
+ if (hasNewlineBefore) {
104
+
105
+ fixes.push(
106
+ fixer.removeRange([
107
+ expression.range[1],
108
+ closingBrace.range[0]
109
+ ])
110
+ );
111
+
112
+ }
113
+ return fixes;
114
+
115
+ }
116
+ });
44
117
 
45
- // Check for JSXAttribute (e.g., href={...})
46
- if (attr.type === 'JSXAttribute') {
118
+ }
47
119
 
48
- return isMultiline(attr.value);
120
+ } else {
49
121
 
50
- }
122
+ // Enforce newlines for other multiline expressions
123
+ if (!hasNewlineAfter || !hasNewlineBefore) {
124
+
125
+ context.report({
126
+ node,
127
+ messageId: 'expand',
128
+ fix(fixer) {
129
+
130
+ const fixes = [];
131
+ if (!hasNewlineAfter) {
132
+
133
+ fixes.push(
134
+ fixer.insertTextAfter(
135
+ openingBrace,
136
+ '\n'
137
+ )
138
+ );
51
139
 
52
- // Check for JSXSpreadAttribute (e.g., {...props})
53
- if (attr.type === 'JSXSpreadAttribute') {
140
+ }
141
+ if (!hasNewlineBefore) {
54
142
 
55
- return isMultiline(attr);
143
+ fixes.push(
144
+ fixer.insertTextBefore(
145
+ closingBrace,
146
+ '\n'
147
+ )
148
+ );
149
+
150
+ }
151
+ return fixes;
152
+
153
+ }
154
+ });
56
155
 
57
156
  }
58
- return false;
59
157
 
60
158
  }
61
- );
62
159
 
63
- if (!hasMultilineProp) {
160
+ } else { // Single-line expression
64
161
 
65
- return;
162
+ if (enforceSingleLine) {
66
163
 
67
- }
164
+ // Collapse newlines
165
+ if (hasNewlineAfter || hasNewlineBefore) {
166
+
167
+ context.report({
168
+ node,
169
+ messageId: 'collapse',
170
+ fix(fixer) {
171
+
172
+ const fixes = [];
173
+ if (hasNewlineAfter) {
68
174
 
69
- const firstProp = node.attributes[0];
70
-
71
- // Check if the tag name and the first prop are on the same line.
72
- if (
73
- node
74
- .name
75
- .loc
76
- .end
77
- .line === firstProp
78
- .loc
79
- .start
80
- .line
81
- ) {
82
-
83
- context.report({
84
- node: firstProp,
85
- messageId: 'error',
86
- fix(fixer) {
87
-
88
- // Get indentation of the line with the opening tag.
89
- const line = sourceCode.getLines()[
90
- node
91
- .loc
92
- .start
93
- .line - 1
94
- ];
95
- const baseIndentMatch = line.match(/^\s*/);
96
- const baseIndent = baseIndentMatch
97
- ? baseIndentMatch[0]
98
- : '';
99
-
100
- // Assume an indent of 2 spaces, a common practice.
101
- const indent = baseIndent + ' ';
102
-
103
- return fixer.insertTextBefore(
104
- firstProp,
105
- `\n${indent}`
106
- );
175
+ fixes.push(
176
+ fixer.removeRange([
177
+ openingBrace.range[1],
178
+ expression.range[0]
179
+ ])
180
+ );
181
+
182
+ }
183
+ if (hasNewlineBefore) {
184
+
185
+ fixes.push(
186
+ fixer.removeRange([
187
+ expression.range[1],
188
+ closingBrace.range[0]
189
+ ])
190
+ );
191
+
192
+ }
193
+ return fixes;
194
+
195
+ }
196
+ });
107
197
 
108
198
  }
109
- });
199
+
200
+ }
110
201
 
111
202
  }
112
203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lint-rules-alvin",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "My own personal linting ruleset for a bunch of different plugins. Includes a few custom rules. Used in a few of my repos.",
5
5
  "keywords": [
6
6
  "eslint"
@@ -1,120 +0,0 @@
1
- /**
2
- * @type {import('eslint').Rule.RuleModule}
3
- */
4
- export const jsxNoSingleObjectCurlyNewlineRule = {
5
- meta: {
6
- type: 'layout',
7
- docs: {
8
- description: 'Disallows newlines inside curly braces for single object or array expressions in JSX.',
9
- category: 'Stylistic Issues',
10
- recommended: false
11
- },
12
- fixable: 'code',
13
- schema: [],
14
- messages: { error: 'Newlines around single object or array expressions in JSX curly braces are not allowed.' }
15
- },
16
-
17
- create(context) {
18
-
19
- const sourceCode = context.sourceCode;
20
-
21
- return {
22
- JSXExpressionContainer(node) {
23
-
24
- const expression = node.expression;
25
-
26
- // This rule applies only to single ArrayExpressions or ObjectExpressions.
27
- if (expression.type !== 'ArrayExpression' && expression.type !== 'ObjectExpression') {
28
-
29
- return;
30
-
31
- }
32
-
33
- // If the expression itself isn't multiline, there are no newlines to collapse.
34
- if (
35
- expression
36
- .loc
37
- .start
38
- .line === expression
39
- .loc
40
- .end
41
- .line
42
- ) {
43
-
44
- return;
45
-
46
- }
47
-
48
- const openingBrace = sourceCode.getFirstToken(node); // `{`
49
- const closingBrace = sourceCode.getLastToken(node); // `}`
50
- const firstTokenInExpression = sourceCode.getFirstToken(expression);
51
- const lastTokenInExpression = sourceCode.getLastToken(expression);
52
-
53
- const hasNewlineBefore = openingBrace
54
- .loc
55
- .end
56
- .line < firstTokenInExpression
57
- .loc
58
- .start
59
- .line;
60
- const hasNewlineAfter = lastTokenInExpression
61
- .loc
62
- .end
63
- .line < closingBrace
64
- .loc
65
- .start
66
- .line;
67
-
68
- if (hasNewlineBefore || hasNewlineAfter) {
69
-
70
- context.report({
71
- node,
72
- messageId: 'error',
73
- fix(fixer) {
74
-
75
- const fixes = [];
76
-
77
- if (hasNewlineBefore) {
78
-
79
- // Range from the end of `{` to the start of the expression's first token.
80
- const range = [
81
- openingBrace.range[1],
82
- firstTokenInExpression.range[0]
83
- ];
84
- fixes.push(
85
- fixer.replaceTextRange(
86
- range,
87
- ''
88
- )
89
- );
90
-
91
- }
92
-
93
- if (hasNewlineAfter) {
94
-
95
- // Range from the end of the expression's last token to the start of `}`.
96
- const range = [
97
- lastTokenInExpression.range[1],
98
- closingBrace.range[0]
99
- ];
100
- fixes.push(
101
- fixer.replaceTextRange(
102
- range,
103
- ''
104
- )
105
- );
106
-
107
- }
108
-
109
- return fixes;
110
-
111
- }
112
- });
113
-
114
- }
115
-
116
- }
117
- };
118
-
119
- }
120
- };