@so1ve/eslint-plugin 4.1.8 → 4.2.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/dist/index.d.mts +25 -83
- package/dist/index.mjs +13 -1254
- package/dist/rules/function-style.d.mts +8 -0
- package/dist/rules/function-style.mjs +145 -0
- package/dist/rules/html-spaced-comment.d.mts +8 -0
- package/dist/rules/html-spaced-comment.mjs +41 -0
- package/dist/rules/import-dedupe.d.mts +8 -0
- package/dist/rules/import-dedupe.mjs +42 -0
- package/dist/rules/import-export-newline.d.mts +8 -0
- package/dist/rules/import-export-newline.mjs +98 -0
- package/dist/rules/no-import-promises-as.d.mts +8 -0
- package/dist/rules/no-import-promises-as.mjs +45 -0
- package/dist/rules/no-inline-type-modifier.d.mts +8 -0
- package/dist/rules/no-inline-type-modifier.mjs +86 -0
- package/dist/rules/no-negated-comparison.d.mts +8 -0
- package/dist/rules/no-negated-comparison.mjs +45 -0
- package/dist/rules/no-useless-template-string.d.mts +8 -0
- package/dist/rules/no-useless-template-string.mjs +30 -0
- package/dist/rules/prefer-ts-expect-error.d.mts +8 -0
- package/dist/rules/prefer-ts-expect-error.mjs +46 -0
- package/dist/rules/require-async-with-await.d.mts +8 -0
- package/dist/rules/require-async-with-await.mjs +51 -0
- package/dist/rules/sort-exports.d.mts +8 -0
- package/dist/rules/sort-exports.mjs +68 -0
- package/dist/rules/sort-imports.d.mts +10 -0
- package/dist/rules/sort-imports.mjs +104 -0
- package/dist/rules/vue-root-element-sort-attributes.d.mts +11 -0
- package/dist/rules/vue-root-element-sort-attributes.mjs +66 -0
- package/dist/utils/index.mjs +19 -0
- package/dist/utils/sort-imports.mjs +417 -0
- package/package.json +1 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
//#region src/rules/require-async-with-await.d.ts
|
|
4
|
+
type MessageIds = "requireAsyncWithAwait";
|
|
5
|
+
type Options = [];
|
|
6
|
+
declare const rule: ESLintUtils.RuleModule<MessageIds, Options>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { rule };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createEslintRule } from "../utils/index.mjs";
|
|
2
|
+
import { TSESTree } from "@typescript-eslint/types";
|
|
3
|
+
|
|
4
|
+
//#region src/rules/require-async-with-await.ts
|
|
5
|
+
const RULE_NAME = "require-async-with-await";
|
|
6
|
+
const rule = createEslintRule({
|
|
7
|
+
name: RULE_NAME,
|
|
8
|
+
meta: {
|
|
9
|
+
type: "problem",
|
|
10
|
+
docs: { description: "Require using async keyword with await." },
|
|
11
|
+
fixable: "code",
|
|
12
|
+
schema: [],
|
|
13
|
+
messages: { requireAsyncWithAwait: "Expect using async keyword with await." }
|
|
14
|
+
},
|
|
15
|
+
defaultOptions: [],
|
|
16
|
+
create: (context) => {
|
|
17
|
+
const functionNodeStack = [];
|
|
18
|
+
function setupNode(node) {
|
|
19
|
+
functionNodeStack.push(node);
|
|
20
|
+
}
|
|
21
|
+
function clearNode() {
|
|
22
|
+
functionNodeStack.pop();
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
"FunctionExpression": setupNode,
|
|
26
|
+
"FunctionExpression:exit": clearNode,
|
|
27
|
+
"FunctionDeclaration": setupNode,
|
|
28
|
+
"FunctionDeclaration:exit": clearNode,
|
|
29
|
+
"ArrowFunctionExpression": setupNode,
|
|
30
|
+
"ArrowFunctionExpression:exit": clearNode,
|
|
31
|
+
AwaitExpression() {
|
|
32
|
+
const node = functionNodeStack[functionNodeStack.length - 1];
|
|
33
|
+
if (!node || node.async) return;
|
|
34
|
+
let fixRange;
|
|
35
|
+
if (node.type === TSESTree.AST_NODE_TYPES.ArrowFunctionExpression) fixRange = node.range;
|
|
36
|
+
if (node.type === TSESTree.AST_NODE_TYPES.FunctionDeclaration || node.type === TSESTree.AST_NODE_TYPES.FunctionExpression) if (node.parent.type === TSESTree.AST_NODE_TYPES.Property || node.parent.type === TSESTree.AST_NODE_TYPES.MethodDefinition) {
|
|
37
|
+
if (node.parent.kind === "method" || node.parent.kind === "init") fixRange = node.parent.key.range;
|
|
38
|
+
} else fixRange = node.range;
|
|
39
|
+
if (fixRange) context.report({
|
|
40
|
+
node,
|
|
41
|
+
messageId: "requireAsyncWithAwait",
|
|
42
|
+
fix: (fixer) => fixer.insertTextBeforeRange(fixRange, "async ")
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
var require_async_with_await_default = rule;
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { require_async_with_await_default as default };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createEslintRule } from "../utils/index.mjs";
|
|
2
|
+
import { extractChunks, getImportExportItems, maybeReportSorting, printSortedItems, printWithSortedSpecifiers, sortImportExportItems } from "../utils/sort-imports.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/rules/sort-exports.ts
|
|
5
|
+
const RULE_NAME = "sort-exports";
|
|
6
|
+
function isParentWithBody(node) {
|
|
7
|
+
if (!node) return false;
|
|
8
|
+
if (!("body" in node)) return false;
|
|
9
|
+
const maybe = node;
|
|
10
|
+
return Array.isArray(maybe.body);
|
|
11
|
+
}
|
|
12
|
+
const isExportFrom = (node) => node.type === "ExportNamedDeclaration" && node.source != null || node.type === "ExportAllDeclaration" && node.source != null;
|
|
13
|
+
function isPartOfChunk(node, lastNode, sourceCode) {
|
|
14
|
+
if (!isExportFrom(node)) return "NotPartOfChunk";
|
|
15
|
+
return sourceCode.getCommentsBefore(node).some((comment) => (lastNode == null || comment.loc.start.line > lastNode.loc.end.line) && comment.loc.end.line < node.loc.start.line) ? "PartOfNewChunk" : "PartOfChunk";
|
|
16
|
+
}
|
|
17
|
+
const getSpecifiers = (exportNode) => exportNode.type === "ExportNamedDeclaration" ? exportNode.specifiers : [];
|
|
18
|
+
function maybeReportChunkSorting(chunk, context) {
|
|
19
|
+
const sourceCode = context.sourceCode;
|
|
20
|
+
const items = getImportExportItems(chunk.filter(isExportFrom), sourceCode, () => false, getSpecifiers);
|
|
21
|
+
const sorted = printSortedItems([[sortImportExportItems(items)]], items, sourceCode);
|
|
22
|
+
const start = items[0].start;
|
|
23
|
+
const end = items[items.length - 1].end;
|
|
24
|
+
maybeReportSorting(context, sorted, start, end);
|
|
25
|
+
}
|
|
26
|
+
function maybeReportExportSpecifierSorting(node, context) {
|
|
27
|
+
const sorted = printWithSortedSpecifiers(node, context.sourceCode, (n) => n.specifiers);
|
|
28
|
+
const [start, end] = node.range;
|
|
29
|
+
maybeReportSorting(context, sorted, start, end);
|
|
30
|
+
}
|
|
31
|
+
const rule = createEslintRule({
|
|
32
|
+
name: RULE_NAME,
|
|
33
|
+
meta: {
|
|
34
|
+
type: "layout",
|
|
35
|
+
docs: {
|
|
36
|
+
description: "Sort export declarations.",
|
|
37
|
+
url: "https://github.com/lydell/eslint-plugin-simple-import-sort#sort-order"
|
|
38
|
+
},
|
|
39
|
+
fixable: "code",
|
|
40
|
+
schema: [],
|
|
41
|
+
messages: { sort: "Run autofix to sort these exports!" }
|
|
42
|
+
},
|
|
43
|
+
defaultOptions: [],
|
|
44
|
+
create: (context) => {
|
|
45
|
+
const parents = /* @__PURE__ */ new Set();
|
|
46
|
+
function addParent(node) {
|
|
47
|
+
if (isExportFrom(node) && isParentWithBody(node.parent)) parents.add(node.parent);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
ExportNamedDeclaration(node) {
|
|
51
|
+
if (node.source == null && node.declaration == null) maybeReportExportSpecifierSorting(node, context);
|
|
52
|
+
else addParent(node);
|
|
53
|
+
},
|
|
54
|
+
ExportAllDeclaration(node) {
|
|
55
|
+
addParent(node);
|
|
56
|
+
},
|
|
57
|
+
"Program:exit"() {
|
|
58
|
+
const sourceCode = context.sourceCode;
|
|
59
|
+
for (const parent of parents) for (const chunk of extractChunks(parent, (node, lastNode) => isPartOfChunk(node, lastNode, sourceCode))) maybeReportChunkSorting(chunk, context);
|
|
60
|
+
parents.clear();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
var sort_exports_default = rule;
|
|
66
|
+
|
|
67
|
+
//#endregion
|
|
68
|
+
export { sort_exports_default as default };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
//#region src/rules/sort-imports.d.ts
|
|
4
|
+
type MessageIds = "sort";
|
|
5
|
+
type Options = [{
|
|
6
|
+
groups?: string[][];
|
|
7
|
+
}];
|
|
8
|
+
declare const rule: ESLintUtils.RuleModule<MessageIds, Options>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { rule };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createEslintRule } from "../utils/index.mjs";
|
|
2
|
+
import { extractChunks, getImportExportItems, isPunctuator, maybeReportSorting, printSortedItems, sortImportExportItems } from "../utils/sort-imports.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/rules/sort-imports.ts
|
|
5
|
+
const RULE_NAME = "sort-imports";
|
|
6
|
+
function isParentWithBody(node) {
|
|
7
|
+
if (!node) return false;
|
|
8
|
+
if (!("body" in node)) return false;
|
|
9
|
+
const maybe = node;
|
|
10
|
+
return Array.isArray(maybe.body);
|
|
11
|
+
}
|
|
12
|
+
const defaultGroups = [
|
|
13
|
+
["^node:"],
|
|
14
|
+
["^@?\\w"],
|
|
15
|
+
["^"],
|
|
16
|
+
["^\\."],
|
|
17
|
+
["^\\u0000"]
|
|
18
|
+
];
|
|
19
|
+
const isImportSpecifier = (node) => node.type === "ImportSpecifier";
|
|
20
|
+
const getSpecifiers = (importNode) => importNode.specifiers.filter(isImportSpecifier);
|
|
21
|
+
const isImport = (node) => node.type === "ImportDeclaration";
|
|
22
|
+
function isSideEffectImport(importNode, sourceCode) {
|
|
23
|
+
const token = sourceCode.getFirstToken(importNode, { skip: 1 });
|
|
24
|
+
const startsWithBrace = token != null && isPunctuator(token, "{");
|
|
25
|
+
return importNode.specifiers.length === 0 && (!importNode.importKind || importNode.importKind === "value") && !startsWithBrace;
|
|
26
|
+
}
|
|
27
|
+
const rule = createEslintRule({
|
|
28
|
+
name: RULE_NAME,
|
|
29
|
+
meta: {
|
|
30
|
+
type: "layout",
|
|
31
|
+
docs: {
|
|
32
|
+
description: "Sort import declarations.",
|
|
33
|
+
url: "https://github.com/lydell/eslint-plugin-simple-import-sort#sort-order"
|
|
34
|
+
},
|
|
35
|
+
fixable: "code",
|
|
36
|
+
schema: [{
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: { groups: {
|
|
39
|
+
type: "array",
|
|
40
|
+
items: {
|
|
41
|
+
type: "array",
|
|
42
|
+
items: { type: "string" }
|
|
43
|
+
}
|
|
44
|
+
} },
|
|
45
|
+
additionalProperties: false
|
|
46
|
+
}],
|
|
47
|
+
messages: { sort: "Run autofix to sort these imports!" }
|
|
48
|
+
},
|
|
49
|
+
defaultOptions: [{ groups: defaultGroups }],
|
|
50
|
+
create: (context) => {
|
|
51
|
+
const outerGroups = (context.options[0]?.groups ?? defaultGroups).map((groups) => groups.map((item) => new RegExp(item, "u")));
|
|
52
|
+
const parents = /* @__PURE__ */ new Set();
|
|
53
|
+
return {
|
|
54
|
+
ImportDeclaration(node) {
|
|
55
|
+
if (isParentWithBody(node.parent)) parents.add(node.parent);
|
|
56
|
+
},
|
|
57
|
+
"Program:exit"() {
|
|
58
|
+
const sourceCode = context.sourceCode;
|
|
59
|
+
for (const parent of parents) for (const chunk of extractChunks(parent, (node, lastNode) => {
|
|
60
|
+
if (!isImport(node)) return "NotPartOfChunk";
|
|
61
|
+
return lastNode != null && isImport(lastNode) && lastNode.loc.end.line === node.loc.start.line ? "PartOfNewChunk" : "PartOfChunk";
|
|
62
|
+
})) maybeReportChunkSorting(chunk, context, outerGroups, sourceCode);
|
|
63
|
+
parents.clear();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
var sort_imports_default = rule;
|
|
69
|
+
function maybeReportChunkSorting(chunk, context, outerGroups, sourceCode) {
|
|
70
|
+
const items = getImportExportItems(chunk.filter(isImport), sourceCode, isSideEffectImport, getSpecifiers);
|
|
71
|
+
const sorted = printSortedItems(makeSortedItems(items, outerGroups), items, sourceCode);
|
|
72
|
+
const start = items[0].start;
|
|
73
|
+
const end = items[items.length - 1].end;
|
|
74
|
+
maybeReportSorting(context, sorted, start, end);
|
|
75
|
+
}
|
|
76
|
+
function makeSortedItems(items, outerGroups) {
|
|
77
|
+
const itemGroups = outerGroups.map((groups) => groups.map((regex) => ({
|
|
78
|
+
regex,
|
|
79
|
+
items: []
|
|
80
|
+
})));
|
|
81
|
+
const rest = [];
|
|
82
|
+
for (const item of items) {
|
|
83
|
+
const { originalSource } = item.source;
|
|
84
|
+
const source = item.isSideEffectImport ? `\0${originalSource}` : item.source.kind === "value" ? originalSource : `${originalSource}\0`;
|
|
85
|
+
let matchedGroup;
|
|
86
|
+
let longestMatchLength = -1;
|
|
87
|
+
for (const groups of itemGroups) for (const group of groups) {
|
|
88
|
+
const match = group.regex.exec(source);
|
|
89
|
+
if (match != null && match[0].length > longestMatchLength) {
|
|
90
|
+
matchedGroup = group;
|
|
91
|
+
longestMatchLength = match[0].length;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (matchedGroup == null) rest.push(item);
|
|
95
|
+
else matchedGroup.items.push(item);
|
|
96
|
+
}
|
|
97
|
+
return [...itemGroups, [{
|
|
98
|
+
regex: /^/,
|
|
99
|
+
items: rest
|
|
100
|
+
}]].map((groups) => groups.filter((group) => group.items.length > 0)).filter((groups) => groups.length > 0).map((groups) => groups.map((group) => sortImportExportItems(group.items)));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
//#endregion
|
|
104
|
+
export { sort_imports_default as default };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
//#region src/rules/vue-root-element-sort-attributes.d.ts
|
|
4
|
+
type MessageIds = "wrongOrder";
|
|
5
|
+
type Options = [{
|
|
6
|
+
script?: string[];
|
|
7
|
+
[otherElement: string]: string[] | undefined;
|
|
8
|
+
}];
|
|
9
|
+
declare const rule: ESLintUtils.RuleModule<MessageIds, Options>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { rule };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createEslintRule } from "../utils/index.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/rules/vue-root-element-sort-attributes.ts
|
|
4
|
+
const RULE_NAME = "vue-root-element-sort-attributes";
|
|
5
|
+
const defaultOptions = { script: ["setup", "lang"] };
|
|
6
|
+
const rule = createEslintRule({
|
|
7
|
+
name: RULE_NAME,
|
|
8
|
+
meta: {
|
|
9
|
+
type: "layout",
|
|
10
|
+
docs: { description: "Sort attributes of root <script>, <template>, and <style> elements in Vue files." },
|
|
11
|
+
fixable: "code",
|
|
12
|
+
schema: [{
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: { script: {
|
|
15
|
+
type: "array",
|
|
16
|
+
items: { type: "string" }
|
|
17
|
+
} },
|
|
18
|
+
additionalProperties: true
|
|
19
|
+
}],
|
|
20
|
+
messages: { wrongOrder: "Attributes of root element should be sorted in a specific order." }
|
|
21
|
+
},
|
|
22
|
+
defaultOptions: [defaultOptions],
|
|
23
|
+
create(context, [options]) {
|
|
24
|
+
const order = {
|
|
25
|
+
...options,
|
|
26
|
+
script: options.script ?? defaultOptions.script
|
|
27
|
+
};
|
|
28
|
+
const sourceCode = context.sourceCode;
|
|
29
|
+
const documentFragment = sourceCode.parserServices.getDocumentFragment?.();
|
|
30
|
+
function getTopLevelHTMLElements() {
|
|
31
|
+
if (documentFragment) return documentFragment.children.filter((n) => n.type === "VElement");
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
return { Program: (_node) => {
|
|
35
|
+
const topLevelElements = getTopLevelHTMLElements();
|
|
36
|
+
for (const element of topLevelElements) {
|
|
37
|
+
if (!(element.name in order)) continue;
|
|
38
|
+
const expectedOrder = order[element.name];
|
|
39
|
+
const attributesToCheck = [];
|
|
40
|
+
let reprintAttributes = false;
|
|
41
|
+
for (const attribute of element.startTag.attributes) {
|
|
42
|
+
if (attribute.key.type !== "VIdentifier" || attribute.directive) continue;
|
|
43
|
+
if (expectedOrder.includes(attribute.key.name)) attributesToCheck.push(attribute);
|
|
44
|
+
}
|
|
45
|
+
const currentOrder = attributesToCheck.map((attr) => attr.key.name);
|
|
46
|
+
const expectedFilteredOrder = expectedOrder.filter((name) => currentOrder.includes(name));
|
|
47
|
+
if (JSON.stringify(currentOrder) !== JSON.stringify(expectedFilteredOrder)) reprintAttributes = true;
|
|
48
|
+
if (reprintAttributes) context.report({
|
|
49
|
+
node: element.startTag,
|
|
50
|
+
messageId: "wrongOrder",
|
|
51
|
+
*fix(fixer) {
|
|
52
|
+
const sortedAttributes = [...attributesToCheck].sort((a, b) => expectedOrder.indexOf(a.key.name) - expectedOrder.indexOf(b.key.name));
|
|
53
|
+
for (const [i, originalAttr] of attributesToCheck.entries()) {
|
|
54
|
+
const sortedAttr = sortedAttributes[i];
|
|
55
|
+
if (originalAttr.key.name !== sortedAttr.key.name) yield fixer.replaceText(originalAttr, sourceCode.getText(sortedAttr));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
} };
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
var vue_root_element_sort_attributes_default = rule;
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
export { vue_root_element_sort_attributes_default as default };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/index.ts
|
|
4
|
+
const createEslintRule = ESLintUtils.RuleCreator((ruleName) => ruleName);
|
|
5
|
+
function getSiblingNode(node, offset) {
|
|
6
|
+
if (!node) return;
|
|
7
|
+
const { parent } = node;
|
|
8
|
+
if (parent && "body" in parent) {
|
|
9
|
+
const { body } = parent;
|
|
10
|
+
if (!Array.isArray(body)) return;
|
|
11
|
+
const targetIndex = body.indexOf(node) + offset;
|
|
12
|
+
if (targetIndex >= 0 && targetIndex < body.length) return body[targetIndex];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const getPreviousNode = (node) => getSiblingNode(node, -1);
|
|
16
|
+
const getNextNode = (node) => getSiblingNode(node, 1);
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
export { createEslintRule, getNextNode, getPreviousNode };
|