@kikiutils/eslint-config 9.3.2 → 10.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/README.md +8 -0
- package/dist/_internals/jsdoc.d.ts +53 -0
- package/dist/_internals/jsdoc.d.ts.map +1 -0
- package/dist/_internals/jsdoc.js +57 -0
- package/dist/_internals/jsdoc.js.map +1 -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 +84 -18
- 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 +13 -4
- package/dist/vue.js.map +1 -1
- package/package.json +11 -38
- package/src/_internals/jsdoc.ts +70 -0
- 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 +132 -23
- package/src/style.ts +1 -2
- package/src/vue.ts +15 -3
package/README.md
CHANGED
|
@@ -26,6 +26,14 @@ You can also use `yarn`, `npm`, or `bun`.
|
|
|
26
26
|
|
|
27
27
|
## Usage
|
|
28
28
|
|
|
29
|
+
TypeScript and Vue configs enable type-aware checks using TypeScript's Project Service.
|
|
30
|
+
Linted files must belong to a discoverable `tsconfig.json`; project references are supported.
|
|
31
|
+
The default setup does not require an additional `typescript.tsconfigPath` option.
|
|
32
|
+
JavaScript files retain syntax-only linting.
|
|
33
|
+
TypeScript tool configs (`*.config.ts`, `*.config.mts`, `*.config.cts`) are excluded from type-aware checks.
|
|
34
|
+
Base configs keep linting Markdown itself, but exclude its code blocks from all lint rules.
|
|
35
|
+
Style configs format CSS and SCSS; indented Sass (`.sass`) is not supported.
|
|
36
|
+
|
|
29
37
|
## License
|
|
30
38
|
|
|
31
39
|
[MIT License](./LICENSE)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export declare const jsdocSettings: {
|
|
2
|
+
jsdoc: {
|
|
3
|
+
tagNamePreference: {
|
|
4
|
+
arg: string;
|
|
5
|
+
argument: string;
|
|
6
|
+
desc: boolean;
|
|
7
|
+
description: boolean;
|
|
8
|
+
exception: string;
|
|
9
|
+
return: string;
|
|
10
|
+
types: boolean;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export declare const typescriptJsdocConfig: {
|
|
15
|
+
rules: {
|
|
16
|
+
'jsdoc/check-tag-names': ["error", {
|
|
17
|
+
definedTags: string[];
|
|
18
|
+
}];
|
|
19
|
+
'jsdoc/multiline-blocks': ["error", {
|
|
20
|
+
noSingleLineBlocks: true;
|
|
21
|
+
singleLineTags: never[];
|
|
22
|
+
}];
|
|
23
|
+
'jsdoc/no-types': "error";
|
|
24
|
+
};
|
|
25
|
+
settings: {
|
|
26
|
+
jsdoc: {
|
|
27
|
+
mode: string;
|
|
28
|
+
tagNamePreference: {
|
|
29
|
+
callback: boolean;
|
|
30
|
+
class: boolean;
|
|
31
|
+
constructor: boolean;
|
|
32
|
+
default: string;
|
|
33
|
+
function: boolean;
|
|
34
|
+
method: boolean;
|
|
35
|
+
readonly: boolean;
|
|
36
|
+
static: boolean;
|
|
37
|
+
template: boolean;
|
|
38
|
+
type: boolean;
|
|
39
|
+
typedef: boolean;
|
|
40
|
+
typeParam: string;
|
|
41
|
+
virtual: string;
|
|
42
|
+
arg: string;
|
|
43
|
+
argument: string;
|
|
44
|
+
desc: boolean;
|
|
45
|
+
description: boolean;
|
|
46
|
+
exception: string;
|
|
47
|
+
return: string;
|
|
48
|
+
types: boolean;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=jsdoc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsdoc.d.ts","names":[],"sources":["../../src/_internals/jsdoc.ts"],"mappings":"A,qBAEa;;;;;;;;;;;;;qBAcA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/_internals/jsdoc.ts
|
|
2
|
+
const jsdocSettings = { jsdoc: { tagNamePreference: {
|
|
3
|
+
arg: "param",
|
|
4
|
+
argument: "param",
|
|
5
|
+
desc: false,
|
|
6
|
+
description: false,
|
|
7
|
+
exception: "throws",
|
|
8
|
+
return: "returns",
|
|
9
|
+
types: false
|
|
10
|
+
} } };
|
|
11
|
+
const typescriptJsdocConfig = {
|
|
12
|
+
rules: {
|
|
13
|
+
"jsdoc/check-tag-names": ["error", { definedTags: [
|
|
14
|
+
"alpha",
|
|
15
|
+
"beta",
|
|
16
|
+
"defaultValue",
|
|
17
|
+
"eventProperty",
|
|
18
|
+
"experimental",
|
|
19
|
+
"hidden",
|
|
20
|
+
"inheritDoc",
|
|
21
|
+
"internal",
|
|
22
|
+
"packageDocumentation",
|
|
23
|
+
"privateRemarks",
|
|
24
|
+
"remarks",
|
|
25
|
+
"sealed",
|
|
26
|
+
"typeParam",
|
|
27
|
+
"virtual"
|
|
28
|
+
] }],
|
|
29
|
+
"jsdoc/multiline-blocks": ["error", {
|
|
30
|
+
noSingleLineBlocks: true,
|
|
31
|
+
singleLineTags: []
|
|
32
|
+
}],
|
|
33
|
+
"jsdoc/no-types": "error"
|
|
34
|
+
},
|
|
35
|
+
settings: { jsdoc: {
|
|
36
|
+
mode: "typescript",
|
|
37
|
+
tagNamePreference: {
|
|
38
|
+
...jsdocSettings.jsdoc.tagNamePreference,
|
|
39
|
+
callback: false,
|
|
40
|
+
class: false,
|
|
41
|
+
constructor: false,
|
|
42
|
+
default: "defaultValue",
|
|
43
|
+
function: false,
|
|
44
|
+
method: false,
|
|
45
|
+
readonly: false,
|
|
46
|
+
static: false,
|
|
47
|
+
template: false,
|
|
48
|
+
type: false,
|
|
49
|
+
typedef: false,
|
|
50
|
+
typeParam: "typeParam",
|
|
51
|
+
virtual: "virtual"
|
|
52
|
+
}
|
|
53
|
+
} }
|
|
54
|
+
};
|
|
55
|
+
export { jsdocSettings, typescriptJsdocConfig };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=jsdoc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsdoc.js","names":[],"sources":["../../src/_internals/jsdoc.ts"],"sourcesContent":["import type { TypedFlatConfigItem } from '@antfu/eslint-config';\n\nexport const jsdocSettings = {\n jsdoc: {\n tagNamePreference: {\n arg: 'param',\n argument: 'param',\n desc: false,\n description: false,\n exception: 'throws',\n return: 'returns',\n types: false,\n },\n },\n};\n\nexport const typescriptJsdocConfig = {\n rules: {\n 'jsdoc/check-tag-names': [\n 'error',\n {\n definedTags: [\n 'alpha',\n 'beta',\n 'defaultValue',\n 'eventProperty',\n 'experimental',\n 'hidden',\n 'inheritDoc',\n 'internal',\n 'packageDocumentation',\n 'privateRemarks',\n 'remarks',\n 'sealed',\n 'typeParam',\n 'virtual',\n ],\n },\n ],\n 'jsdoc/multiline-blocks': [\n 'error',\n {\n noSingleLineBlocks: true,\n singleLineTags: [],\n },\n ],\n 'jsdoc/no-types': 'error',\n },\n settings: {\n jsdoc: {\n mode: 'typescript',\n tagNamePreference: {\n ...jsdocSettings.jsdoc.tagNamePreference,\n callback: false,\n class: false,\n constructor: false,\n default: 'defaultValue',\n function: false,\n method: false,\n readonly: false,\n static: false,\n template: false,\n type: false,\n typedef: false,\n typeParam: 'typeParam',\n virtual: 'virtual',\n },\n },\n },\n} satisfies TypedFlatConfigItem;\n"],"mappings":";AAEA,MAAa,gBAAgB,EACzB,OAAO,EACH,mBAAmB;CACf,KAAK;CACL,UAAU;CACV,MAAM;CACN,aAAa;CACb,WAAW;CACX,QAAQ;CACR,OAAO;AACX,EACJ,EACJ;AAEA,MAAa,wBAAwB;CACjC,OAAO;EACH,yBAAyB,CACrB,SACA,EACI,aAAa;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACJ,EACJ,CACJ;EACA,0BAA0B,CACtB,SACA;GACI,oBAAoB;GACpB,gBAAgB,CAAC;EACrB,CACJ;EACA,kBAAkB;CACtB;CACA,UAAU,EACN,OAAO;EACH,MAAM;EACN,mBAAmB;GACf,GAAG,cAAc,MAAM;GACvB,UAAU;GACV,OAAO;GACP,aAAa;GACb,SAAS;GACT,UAAU;GACV,QAAQ;GACR,UAAU;GACV,QAAQ;GACR,UAAU;GACV,MAAM;GACN,SAAS;GACT,WAAW;GACX,SAAS;EACb;CACJ,EACJ;AACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consistent-parameter-layout.d.ts","names":[],"sources":["../../../src/_internals/rules/consistent-parameter-layout.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"consistent-parameter-layout.d.ts","names":[],"sources":["../../../src/_internals/rules/consistent-parameter-layout.ts"],"mappings":";qBAgBa,2BAA2B,KAAK"}
|
|
@@ -2,86 +2,133 @@ import { defineScriptAndTemplateVisitors } from "../utils.js";
|
|
|
2
2
|
// src/_internals/rules/consistent-parameter-layout.ts
|
|
3
3
|
const consistentParameterLayout = {
|
|
4
4
|
create(context) {
|
|
5
|
-
const
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
5
|
+
const compactTestCalls = context.options[0]?.compactTestCalls === true;
|
|
6
|
+
function createVisitor(tokenStore, isTemplate) {
|
|
7
|
+
function checkTestCallLayout(node) {
|
|
8
|
+
const items = node.arguments;
|
|
9
|
+
if (!items.length) return;
|
|
10
|
+
const openingParen = tokenStore.getTokenAfter(node.typeArguments ?? node.callee, (token) => token.type === "Punctuator" && token.value === "(");
|
|
11
|
+
const closingParen = tokenStore.getLastToken(node);
|
|
12
|
+
const previous = tokenStore.getTokenBefore(closingParen);
|
|
13
|
+
const gaps = [[
|
|
14
|
+
openingParen,
|
|
15
|
+
tokenStore.getTokenAfter(openingParen),
|
|
16
|
+
""
|
|
17
|
+
], [
|
|
18
|
+
previous,
|
|
19
|
+
closingParen,
|
|
20
|
+
""
|
|
21
|
+
]];
|
|
22
|
+
for (let index = 1; index < items.length; index++) {
|
|
23
|
+
const comma = tokenStore.getTokensBetween(items[index - 1], items[index]).find((token) => token.type === "Punctuator" && token.value === ",");
|
|
24
|
+
gaps.push([
|
|
25
|
+
tokenStore.getTokenBefore(comma),
|
|
26
|
+
comma,
|
|
27
|
+
""
|
|
28
|
+
], [
|
|
29
|
+
comma,
|
|
30
|
+
tokenStore.getTokenAfter(comma),
|
|
31
|
+
" "
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
if (previous.value === ",") gaps.push([
|
|
35
|
+
tokenStore.getTokenBefore(previous),
|
|
36
|
+
previous,
|
|
37
|
+
""
|
|
38
|
+
]);
|
|
39
|
+
if (gaps.some(([left, right]) => tokenStore.commentsExistBetween(left, right))) return;
|
|
40
|
+
const multilineGaps = gaps.filter(([left, right]) => left.loc.end.line !== right.loc.start.line);
|
|
41
|
+
if (!multilineGaps.length) return;
|
|
28
42
|
context.report({
|
|
29
|
-
fix: (fixer) =>
|
|
30
|
-
messageId: "
|
|
43
|
+
fix: (fixer) => multilineGaps.map(([left, right, text]) => fixer.replaceTextRange([left.range[1], right.range[0]], text)),
|
|
44
|
+
messageId: "compactTestCall",
|
|
31
45
|
node
|
|
32
46
|
});
|
|
33
|
-
return;
|
|
34
47
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
function checkParameterLayout(node, items) {
|
|
49
|
+
const first = items[0];
|
|
50
|
+
if (!first) return;
|
|
51
|
+
const isSingleParameter = items.length === 1;
|
|
52
|
+
const firstType = first.type;
|
|
53
|
+
if (isSingleParameter && (firstType === "JSXElement" || firstType === "JSXFragment")) return;
|
|
54
|
+
let openingParen;
|
|
55
|
+
if (node.type === "CallExpression" || node.type === "NewExpression") openingParen = tokenStore.getTokenAfter(node.typeArguments ?? node.callee, (token) => token.type === "Punctuator" && token.value === "(");
|
|
56
|
+
else openingParen = tokenStore.getTokenBefore(first);
|
|
57
|
+
if (!openingParen || openingParen.value !== "(") return;
|
|
58
|
+
if (openingParen.range[0] < node.range[0]) return;
|
|
59
|
+
const closingParen = node.type === "CallExpression" || node.type === "NewExpression" ? tokenStore.getLastToken(node) : tokenStore.getTokenAfter(items.at(-1), (token) => token.value === ")");
|
|
60
|
+
const next = tokenStore.getTokenAfter(openingParen);
|
|
61
|
+
if (isSingleParameter) {
|
|
62
|
+
if (!closingParen || closingParen.value !== ")") return;
|
|
63
|
+
let previous = tokenStore.getTokenBefore(closingParen);
|
|
64
|
+
if (previous.value === ",") previous = tokenStore.getTokenBefore(previous);
|
|
65
|
+
if (tokenStore.commentsExistBetween(openingParen, next) || tokenStore.commentsExistBetween(previous, closingParen)) return;
|
|
66
|
+
const hasOpeningNewline = openingParen.loc.end.line !== next.loc.start.line;
|
|
67
|
+
if (hasOpeningNewline === (tokenStore.getTokenBefore(closingParen).loc.end.line !== closingParen.loc.start.line)) return;
|
|
68
|
+
context.report({
|
|
69
|
+
fix: (fixer) => hasOpeningNewline ? fixer.insertTextBefore(closingParen, "\n") : fixer.insertTextAfter(openingParen, "\n"),
|
|
70
|
+
messageId: "singleBoundary",
|
|
71
|
+
node
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
43
74
|
}
|
|
44
|
-
if (closingParen
|
|
45
|
-
if (
|
|
46
|
-
|
|
75
|
+
if (!closingParen || openingParen.loc.start.line === closingParen.loc.end.line) return;
|
|
76
|
+
if (isTemplate) {
|
|
77
|
+
const newlineOffsets = [];
|
|
78
|
+
if (openingParen.loc.end.line === next.loc.start.line) newlineOffsets.push(openingParen.range[1]);
|
|
79
|
+
for (let index = 1; index < items.length; index++) {
|
|
80
|
+
const item = items[index];
|
|
81
|
+
const comma = tokenStore.getTokensBetween(items[index - 1], item).find((token) => token.value === ",");
|
|
82
|
+
if (comma && comma.loc.end.line === tokenStore.getTokenAfter(comma).loc.start.line) newlineOffsets.push(comma.range[1]);
|
|
83
|
+
}
|
|
84
|
+
if (closingParen.value === ")" && tokenStore.getTokenBefore(closingParen).loc.end.line === closingParen.loc.start.line) newlineOffsets.push(closingParen.range[0]);
|
|
85
|
+
if (newlineOffsets.length) context.report({
|
|
86
|
+
fix: (fixer) => newlineOffsets.map((offset) => fixer.insertTextBeforeRange([offset, offset], "\n")),
|
|
87
|
+
messageId: "newline",
|
|
88
|
+
node: first
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (openingParen.loc.end.line !== next.loc.start.line) return;
|
|
93
|
+
context.report({
|
|
94
|
+
fix: (fixer) => fixer.insertTextAfter(openingParen, "\n"),
|
|
47
95
|
messageId: "newline",
|
|
48
96
|
node: first
|
|
49
97
|
});
|
|
50
|
-
return;
|
|
51
98
|
}
|
|
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
99
|
return {
|
|
61
|
-
ArrowFunctionExpression: (node) => checkParameterLayout(node, node.params
|
|
100
|
+
ArrowFunctionExpression: (node) => checkParameterLayout(node, node.params),
|
|
62
101
|
CallExpression: (node) => {
|
|
63
102
|
const { callee } = node;
|
|
64
|
-
if (
|
|
103
|
+
if (compactTestCalls) {
|
|
65
104
|
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"))
|
|
105
|
+
if (target.type === "Identifier" && (target.name === "describe" || target.name === "it")) {
|
|
106
|
+
checkTestCallLayout(node);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
67
109
|
}
|
|
68
|
-
checkParameterLayout(node, node.arguments
|
|
110
|
+
checkParameterLayout(node, node.arguments);
|
|
69
111
|
},
|
|
70
|
-
FunctionDeclaration: (node) => checkParameterLayout(node, node.params
|
|
71
|
-
FunctionExpression: (node) => checkParameterLayout(node, node.params
|
|
72
|
-
NewExpression: (node) => checkParameterLayout(node, node.arguments
|
|
112
|
+
FunctionDeclaration: (node) => checkParameterLayout(node, node.params),
|
|
113
|
+
FunctionExpression: (node) => checkParameterLayout(node, node.params),
|
|
114
|
+
NewExpression: (node) => checkParameterLayout(node, node.arguments)
|
|
73
115
|
};
|
|
74
116
|
}
|
|
75
117
|
return defineScriptAndTemplateVisitors(context.sourceCode, createVisitor);
|
|
76
118
|
},
|
|
77
119
|
meta: {
|
|
78
|
-
docs: { description: "
|
|
120
|
+
docs: { description: "Keep parameter boundaries consistent, expand multiline lists, and compact test calls." },
|
|
79
121
|
fixable: "whitespace",
|
|
80
122
|
messages: {
|
|
123
|
+
compactTestCall: "Keep test-call arguments beside their parentheses and each other.",
|
|
81
124
|
newline: "Start a multiline parameter list on a new line.",
|
|
82
125
|
singleBoundary: "Use matching newline boundaries around a single parameter."
|
|
83
126
|
},
|
|
84
|
-
schema: [
|
|
127
|
+
schema: [{
|
|
128
|
+
additionalProperties: false,
|
|
129
|
+
properties: { compactTestCalls: { type: "boolean" } },
|
|
130
|
+
type: "object"
|
|
131
|
+
}],
|
|
85
132
|
type: "layout"
|
|
86
133
|
}
|
|
87
134
|
};
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"version":3,"file":"consistent-parameter-layout.js","names":[],"sources":["../../../src/_internals/rules/consistent-parameter-layout.ts"],"sourcesContent":["import type {\n AST,\n Rule,\n} 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 compactTestCalls = context.options[0]?.compactTestCalls === true;\n\n function createVisitor(tokenStore: TokenStore, isTemplate: boolean): Rule.RuleListener {\n function checkTestCallLayout(node: CallNode): void {\n const items = node.arguments;\n if (!items.length) return;\n const openingParen = tokenStore.getTokenAfter(\n node.typeArguments ?? node.callee,\n (token) => token.type === 'Punctuator' && token.value === '(',\n )!;\n\n const closingParen = tokenStore.getLastToken(node)!;\n const previous = tokenStore.getTokenBefore(closingParen)!;\n const gaps: Array<[AST.Token, AST.Token, string]> = [\n [\n openingParen,\n tokenStore.getTokenAfter(openingParen)!,\n '',\n ],\n [\n previous,\n closingParen,\n '',\n ],\n ];\n\n for (let index = 1; index < items.length; index++) {\n const comma = tokenStore.getTokensBetween(items[index - 1]!, items[index]!)\n .find((token) => token.type === 'Punctuator' && token.value === ',')!;\n\n gaps.push(\n [\n tokenStore.getTokenBefore(comma)!,\n comma,\n '',\n ],\n [\n comma,\n tokenStore.getTokenAfter(comma)!,\n ' ',\n ],\n );\n }\n\n if (previous.value === ',') {\n gaps.push([\n tokenStore.getTokenBefore(previous)!,\n previous,\n '',\n ]);\n }\n\n // Keep the whole list's layout when a boundary comment prevents safe compaction.\n if (gaps.some(([left, right]) => tokenStore.commentsExistBetween(left, right))) return;\n const multilineGaps = gaps.filter(([left, right]) => left.loc.end.line !== right.loc.start.line);\n if (!multilineGaps.length) return;\n\n context.report({\n fix: (fixer) => multilineGaps.map(([\n left,\n right,\n text,\n ]) => fixer.replaceTextRange(\n [\n left.range[1],\n right.range[0],\n ],\n text,\n )),\n messageId: 'compactTestCall',\n node,\n });\n }\n\n function checkParameterLayout(\n node: CallNode | FunctionNode,\n items: readonly ListItem[],\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 openingParen = tokenStore.getTokenAfter(\n node.typeArguments ?? node.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.insertTextBeforeRange(\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 return {\n ArrowFunctionExpression: (node) => checkParameterLayout(node, node.params),\n CallExpression: (node) => {\n const { callee } = node;\n if (compactTestCalls) {\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 ) {\n checkTestCallLayout(node);\n return;\n }\n }\n\n checkParameterLayout(node, node.arguments);\n },\n FunctionDeclaration: (node) => checkParameterLayout(node, node.params),\n FunctionExpression: (node) => checkParameterLayout(node, node.params),\n NewExpression: (node) => checkParameterLayout(node, node.arguments),\n };\n }\n\n return defineScriptAndTemplateVisitors(context.sourceCode, createVisitor);\n },\n meta: {\n docs: { description: 'Keep parameter boundaries consistent, expand multiline lists, and compact test calls.' },\n fixable: 'whitespace',\n messages: {\n compactTestCall: 'Keep test-call arguments beside their parentheses and each other.',\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 {\n additionalProperties: false,\n properties: { compactTestCalls: { type: 'boolean' } },\n type: 'object',\n },\n ],\n type: 'layout',\n },\n};\n"],"mappings":"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"}
|
|
@@ -6,12 +6,11 @@ const statementPadding = {
|
|
|
6
6
|
for (let index = 1; index < statements.length; index++) {
|
|
7
7
|
const previous = statements[index - 1];
|
|
8
8
|
const next = statements[index];
|
|
9
|
+
if ([previous, next].some((node) => node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration" && node.source)) continue;
|
|
10
|
+
const previousFirstToken = sourceCode.getFirstToken(previous);
|
|
9
11
|
const lastToken = sourceCode.getLastToken(previous);
|
|
10
12
|
const firstToken = sourceCode.getFirstToken(next);
|
|
11
|
-
const
|
|
12
|
-
const openingLine = sourceCode.lines[firstToken.loc.start.line - 1];
|
|
13
|
-
const hasDifferentIndent = /^\s*/.exec(closingLine)[0] !== /^\s*/.exec(openingLine)[0];
|
|
14
|
-
const hasStandaloneClosing = /^[)\]}]+;?$/.test(closingLine.slice(0, lastToken.loc.end.column).trim());
|
|
13
|
+
const isMultiline = previousFirstToken.loc.start.line !== lastToken.loc.end.line;
|
|
15
14
|
const blockNode = previous.type === "ExportDefaultDeclaration" || previous.type === "ExportNamedDeclaration" ? previous.declaration : previous;
|
|
16
15
|
const hasBlockClosing = lastToken.value === "}" && blockNode && [
|
|
17
16
|
"BlockStatement",
|
|
@@ -27,13 +26,24 @@ const statementPadding = {
|
|
|
27
26
|
"WhileStatement",
|
|
28
27
|
"WithStatement"
|
|
29
28
|
].includes(blockNode.type);
|
|
30
|
-
if (!
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
if (!isMultiline && !hasBlockClosing) continue;
|
|
30
|
+
let previousEnd = lastToken.range[1];
|
|
31
|
+
let previousEndLine = lastToken.loc.end.line;
|
|
32
|
+
let nextStart = firstToken.range[0];
|
|
33
|
+
let nextStartLine = firstToken.loc.start.line;
|
|
34
|
+
for (const comment of sourceCode.getCommentsBefore(next)) if (comment.loc.start.line === previousEndLine) {
|
|
35
|
+
previousEnd = comment.range[1];
|
|
36
|
+
previousEndLine = comment.loc.end.line;
|
|
37
|
+
} else {
|
|
38
|
+
nextStart = comment.range[0];
|
|
39
|
+
nextStartLine = comment.loc.start.line;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
if (nextStartLine > previousEndLine + 1) continue;
|
|
43
|
+
const linebreakIndex = sourceCode.text.slice(previousEnd, nextStart).indexOf("\n");
|
|
44
|
+
const offset = linebreakIndex === -1 ? nextStart : previousEnd + linebreakIndex + 1;
|
|
35
45
|
context.report({
|
|
36
|
-
fix: (fixer) => fixer.insertTextBeforeRange([offset, offset],
|
|
46
|
+
fix: (fixer) => fixer.insertTextBeforeRange([offset, offset], linebreakIndex === -1 ? "\n\n" : "\n"),
|
|
37
47
|
messageId: "blankLine",
|
|
38
48
|
node: next
|
|
39
49
|
});
|
|
@@ -47,7 +57,7 @@ const statementPadding = {
|
|
|
47
57
|
};
|
|
48
58
|
},
|
|
49
59
|
meta: {
|
|
50
|
-
docs: { description: "Separate statements
|
|
60
|
+
docs: { description: "Separate complete multiline statements and block-ending statements with blank lines." },
|
|
51
61
|
fixable: "whitespace",
|
|
52
62
|
messages: { blankLine: "Add a blank line between these complete statements." },
|
|
53
63
|
schema: [],
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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 if (\n [\n previous,\n next,\n ].some((node) =>\n node.type === 'ImportDeclaration'\n || node.type === 'ExportAllDeclaration'\n || (node.type === 'ExportNamedDeclaration' && node.source))\n ) continue;\n\n const previousFirstToken = sourceCode.getFirstToken(previous)!;\n const lastToken = sourceCode.getLastToken(previous)!;\n const firstToken = sourceCode.getFirstToken(next)!;\n const isMultiline = previousFirstToken.loc.start.line !== lastToken.loc.end.line;\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 (!isMultiline && !hasBlockClosing) continue;\n let previousEnd = lastToken.range[1];\n let previousEndLine = lastToken.loc.end.line;\n let nextStart = firstToken.range[0];\n let nextStartLine = firstToken.loc.start.line;\n\n // Include trailing comments in the previous boundary, not leading comments of the next statement.\n for (const comment of sourceCode.getCommentsBefore(next)) {\n if (comment.loc!.start.line === previousEndLine) {\n previousEnd = comment.range![1];\n previousEndLine = comment.loc!.end.line;\n } else {\n nextStart = comment.range![0];\n nextStartLine = comment.loc!.start.line;\n break;\n }\n }\n\n if (nextStartLine > previousEndLine + 1) continue;\n const linebreakIndex = sourceCode.text.slice(previousEnd, nextStart).indexOf('\\n');\n const offset = linebreakIndex === -1 ? nextStart : previousEnd + linebreakIndex + 1;\n\n context.report({\n fix: (fixer) => fixer.insertTextBeforeRange(\n [\n offset,\n offset,\n ],\n linebreakIndex === -1 ? '\\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 complete multiline statements and block-ending statements with blank lines.' },\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,IACI,CACI,UACA,IACJ,CAAC,CAAC,MAAM,SACJ,KAAK,SAAS,uBACX,KAAK,SAAS,0BACb,KAAK,SAAS,4BAA4B,KAAK,MAAO,GAChE;IAEF,MAAM,qBAAqB,WAAW,cAAc,QAAQ;IAC5D,MAAM,YAAY,WAAW,aAAa,QAAQ;IAClD,MAAM,aAAa,WAAW,cAAc,IAAI;IAChD,MAAM,cAAc,mBAAmB,IAAI,MAAM,SAAS,UAAU,IAAI,IAAI;IAC5E,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,eAAe,CAAC,iBAAiB;IACtC,IAAI,cAAc,UAAU,MAAM;IAClC,IAAI,kBAAkB,UAAU,IAAI,IAAI;IACxC,IAAI,YAAY,WAAW,MAAM;IACjC,IAAI,gBAAgB,WAAW,IAAI,MAAM;IAGzC,KAAK,MAAM,WAAW,WAAW,kBAAkB,IAAI,GACnD,IAAI,QAAQ,IAAK,MAAM,SAAS,iBAAiB;KAC7C,cAAc,QAAQ,MAAO;KAC7B,kBAAkB,QAAQ,IAAK,IAAI;IACvC,OAAO;KACH,YAAY,QAAQ,MAAO;KAC3B,gBAAgB,QAAQ,IAAK,MAAM;KACnC;IACJ;IAGJ,IAAI,gBAAgB,kBAAkB,GAAG;IACzC,MAAM,iBAAiB,WAAW,KAAK,MAAM,aAAa,SAAS,CAAC,CAAC,QAAQ,IAAI;IACjF,MAAM,SAAS,mBAAmB,KAAK,YAAY,cAAc,iBAAiB;IAElF,QAAQ,OAAO;KACX,MAAM,UAAU,MAAM,sBAClB,CACI,QACA,MACJ,GACA,mBAAmB,KAAK,SAAS,IACrC;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,uFAAuF;EAC5G,SAAS;EACT,UAAU,EAAE,WAAW,sDAAsD;EAC7E,QAAQ,CAAC;EACT,MAAM;CACV;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-aware.d.ts","names":[],"sources":["../../src/_internals/type-aware.ts"],"mappings":";wBAEgB,yBAAyB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/_internals/type-aware.ts
|
|
2
|
+
function createTypeAwareConfig() {
|
|
3
|
+
return {
|
|
4
|
+
languageOptions: { parserOptions: {
|
|
5
|
+
projectService: true,
|
|
6
|
+
tsconfigRootDir: process.cwd()
|
|
7
|
+
} },
|
|
8
|
+
rules: {
|
|
9
|
+
"require-await": "off",
|
|
10
|
+
"ts/await-thenable": "error",
|
|
11
|
+
"ts/no-base-to-string": "error",
|
|
12
|
+
"ts/no-floating-promises": "error",
|
|
13
|
+
"ts/no-for-in-array": "error",
|
|
14
|
+
"ts/no-misused-promises": "error",
|
|
15
|
+
"ts/no-misused-spread": "error",
|
|
16
|
+
"ts/no-unnecessary-type-assertion": "error",
|
|
17
|
+
"ts/require-await": "error",
|
|
18
|
+
"ts/return-await": "error",
|
|
19
|
+
"ts/switch-exhaustiveness-check": ["error", { considerDefaultExhaustiveForUnions: true }]
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export { createTypeAwareConfig };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=type-aware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-aware.js","names":[],"sources":["../../src/_internals/type-aware.ts"],"sourcesContent":["import type { TypedFlatConfigItem } from '@antfu/eslint-config';\n\nexport function createTypeAwareConfig(): TypedFlatConfigItem {\n return {\n languageOptions: {\n parserOptions: {\n projectService: true,\n tsconfigRootDir: process.cwd(),\n },\n },\n rules: {\n 'require-await': 'off',\n 'ts/await-thenable': 'error',\n 'ts/no-base-to-string': 'error',\n 'ts/no-floating-promises': 'error',\n 'ts/no-for-in-array': 'error',\n 'ts/no-misused-promises': 'error',\n 'ts/no-misused-spread': 'error',\n 'ts/no-unnecessary-type-assertion': 'error',\n 'ts/require-await': 'error',\n 'ts/return-await': 'error',\n 'ts/switch-exhaustiveness-check': [\n 'error',\n { considerDefaultExhaustiveForUnions: true },\n ],\n },\n };\n}\n"],"mappings":";AAEA,SAAgB,wBAA6C;CACzD,OAAO;EACH,iBAAiB,EACb,eAAe;GACX,gBAAgB;GAChB,iBAAiB,QAAQ,IAAI;EACjC,EACJ;EACA,OAAO;GACH,iBAAiB;GACjB,qBAAqB;GACrB,wBAAwB;GACxB,2BAA2B;GAC3B,sBAAsB;GACtB,0BAA0B;GAC1B,wBAAwB;GACxB,oCAAoC;GACpC,oBAAoB;GACpB,mBAAmB;GACnB,kCAAkC,CAC9B,SACA,EAAE,oCAAoC,KAAK,CAC/C;EACJ;CACJ;AACJ"}
|
package/dist/base.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","names":[],"sources":["../src/base.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"base.d.ts","names":[],"sources":["../src/base.ts"],"mappings":";wBAwCgB,kBAAkB,+BAAuC;wBA6IzD,gBAAgB,+BAAuC"}
|