@kikiutils/eslint-config 9.3.1 → 10.0.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.
Files changed (61) hide show
  1. package/README.md +8 -0
  2. package/dist/{internals → _internals}/plugins.d.ts.map +1 -1
  3. package/dist/{internals → _internals}/plugins.js +1 -1
  4. package/dist/_internals/plugins.js.map +1 -0
  5. package/dist/_internals/rules/compact-iteration-layout.d.ts.map +1 -0
  6. package/dist/{internals → _internals}/rules/compact-iteration-layout.js +1 -1
  7. package/dist/_internals/rules/compact-iteration-layout.js.map +1 -0
  8. package/dist/_internals/rules/consistent-condition-layout.d.ts.map +1 -0
  9. package/dist/{internals → _internals}/rules/consistent-condition-layout.js +1 -1
  10. package/dist/_internals/rules/consistent-condition-layout.js.map +1 -0
  11. package/dist/_internals/rules/consistent-parameter-layout.d.ts.map +1 -0
  12. package/dist/_internals/rules/consistent-parameter-layout.js +137 -0
  13. package/dist/_internals/rules/consistent-parameter-layout.js.map +1 -0
  14. package/dist/_internals/rules/statement-padding.d.ts.map +1 -0
  15. package/dist/{internals → _internals}/rules/statement-padding.js +22 -12
  16. package/dist/_internals/rules/statement-padding.js.map +1 -0
  17. package/dist/_internals/type-aware.d.ts +3 -0
  18. package/dist/_internals/type-aware.d.ts.map +1 -0
  19. package/dist/_internals/type-aware.js +25 -0
  20. package/dist/_internals/type-aware.js.map +1 -0
  21. package/dist/_internals/utils.d.ts.map +1 -0
  22. package/dist/{internals → _internals}/utils.js +1 -1
  23. package/dist/_internals/utils.js.map +1 -0
  24. package/dist/base.d.ts.map +1 -1
  25. package/dist/base.js +44 -17
  26. package/dist/base.js.map +1 -1
  27. package/dist/style.js +1 -5
  28. package/dist/style.js.map +1 -1
  29. package/dist/vue.d.ts.map +1 -1
  30. package/dist/vue.js +8 -4
  31. package/dist/vue.js.map +1 -1
  32. package/package.json +19 -70
  33. package/src/_internals/rules/consistent-parameter-layout.ts +244 -0
  34. package/src/{internals → _internals}/rules/statement-padding.ts +33 -22
  35. package/src/_internals/type-aware.ts +28 -0
  36. package/src/base.ts +67 -24
  37. package/src/style.ts +1 -2
  38. package/src/vue.ts +11 -4
  39. package/dist/internals/plugins.js.map +0 -1
  40. package/dist/internals/rules/compact-iteration-layout.d.ts.map +0 -1
  41. package/dist/internals/rules/compact-iteration-layout.js.map +0 -1
  42. package/dist/internals/rules/consistent-condition-layout.d.ts.map +0 -1
  43. package/dist/internals/rules/consistent-condition-layout.js.map +0 -1
  44. package/dist/internals/rules/consistent-parameter-layout.d.ts.map +0 -1
  45. package/dist/internals/rules/consistent-parameter-layout.js +0 -90
  46. package/dist/internals/rules/consistent-parameter-layout.js.map +0 -1
  47. package/dist/internals/rules/statement-padding.d.ts.map +0 -1
  48. package/dist/internals/rules/statement-padding.js.map +0 -1
  49. package/dist/internals/utils.d.ts.map +0 -1
  50. package/dist/internals/utils.js.map +0 -1
  51. package/src/internals/rules/consistent-parameter-layout.ts +0 -163
  52. /package/dist/{internals → _internals}/plugins.d.ts +0 -0
  53. /package/dist/{internals → _internals}/rules/compact-iteration-layout.d.ts +0 -0
  54. /package/dist/{internals → _internals}/rules/consistent-condition-layout.d.ts +0 -0
  55. /package/dist/{internals → _internals}/rules/consistent-parameter-layout.d.ts +0 -0
  56. /package/dist/{internals → _internals}/rules/statement-padding.d.ts +0 -0
  57. /package/dist/{internals → _internals}/utils.d.ts +0 -0
  58. /package/src/{internals → _internals}/plugins.ts +0 -0
  59. /package/src/{internals → _internals}/rules/compact-iteration-layout.ts +0 -0
  60. /package/src/{internals → _internals}/rules/consistent-condition-layout.ts +0 -0
  61. /package/src/{internals → _internals}/utils.ts +0 -0
package/src/base.ts CHANGED
@@ -2,10 +2,18 @@ import type {
2
2
  Rules,
3
3
  TypedFlatConfigItem,
4
4
  } from '@antfu/eslint-config';
5
+ import {
6
+ GLOB_JS,
7
+ GLOB_JSX,
8
+ GLOB_TESTS,
9
+ GLOB_TS,
10
+ GLOB_TSX,
11
+ } from '@antfu/eslint-config';
5
12
  // @ts-expect-error No declare file.
6
13
  import promise from 'eslint-plugin-promise';
7
14
 
8
- import { customPlugins } from './internals/plugins';
15
+ import { customPlugins } from './_internals/plugins';
16
+ import { createTypeAwareConfig } from './_internals/type-aware';
9
17
 
10
18
  // Constants/Variables
