@kikiutils/eslint-config 9.3.2 → 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.
- package/README.md +8 -0
- package/dist/_internals/rules/consistent-parameter-layout.d.ts.map +1 -1
- package/dist/_internals/rules/consistent-parameter-layout.js +102 -55
- package/dist/_internals/rules/consistent-parameter-layout.js.map +1 -1
- package/dist/_internals/rules/statement-padding.js +21 -11
- package/dist/_internals/rules/statement-padding.js.map +1 -1
- package/dist/_internals/type-aware.d.ts +3 -0
- package/dist/_internals/type-aware.d.ts.map +1 -0
- package/dist/_internals/type-aware.js +25 -0
- package/dist/_internals/type-aware.js.map +1 -0
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +43 -16
- package/dist/base.js.map +1 -1
- package/dist/style.js +1 -5
- package/dist/style.js.map +1 -1
- package/dist/vue.d.ts.map +1 -1
- package/dist/vue.js +7 -3
- package/dist/vue.js.map +1 -1
- package/package.json +11 -38
- package/src/_internals/rules/consistent-parameter-layout.ts +179 -98
- package/src/_internals/rules/statement-padding.ts +33 -22
- package/src/_internals/type-aware.ts +28 -0
- package/src/base.ts +66 -23
- package/src/style.ts +1 -2
- package/src/vue.ts +10 -3
package/dist/style.js
CHANGED
|
@@ -14,11 +14,7 @@ function createPrettierCssConfig(parser) {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
function createStyleFilesConfigs() {
|
|
17
|
-
return [
|
|
18
|
-
createPrettierCssConfig("css"),
|
|
19
|
-
createPrettierCssConfig("sass"),
|
|
20
|
-
createPrettierCssConfig("scss")
|
|
21
|
-
];
|
|
17
|
+
return [createPrettierCssConfig("css"), createPrettierCssConfig("scss")];
|
|
22
18
|
}
|
|
23
19
|
export { createStyleFilesConfigs };
|
|
24
20
|
|
package/dist/style.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","names":[],"sources":["../src/style.ts"],"sourcesContent":["import type { TypedFlatConfigItem } from '@antfu/eslint-config';\nimport format from 'eslint-plugin-format';\n\nfunction createPrettierCssConfig(parser: 'css' | '
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../src/style.ts"],"sourcesContent":["import type { TypedFlatConfigItem } from '@antfu/eslint-config';\nimport format from 'eslint-plugin-format';\n\nfunction createPrettierCssConfig(parser: 'css' | 'scss'): TypedFlatConfigItem {\n return {\n files: [`**/*.${parser}`],\n languageOptions: { parser: format.parserPlain },\n plugins: { format },\n rules: {\n 'format/prettier': [\n 'error',\n {\n parser,\n printWidth: 120,\n singleQuote: true,\n tabWidth: 4,\n },\n ],\n },\n };\n}\n\nexport function createStyleFilesConfigs(): TypedFlatConfigItem[] {\n return [\n createPrettierCssConfig('css'),\n createPrettierCssConfig('scss'),\n ];\n}\n"],"mappings":";;AAGA,SAAS,wBAAwB,QAA6C;CAC1E,OAAO;EACH,OAAO,CAAC,QAAQ,QAAQ;EACxB,iBAAiB,EAAE,QAAQ,OAAO,YAAY;EAC9C,SAAS,EAAE,OAAO;EAClB,OAAO,EACH,mBAAmB,CACf,SACA;GACI;GACA,YAAY;GACZ,aAAa;GACb,UAAU;EACd,CACJ,EACJ;CACJ;AACJ;AAEA,SAAgB,0BAAiD;CAC7D,OAAO,CACH,wBAAwB,KAAK,GAC7B,wBAAwB,MAAM,CAClC;AACJ"}
|
package/dist/vue.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.d.ts","names":[],"sources":["../src/vue.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"vue.d.ts","names":[],"sources":["../src/vue.ts"],"mappings":";wBAUgB,gBAAgB,+BAAuC"}
|
package/dist/vue.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { customPlugins } from "./_internals/plugins.js";
|
|
2
|
+
import { createTypeAwareConfig } from "./_internals/type-aware.js";
|
|
2
3
|
import { createBaseRules } from "./base.js";
|
|
3
4
|
import promise from "eslint-plugin-promise";
|
|
4
5
|
import kikiutilsEslintPluginVue from "@kikiutils/eslint-plugin-vue";
|
|
5
|
-
import
|
|
6
|
+
import betterTailwindcss from "eslint-plugin-better-tailwindcss";
|
|
6
7
|
// src/vue.ts
|
|
7
8
|
function createVueConfig(environment = "node") {
|
|
9
|
+
const typeAwareConfig = createTypeAwareConfig();
|
|
8
10
|
return {
|
|
9
11
|
files: ["**/*.vue"],
|
|
12
|
+
languageOptions: typeAwareConfig.languageOptions,
|
|
10
13
|
plugins: {
|
|
11
14
|
"@kikiutils/vue": kikiutilsEslintPluginVue,
|
|
12
|
-
"better-tailwindcss":
|
|
15
|
+
"better-tailwindcss": betterTailwindcss,
|
|
13
16
|
"kikiutils": customPlugins,
|
|
14
17
|
promise
|
|
15
18
|
},
|
|
16
19
|
rules: {
|
|
17
20
|
...createBaseRules(environment),
|
|
21
|
+
...typeAwareConfig.rules,
|
|
18
22
|
"@kikiutils/vue/attributes-order": ["error", {
|
|
19
23
|
alphabetical: true,
|
|
20
24
|
alphabeticalEnhanced: true
|
|
@@ -29,6 +33,7 @@ function createVueConfig(environment = "node") {
|
|
|
29
33
|
"better-tailwindcss/no-duplicate-classes": "error",
|
|
30
34
|
"better-tailwindcss/no-unnecessary-whitespace": "error",
|
|
31
35
|
"style/max-len": "off",
|
|
36
|
+
"ts/array-type": ["error", { default: "array-simple" }],
|
|
32
37
|
"vue/attribute-hyphenation": "error",
|
|
33
38
|
"vue/attributes-order": "off",
|
|
34
39
|
"vue/block-order": ["error", { order: [
|
|
@@ -60,7 +65,6 @@ function createVueConfig(environment = "node") {
|
|
|
60
65
|
"vue/no-template-target-blank": "error",
|
|
61
66
|
"vue/no-v-html": "error",
|
|
62
67
|
"vue/prefer-define-options": "error",
|
|
63
|
-
"vue/prefer-separate-static-class": "error",
|
|
64
68
|
"vue/require-explicit-emits": "error",
|
|
65
69
|
"vue/require-typed-ref": "error"
|
|
66
70
|
}
|
package/dist/vue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.js","names":[],"sources":["../src/vue.ts"],"sourcesContent":["import type { TypedFlatConfigItem } from '@antfu/eslint-config';\nimport kikiutilsEslintPluginVue from '@kikiutils/eslint-plugin-vue';\nimport
|
|
1
|
+
{"version":3,"file":"vue.js","names":[],"sources":["../src/vue.ts"],"sourcesContent":["import type { TypedFlatConfigItem } from '@antfu/eslint-config';\nimport kikiutilsEslintPluginVue from '@kikiutils/eslint-plugin-vue';\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss';\n// @ts-expect-error No declare file.\nimport promise from 'eslint-plugin-promise';\n\nimport { customPlugins } from './_internals/plugins';\nimport { createTypeAwareConfig } from './_internals/type-aware';\nimport { createBaseRules } from './base';\n\nexport function createVueConfig(environment: 'bun' | 'node' = 'node'): TypedFlatConfigItem {\n const typeAwareConfig = createTypeAwareConfig();\n return {\n files: ['**/*.vue'],\n languageOptions: typeAwareConfig.languageOptions,\n plugins: {\n '@kikiutils/vue': kikiutilsEslintPluginVue,\n 'better-tailwindcss': betterTailwindcss,\n 'kikiutils': customPlugins,\n promise,\n },\n rules: {\n ...createBaseRules(environment),\n ...typeAwareConfig.rules,\n '@kikiutils/vue/attributes-order': [\n 'error',\n {\n alphabetical: true,\n alphabeticalEnhanced: true,\n },\n ],\n '@kikiutils/vue/class-hex-color-case': 'error',\n 'better-tailwindcss/enforce-consistent-class-order': 'error',\n 'better-tailwindcss/enforce-consistent-important-position': 'error',\n 'better-tailwindcss/enforce-consistent-variable-syntax': 'error',\n 'better-tailwindcss/enforce-consistent-variant-order': 'error',\n 'better-tailwindcss/enforce-shorthand-classes': 'error',\n 'better-tailwindcss/no-deprecated-classes': 'warn',\n 'better-tailwindcss/no-duplicate-classes': 'error',\n 'better-tailwindcss/no-unnecessary-whitespace': 'error',\n 'style/max-len': 'off',\n 'ts/array-type': [\n 'error',\n { default: 'array-simple' },\n ],\n 'vue/attribute-hyphenation': 'error',\n 'vue/attributes-order': 'off',\n 'vue/block-order': [\n 'error',\n {\n order: [\n 'template',\n 'script',\n 'style',\n ],\n },\n ],\n 'vue/component-api-style': 'error',\n 'vue/define-emits-declaration': 'error',\n 'vue/define-props-declaration': 'error',\n 'vue/enforce-style-attribute': 'error',\n 'vue/html-closing-bracket-newline': 'error',\n 'vue/html-closing-bracket-spacing': 'error',\n 'vue/html-indent': [\n 'error',\n 4,\n ],\n 'vue/max-attributes-per-line': [\n 'error',\n {\n multiline: 1,\n singleline: 1,\n },\n ],\n 'vue/max-len': [\n 'warn',\n {\n code: 120,\n comments: 120,\n template: 120,\n },\n ],\n 'vue/no-dupe-keys': 'error',\n 'vue/no-lone-template': 'error',\n 'vue/no-multi-spaces': 'error',\n 'vue/no-multiple-objects-in-class': 'error',\n 'vue/no-root-v-if': 'error',\n 'vue/no-template-target-blank': 'error',\n 'vue/no-v-html': 'error',\n 'vue/prefer-define-options': 'error',\n 'vue/require-explicit-emits': 'error',\n 'vue/require-typed-ref': 'error',\n },\n };\n}\n"],"mappings":";;;;;;;AAUA,SAAgB,gBAAgB,cAA8B,QAA6B;CACvF,MAAM,kBAAkB,sBAAsB;CAC9C,OAAO;EACH,OAAO,CAAC,UAAU;EAClB,iBAAiB,gBAAgB;EACjC,SAAS;GACL,kBAAkB;GAClB,sBAAsB;GACtB,aAAa;GACb;EACJ;EACA,OAAO;GACH,GAAG,gBAAgB,WAAW;GAC9B,GAAG,gBAAgB;GACnB,mCAAmC,CAC/B,SACA;IACI,cAAc;IACd,sBAAsB;GAC1B,CACJ;GACA,uCAAuC;GACvC,qDAAqD;GACrD,4DAA4D;GAC5D,yDAAyD;GACzD,uDAAuD;GACvD,gDAAgD;GAChD,4CAA4C;GAC5C,2CAA2C;GAC3C,gDAAgD;GAChD,iBAAiB;GACjB,iBAAiB,CACb,SACA,EAAE,SAAS,eAAe,CAC9B;GACA,6BAA6B;GAC7B,wBAAwB;GACxB,mBAAmB,CACf,SACA,EACI,OAAO;IACH;IACA;IACA;GACJ,EACJ,CACJ;GACA,2BAA2B;GAC3B,gCAAgC;GAChC,gCAAgC;GAChC,+BAA+B;GAC/B,oCAAoC;GACpC,oCAAoC;GACpC,mBAAmB,CACf,SACA,CACJ;GACA,+BAA+B,CAC3B,SACA;IACI,WAAW;IACX,YAAY;GAChB,CACJ;GACA,eAAe,CACX,QACA;IACI,MAAM;IACN,UAAU;IACV,UAAU;GACd,CACJ;GACA,oBAAoB;GACpB,wBAAwB;GACxB,uBAAuB;GACvB,oCAAoC;GACpC,oBAAoB;GACpB,gCAAgC;GAChC,iBAAiB;GACjB,6BAA6B;GAC7B,8BAA8B;GAC9B,yBAAyB;EAC7B;CACJ;AACJ"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kikiutils/eslint-config",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "10.0.0",
|
|
5
5
|
"description": "Using antfu/eslint-config as a base for eslint-config, slightly modified the settings and added additional plugins, mainly for my own use.",
|
|
6
6
|
"author": "kiki-kanri",
|
|
7
7
|
"license": "MIT",
|
|
@@ -12,38 +12,10 @@
|
|
|
12
12
|
"keywords": [],
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"exports": {
|
|
15
|
-
"./
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
"./_internals/rules/compact-iteration-layout": {
|
|
21
|
-
"types": null,
|
|
22
|
-
"import": null,
|
|
23
|
-
"require": null
|
|
24
|
-
},
|
|
25
|
-
"./_internals/rules/consistent-condition-layout": {
|
|
26
|
-
"types": null,
|
|
27
|
-
"import": null,
|
|
28
|
-
"require": null
|
|
29
|
-
},
|
|
30
|
-
"./_internals/rules/consistent-parameter-layout": {
|
|
31
|
-
"types": null,
|
|
32
|
-
"import": null,
|
|
33
|
-
"require": null
|
|
34
|
-
},
|
|
35
|
-
"./_internals/rules/statement-padding": {
|
|
36
|
-
"types": null,
|
|
37
|
-
"import": null,
|
|
38
|
-
"require": null
|
|
39
|
-
},
|
|
40
|
-
"./_internals/utils": {
|
|
41
|
-
"types": null,
|
|
42
|
-
"import": null,
|
|
43
|
-
"require": null
|
|
44
|
-
},
|
|
45
|
-
"./package.json": "./package.json",
|
|
46
|
-
"./*": "./dist/*.js"
|
|
15
|
+
"./base": "./dist/base.js",
|
|
16
|
+
"./style": "./dist/style.js",
|
|
17
|
+
"./vue": "./dist/vue.js",
|
|
18
|
+
"./package.json": "./package.json"
|
|
47
19
|
},
|
|
48
20
|
"files": [
|
|
49
21
|
"./dist",
|
|
@@ -85,19 +57,20 @@
|
|
|
85
57
|
"eslint-plugin-better-tailwindcss": "^4.10.0",
|
|
86
58
|
"eslint-plugin-format": "^2.0.1",
|
|
87
59
|
"eslint-plugin-promise": "^7.3.0",
|
|
88
|
-
"tailwind-csstree": "^0.4.
|
|
60
|
+
"tailwind-csstree": "^0.4.2",
|
|
89
61
|
"vue-eslint-parser": "^10.4.1"
|
|
90
62
|
},
|
|
91
63
|
"devDependencies": {
|
|
92
64
|
"@antfu/eslint-config": "^10.0.0",
|
|
65
|
+
"@arethetypeswrong/core": "^0.18.5",
|
|
93
66
|
"@kikiutils/tsconfigs": "^7.0.0",
|
|
94
|
-
"@types/node": "^26.6.
|
|
67
|
+
"@types/node": "^26.6.6",
|
|
95
68
|
"@vitest/coverage-v8": "^5.0.5",
|
|
96
69
|
"changelogen": "^0.6.2",
|
|
97
|
-
"eslint": "^10.
|
|
98
|
-
"knip": "^6.
|
|
70
|
+
"eslint": "^10.14.0",
|
|
71
|
+
"knip": "^6.44.0",
|
|
99
72
|
"publint": "^0.3.25",
|
|
100
|
-
"tsdown": "^0.23.
|
|
73
|
+
"tsdown": "^0.23.4",
|
|
101
74
|
"typescript": "^6.0.3",
|
|
102
75
|
"unplugin-unused": "^0.6.0",
|
|
103
76
|
"vitest": "^5.0.5"
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
AST,
|
|
3
|
+
Rule,
|
|
4
|
+
} from 'eslint';
|
|
2
5
|
|
|
3
6
|
import type { TokenStore } from '../utils';
|
|
4
7
|
import { defineScriptAndTemplateVisitors } from '../utils';
|
|
@@ -13,120 +16,188 @@ type ListItem = CallNode['arguments'][number] | FunctionNode['params'][number];
|
|
|
13
16
|
|
|
14
17
|
export const consistentParameterLayout: Rule.RuleModule = {
|
|
15
18
|
create(context) {
|
|
16
|
-
const
|
|
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
|
-
}
|
|
19
|
+
const compactTestCalls = context.options[0]?.compactTestCalls === true;
|
|
41
20
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
21
|
+
function createVisitor(tokenStore: TokenStore, isTemplate: boolean): Rule.RuleListener {
|
|
22
|
+
function checkTestCallLayout(node: CallNode): void {
|
|
23
|
+
const items = node.arguments;
|
|
24
|
+
if (!items.length) return;
|
|
25
|
+
const openingParen = tokenStore.getTokenAfter(
|
|
26
|
+
node.typeArguments ?? node.callee,
|
|
27
|
+
(token) => token.type === 'Punctuator' && token.value === '(',
|
|
28
|
+
)!;
|
|
29
|
+
|
|
30
|
+
const closingParen = tokenStore.getLastToken(node)!;
|
|
31
|
+
const previous = tokenStore.getTokenBefore(closingParen)!;
|
|
32
|
+
const gaps: Array<[AST.Token, AST.Token, string]> = [
|
|
33
|
+
[
|
|
34
|
+
openingParen,
|
|
35
|
+
tokenStore.getTokenAfter(openingParen)!,
|
|
36
|
+
'',
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
previous,
|
|
40
|
+
closingParen,
|
|
41
|
+
'',
|
|
42
|
+
],
|
|
43
|
+
];
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
for (let index = 1; index < items.length; index++) {
|
|
46
|
+
const comma = tokenStore.getTokensBetween(items[index - 1]!, items[index]!)
|
|
47
|
+
.find((token) => token.type === 'Punctuator' && token.value === ',')!;
|
|
48
|
+
|
|
49
|
+
gaps.push(
|
|
50
|
+
[
|
|
51
|
+
tokenStore.getTokenBefore(comma)!,
|
|
52
|
+
comma,
|
|
53
|
+
'',
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
comma,
|
|
57
|
+
tokenStore.getTokenAfter(comma)!,
|
|
58
|
+
' ',
|
|
59
|
+
],
|
|
60
|
+
);
|
|
61
|
+
}
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
if (previous.value === ',') {
|
|
64
|
+
gaps.push([
|
|
65
|
+
tokenStore.getTokenBefore(previous)!,
|
|
66
|
+
previous,
|
|
67
|
+
'',
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
// Keep the whole list's layout when a boundary comment prevents safe compaction.
|
|
72
|
+
if (gaps.some(([left, right]) => tokenStore.commentsExistBetween(left, right))) return;
|
|
73
|
+
const multilineGaps = gaps.filter(([left, right]) => left.loc.end.line !== right.loc.start.line);
|
|
74
|
+
if (!multilineGaps.length) return;
|
|
65
75
|
|
|
66
76
|
context.report({
|
|
67
|
-
fix: (fixer) =>
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
fix: (fixer) => multilineGaps.map(([
|
|
78
|
+
left,
|
|
79
|
+
right,
|
|
80
|
+
text,
|
|
81
|
+
]) => fixer.replaceTextRange(
|
|
82
|
+
[
|
|
83
|
+
left.range[1],
|
|
84
|
+
right.range[0],
|
|
85
|
+
],
|
|
86
|
+
text,
|
|
87
|
+
)),
|
|
88
|
+
messageId: 'compactTestCall',
|
|
71
89
|
node,
|
|
72
90
|
});
|
|
73
|
-
|
|
74
|
-
return;
|
|
75
91
|
}
|
|
76
92
|
|
|
77
|
-
|
|
93
|
+
function checkParameterLayout(
|
|
94
|
+
node: CallNode | FunctionNode,
|
|
95
|
+
items: readonly ListItem[],
|
|
96
|
+
): void {
|
|
97
|
+
const first = items[0];
|
|
98
|
+
if (!first) return;
|
|
99
|
+
const isSingleParameter = items.length === 1;
|
|
100
|
+
|
|
101
|
+
// JSX argument layout belongs to the JSX formatting rules.
|
|
102
|
+
const firstType: string = first.type;
|
|
103
|
+
if (isSingleParameter && (firstType === 'JSXElement' || firstType === 'JSXFragment')) return;
|
|
104
|
+
let openingParen;
|
|
105
|
+
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
|
106
|
+
openingParen = tokenStore.getTokenAfter(
|
|
107
|
+
node.typeArguments ?? node.callee,
|
|
108
|
+
(token) => token.type === 'Punctuator' && token.value === '(',
|
|
109
|
+
);
|
|
110
|
+
} else {
|
|
111
|
+
openingParen = tokenStore.getTokenBefore(first);
|
|
112
|
+
}
|
|
78
113
|
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const comma = tokenStore.getTokensBetween(items[index - 1]!, item)
|
|
86
|
-
.find((token) => token.value === ',');
|
|
114
|
+
if (!openingParen || openingParen.value !== '(') return;
|
|
115
|
+
// An unparenthesized arrow must not borrow its parent's delimiters.
|
|
116
|
+
if (openingParen.range[0] < node.range![0]) return;
|
|
117
|
+
const closingParen = node.type === 'CallExpression' || node.type === 'NewExpression'
|
|
118
|
+
? tokenStore.getLastToken(node)
|
|
119
|
+
: tokenStore.getTokenAfter(items.at(-1)!, (token) => token.value === ')');
|
|
87
120
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
121
|
+
const next = tokenStore.getTokenAfter(openingParen)!;
|
|
122
|
+
|
|
123
|
+
if (isSingleParameter) {
|
|
124
|
+
if (!closingParen || closingParen.value !== ')') return;
|
|
125
|
+
let previous = tokenStore.getTokenBefore(closingParen)!;
|
|
126
|
+
if (previous.value === ',') previous = tokenStore.getTokenBefore(previous)!;
|
|
127
|
+
if (
|
|
128
|
+
tokenStore.commentsExistBetween(openingParen, next)
|
|
129
|
+
|| tokenStore.commentsExistBetween(previous, closingParen)
|
|
130
|
+
) return;
|
|
131
|
+
|
|
132
|
+
const hasOpeningNewline = openingParen.loc.end.line !== next.loc.start.line;
|
|
133
|
+
const hasClosingNewline = tokenStore.getTokenBefore(closingParen)!.loc.end.line
|
|
134
|
+
!== closingParen.loc.start.line;
|
|
92
135
|
|
|
93
|
-
|
|
94
|
-
closingParen.value === ')'
|
|
95
|
-
&& tokenStore.getTokenBefore(closingParen)!.loc.end.line === closingParen.loc.start.line
|
|
96
|
-
) newlineOffsets.push(closingParen.range[0]);
|
|
136
|
+
if (hasOpeningNewline === hasClosingNewline) return;
|
|
97
137
|
|
|
98
|
-
if (newlineOffsets.length) {
|
|
99
138
|
context.report({
|
|
100
|
-
fix: (fixer) =>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
'\n',
|
|
106
|
-
)),
|
|
107
|
-
messageId: 'newline',
|
|
108
|
-
node: first,
|
|
139
|
+
fix: (fixer) => hasOpeningNewline
|
|
140
|
+
? fixer.insertTextBefore(closingParen, '\n')
|
|
141
|
+
: fixer.insertTextAfter(openingParen, '\n'),
|
|
142
|
+
messageId: 'singleBoundary',
|
|
143
|
+
node,
|
|
109
144
|
});
|
|
145
|
+
|
|
146
|
+
return;
|
|
110
147
|
}
|
|
111
148
|
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
149
|
+
if (!closingParen || openingParen.loc.start.line === closingParen.loc.end.line) return;
|
|
114
150
|
|
|
115
|
-
|
|
151
|
+
if (isTemplate) {
|
|
152
|
+
// Script list formatters do not traverse Vue template expressions.
|
|
153
|
+
const newlineOffsets: number[] = [];
|
|
154
|
+
if (openingParen.loc.end.line === next.loc.start.line) newlineOffsets.push(openingParen.range[1]);
|
|
155
|
+
for (let index = 1; index < items.length; index++) {
|
|
156
|
+
const item = items[index]!;
|
|
157
|
+
const comma = tokenStore.getTokensBetween(items[index - 1]!, item)
|
|
158
|
+
.find((token) => token.value === ',');
|
|
116
159
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
160
|
+
if (comma && comma.loc.end.line === tokenStore.getTokenAfter(comma)!.loc.start.line) {
|
|
161
|
+
newlineOffsets.push(comma.range[1]);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (
|
|
166
|
+
closingParen.value === ')'
|
|
167
|
+
&& tokenStore.getTokenBefore(closingParen)!.loc.end.line === closingParen.loc.start.line
|
|
168
|
+
) newlineOffsets.push(closingParen.range[0]);
|
|
169
|
+
|
|
170
|
+
if (newlineOffsets.length) {
|
|
171
|
+
context.report({
|
|
172
|
+
fix: (fixer) => newlineOffsets.map((offset) => fixer.insertTextBeforeRange(
|
|
173
|
+
[
|
|
174
|
+
offset,
|
|
175
|
+
offset,
|
|
176
|
+
],
|
|
177
|
+
'\n',
|
|
178
|
+
)),
|
|
179
|
+
messageId: 'newline',
|
|
180
|
+
node: first,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (openingParen.loc.end.line !== next.loc.start.line) return;
|
|
188
|
+
|
|
189
|
+
context.report({
|
|
190
|
+
fix: (fixer) => fixer.insertTextAfter(openingParen, '\n'),
|
|
191
|
+
messageId: 'newline',
|
|
192
|
+
node: first,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
123
195
|
|
|
124
|
-
function createVisitor(tokenStore: TokenStore, isTemplate: boolean): Rule.RuleListener {
|
|
125
196
|
return {
|
|
126
|
-
ArrowFunctionExpression: (node) => checkParameterLayout(node, node.params
|
|
197
|
+
ArrowFunctionExpression: (node) => checkParameterLayout(node, node.params),
|
|
127
198
|
CallExpression: (node) => {
|
|
128
199
|
const { callee } = node;
|
|
129
|
-
if (
|
|
200
|
+
if (compactTestCalls) {
|
|
130
201
|
const target = callee.type === 'MemberExpression'
|
|
131
202
|
&& !callee.computed
|
|
132
203
|
&& callee.property.type === 'Identifier'
|
|
@@ -137,27 +208,37 @@ export const consistentParameterLayout: Rule.RuleModule = {
|
|
|
137
208
|
if (
|
|
138
209
|
target.type === 'Identifier'
|
|
139
210
|
&& (target.name === 'describe' || target.name === 'it')
|
|
140
|
-
)
|
|
211
|
+
) {
|
|
212
|
+
checkTestCallLayout(node);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
141
215
|
}
|
|
142
216
|
|
|
143
|
-
checkParameterLayout(node, node.arguments
|
|
217
|
+
checkParameterLayout(node, node.arguments);
|
|
144
218
|
},
|
|
145
|
-
FunctionDeclaration: (node) => checkParameterLayout(node, node.params
|
|
146
|
-
FunctionExpression: (node) => checkParameterLayout(node, node.params
|
|
147
|
-
NewExpression: (node) => checkParameterLayout(node, node.arguments
|
|
219
|
+
FunctionDeclaration: (node) => checkParameterLayout(node, node.params),
|
|
220
|
+
FunctionExpression: (node) => checkParameterLayout(node, node.params),
|
|
221
|
+
NewExpression: (node) => checkParameterLayout(node, node.arguments),
|
|
148
222
|
};
|
|
149
223
|
}
|
|
150
224
|
|
|
151
225
|
return defineScriptAndTemplateVisitors(context.sourceCode, createVisitor);
|
|
152
226
|
},
|
|
153
227
|
meta: {
|
|
154
|
-
docs: { description: '
|
|
228
|
+
docs: { description: 'Keep parameter boundaries consistent, expand multiline lists, and compact test calls.' },
|
|
155
229
|
fixable: 'whitespace',
|
|
156
230
|
messages: {
|
|
231
|
+
compactTestCall: 'Keep test-call arguments beside their parentheses and each other.',
|
|
157
232
|
newline: 'Start a multiline parameter list on a new line.',
|
|
158
233
|
singleBoundary: 'Use matching newline boundaries around a single parameter.',
|
|
159
234
|
},
|
|
160
|
-
schema: [
|
|
235
|
+
schema: [
|
|
236
|
+
{
|
|
237
|
+
additionalProperties: false,
|
|
238
|
+
properties: { compactTestCalls: { type: 'boolean' } },
|
|
239
|
+
type: 'object',
|
|
240
|
+
},
|
|
241
|
+
],
|
|
161
242
|
type: 'layout',
|
|
162
243
|
},
|
|
163
244
|
};
|
|
@@ -10,12 +10,20 @@ export const statementPadding: Rule.RuleModule = {
|
|
|
10
10
|
for (let index = 1; index < statements.length; index++) {
|
|
11
11
|
const previous = statements[index - 1]!;
|
|
12
12
|
const next = statements[index]!;
|
|
13
|
+
if (
|
|
14
|
+
[
|
|
15
|
+
previous,
|
|
16
|
+
next,
|
|
17
|
+
].some((node) =>
|
|
18
|
+
node.type === 'ImportDeclaration'
|
|
19
|
+
|| node.type === 'ExportAllDeclaration'
|
|
20
|
+
|| (node.type === 'ExportNamedDeclaration' && node.source))
|
|
21
|
+
) continue;
|
|
22
|
+
|
|
23
|
+
const previousFirstToken = sourceCode.getFirstToken(previous)!;
|
|
13
24
|
const lastToken = sourceCode.getLastToken(previous)!;
|
|
14
25
|
const firstToken = sourceCode.getFirstToken(next)!;
|
|
15
|
-
const
|
|
16
|
-
const openingLine = sourceCode.lines[firstToken.loc.start.line - 1]!;
|
|
17
|
-
const hasDifferentIndent = /^\s*/.exec(closingLine)![0] !== /^\s*/.exec(openingLine)![0];
|
|
18
|
-
const hasStandaloneClosing = /^[)\]}]+;?$/.test(closingLine.slice(0, lastToken.loc.end.column).trim());
|
|
26
|
+
const isMultiline = previousFirstToken.loc.start.line !== lastToken.loc.end.line;
|
|
19
27
|
const blockNode = previous.type === 'ExportDefaultDeclaration'
|
|
20
28
|
|| previous.type === 'ExportNamedDeclaration'
|
|
21
29
|
? previous.declaration
|
|
@@ -36,24 +44,27 @@ export const statementPadding: Rule.RuleModule = {
|
|
|
36
44
|
'WithStatement',
|
|
37
45
|
].includes(blockNode.type);
|
|
38
46
|
|
|
39
|
-
if (!
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
.slice(lastToken.loc.end.line, firstToken.loc.start.line - 1)
|
|
45
|
-
.some((line, index) => !line.trim() && !comments.some((comment) =>
|
|
46
|
-
comment.loc!.start.line <= lastToken.loc.end.line + index + 1
|
|
47
|
-
&& comment.loc!.end.line >= lastToken.loc.end.line + index + 1))
|
|
48
|
-
) continue;
|
|
47
|
+
if (!isMultiline && !hasBlockClosing) continue;
|
|
48
|
+
let previousEnd = lastToken.range[1];
|
|
49
|
+
let previousEndLine = lastToken.loc.end.line;
|
|
50
|
+
let nextStart = firstToken.range[0];
|
|
51
|
+
let nextStartLine = firstToken.loc.start.line;
|
|
49
52
|
|
|
50
|
-
//
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
+
// Include trailing comments in the previous boundary, not leading comments of the next statement.
|
|
54
|
+
for (const comment of sourceCode.getCommentsBefore(next)) {
|
|
55
|
+
if (comment.loc!.start.line === previousEndLine) {
|
|
56
|
+
previousEnd = comment.range![1];
|
|
57
|
+
previousEndLine = comment.loc!.end.line;
|
|
58
|
+
} else {
|
|
59
|
+
nextStart = comment.range![0];
|
|
60
|
+
nextStartLine = comment.loc!.start.line;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
53
64
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
65
|
+
if (nextStartLine > previousEndLine + 1) continue;
|
|
66
|
+
const linebreakIndex = sourceCode.text.slice(previousEnd, nextStart).indexOf('\n');
|
|
67
|
+
const offset = linebreakIndex === -1 ? nextStart : previousEnd + linebreakIndex + 1;
|
|
57
68
|
|
|
58
69
|
context.report({
|
|
59
70
|
fix: (fixer) => fixer.insertTextBeforeRange(
|
|
@@ -61,7 +72,7 @@ export const statementPadding: Rule.RuleModule = {
|
|
|
61
72
|
offset,
|
|
62
73
|
offset,
|
|
63
74
|
],
|
|
64
|
-
|
|
75
|
+
linebreakIndex === -1 ? '\n\n' : '\n',
|
|
65
76
|
),
|
|
66
77
|
messageId: 'blankLine',
|
|
67
78
|
node: next,
|
|
@@ -77,7 +88,7 @@ export const statementPadding: Rule.RuleModule = {
|
|
|
77
88
|
};
|
|
78
89
|
},
|
|
79
90
|
meta: {
|
|
80
|
-
docs: { description: 'Separate statements
|
|
91
|
+
docs: { description: 'Separate complete multiline statements and block-ending statements with blank lines.' },
|
|
81
92
|
fixable: 'whitespace',
|
|
82
93
|
messages: { blankLine: 'Add a blank line between these complete statements.' },
|
|
83
94
|
schema: [],
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { TypedFlatConfigItem } from '@antfu/eslint-config';
|
|
2
|
+
|
|
3
|
+
export function createTypeAwareConfig(): TypedFlatConfigItem {
|
|
4
|
+
return {
|
|
5
|
+
languageOptions: {
|
|
6
|
+
parserOptions: {
|
|
7
|
+
projectService: true,
|
|
8
|
+
tsconfigRootDir: process.cwd(),
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
rules: {
|
|
12
|
+
'require-await': 'off',
|
|
13
|
+
'ts/await-thenable': 'error',
|
|
14
|
+
'ts/no-base-to-string': 'error',
|
|
15
|
+
'ts/no-floating-promises': 'error',
|
|
16
|
+
'ts/no-for-in-array': 'error',
|
|
17
|
+
'ts/no-misused-promises': 'error',
|
|
18
|
+
'ts/no-misused-spread': 'error',
|
|
19
|
+
'ts/no-unnecessary-type-assertion': 'error',
|
|
20
|
+
'ts/require-await': 'error',
|
|
21
|
+
'ts/return-await': 'error',
|
|
22
|
+
'ts/switch-exhaustiveness-check': [
|
|
23
|
+
'error',
|
|
24
|
+
{ considerDefaultExhaustiveForUnions: true },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|