11
19
  const basePerfectionistSortOptions = {
@@ -13,6 +21,18 @@ const basePerfectionistSortOptions = {
13
21
  type: 'natural',
14
22
  } as const;
15
23
 
24
+ const vscodeOrderSensitiveArrayProperties = [
25
+ 'args',
26
+ 'autoAttachSmartPattern',
27
+ 'dependsOn',
28
+ 'options',
29
+ 'outFiles',
30
+ 'problemMatcher',
31
+ 'resolveSourceMapLocations',
32
+ 'runtimeArgs',
33
+ 'skipFiles',
34
+ ].join('|');
35
+
16
36
  // Functions
17
37
  export function createBaseConfigs(environment: 'bun' | 'node' = 'node'): TypedFlatConfigItem[] {
18
38
  return [
@@ -21,19 +41,19 @@ export function createBaseConfigs(environment: 'bun' | 'node' = 'node'): TypedFl
21
41
  rules: {
22
42
  'e18e/ban-dependencies': [
23
43
  'error',
24
- {
25
- allowed: [
26
- 'axios',
27
- 'rimraf',
28
- ],
29
- },
44
+ { allowed: ['axios'] },
30
45
  ],
31
- 'e18e/prefer-static-regex': ['off'],
46
+ 'e18e/prefer-static-regex': 'off',
32
47
  },
33
48
  },
34
- // JavaScript
49
+ // JavaScript and TypeScript
35
50
  {
36
- files: ['**/*.{cjs,js,mjs}'],
51
+ files: [
52
+ GLOB_JS,
53
+ GLOB_JSX,
54
+ GLOB_TS,
55
+ GLOB_TSX,
56
+ ],
37
57
  plugins: {
38
58
  kikiutils: customPlugins,
39
59
  promise,
@@ -48,16 +68,14 @@ export function createBaseConfigs(environment: 'bun' | 'node' = 'node'): TypedFl
48
68
  },
49
69
  // TypeScript
50
70
  {
51
- files: ['**/*.{ts,tsx}'],
52
- plugins: {
53
- kikiutils: customPlugins,
54
- promise,
55
- },
71
+ files: [
72
+ GLOB_TS,
73
+ GLOB_TSX,
74
+ ],
56
75
  rules: {
57
- ...createBaseRules(environment),
58
- 'node/prefer-global/process': [
76
+ 'ts/array-type': [
59
77
  'error',
60
- 'always',
78
+ { default: 'array-simple' },
61
79
  ],
62
80
  'ts/consistent-generic-constructors': [
63
81
  'error',
@@ -66,7 +84,28 @@ export function createBaseConfigs(environment: 'bun' | 'node' = 'node'): TypedFl
66
84
  'ts/no-redeclare': 'off',
67
85
  },
68
86
  },
69
- // VsCode jsonc
87
+ // Type-aware TypeScript
88
+ {
89
+ ...createTypeAwareConfig(),
90
+ files: [
91
+ GLOB_TS,
92
+ GLOB_TSX,
93
+ ],
94
+ ignores: ['**/*.config.?([cm])ts'],
95
+ },
96
+ // Tests
97
+ {
98
+ files: GLOB_TESTS,
99
+ rules: {
100
+ 'kikiutils/consistent-parameter-layout': [
101
+ 'error',
102
+ { compactTestCalls: true },
103
+ ],
104
+ 'test/valid-expect': 'error',
105
+ 'test/valid-expect-in-promise': 'error',
106
+ },
107
+ },
108
+ // VS Code JSONC
70
109
  {
71
110
  files: ['**/.vscode/*.json'],
72
111
  rules: {
@@ -77,7 +116,9 @@ export function createBaseConfigs(environment: 'bun' | 'node' = 'node'): TypedFl
77
116
  natural: true,
78
117
  type: 'asc',
79
118
  },
80
- pathPattern: '^.*$',
119
+ // Preserve ordered fields and any argument or option arrays nested inside them.
120
+ // eslint-disable-next-line style/max-len
121
+ pathPattern: String.raw`^(?!.*(?:^|\.|\[")(?:${vscodeOrderSensitiveArrayProperties})(?:$|\.|\[|"\])).*`,
81
122
  },
82
123
  ],
83
124
  'jsonc/sort-keys': [
@@ -90,6 +131,8 @@ export function createBaseConfigs(environment: 'bun' | 'node' = 'node'): TypedFl
90
131
  ],
91
132
  },
92
133
  },
134
+ // Ignore all extracted Markdown code blocks, but keep linting Markdown itself.
135
+ { ignores: ['**/*.md/**'] },
93
136
  ];
94
137
  }
95
138
 
@@ -122,10 +165,12 @@ export function createBaseRules(environment: 'bun' | 'node' = 'node'): Rules {
122
165
  'error',
123
166
  1,
124
167
  ],
168
+ 'no-constant-binary-expression': 'error',
125
169
  'no-promise-executor-return': [
126
170
  'error',
127
171
  { allowVoid: true },
128
172
  ],
173
+ 'no-useless-assignment': 'error',
129
174
  'perfectionist/sort-array-includes': [
130
175
  'error',
131
176
  basePerfectionistSortOptions,
@@ -139,10 +184,7 @@ export function createBaseRules(environment: 'bun' | 'node' = 'node'): Rules {
139
184
  ],
140
185
  'perfectionist/sort-heritage-clauses': [
141
186
  'error',
142
- {
143
- ignoreCase: false,
144
- type: 'natural',
145
- },
187
+ basePerfectionistSortOptions,
146
188
  ],
147
189
  'perfectionist/sort-imports': [
148
190
  'error',
@@ -259,6 +301,7 @@ export function createBaseRules(environment: 'bun' | 'node' = 'node'): Rules {
259
301
  'error',
260
302
  basePerfectionistSortOptions,
261
303
  ],
304
+ 'preserve-caught-error': 'error',
262
305
  'promise/no-multiple-resolved': 'error',
263
306
  'promise/no-return-in-finally': 'error',
264
307
  'promise/no-return-wrap': 'error',
package/src/style.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { TypedFlatConfigItem } from '@antfu/eslint-config';
2
2
  import format from 'eslint-plugin-format';
3
3
 
4
- function createPrettierCssConfig(parser: 'css' | 'sass' | 'scss'): TypedFlatConfigItem {
4
+ function createPrettierCssConfig(parser: 'css' | 'scss'): TypedFlatConfigItem {
5
5
  return {
6
6
  files: [`**/*.${parser}`],
7
7
  languageOptions: { parser: format.parserPlain },
@@ -23,7 +23,6 @@ function createPrettierCssConfig(parser: 'css' | 'sass' | 'scss'): TypedFlatConf
23
23
  export function createStyleFilesConfigs(): TypedFlatConfigItem[] {
24
24
  return [
25
25
  createPrettierCssConfig('css'),
26
- createPrettierCssConfig('sass'),
27
26
  createPrettierCssConfig('scss'),
28
27
  ];
29
28
  }
package/src/vue.ts CHANGED
@@ -1,23 +1,27 @@
1
1
  import type { TypedFlatConfigItem } from '@antfu/eslint-config';
2
2
  import kikiutilsEslintPluginVue from '@kikiutils/eslint-plugin-vue';
3
- import betteTailwindcss from 'eslint-plugin-better-tailwindcss';
3
+ import betterTailwindcss from 'eslint-plugin-better-tailwindcss';
4
4
  // @ts-expect-error No declare file.
5
5
  import promise from 'eslint-plugin-promise';
6
6
 
7
+ import { customPlugins } from './_internals/plugins';
8
+ import { createTypeAwareConfig } from './_internals/type-aware';
7
9
  import { createBaseRules } from './base';
8
- import { customPlugins } from './internals/plugins';
9
10
 
10
11
  export function createVueConfig(environment: 'bun' | 'node' = 'node'): TypedFlatConfigItem {
12
+ const typeAwareConfig = createTypeAwareConfig();
11
13
  return {
12
14
  files: ['**/*.vue'],
15
+ languageOptions: typeAwareConfig.languageOptions,
13
16
  plugins: {
14
17
  '@kikiutils/vue': kikiutilsEslintPluginVue,
15
- 'better-tailwindcss': betteTailwindcss,
18
+ 'better-tailwindcss': betterTailwindcss,
16
19
  'kikiutils': customPlugins,
17
20
  promise,
18
21
  },
19
22
  rules: {
20
23
  ...createBaseRules(environment),
24
+ ...typeAwareConfig.rules,
21
25
  '@kikiutils/vue/attributes-order': [
22
26
  'error',
23
27
  {
@@ -35,6 +39,10 @@ export function createVueConfig(environment: 'bun' | 'node' = 'node'): TypedFlat
35
39
  'better-tailwindcss/no-duplicate-classes': 'error',
36
40
  'better-tailwindcss/no-unnecessary-whitespace': 'error',
37
41
  'style/max-len': 'off',
42
+ 'ts/array-type': [
43
+ 'error',
44
+ { default: 'array-simple' },
45
+ ],
38
46
  'vue/attribute-hyphenation': 'error',
39
47
  'vue/attributes-order': 'off',
40
48
  'vue/block-order': [
@@ -80,7 +88,6 @@ export function createVueConfig(environment: 'bun' | 'node' = 'node'): TypedFlat
80
88
  'vue/no-template-target-blank': 'error',
81
89
  'vue/no-v-html': 'error',
82
90
  'vue/prefer-define-options': 'error',
83
- 'vue/prefer-separate-static-class': 'error',
84
91
  'vue/require-explicit-emits': 'error',
85
92
  'vue/require-typed-ref': 'error',
86
93
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugins.js","names":[],"sources":["../../src/internals/plugins.ts"],"sourcesContent":["import { compactIterationLayout } from './rules/compact-iteration-layout';\nimport { consistentConditionLayout } from './rules/consistent-condition-layout';\nimport { consistentParameterLayout } from './rules/consistent-parameter-layout';\nimport { statementPadding } from './rules/statement-padding';\n\nexport const customPlugins = {\n rules: {\n 'compact-iteration-layout': compactIterationLayout,\n 'consistent-condition-layout': consistentConditionLayout,\n 'consistent-parameter-layout': consistentParameterLayout,\n 'statement-padding': statementPadding,\n },\n};\n"],"mappings":";;;;;AAKA,MAAa,gBAAgB,EACzB,OAAO;CACH,4BAA4B;CAC5B,+BAA+B;CAC/B,+BAA+B;CAC/B,qBAAqB;AACzB,EACJ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"compact-iteration-layout.d.ts","names":[],"sources":["../../../src/internals/rules/compact-iteration-layout.ts"],"mappings":";qBAIa,wBAAwB,KAAK"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"compact-iteration-layout.js","names":[],"sources":["../../../src/internals/rules/compact-iteration-layout.ts"],"sourcesContent":["import type { Rule } from 'eslint';\n\ntype IterationNode = Extract<Rule.Node, { type: 'ForInStatement' | 'ForOfStatement' }>;\n\nexport const compactIterationLayout: Rule.RuleModule = {\n create(context) {\n const { sourceCode } = context;\n\n function checkParentheses(node: IterationNode): void {\n const openingParen = sourceCode.getFirstToken(node, (token) => token.value === '(')!;\n const closingParen = sourceCode.getTokenBefore(node.body)!;\n const next = sourceCode.getTokenAfter(openingParen)!;\n const previous = sourceCode.getTokenBefore(closingParen)!;\n const ranges: Array<[number, number]> = [];\n\n if (\n openingParen.loc.end.line !== next.loc.start.line\n && !sourceCode.commentsExistBetween(openingParen, next)\n ) {\n ranges.push([\n openingParen.range[1],\n next.range[0],\n ]);\n }\n\n if (\n previous.loc.end.line !== closingParen.loc.start.line\n && !sourceCode.commentsExistBetween(previous, closingParen)\n ) {\n ranges.push([\n previous.range[1],\n closingParen.range[0],\n ]);\n }\n\n if (ranges.length) {\n context.report({\n fix: (fixer) => ranges.map((range) => fixer.replaceTextRange(range, '')),\n messageId: 'compact',\n node,\n });\n }\n }\n\n return {\n ForInStatement: checkParentheses,\n ForOfStatement: checkParentheses,\n };\n },\n meta: {\n docs: { description: 'Keep for-in and for-of parentheses beside their header contents.' },\n fixable: 'whitespace',\n messages: { compact: 'Keep iteration parentheses on the same lines as their adjacent header contents.' },\n schema: [],\n type: 'layout',\n },\n};\n"],"mappings":";AAIA,MAAa,yBAA0C;CACnD,OAAO,SAAS;EACZ,MAAM,EAAE,eAAe;EAEvB,SAAS,iBAAiB,MAA2B;GACjD,MAAM,eAAe,WAAW,cAAc,OAAO,UAAU,MAAM,UAAU,GAAG;GAClF,MAAM,eAAe,WAAW,eAAe,KAAK,IAAI;GACxD,MAAM,OAAO,WAAW,cAAc,YAAY;GAClD,MAAM,WAAW,WAAW,eAAe,YAAY;GACvD,MAAM,SAAkC,CAAC;GAEzC,IACI,aAAa,IAAI,IAAI,SAAS,KAAK,IAAI,MAAM,QAC1C,CAAC,WAAW,qBAAqB,cAAc,IAAI,GAEtD,OAAO,KAAK,CACR,aAAa,MAAM,IACnB,KAAK,MAAM,EACf,CAAC;GAGL,IACI,SAAS,IAAI,IAAI,SAAS,aAAa,IAAI,MAAM,QAC9C,CAAC,WAAW,qBAAqB,UAAU,YAAY,GAE1D,OAAO,KAAK,CACR,SAAS,MAAM,IACf,aAAa,MAAM,EACvB,CAAC;GAGL,IAAI,OAAO,QACP,QAAQ,OAAO;IACX,MAAM,UAAU,OAAO,KAAK,UAAU,MAAM,iBAAiB,OAAO,EAAE,CAAC;IACvE,WAAW;IACX;GACJ,CAAC;EAET;EAEA,OAAO;GACH,gBAAgB;GAChB,gBAAgB;EACpB;CACJ;CACA,MAAM;EACF,MAAM,EAAE,aAAa,mEAAmE;EACxF,SAAS;EACT,UAAU,EAAE,SAAS,kFAAkF;EACvG,QAAQ,CAAC;EACT,MAAM;CACV;AACJ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"consistent-condition-layout.d.ts","names":[],"sources":["../../../src/internals/rules/consistent-condition-layout.ts"],"mappings":";qBAUa,2BAA2B,KAAK"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"consistent-condition-layout.js","names":[],"sources":["../../../src/internals/rules/consistent-condition-layout.ts"],"sourcesContent":["import type {\n AST,\n Rule,\n} from 'eslint';\n\nimport type { TokenStore } from '../utils';\nimport { defineScriptAndTemplateVisitors } from '../utils';\n\ntype LogicalNode = Extract<Rule.Node, { type: 'LogicalExpression' }>;\n\nexport const consistentConditionLayout: Rule.RuleModule = {\n create(context) {\n function createVisitor(tokenStore: TokenStore): Rule.RuleListener {\n const checkedOpeningOffsets = new Set<number>();\n\n function collectOperators(node: LogicalNode['left'], operators: AST.Token[]): void {\n if (node.type !== 'LogicalExpression' || (node.operator !== '&&' && node.operator !== '||')) return;\n const operator = tokenStore.getTokensBetween(node.left, node.right)\n .find((token) => token.type === 'Punctuator' && token.value === node.operator)!;\n\n function collectChild(child: LogicalNode['left']): void {\n const before = tokenStore.getTokenBefore(child);\n const after = tokenStore.getTokenAfter(child);\n if (\n before?.value === '('\n && after?.value === ')'\n && before.range[0] >= node.range![0]\n && after.range[1] <= node.range![1]\n ) return;\n\n collectOperators(child, operators);\n }\n\n collectChild(node.left);\n operators.push(operator);\n collectChild(node.right);\n }\n\n function checkParentheses(node: LogicalNode['left']): void {\n let openingParen = tokenStore.getTokenBefore(node);\n let closingParen = tokenStore.getTokenAfter(node);\n let operators: AST.Token[] | undefined;\n const newlineOffsets: number[] = [];\n while (openingParen?.value === '(' && closingParen?.value === ')') {\n if (\n !checkedOpeningOffsets.has(openingParen.range[0])\n && openingParen.loc.start.line !== closingParen.loc.end.line\n ) {\n checkedOpeningOffsets.add(openingParen.range[0]);\n if (!operators) {\n operators = [];\n collectOperators(node, operators);\n }\n\n const next = tokenStore.getTokenAfter(openingParen)!;\n if (openingParen.loc.end.line === next.loc.start.line) {\n newlineOffsets.push(openingParen.range[1]);\n }\n\n for (const operator of operators) {\n if (tokenStore.getTokenBefore(operator)!.loc.end.line === operator.loc.start.line) {\n newlineOffsets.push(operator.range[0]);\n }\n }\n\n if (tokenStore.getTokenBefore(closingParen)!.loc.end.line === closingParen.loc.start.line) {\n newlineOffsets.push(closingParen.range[0]);\n }\n }\n\n openingParen = tokenStore.getTokenBefore(openingParen);\n closingParen = tokenStore.getTokenAfter(closingParen);\n // An outer pair contains one grouped item, not the inner logical chain.\n operators = [];\n }\n\n if (newlineOffsets.length) {\n context.report({\n fix: (fixer) => newlineOffsets.map((offset) => fixer.insertTextBeforeRange(\n [\n offset,\n offset,\n ],\n '\\n',\n )),\n messageId: 'newline',\n node,\n });\n }\n }\n\n return {\n BinaryExpression: (node) => {\n if (\n [\n '!=',\n '!==',\n '<',\n '<=',\n '==',\n '===',\n '>',\n '>=',\n 'in',\n 'instanceof',\n ].includes(node.operator)\n ) checkParentheses(node);\n },\n DoWhileStatement: (node) => checkParentheses(node.test),\n IfStatement: (node) => checkParentheses(node.test),\n LogicalExpression: (node) => {\n if (node.operator === '&&' || node.operator === '||') checkParentheses(node);\n },\n UnaryExpression: (node) => {\n if (node.operator === '!') checkParentheses(node);\n },\n WhileStatement: (node) => checkParentheses(node.test),\n };\n }\n\n return defineScriptAndTemplateVisitors(context.sourceCode, createVisitor);\n },\n meta: {\n docs: { description: 'Expand multiline parenthesized conditions one logical item per line.' },\n fixable: 'whitespace',\n messages: { newline: 'Put each logical item and both parentheses on separate lines in a multiline condition.' },\n schema: [],\n type: 'layout',\n },\n};\n"],"mappings":";;AAUA,MAAa,4BAA6C;CACtD,OAAO,SAAS;EACZ,SAAS,cAAc,YAA2C;GAC9D,MAAM,wCAAwB,IAAI,IAAY;GAE9C,SAAS,iBAAiB,MAA2B,WAA8B;IAC/E,IAAI,KAAK,SAAS,uBAAwB,KAAK,aAAa,QAAQ,KAAK,aAAa,MAAO;IAC7F,MAAM,WAAW,WAAW,iBAAiB,KAAK,MAAM,KAAK,KAAK,CAAC,CAC9D,MAAM,UAAU,MAAM,SAAS,gBAAgB,MAAM,UAAU,KAAK,QAAQ;IAEjF,SAAS,aAAa,OAAkC;KACpD,MAAM,SAAS,WAAW,eAAe,KAAK;KAC9C,MAAM,QAAQ,WAAW,cAAc,KAAK;KAC5C,IACI,QAAQ,UAAU,OACf,OAAO,UAAU,OACjB,OAAO,MAAM,MAAM,KAAK,MAAO,MAC/B,MAAM,MAAM,MAAM,KAAK,MAAO,IACnC;KAEF,iBAAiB,OAAO,SAAS;IACrC;IAEA,aAAa,KAAK,IAAI;IACtB,UAAU,KAAK,QAAQ;IACvB,aAAa,KAAK,KAAK;GAC3B;GAEA,SAAS,iBAAiB,MAAiC;IACvD,IAAI,eAAe,WAAW,eAAe,IAAI;IACjD,IAAI,eAAe,WAAW,cAAc,IAAI;IAChD,IAAI;IACJ,MAAM,iBAA2B,CAAC;IAClC,OAAO,cAAc,UAAU,OAAO,cAAc,UAAU,KAAK;KAC/D,IACI,CAAC,sBAAsB,IAAI,aAAa,MAAM,EAAE,KAC7C,aAAa,IAAI,MAAM,SAAS,aAAa,IAAI,IAAI,MAC1D;MACE,sBAAsB,IAAI,aAAa,MAAM,EAAE;MAC/C,IAAI,CAAC,WAAW;OACZ,YAAY,CAAC;OACb,iBAAiB,MAAM,SAAS;MACpC;MAEA,MAAM,OAAO,WAAW,cAAc,YAAY;MAClD,IAAI,aAAa,IAAI,IAAI,SAAS,KAAK,IAAI,MAAM,MAC7C,eAAe,KAAK,aAAa,MAAM,EAAE;MAG7C,KAAK,MAAM,YAAY,WACnB,IAAI,WAAW,eAAe,QAAQ,CAAC,CAAE,IAAI,IAAI,SAAS,SAAS,IAAI,MAAM,MACzE,eAAe,KAAK,SAAS,MAAM,EAAE;MAI7C,IAAI,WAAW,eAAe,YAAY,CAAC,CAAE,IAAI,IAAI,SAAS,aAAa,IAAI,MAAM,MACjF,eAAe,KAAK,aAAa,MAAM,EAAE;KAEjD;KAEA,eAAe,WAAW,eAAe,YAAY;KACrD,eAAe,WAAW,cAAc,YAAY;KAEpD,YAAY,CAAC;IACjB;IAEA,IAAI,eAAe,QACf,QAAQ,OAAO;KACX,MAAM,UAAU,eAAe,KAAK,WAAW,MAAM,sBACjD,CACI,QACA,MACJ,GACA,IACJ,CAAC;KACD,WAAW;KACX;IACJ,CAAC;GAET;GAEA,OAAO;IACH,mBAAmB,SAAS;KACxB,IACI;MACI;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;KACJ,CAAC,CAAC,SAAS,KAAK,QAAQ,GAC1B,iBAAiB,IAAI;IAC3B;IACA,mBAAmB,SAAS,iBAAiB,KAAK,IAAI;IACtD,cAAc,SAAS,iBAAiB,KAAK,IAAI;IACjD,oBAAoB,SAAS;KACzB,IAAI,KAAK,aAAa,QAAQ,KAAK,aAAa,MAAM,iBAAiB,IAAI;IAC/E;IACA,kBAAkB,SAAS;KACvB,IAAI,KAAK,aAAa,KAAK,iBAAiB,IAAI;IACpD;IACA,iBAAiB,SAAS,iBAAiB,KAAK,IAAI;GACxD;EACJ;EAEA,OAAO,gCAAgC,QAAQ,YAAY,aAAa;CAC5E;CACA,MAAM;EACF,MAAM,EAAE,aAAa,uEAAuE;EAC5F,SAAS;EACT,UAAU,EAAE,SAAS,yFAAyF;EAC9G,QAAQ,CAAC;EACT,MAAM;CACV;AACJ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"consistent-parameter-layout.d.ts","names":[],"sources":["../../../src/internals/rules/consistent-parameter-layout.ts"],"mappings":";qBAaa,2BAA2B,KAAK"}
@@ -1,90 +0,0 @@
1
- import { defineScriptAndTemplateVisitors } from "../utils.js";
2
- // src/internals/rules/consistent-parameter-layout.ts
3
- const consistentParameterLayout = {
4
- create(context) {
5
- const isTestFile = /\.test\.[jt]sx?$/.test(context.filename);
6
- function checkParameterLayout(node, items, tokenStore, isTemplate) {
7
- const first = items[0];
8
- if (!first) return;
9
- const isSingleParameter = items.length === 1;
10
- const firstType = first.type;
11
- if (isSingleParameter && (firstType === "JSXElement" || firstType === "JSXFragment")) return;
12
- let openingParen;
13
- if (node.type === "CallExpression" || node.type === "NewExpression") {
14
- const call = node;
15
- openingParen = tokenStore.getTokenAfter(call.typeArguments ?? call.callee, (token) => token.type === "Punctuator" && token.value === "(");
16
- } else openingParen = tokenStore.getTokenBefore(first);
17
- if (!openingParen || openingParen.value !== "(") return;
18
- if (openingParen.range[0] < node.range[0]) return;
19
- const closingParen = node.type === "CallExpression" || node.type === "NewExpression" ? tokenStore.getLastToken(node) : tokenStore.getTokenAfter(items.at(-1), (token) => token.value === ")");
20
- const next = tokenStore.getTokenAfter(openingParen);
21
- if (isSingleParameter) {
22
- if (!closingParen || closingParen.value !== ")") return;
23
- let previous = tokenStore.getTokenBefore(closingParen);
24
- if (previous.value === ",") previous = tokenStore.getTokenBefore(previous);
25
- if (tokenStore.commentsExistBetween(openingParen, next) || tokenStore.commentsExistBetween(previous, closingParen)) return;
26
- const hasOpeningNewline = openingParen.loc.end.line !== next.loc.start.line;
27
- if (hasOpeningNewline === (tokenStore.getTokenBefore(closingParen).loc.end.line !== closingParen.loc.start.line)) return;
28
- context.report({
29
- fix: (fixer) => hasOpeningNewline ? fixer.insertTextBefore(closingParen, "\n") : fixer.insertTextAfter(openingParen, "\n"),
30
- messageId: "singleBoundary",
31
- node
32
- });
33
- return;
34
- }
35
- if (!closingParen || openingParen.loc.start.line === closingParen.loc.end.line) return;
36
- if (isTemplate) {
37
- const newlineOffsets = [];
38
- if (openingParen.loc.end.line === next.loc.start.line) newlineOffsets.push(openingParen.range[1]);
39
- for (let index = 1; index < items.length; index++) {
40
- const item = items[index];
41
- const comma = tokenStore.getTokensBetween(items[index - 1], item).find((token) => token.value === ",");
42
- if (comma && comma.loc.end.line === tokenStore.getTokenAfter(comma).loc.start.line) newlineOffsets.push(comma.range[1]);
43
- }
44
- if (closingParen.value === ")" && tokenStore.getTokenBefore(closingParen).loc.end.line === closingParen.loc.start.line) newlineOffsets.push(closingParen.range[0]);
45
- if (newlineOffsets.length) context.report({
46
- fix: (fixer) => newlineOffsets.map((offset) => fixer.insertTextAfterRange([offset, offset], "\n")),
47
- messageId: "newline",
48
- node: first
49
- });
50
- return;
51
- }
52
- if (openingParen.loc.end.line !== next.loc.start.line) return;
53
- context.report({
54
- fix: (fixer) => fixer.insertTextAfter(openingParen, "\n"),
55
- messageId: "newline",
56
- node: first
57
- });
58
- }
59
- function createVisitor(tokenStore, isTemplate) {
60
- return {
61
- ArrowFunctionExpression: (node) => checkParameterLayout(node, node.params, tokenStore, isTemplate),
62
- CallExpression: (node) => {
63
- const { callee } = node;
64
- if (isTestFile) {
65
- const target = callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier" && callee.property.name === "concurrent" ? callee.object : callee;
66
- if (target.type === "Identifier" && (target.name === "describe" || target.name === "it")) return;
67
- }
68
- checkParameterLayout(node, node.arguments, tokenStore, isTemplate);
69
- },
70
- FunctionDeclaration: (node) => checkParameterLayout(node, node.params, tokenStore, isTemplate),
71
- FunctionExpression: (node) => checkParameterLayout(node, node.params, tokenStore, isTemplate),
72
- NewExpression: (node) => checkParameterLayout(node, node.arguments, tokenStore, isTemplate)
73
- };
74
- }
75
- return defineScriptAndTemplateVisitors(context.sourceCode, createVisitor);
76
- },
77
- meta: {
78
- docs: { description: "Preserve consistent single-parameter boundaries and expand multiline parameter lists." },
79
- fixable: "whitespace",
80
- messages: {
81
- newline: "Start a multiline parameter list on a new line.",
82
- singleBoundary: "Use matching newline boundaries around a single parameter."
83
- },
84
- schema: [],
85
- type: "layout"
86
- }
87
- };
88
- export { consistentParameterLayout };
89
-
90
- //# sourceMappingURL=consistent-parameter-layout.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"consistent-parameter-layout.js","names":[],"sources":["../../../src/internals/rules/consistent-parameter-layout.ts"],"sourcesContent":["import type { Rule } from 'eslint';\n\nimport type { TokenStore } from '../utils';\nimport { defineScriptAndTemplateVisitors } from '../utils';\n\ntype CallNode = Extract<Rule.Node, { type: 'CallExpression' | 'NewExpression' }> & { typeArguments?: Rule.Node };\ntype FunctionNode = Extract<\n Rule.Node,\n { type: 'ArrowFunctionExpression' | 'FunctionDeclaration' | 'FunctionExpression' }\n>;\n\ntype ListItem = CallNode['arguments'][number] | FunctionNode['params'][number];\n\nexport const consistentParameterLayout: Rule.RuleModule = {\n create(context) {\n const isTestFile = /\\.test\\.[jt]sx?$/.test(context.filename);\n\n function checkParameterLayout(\n node: Rule.Node,\n items: readonly ListItem[],\n tokenStore: TokenStore,\n isTemplate: boolean,\n ): void {\n const first = items[0];\n if (!first) return;\n const isSingleParameter = items.length === 1;\n\n // JSX argument layout belongs to the JSX formatting rules.\n const firstType: string = first.type;\n if (isSingleParameter && (firstType === 'JSXElement' || firstType === 'JSXFragment')) return;\n let openingParen;\n if (node.type === 'CallExpression' || node.type === 'NewExpression') {\n const call = node as CallNode;\n openingParen = tokenStore.getTokenAfter(\n call.typeArguments ?? call.callee,\n (token) => token.type === 'Punctuator' && token.value === '(',\n );\n } else {\n openingParen = tokenStore.getTokenBefore(first);\n }\n\n if (!openingParen || openingParen.value !== '(') return;\n // An unparenthesized arrow must not borrow its parent's delimiters.\n if (openingParen.range[0] < node.range![0]) return;\n const closingParen = node.type === 'CallExpression' || node.type === 'NewExpression'\n ? tokenStore.getLastToken(node)\n : tokenStore.getTokenAfter(items.at(-1)!, (token) => token.value === ')');\n\n const next = tokenStore.getTokenAfter(openingParen)!;\n\n if (isSingleParameter) {\n if (!closingParen || closingParen.value !== ')') return;\n let previous = tokenStore.getTokenBefore(closingParen)!;\n if (previous.value === ',') previous = tokenStore.getTokenBefore(previous)!;\n if (\n tokenStore.commentsExistBetween(openingParen, next)\n || tokenStore.commentsExistBetween(previous, closingParen)\n ) return;\n\n const hasOpeningNewline = openingParen.loc.end.line !== next.loc.start.line;\n const hasClosingNewline = tokenStore.getTokenBefore(closingParen)!.loc.end.line\n !== closingParen.loc.start.line;\n\n if (hasOpeningNewline === hasClosingNewline) return;\n\n context.report({\n fix: (fixer) => hasOpeningNewline\n ? fixer.insertTextBefore(closingParen, '\\n')\n : fixer.insertTextAfter(openingParen, '\\n'),\n messageId: 'singleBoundary',\n node,\n });\n\n return;\n }\n\n if (!closingParen || openingParen.loc.start.line === closingParen.loc.end.line) return;\n\n if (isTemplate) {\n // Script list formatters do not traverse Vue template expressions.\n const newlineOffsets: number[] = [];\n if (openingParen.loc.end.line === next.loc.start.line) newlineOffsets.push(openingParen.range[1]);\n for (let index = 1; index < items.length; index++) {\n const item = items[index]!;\n const comma = tokenStore.getTokensBetween(items[index - 1]!, item)\n .find((token) => token.value === ',');\n\n if (comma && comma.loc.end.line === tokenStore.getTokenAfter(comma)!.loc.start.line) {\n newlineOffsets.push(comma.range[1]);\n }\n }\n\n if (\n closingParen.value === ')'\n && tokenStore.getTokenBefore(closingParen)!.loc.end.line === closingParen.loc.start.line\n ) newlineOffsets.push(closingParen.range[0]);\n\n if (newlineOffsets.length) {\n context.report({\n fix: (fixer) => newlineOffsets.map((offset) => fixer.insertTextAfterRange(\n [\n offset,\n offset,\n ],\n '\\n',\n )),\n messageId: 'newline',\n node: first,\n });\n }\n\n return;\n }\n\n if (openingParen.loc.end.line !== next.loc.start.line) return;\n\n context.report({\n fix: (fixer) => fixer.insertTextAfter(openingParen, '\\n'),\n messageId: 'newline',\n node: first,\n });\n }\n\n function createVisitor(tokenStore: TokenStore, isTemplate: boolean): Rule.RuleListener {\n return {\n ArrowFunctionExpression: (node) => checkParameterLayout(node, node.params, tokenStore, isTemplate),\n CallExpression: (node) => {\n const { callee } = node;\n if (isTestFile) {\n const target = callee.type === 'MemberExpression'\n && !callee.computed\n && callee.property.type === 'Identifier'\n && callee.property.name === 'concurrent'\n ? callee.object\n : callee;\n\n if (\n target.type === 'Identifier'\n && (target.name === 'describe' || target.name === 'it')\n ) return;\n }\n\n checkParameterLayout(node, node.arguments, tokenStore, isTemplate);\n },\n FunctionDeclaration: (node) => checkParameterLayout(node, node.params, tokenStore, isTemplate),\n FunctionExpression: (node) => checkParameterLayout(node, node.params, tokenStore, isTemplate),\n NewExpression: (node) => checkParameterLayout(node, node.arguments, tokenStore, isTemplate),\n };\n }\n\n return defineScriptAndTemplateVisitors(context.sourceCode, createVisitor);\n },\n meta: {\n docs: { description: 'Preserve consistent single-parameter boundaries and expand multiline parameter lists.' },\n fixable: 'whitespace',\n messages: {\n newline: 'Start a multiline parameter list on a new line.',\n singleBoundary: 'Use matching newline boundaries around a single parameter.',\n },\n schema: [],\n type: 'layout',\n },\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"statement-padding.d.ts","names":[],"sources":["../../../src/internals/rules/statement-padding.ts"],"mappings":";qBAIa,kBAAkB,KAAK"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"statement-padding.js","names":[],"sources":["../../../src/internals/rules/statement-padding.ts"],"sourcesContent":["import type { Rule } from 'eslint';\n\ntype StatementNode = Extract<Rule.Node, { type: 'Program' }>['body'][number];\n\nexport const statementPadding: Rule.RuleModule = {\n create(context) {\n const { sourceCode } = context;\n\n function checkStatements(statements: readonly StatementNode[]): void {\n for (let index = 1; index < statements.length; index++) {\n const previous = statements[index - 1]!;\n const next = statements[index]!;\n const lastToken = sourceCode.getLastToken(previous)!;\n const firstToken = sourceCode.getFirstToken(next)!;\n const closingLine = sourceCode.lines[lastToken.loc.end.line - 1]!;\n const openingLine = sourceCode.lines[firstToken.loc.start.line - 1]!;\n const hasDifferentIndent = /^\\s*/.exec(closingLine)![0] !== /^\\s*/.exec(openingLine)![0];\n const hasStandaloneClosing = /^[)\\]}]+;?$/.test(closingLine.slice(0, lastToken.loc.end.column).trim());\n const blockNode = previous.type === 'ExportDefaultDeclaration'\n || previous.type === 'ExportNamedDeclaration'\n ? previous.declaration\n : previous;\n\n const hasBlockClosing = lastToken.value === '}' && blockNode && [\n 'BlockStatement',\n 'ClassDeclaration',\n 'ForInStatement',\n 'ForOfStatement',\n 'ForStatement',\n 'FunctionDeclaration',\n 'IfStatement',\n 'LabeledStatement',\n 'SwitchStatement',\n 'TryStatement',\n 'WhileStatement',\n 'WithStatement',\n ].includes(blockNode.type);\n\n if (!hasDifferentIndent && !hasStandaloneClosing && !hasBlockClosing) continue;\n const comments = sourceCode.getCommentsBefore(next);\n if (\n sourceCode\n .lines\n .slice(lastToken.loc.end.line, firstToken.loc.start.line - 1)\n .some((line, index) => !line.trim() && !comments.some((comment) =>\n comment.loc!.start.line <= lastToken.loc.end.line + index + 1\n && comment.loc!.end.line >= lastToken.loc.end.line + index + 1))\n ) continue;\n\n // Keep leading comments attached to the next statement and trailing comments in place.\n const boundary = comments\n .find((comment) => comment.loc!.start.line > lastToken.loc.end.line) ?? firstToken;\n\n const offset = boundary.loc!.start.line > lastToken.loc.end.line\n ? boundary.range![0] - boundary.loc!.start.column\n : boundary.range![0];\n\n context.report({\n fix: (fixer) => fixer.insertTextBeforeRange(\n [\n offset,\n offset,\n ],\n boundary.loc!.start.line === lastToken.loc.end.line ? '\\n\\n' : '\\n',\n ),\n messageId: 'blankLine',\n node: next,\n });\n }\n }\n\n return {\n BlockStatement: (node) => checkStatements(node.body),\n Program: (node) => checkStatements(node.body),\n StaticBlock: (node) => checkStatements(node.body),\n SwitchCase: (node) => checkStatements(node.consequent),\n };\n },\n meta: {\n docs: { description: 'Separate statements after closing lines, blocks, or indentation changes.' },\n fixable: 'whitespace',\n messages: { blankLine: 'Add a blank line between these complete statements.' },\n schema: [],\n type: 'layout',\n },\n};\n"],"mappings":";AAIA,MAAa,mBAAoC;CAC7C,OAAO,SAAS;EACZ,MAAM,EAAE,eAAe;EAEvB,SAAS,gBAAgB,YAA4C;GACjE,KAAK,IAAI,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS;IACpD,MAAM,WAAW,WAAW,QAAQ;IACpC,MAAM,OAAO,WAAW;IACxB,MAAM,YAAY,WAAW,aAAa,QAAQ;IAClD,MAAM,aAAa,WAAW,cAAc,IAAI;IAChD,MAAM,cAAc,WAAW,MAAM,UAAU,IAAI,IAAI,OAAO;IAC9D,MAAM,cAAc,WAAW,MAAM,WAAW,IAAI,MAAM,OAAO;IACjE,MAAM,qBAAqB,OAAO,KAAK,WAAW,CAAC,CAAE,OAAO,OAAO,KAAK,WAAW,CAAC,CAAE;IACtF,MAAM,uBAAuB,cAAc,KAAK,YAAY,MAAM,GAAG,UAAU,IAAI,IAAI,MAAM,CAAC,CAAC,KAAK,CAAC;IACrG,MAAM,YAAY,SAAS,SAAS,8BAC/B,SAAS,SAAS,2BACjB,SAAS,cACT;IAEN,MAAM,kBAAkB,UAAU,UAAU,OAAO,aAAa;KAC5D;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACJ,CAAC,CAAC,SAAS,UAAU,IAAI;IAEzB,IAAI,CAAC,sBAAsB,CAAC,wBAAwB,CAAC,iBAAiB;IACtE,MAAM,WAAW,WAAW,kBAAkB,IAAI;IAClD,IACI,WACK,MACA,MAAM,UAAU,IAAI,IAAI,MAAM,WAAW,IAAI,MAAM,OAAO,CAAC,CAAC,CAC5D,MAAM,MAAM,UAAU,CAAC,KAAK,KAAK,KAAK,CAAC,SAAS,MAAM,YACnD,QAAQ,IAAK,MAAM,QAAQ,UAAU,IAAI,IAAI,OAAO,QAAQ,KACzD,QAAQ,IAAK,IAAI,QAAQ,UAAU,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC,GACzE;IAGF,MAAM,WAAW,SACZ,MAAM,YAAY,QAAQ,IAAK,MAAM,OAAO,UAAU,IAAI,IAAI,IAAI,KAAK;IAE5E,MAAM,SAAS,SAAS,IAAK,MAAM,OAAO,UAAU,IAAI,IAAI,OACtD,SAAS,MAAO,KAAK,SAAS,IAAK,MAAM,SACzC,SAAS,MAAO;IAEtB,QAAQ,OAAO;KACX,MAAM,UAAU,MAAM,sBAClB,CACI,QACA,MACJ,GACA,SAAS,IAAK,MAAM,SAAS,UAAU,IAAI,IAAI,OAAO,SAAS,IACnE;KACA,WAAW;KACX,MAAM;IACV,CAAC;GACL;EACJ;EAEA,OAAO;GACH,iBAAiB,SAAS,gBAAgB,KAAK,IAAI;GACnD,UAAU,SAAS,gBAAgB,KAAK,IAAI;GAC5C,cAAc,SAAS,gBAAgB,KAAK,IAAI;GAChD,aAAa,SAAS,gBAAgB,KAAK,UAAU;EACzD;CACJ;CACA,MAAM;EACF,MAAM,EAAE,aAAa,2EAA2E;EAChG,SAAS;EACT,UAAU,EAAE,WAAW,sDAAsD;EAC7E,QAAQ,CAAC;EACT,MAAM;CACV;AACJ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","names":[],"sources":["../../src/internals/utils.ts"],"mappings":";YAKY,aAAa,KACrB;wBAaY,gCACZ,YAAY,YACZ,gBAAgB,YAAY,YAAY,wBAAwB,KAAK,eACtE,KAAK"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../src/internals/utils.ts"],"sourcesContent":["import type {\n Rule,\n SourceCode,\n} from 'eslint';\n\nexport type TokenStore = Pick<\n SourceCode,\n 'commentsExistBetween' | 'getLastToken' | 'getTokenAfter' | 'getTokenBefore' | 'getTokensBetween'\n>;\n\ninterface TemplateParserServices {\n defineTemplateBodyVisitor?: (\n templateVisitor: Rule.RuleListener,\n scriptVisitor: Rule.RuleListener,\n ) => Rule.RuleListener;\n\n getTemplateBodyTokenStore?: () => TokenStore;\n}\n\nexport function defineScriptAndTemplateVisitors(\n sourceCode: SourceCode,\n createVisitor: (tokenStore: TokenStore, isTemplate: boolean) => Rule.RuleListener,\n): Rule.RuleListener {\n const scriptVisitor = createVisitor(sourceCode, false);\n const parserServices = sourceCode.parserServices as TemplateParserServices;\n if (!parserServices.defineTemplateBodyVisitor || !parserServices.getTemplateBodyTokenStore) return scriptVisitor;\n\n return parserServices.defineTemplateBodyVisitor(\n createVisitor(parserServices.getTemplateBodyTokenStore(), true),\n scriptVisitor,\n );\n}\n"],"mappings":";AAmBA,SAAgB,gCACZ,YACA,eACiB;CACjB,MAAM,gBAAgB,cAAc,YAAY,KAAK;CACrD,MAAM,iBAAiB,WAAW;CAClC,IAAI,CAAC,eAAe,6BAA6B,CAAC,eAAe,2BAA2B,OAAO;CAEnG,OAAO,eAAe,0BAClB,cAAc,eAAe,0BAA0B,GAAG,IAAI,GAC9D,aACJ;AACJ"}
@@ -1,163 +0,0 @@
1
- import type { Rule } from 'eslint';
2
-
3
- import type { TokenStore } from '../utils';
4
- import { defineScriptAndTemplateVisitors } from '../utils';
5
-
6
- type CallNode = Extract<Rule.Node, { type: 'CallExpression' | 'NewExpression' }> & { typeArguments?: Rule.Node };
7
- type FunctionNode = Extract<
8
- Rule.Node,
9
- { type: 'ArrowFunctionExpression' | 'FunctionDeclaration' | 'FunctionExpression' }
10
- >;
11
-
12
- type ListItem = CallNode['arguments'][number] | FunctionNode['params'][number];
13
-
14
- export const consistentParameterLayout: Rule.RuleModule = {
15
- create(context) {
16
- const isTestFile = /\.test\.[jt]sx?$/.test(context.filename);
17
-
18
- function checkParameterLayout(
19
- node: Rule.Node,
20
- items: readonly ListItem[],
21
- tokenStore: TokenStore,
22
- isTemplate: boolean,
23
- ): void {
24
- const first = items[0];
25
- if (!first) return;
26
- const isSingleParameter = items.length === 1;
27
-
28
- // JSX argument layout belongs to the JSX formatting rules.
29
- const firstType: string = first.type;
30
- if (isSingleParameter && (firstType === 'JSXElement' || firstType === 'JSXFragment')) return;
31
- let openingParen;
32
- if (node.type === 'CallExpression' || node.type === 'NewExpression') {
33
- const call = node as CallNode;
34
- openingParen = tokenStore.getTokenAfter(
35
- call.typeArguments ?? call.callee,
36
- (token) => token.type === 'Punctuator' && token.value === '(',
37
- );
38
- } else {
39
- openingParen = tokenStore.getTokenBefore(first);
40
- }
41
-
42
- if (!openingParen || openingParen.value !== '(') return;
43
- // An unparenthesized arrow must not borrow its parent's delimiters.
44
- if (openingParen.range[0] < node.range![0]) return;
45
- const closingParen = node.type === 'CallExpression' || node.type === 'NewExpression'
46
- ? tokenStore.getLastToken(node)
47
- : tokenStore.getTokenAfter(items.at(-1)!, (token) => token.value === ')');
48
-
49
- const next = tokenStore.getTokenAfter(openingParen)!;
50
-
51
- if (isSingleParameter) {
52
- if (!closingParen || closingParen.value !== ')') return;
53
- let previous = tokenStore.getTokenBefore(closingParen)!;
54
- if (previous.value === ',') previous = tokenStore.getTokenBefore(previous)!;
55
- if (
56
- tokenStore.commentsExistBetween(openingParen, next)
57
- || tokenStore.commentsExistBetween(previous, closingParen)
58
- ) return;
59
-
60
- const hasOpeningNewline = openingParen.loc.end.line !== next.loc.start.line;
61
- const hasClosingNewline = tokenStore.getTokenBefore(closingParen)!.loc.end.line
62
- !== closingParen.loc.start.line;
63
-
64
- if (hasOpeningNewline === hasClosingNewline) return;
65
-
66
- context.report({
67
- fix: (fixer) => hasOpeningNewline
68
- ? fixer.insertTextBefore(closingParen, '\n')
69
- : fixer.insertTextAfter(openingParen, '\n'),
70
- messageId: 'singleBoundary',
71
- node,
72
- });
73
-
74
- return;
75
- }
76
-
77
- if (!closingParen || openingParen.loc.start.line === closingParen.loc.end.line) return;
78
-
79
- if (isTemplate) {
80
- // Script list formatters do not traverse Vue template expressions.
81
- const newlineOffsets: number[] = [];
82
- if (openingParen.loc.end.line === next.loc.start.line) newlineOffsets.push(openingParen.range[1]);
83
- for (let index = 1; index < items.length; index++) {
84
- const item = items[index]!;
85
- const comma = tokenStore.getTokensBetween(items[index - 1]!, item)
86
- .find((token) => token.value === ',');
87
-
88
- if (comma && comma.loc.end.line === tokenStore.getTokenAfter(comma)!.loc.start.line) {
89
- newlineOffsets.push(comma.range[1]);
90
- }
91
- }
92
-
93
- if (
94
- closingParen.value === ')'
95
- && tokenStore.getTokenBefore(closingParen)!.loc.end.line === closingParen.loc.start.line
96
- ) newlineOffsets.push(closingParen.range[0]);
97
-
98
- if (newlineOffsets.length) {
99
- context.report({
100
- fix: (fixer) => newlineOffsets.map((offset) => fixer.insertTextAfterRange(
101
- [
102
- offset,
103
- offset,
104
- ],
105
- '\n',
106
- )),
107
- messageId: 'newline',
108
- node: first,
109
- });
110
- }
111
-
112
- return;
113
- }
114
-
115
- if (openingParen.loc.end.line !== next.loc.start.line) return;
116
-
117
- context.report({
118
- fix: (fixer) => fixer.insertTextAfter(openingParen, '\n'),
119
- messageId: 'newline',
120
- node: first,
121
- });
122
- }
123
-
124
- function createVisitor(tokenStore: TokenStore, isTemplate: boolean): Rule.RuleListener {
125
- return {
126
- ArrowFunctionExpression: (node) => checkParameterLayout(node, node.params, tokenStore, isTemplate),
127
- CallExpression: (node) => {
128
- const { callee } = node;
129
- if (isTestFile) {
130
- const target = callee.type === 'MemberExpression'
131
- && !callee.computed
132
- && callee.property.type === 'Identifier'
133
- && callee.property.name === 'concurrent'
134
- ? callee.object
135
- : callee;
136
-
137
- if (
138
- target.type === 'Identifier'
139
- && (target.name === 'describe' || target.name === 'it')
140
- ) return;
141
- }
142
-
143
- checkParameterLayout(node, node.arguments, tokenStore, isTemplate);
144
- },
145
- FunctionDeclaration: (node) => checkParameterLayout(node, node.params, tokenStore, isTemplate),
146
- FunctionExpression: (node) => checkParameterLayout(node, node.params, tokenStore, isTemplate),
147
- NewExpression: (node) => checkParameterLayout(node, node.arguments, tokenStore, isTemplate),
148
- };
149
- }
150
-
151
- return defineScriptAndTemplateVisitors(context.sourceCode, createVisitor);
152
- },
153
- meta: {
154
- docs: { description: 'Preserve consistent single-parameter boundaries and expand multiline parameter lists.' },
155
- fixable: 'whitespace',
156
- messages: {
157
- newline: 'Start a multiline parameter list on a new line.',
158
- singleBoundary: 'Use matching newline boundaries around a single parameter.',
159
- },
160
- schema: [],
161
- type: 'layout',
162
- },
163
- };
File without changes
File without changes
File without changes
File without changes