@simplysm/lint 13.0.100 → 14.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -40
- package/dist/eslint-plugin.d.ts +12 -0
- package/dist/eslint-plugin.d.ts.map +1 -1
- package/dist/eslint-plugin.js +15 -10
- package/dist/eslint-plugin.js.map +1 -6
- package/dist/eslint-recommended.d.ts +2 -1
- package/dist/eslint-recommended.d.ts.map +1 -1
- package/dist/eslint-recommended.js +159 -242
- package/dist/eslint-recommended.js.map +1 -6
- package/dist/rules/ng-template-no-todo-comments.d.ts +12 -0
- package/dist/rules/ng-template-no-todo-comments.d.ts.map +1 -0
- package/dist/rules/ng-template-no-todo-comments.js +46 -0
- package/dist/rules/ng-template-no-todo-comments.js.map +1 -0
- package/dist/rules/ng-template-sd-require-binding-attrs.d.ts +18 -0
- package/dist/rules/ng-template-sd-require-binding-attrs.d.ts.map +1 -0
- package/dist/rules/ng-template-sd-require-binding-attrs.js +88 -0
- package/dist/rules/ng-template-sd-require-binding-attrs.js.map +1 -0
- package/dist/rules/no-hard-private.d.ts +6 -6
- package/dist/rules/no-hard-private.js +109 -88
- package/dist/rules/no-hard-private.js.map +1 -6
- package/dist/rules/no-subpath-imports-from-simplysm.d.ts +5 -5
- package/dist/rules/no-subpath-imports-from-simplysm.js +72 -60
- package/dist/rules/no-subpath-imports-from-simplysm.js.map +1 -6
- package/dist/rules/ts-no-throw-not-implemented-error.d.ts +5 -5
- package/dist/rules/ts-no-throw-not-implemented-error.js +92 -58
- package/dist/rules/ts-no-throw-not-implemented-error.js.map +1 -6
- package/dist/rules/ts-no-unused-injects.d.ts +13 -0
- package/dist/rules/ts-no-unused-injects.d.ts.map +1 -0
- package/dist/rules/ts-no-unused-injects.js +81 -0
- package/dist/rules/ts-no-unused-injects.js.map +1 -0
- package/dist/rules/ts-no-unused-protected-readonly.d.ts +13 -0
- package/dist/rules/ts-no-unused-protected-readonly.d.ts.map +1 -0
- package/dist/rules/ts-no-unused-protected-readonly.js +127 -0
- package/dist/rules/ts-no-unused-protected-readonly.js.map +1 -0
- package/dist/utils/create-rule.d.ts +3 -3
- package/dist/utils/create-rule.js +19 -7
- package/dist/utils/create-rule.js.map +1 -6
- package/package.json +10 -11
- package/src/eslint-plugin.ts +8 -0
- package/src/eslint-recommended.ts +43 -116
- package/src/rules/ng-template-no-todo-comments.ts +48 -0
- package/src/rules/ng-template-sd-require-binding-attrs.ts +111 -0
- package/src/rules/no-hard-private.ts +23 -23
- package/src/rules/no-subpath-imports-from-simplysm.ts +13 -13
- package/src/rules/ts-no-throw-not-implemented-error.ts +14 -14
- package/src/rules/ts-no-unused-injects.ts +88 -0
- package/src/rules/ts-no-unused-protected-readonly.ts +151 -0
- package/src/utils/create-rule.ts +3 -3
- package/tests/no-hard-private.spec.ts +0 -888
- package/tests/no-subpath-imports-from-simplysm.spec.ts +0 -311
- package/tests/recommended.spec.ts +0 -145
- package/tests/ts-no-throw-not-implemented-error.spec.ts +0 -245
- package/tests/vitest.setup.ts +0 -10
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
import { createRule } from "../utils/create-rule.js";
|
|
3
|
+
function traverseNode(node, callback) {
|
|
4
|
+
callback(node);
|
|
5
|
+
for (const key of Object.keys(node)) {
|
|
6
|
+
if (key === "parent")
|
|
7
|
+
continue;
|
|
8
|
+
const child = node[key];
|
|
9
|
+
if (Array.isArray(child)) {
|
|
10
|
+
for (const c of child) {
|
|
11
|
+
if (c != null && typeof c === "object" && "type" in c) {
|
|
12
|
+
traverseNode(c, callback);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else if (child != null && typeof child === "object" && "type" in child) {
|
|
17
|
+
traverseNode(child, callback);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 미사용 Angular `inject()` 필드를 감지하는 ESLint 규칙.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* 클래스 내에서 `inject()` 호출로 초기화된 프로퍼티 중
|
|
26
|
+
* 같은 클래스 내 다른 곳에서 참조되지 않는 필드를 보고합니다.
|
|
27
|
+
* autofix로 해당 필드를 제거합니다.
|
|
28
|
+
*/
|
|
29
|
+
export default createRule({
|
|
30
|
+
name: "ts-no-unused-injects",
|
|
31
|
+
meta: {
|
|
32
|
+
type: "problem",
|
|
33
|
+
docs: {
|
|
34
|
+
description: "Disallow unused Angular inject() fields",
|
|
35
|
+
},
|
|
36
|
+
fixable: "code",
|
|
37
|
+
messages: {
|
|
38
|
+
unusedInject: 'inject() field "{{name}}" is never used.',
|
|
39
|
+
},
|
|
40
|
+
schema: [],
|
|
41
|
+
},
|
|
42
|
+
defaultOptions: [],
|
|
43
|
+
create(context) {
|
|
44
|
+
const sourceCode = context.sourceCode;
|
|
45
|
+
return {
|
|
46
|
+
ClassBody(classBody) {
|
|
47
|
+
const injectFields = classBody.body.filter((node) => node.type === AST_NODE_TYPES.PropertyDefinition &&
|
|
48
|
+
node.value != null &&
|
|
49
|
+
node.value.type === AST_NODE_TYPES.CallExpression &&
|
|
50
|
+
node.value.callee.type === AST_NODE_TYPES.Identifier &&
|
|
51
|
+
node.value.callee.name === "inject" &&
|
|
52
|
+
node.key.type === AST_NODE_TYPES.Identifier);
|
|
53
|
+
for (const field of injectFields) {
|
|
54
|
+
const fieldName = field.key.name;
|
|
55
|
+
const allIdentifiers = [];
|
|
56
|
+
traverseNode(classBody, (node) => {
|
|
57
|
+
if (node.type === AST_NODE_TYPES.Identifier && node.name === fieldName) {
|
|
58
|
+
allIdentifiers.push(node);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const references = allIdentifiers.filter((id) => id !== field.key);
|
|
62
|
+
if (references.length === 0) {
|
|
63
|
+
context.report({
|
|
64
|
+
node: field,
|
|
65
|
+
messageId: "unusedInject",
|
|
66
|
+
data: { name: fieldName },
|
|
67
|
+
fix(fixer) {
|
|
68
|
+
const tokenBefore = sourceCode.getTokenBefore(field);
|
|
69
|
+
const tokenAfter = sourceCode.getTokenAfter(field);
|
|
70
|
+
const start = tokenBefore ? tokenBefore.range[1] : field.range[0];
|
|
71
|
+
const end = tokenAfter ? field.range[1] : field.range[1];
|
|
72
|
+
return fixer.removeRange([start, end]);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=ts-no-unused-injects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ts-no-unused-injects.js","sourceRoot":"","sources":["../../src/rules/ts-no-unused-injects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Angular `@Component` 내 미사용 `protected readonly` 필드를 감지하는 ESLint 규칙.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* `@Component` 데코레이터가 있는 클래스에서 `protected readonly` 필드가
|
|
6
|
+
* 인라인 템플릿과 클래스 본문 어디에서도 참조되지 않으면 보고합니다.
|
|
7
|
+
* autofix로 해당 필드를 제거합니다.
|
|
8
|
+
*/
|
|
9
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"unusedField", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
10
|
+
name: string;
|
|
11
|
+
};
|
|
12
|
+
export default _default;
|
|
13
|
+
//# sourceMappingURL=ts-no-unused-protected-readonly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ts-no-unused-protected-readonly.d.ts","sourceRoot":"","sources":["../../src/rules/ts-no-unused-protected-readonly.ts"],"names":[],"mappings":"AA2BA;;;;;;;GAOG;;;;AACH,wBAmHG"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
import { createRule } from "../utils/create-rule.js";
|
|
3
|
+
function traverseNode(node, callback) {
|
|
4
|
+
callback(node);
|
|
5
|
+
for (const key of Object.keys(node)) {
|
|
6
|
+
if (key === "parent" || key === "range" || key === "loc")
|
|
7
|
+
continue;
|
|
8
|
+
const child = node[key];
|
|
9
|
+
if (Array.isArray(child)) {
|
|
10
|
+
for (const c of child) {
|
|
11
|
+
if (c != null && typeof c === "object" && "type" in c) {
|
|
12
|
+
traverseNode(c, callback);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else if (child != null && typeof child === "object" && "type" in child) {
|
|
17
|
+
traverseNode(child, callback);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function escapeRegExp(string) {
|
|
22
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Angular `@Component` 내 미사용 `protected readonly` 필드를 감지하는 ESLint 규칙.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* `@Component` 데코레이터가 있는 클래스에서 `protected readonly` 필드가
|
|
29
|
+
* 인라인 템플릿과 클래스 본문 어디에서도 참조되지 않으면 보고합니다.
|
|
30
|
+
* autofix로 해당 필드를 제거합니다.
|
|
31
|
+
*/
|
|
32
|
+
export default createRule({
|
|
33
|
+
name: "ts-no-unused-protected-readonly",
|
|
34
|
+
meta: {
|
|
35
|
+
type: "problem",
|
|
36
|
+
docs: {
|
|
37
|
+
description: "Disallow unused protected readonly fields in Angular components",
|
|
38
|
+
},
|
|
39
|
+
fixable: "code",
|
|
40
|
+
messages: {
|
|
41
|
+
unusedField: 'Protected readonly field "{{name}}" is not used in class or template.',
|
|
42
|
+
},
|
|
43
|
+
schema: [],
|
|
44
|
+
},
|
|
45
|
+
defaultOptions: [],
|
|
46
|
+
create(context) {
|
|
47
|
+
const sourceCode = context.sourceCode;
|
|
48
|
+
return {
|
|
49
|
+
"ClassDeclaration, ClassExpression"(classNode) {
|
|
50
|
+
const componentDecorator = classNode.decorators.find((d) => {
|
|
51
|
+
if (d.expression.type === AST_NODE_TYPES.CallExpression) {
|
|
52
|
+
const callee = d.expression.callee;
|
|
53
|
+
return callee.type === AST_NODE_TYPES.Identifier && callee.name === "Component";
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
});
|
|
57
|
+
if (componentDecorator == null)
|
|
58
|
+
return;
|
|
59
|
+
const expr = componentDecorator.expression;
|
|
60
|
+
const args = expr.arguments;
|
|
61
|
+
const firstArg = args.at(0);
|
|
62
|
+
if (firstArg == null || firstArg.type !== AST_NODE_TYPES.ObjectExpression)
|
|
63
|
+
return;
|
|
64
|
+
const templateProp = firstArg.properties.find((p) => p.type === AST_NODE_TYPES.Property &&
|
|
65
|
+
p.key.type === AST_NODE_TYPES.Identifier &&
|
|
66
|
+
p.key.name === "template");
|
|
67
|
+
if (templateProp == null)
|
|
68
|
+
return;
|
|
69
|
+
let templateText = "";
|
|
70
|
+
const templateValue = templateProp.value;
|
|
71
|
+
if (templateValue.type === AST_NODE_TYPES.TemplateLiteral) {
|
|
72
|
+
templateText = templateValue.quasis.map((q) => q.value.raw).join("");
|
|
73
|
+
}
|
|
74
|
+
else if (templateValue.type === AST_NODE_TYPES.Literal &&
|
|
75
|
+
typeof templateValue.value === "string") {
|
|
76
|
+
templateText = templateValue.value;
|
|
77
|
+
}
|
|
78
|
+
if (templateText === "")
|
|
79
|
+
return;
|
|
80
|
+
const protectedReadonlyFields = classNode.body.body.filter((node) => node.type === AST_NODE_TYPES.PropertyDefinition &&
|
|
81
|
+
node.accessibility === "protected" &&
|
|
82
|
+
node.readonly === true &&
|
|
83
|
+
!node.static &&
|
|
84
|
+
node.key.type === AST_NODE_TYPES.Identifier);
|
|
85
|
+
for (const field of protectedReadonlyFields) {
|
|
86
|
+
const fieldName = field.key.name;
|
|
87
|
+
const identifierPattern = new RegExp(`(?<![a-zA-Z0-9_$])${escapeRegExp(fieldName)}(?![a-zA-Z0-9_$])`);
|
|
88
|
+
const usedInTemplate = identifierPattern.test(templateText);
|
|
89
|
+
const usedInClass = classNode.body.body.some((member) => {
|
|
90
|
+
if (member === field)
|
|
91
|
+
return false;
|
|
92
|
+
let found = false;
|
|
93
|
+
traverseNode(member, (node) => {
|
|
94
|
+
if (node.type === AST_NODE_TYPES.Identifier && node.name === fieldName) {
|
|
95
|
+
found = true;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return found;
|
|
99
|
+
});
|
|
100
|
+
if (!usedInTemplate && !usedInClass) {
|
|
101
|
+
context.report({
|
|
102
|
+
node: field,
|
|
103
|
+
messageId: "unusedField",
|
|
104
|
+
data: { name: fieldName },
|
|
105
|
+
fix(fixer) {
|
|
106
|
+
let start = field.range[0];
|
|
107
|
+
let end = field.range[1];
|
|
108
|
+
const textBefore = sourceCode.text.slice(0, start);
|
|
109
|
+
const leadingMatch = textBefore.match(/\n[ \t]*$/);
|
|
110
|
+
if (leadingMatch) {
|
|
111
|
+
start -= leadingMatch[0].length - 1;
|
|
112
|
+
}
|
|
113
|
+
const afterText = sourceCode.text.slice(end);
|
|
114
|
+
const trailingMatch = afterText.match(/^;?[ \t]*\r?\n/);
|
|
115
|
+
if (trailingMatch) {
|
|
116
|
+
end += trailingMatch[0].length;
|
|
117
|
+
}
|
|
118
|
+
return fixer.removeRange([start, end]);
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=ts-no-unused-protected-readonly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ts-no-unused-protected-readonly.js","sourceRoot":"","sources":["../../src/rules/ts-no-unused-protected-readonly.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAiB,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,SAAS,YAAY,CACnB,IAAmB,EACnB,QAAoC;IAEpC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACf,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,KAAK;YAAE,SAAS;QACnE,MAAM,KAAK,GAAI,IAA2C,CAAC,GAAG,CAAC,CAAC;QAChE,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,IAAI,CAAC,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;oBACtD,YAAY,CAAC,CAAkB,EAAE,QAAQ,CAAC,CAAC;gBAC7C,CAAC;YACH,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;YACzE,YAAY,CAAC,KAAsB,EAAE,QAAQ,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,MAAc;IAClC,OAAO,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;GAOG;AACH,eAAe,UAAU,CAAC;IACxB,IAAI,EAAE,iCAAiC;IACvC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,iEAAiE;SAC/E;QACD,OAAO,EAAE,MAAM;QACf,QAAQ,EAAE;YACR,WAAW,EAAE,uEAAuE;SACrF;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QAEtC,OAAO;YACL,mCAAmC,CACjC,SAA+D;gBAE/D,MAAM,kBAAkB,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;oBACzD,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,KAAK,cAAc,CAAC,cAAc,EAAE,CAAC;wBACxD,MAAM,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;wBACnC,OAAO,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,CAAC;oBAClF,CAAC;oBACD,OAAO,KAAK,CAAC;gBACf,CAAC,CAAC,CAAC;gBAEH,IAAI,kBAAkB,IAAI,IAAI;oBAAE,OAAO;gBAEvC,MAAM,IAAI,GAAG,kBAAkB,CAAC,UAAqC,CAAC;gBACtE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;gBAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC5B,IAAI,QAAQ,IAAI,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,gBAAgB;oBAAE,OAAO;gBAElF,MAAM,YAAY,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAC3C,CAAC,CAAC,EAA0B,EAAE,CAC5B,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,QAAQ;oBAClC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;oBACxC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,UAAU,CAC5B,CAAC;gBAEF,IAAI,YAAY,IAAI,IAAI;oBAAE,OAAO;gBAEjC,IAAI,YAAY,GAAG,EAAE,CAAC;gBACtB,MAAM,aAAa,GAAG,YAAY,CAAC,KAAK,CAAC;gBACzC,IAAI,aAAa,CAAC,IAAI,KAAK,cAAc,CAAC,eAAe,EAAE,CAAC;oBAC1D,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACvE,CAAC;qBAAM,IACL,aAAa,CAAC,IAAI,KAAK,cAAc,CAAC,OAAO;oBAC7C,OAAO,aAAa,CAAC,KAAK,KAAK,QAAQ,EACvC,CAAC;oBACD,YAAY,GAAG,aAAa,CAAC,KAAK,CAAC;gBACrC,CAAC;gBAED,IAAI,YAAY,KAAK,EAAE;oBAAE,OAAO;gBAEhC,MAAM,uBAAuB,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CACxD,CAAC,IAAI,EAAuC,EAAE,CAC5C,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,kBAAkB;oBAC/C,IAAI,CAAC,aAAa,KAAK,WAAW;oBAClC,IAAI,CAAC,QAAQ,KAAK,IAAI;oBACtB,CAAC,IAAI,CAAC,MAAM;oBACZ,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,CAC9C,CAAC;gBAEF,KAAK,MAAM,KAAK,IAAI,uBAAuB,EAAE,CAAC;oBAC5C,MAAM,SAAS,GAAI,KAAK,CAAC,GAA2B,CAAC,IAAI,CAAC;oBAE1D,MAAM,iBAAiB,GAAG,IAAI,MAAM,CAClC,qBAAqB,YAAY,CAAC,SAAS,CAAC,mBAAmB,CAChE,CAAC;oBACF,MAAM,cAAc,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;oBAE5D,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;wBACtD,IAAI,MAAM,KAAK,KAAK;4BAAE,OAAO,KAAK,CAAC;wBACnC,IAAI,KAAK,GAAG,KAAK,CAAC;wBAClB,YAAY,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;4BAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gCACvE,KAAK,GAAG,IAAI,CAAC;4BACf,CAAC;wBACH,CAAC,CAAC,CAAC;wBACH,OAAO,KAAK,CAAC;oBACf,CAAC,CAAC,CAAC;oBAEH,IAAI,CAAC,cAAc,IAAI,CAAC,WAAW,EAAE,CAAC;wBACpC,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI,EAAE,KAAK;4BACX,SAAS,EAAE,aAAa;4BACxB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;4BACzB,GAAG,CAAC,KAAK;gCACP,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gCAC3B,IAAI,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gCAEzB,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gCACnD,MAAM,YAAY,GAAG,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;gCACnD,IAAI,YAAY,EAAE,CAAC;oCACjB,KAAK,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;gCACtC,CAAC;gCAED,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gCAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;gCACxD,IAAI,aAAa,EAAE,CAAC;oCAClB,GAAG,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;gCACjC,CAAC;gCAED,OAAO,KAAK,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;4BACzC,CAAC;yBACF,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* ESLint 규칙을 생성하는 팩토리 함수.
|
|
4
4
|
*
|
|
5
5
|
* @remarks
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* `@typescript-eslint/utils`의 `RuleCreator`를 래핑하며
|
|
7
|
+
* 규칙 문서 URL을 자동으로 생성합니다.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* ```typescript
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
/**
|
|
3
|
+
* ESLint 규칙을 생성하는 팩토리 함수.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* `@typescript-eslint/utils`의 `RuleCreator`를 래핑하며
|
|
7
|
+
* 규칙 문서 URL을 자동으로 생성합니다.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* export default createRule({
|
|
12
|
+
* name: "my-rule",
|
|
13
|
+
* meta: { ... },
|
|
14
|
+
* defaultOptions: [],
|
|
15
|
+
* create(context) { ... },
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/kslhunter/simplysm/blob/master/packages/eslint-plugin/README.md#${name}`);
|
|
20
|
+
//# sourceMappingURL=create-rule.js.map
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/utils/create-rule.ts"],
|
|
4
|
-
"mappings": "AAAA,SAAS,mBAAmB;AAmBrB,MAAM,aAAa,YAAY;AAAA,EACpC,CAAC,SACC,sFAAsF,IAAI;AAC9F;",
|
|
5
|
-
"names": []
|
|
6
|
-
}
|
|
1
|
+
{"version":3,"file":"create-rule.js","sourceRoot":"","sources":["../../src/utils/create-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CAC/C,CAAC,IAAI,EAAE,EAAE,CACP,sFAAsF,IAAI,EAAE,CAC/F,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/lint",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"author": "
|
|
3
|
+
"version": "14.0.4",
|
|
4
|
+
"description": "심플리즘 패키지 - ESLint plugin",
|
|
5
|
+
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
"type": "module",
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
15
|
-
"src"
|
|
16
|
-
"tests"
|
|
15
|
+
"src"
|
|
17
16
|
],
|
|
18
17
|
"sideEffects": false,
|
|
19
18
|
"exports": {
|
|
@@ -27,19 +26,19 @@
|
|
|
27
26
|
}
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
|
-
"@
|
|
29
|
+
"@angular-eslint/utils": "^21.3.1",
|
|
30
|
+
"@typescript-eslint/utils": "^8.57.2",
|
|
31
|
+
"angular-eslint": "^21.3.1",
|
|
31
32
|
"eslint": "^9.39.4",
|
|
32
33
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
33
34
|
"eslint-plugin-import": "^2.32.0",
|
|
34
|
-
"eslint-plugin-solid": "^0.14.5",
|
|
35
|
-
"eslint-plugin-tailwindcss": "^3.18.2",
|
|
36
35
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
37
36
|
"globals": "^17.4.0",
|
|
38
37
|
"typescript": "^5.9.3",
|
|
39
|
-
"typescript-eslint": "^8.57.
|
|
38
|
+
"typescript-eslint": "^8.57.2"
|
|
40
39
|
},
|
|
41
40
|
"devDependencies": {
|
|
42
|
-
"@types/
|
|
43
|
-
"@typescript-eslint/rule-tester": "^8.57.
|
|
41
|
+
"@types/node": "^20.19.37",
|
|
42
|
+
"@typescript-eslint/rule-tester": "^8.57.2"
|
|
44
43
|
}
|
|
45
44
|
}
|
package/src/eslint-plugin.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
import ngTemplateNoTodoComments from "./rules/ng-template-no-todo-comments";
|
|
2
|
+
import ngTemplateSdRequireBindingAttrs from "./rules/ng-template-sd-require-binding-attrs";
|
|
1
3
|
import noHardPrivate from "./rules/no-hard-private";
|
|
2
4
|
import noSubpathImportsFromSimplysm from "./rules/no-subpath-imports-from-simplysm";
|
|
3
5
|
import tsNoThrowNotImplementedError from "./rules/ts-no-throw-not-implemented-error";
|
|
6
|
+
import tsNoUnusedInjects from "./rules/ts-no-unused-injects";
|
|
7
|
+
import tsNoUnusedProtectedReadonly from "./rules/ts-no-unused-protected-readonly";
|
|
4
8
|
|
|
5
9
|
export default {
|
|
6
10
|
rules: {
|
|
11
|
+
"ng-template-no-todo-comments": ngTemplateNoTodoComments,
|
|
12
|
+
"ng-template-sd-require-binding-attrs": ngTemplateSdRequireBindingAttrs,
|
|
7
13
|
"no-hard-private": noHardPrivate,
|
|
8
14
|
"no-subpath-imports-from-simplysm": noSubpathImportsFromSimplysm,
|
|
9
15
|
"ts-no-throw-not-implemented-error": tsNoThrowNotImplementedError,
|
|
16
|
+
"ts-no-unused-injects": tsNoUnusedInjects,
|
|
17
|
+
"ts-no-unused-protected-readonly": tsNoUnusedProtectedReadonly,
|
|
10
18
|
},
|
|
11
19
|
};
|
|
@@ -1,26 +1,13 @@
|
|
|
1
|
-
import globals from "globals";
|
|
2
1
|
import tseslint, { type FlatConfig } from "typescript-eslint";
|
|
2
|
+
import angular from "angular-eslint";
|
|
3
|
+
import globals from "globals";
|
|
3
4
|
import plugin from "./eslint-plugin";
|
|
4
5
|
import importPlugin from "eslint-plugin-import";
|
|
5
6
|
import unusedImportsPlugin from "eslint-plugin-unused-imports";
|
|
6
|
-
import solidPlugin from "eslint-plugin-solid";
|
|
7
|
-
import tailwindcssPlugin from "eslint-plugin-tailwindcss";
|
|
8
|
-
import { defineConfig, globalIgnores } from "eslint/config";
|
|
9
7
|
import { ESLint } from "eslint";
|
|
10
8
|
import { fileURLToPath } from "url";
|
|
11
9
|
|
|
12
|
-
//#region Common rules configuration
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Common rules for JS/TS
|
|
16
|
-
* - no-console: Prohibit console usage in production code (prevent performance degradation)
|
|
17
|
-
* - no-warning-comments: Warn about TODO/FIXME comments (to check for incomplete code)
|
|
18
|
-
* - eqeqeq: Enforce `===` usage (allow `== null` for null checks)
|
|
19
|
-
* - no-self-compare: Prevent typos like `x === x`
|
|
20
|
-
* - array-callback-return: Prevent missing return in map/filter, etc.
|
|
21
|
-
*/
|
|
22
10
|
const commonRules: FlatConfig.Rules = {
|
|
23
|
-
"no-console": "error",
|
|
24
11
|
"no-warning-comments": "warn",
|
|
25
12
|
"eqeqeq": ["error", "always", { null: "ignore" }],
|
|
26
13
|
"no-self-compare": "error",
|
|
@@ -32,7 +19,7 @@ const noNodeBuiltinsRules: FlatConfig.Rules = {
|
|
|
32
19
|
"error",
|
|
33
20
|
{
|
|
34
21
|
name: "Buffer",
|
|
35
|
-
message: "
|
|
22
|
+
message: "Uint8Array를 사용하세요. 복잡한 연산에는 @simplysm/core-common의 BytesUtils를 사용하세요.",
|
|
36
23
|
},
|
|
37
24
|
],
|
|
38
25
|
"no-restricted-imports": [
|
|
@@ -42,26 +29,21 @@ const noNodeBuiltinsRules: FlatConfig.Rules = {
|
|
|
42
29
|
{
|
|
43
30
|
name: "buffer",
|
|
44
31
|
message:
|
|
45
|
-
"
|
|
32
|
+
"Uint8Array를 사용하세요. 복잡한 연산에는 @simplysm/core-common의 BytesUtils를 사용하세요.",
|
|
46
33
|
},
|
|
47
34
|
{
|
|
48
35
|
name: "events",
|
|
49
|
-
message: "
|
|
36
|
+
message: "@simplysm/core-common의 EventEmitter를 사용하세요.",
|
|
50
37
|
},
|
|
51
38
|
{
|
|
52
39
|
name: "eventemitter3",
|
|
53
|
-
message: "
|
|
40
|
+
message: "@simplysm/core-common의 EventEmitter를 사용하세요.",
|
|
54
41
|
},
|
|
55
42
|
],
|
|
56
43
|
},
|
|
57
44
|
],
|
|
58
45
|
};
|
|
59
46
|
|
|
60
|
-
/**
|
|
61
|
-
* Unused import handling rules
|
|
62
|
-
* - Auto-remove unused imports
|
|
63
|
-
* - Allow unused variables/parameters with `_` prefix (e.g., `_unused`)
|
|
64
|
-
*/
|
|
65
47
|
const unusedImportsRules: FlatConfig.Rules = {
|
|
66
48
|
"unused-imports/no-unused-imports": "error",
|
|
67
49
|
"unused-imports/no-unused-vars": [
|
|
@@ -75,29 +57,27 @@ const unusedImportsRules: FlatConfig.Rules = {
|
|
|
75
57
|
],
|
|
76
58
|
};
|
|
77
59
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
60
|
+
export default tseslint.config(
|
|
61
|
+
{
|
|
62
|
+
ignores: [
|
|
63
|
+
// directory/** 형태의 순회 자체를 건너뜀
|
|
64
|
+
"**/node_modules/**",
|
|
65
|
+
"**/dist/**",
|
|
66
|
+
"**/.*/**",
|
|
67
|
+
"**/_*/**",
|
|
68
|
+
],
|
|
69
|
+
},
|
|
88
70
|
{
|
|
89
71
|
languageOptions: {
|
|
90
|
-
|
|
91
|
-
...globals.node,
|
|
92
|
-
...globals.es2024,
|
|
93
|
-
...globals.browser,
|
|
94
|
-
},
|
|
95
|
-
ecmaVersion: 2024,
|
|
72
|
+
ecmaVersion: "latest",
|
|
96
73
|
sourceType: "module",
|
|
97
74
|
},
|
|
98
75
|
},
|
|
99
76
|
{
|
|
100
|
-
files: ["**/*.js", "**/*.
|
|
77
|
+
files: ["**/*.js", "**/*.mjs", "**/*.cjs"],
|
|
78
|
+
languageOptions: {
|
|
79
|
+
globals: globals.node,
|
|
80
|
+
},
|
|
101
81
|
plugins: {
|
|
102
82
|
"import": importPlugin,
|
|
103
83
|
"@simplysm": plugin as unknown as ESLint.Plugin,
|
|
@@ -126,15 +106,17 @@ export default defineConfig([
|
|
|
126
106
|
},
|
|
127
107
|
],
|
|
128
108
|
|
|
129
|
-
//
|
|
109
|
+
// JS/TS 공통 규칙
|
|
130
110
|
"@simplysm/no-subpath-imports-from-simplysm": "error",
|
|
131
111
|
"@simplysm/no-hard-private": "error",
|
|
132
112
|
|
|
133
113
|
...noNodeBuiltinsRules,
|
|
134
114
|
},
|
|
135
115
|
},
|
|
116
|
+
...angular.configs.tsRecommended,
|
|
136
117
|
{
|
|
137
|
-
files: ["**/*.ts"
|
|
118
|
+
files: ["**/*.ts"],
|
|
119
|
+
processor: angular.processInlineTemplates,
|
|
138
120
|
plugins: {
|
|
139
121
|
"@typescript-eslint": tseslint.plugin,
|
|
140
122
|
"@simplysm": plugin as unknown as ESLint.Plugin,
|
|
@@ -156,6 +138,7 @@ export default defineConfig([
|
|
|
156
138
|
},
|
|
157
139
|
rules: {
|
|
158
140
|
...commonRules,
|
|
141
|
+
"no-console": "error",
|
|
159
142
|
|
|
160
143
|
"@typescript-eslint/require-await": "error",
|
|
161
144
|
"@typescript-eslint/await-thenable": "error",
|
|
@@ -187,99 +170,43 @@ export default defineConfig([
|
|
|
187
170
|
],
|
|
188
171
|
"@typescript-eslint/prefer-readonly": "error",
|
|
189
172
|
|
|
190
|
-
// Prevent mistakes: passing async function to void callback (prevent error loss)
|
|
191
|
-
// - arguments: false → allow socket.on("event", async () => {}) (handled with internal try-catch)
|
|
192
|
-
// - attributes: false → allow JSX event handlers (SolidJS compatible)
|
|
193
173
|
"@typescript-eslint/no-misused-promises": [
|
|
194
174
|
"error",
|
|
195
|
-
{ checksVoidReturn: { arguments: false
|
|
175
|
+
{ checksVoidReturn: { arguments: false } },
|
|
196
176
|
],
|
|
197
|
-
// Prevent mistakes: throwing non-Error objects (prevent stack trace loss)
|
|
198
177
|
"@typescript-eslint/only-throw-error": "error",
|
|
199
|
-
// Prevent mistakes: using delete on arrays (prevent sparse array bugs)
|
|
200
178
|
"@typescript-eslint/no-array-delete": "error",
|
|
201
179
|
|
|
202
180
|
"@simplysm/no-hard-private": "error",
|
|
203
181
|
"@simplysm/no-subpath-imports-from-simplysm": "error",
|
|
204
182
|
"@simplysm/ts-no-throw-not-implemented-error": "warn",
|
|
183
|
+
"@simplysm/ts-no-unused-injects": "error",
|
|
184
|
+
"@simplysm/ts-no-unused-protected-readonly": "error",
|
|
185
|
+
"@angular-eslint/no-output-native": "off",
|
|
205
186
|
|
|
206
187
|
...unusedImportsRules,
|
|
207
188
|
...noNodeBuiltinsRules,
|
|
208
189
|
|
|
209
|
-
"import/no-extraneous-dependencies":
|
|
210
|
-
"error",
|
|
211
|
-
{
|
|
212
|
-
devDependencies: [
|
|
213
|
-
"**/lib/**",
|
|
214
|
-
"**/eslint.config.ts",
|
|
215
|
-
"**/simplysm.ts",
|
|
216
|
-
"**/vitest.config.ts",
|
|
217
|
-
"**/vitest-e2e.config.ts",
|
|
218
|
-
"**/vitest.setup.ts",
|
|
219
|
-
"**/vitest.setup.ts",
|
|
220
|
-
],
|
|
221
|
-
},
|
|
222
|
-
],
|
|
190
|
+
"import/no-extraneous-dependencies": "error",
|
|
223
191
|
},
|
|
224
192
|
},
|
|
225
|
-
// Test folders: allow root devDependencies (vitest, etc.)
|
|
226
193
|
{
|
|
227
|
-
files: [
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"
|
|
231
|
-
|
|
232
|
-
],
|
|
194
|
+
files: ["**/*.html"],
|
|
195
|
+
extends: [...angular.configs.templateRecommended, ...angular.configs.templateAccessibility],
|
|
196
|
+
plugins: {
|
|
197
|
+
"@simplysm": plugin as unknown as ESLint.Plugin,
|
|
198
|
+
},
|
|
233
199
|
rules: {
|
|
234
|
-
"no-
|
|
235
|
-
"
|
|
236
|
-
"@simplysm/ts-no-throw-not-implemented-error": "off",
|
|
200
|
+
"@simplysm/ng-template-no-todo-comments": "warn",
|
|
201
|
+
"@simplysm/ng-template-sd-require-binding-attrs": "error",
|
|
237
202
|
},
|
|
238
203
|
},
|
|
239
|
-
// SolidJS TSX files: explicitly configure all rules (error)
|
|
240
204
|
{
|
|
241
|
-
files: ["
|
|
242
|
-
plugins: {
|
|
243
|
-
solid: solidPlugin as unknown as ESLint.Plugin,
|
|
244
|
-
tailwindcss: tailwindcssPlugin as unknown as ESLint.Plugin,
|
|
245
|
-
},
|
|
246
|
-
settings: {
|
|
247
|
-
tailwindcss: {
|
|
248
|
-
// Support template literal tags: recognize clsx`py-0.5 px-1.5` form
|
|
249
|
-
tags: ["clsx"],
|
|
250
|
-
},
|
|
251
|
-
},
|
|
205
|
+
files: ["**/tests/**/*.ts"],
|
|
252
206
|
rules: {
|
|
253
|
-
|
|
254
|
-
"
|
|
255
|
-
"
|
|
256
|
-
"solid/components-return-once": "error", // early return → bugs
|
|
257
|
-
"solid/jsx-no-duplicate-props": "error", // Duplicate props
|
|
258
|
-
"solid/jsx-no-undef": ["error", { typescriptEnabled: true }], // Undefined variables
|
|
259
|
-
"solid/no-react-deps": "error", // React dependency array mistakes
|
|
260
|
-
"solid/no-react-specific-props": "error", // React props mistakes (className, etc.)
|
|
261
|
-
|
|
262
|
-
// ─── Security ───
|
|
263
|
-
"solid/no-innerhtml": "error", // Prevent XSS
|
|
264
|
-
"solid/jsx-no-script-url": "error", // Prevent javascript: URLs
|
|
265
|
-
|
|
266
|
-
// ─── Tooling support ───
|
|
267
|
-
"solid/jsx-uses-vars": "error", // Prevent false positives in unused import detection
|
|
268
|
-
|
|
269
|
-
// ─── Conventions ───
|
|
270
|
-
"solid/prefer-for": "error", // Recommend For component
|
|
271
|
-
"solid/event-handlers": "error", // Event handler naming
|
|
272
|
-
"solid/imports": "error", // Import consistency
|
|
273
|
-
"solid/style-prop": "error", // style prop format
|
|
274
|
-
"solid/self-closing-comp": "error", // Self-closing tags
|
|
275
|
-
|
|
276
|
-
// ─── Tailwind CSS ───
|
|
277
|
-
"tailwindcss/classnames-order": "warn", // Auto-sort class order
|
|
278
|
-
"tailwindcss/enforces-negative-arbitrary-values": "error", // Unify negative arbitrary value format
|
|
279
|
-
"tailwindcss/enforces-shorthand": "error", // Recommend shorthand usage
|
|
280
|
-
"tailwindcss/no-contradicting-classname": "error", // Prohibit conflicting classes (p-2 p-4, etc.)
|
|
281
|
-
"tailwindcss/no-custom-classname": "error", // Prohibit custom classes not in Tailwind
|
|
282
|
-
"tailwindcss/no-unnecessary-arbitrary-value": "error", // Prohibit unnecessary arbitrary values
|
|
207
|
+
"no-console": "off",
|
|
208
|
+
"import/no-extraneous-dependencies": "off",
|
|
209
|
+
"@simplysm/ts-no-throw-not-implemented-error": "off",
|
|
283
210
|
},
|
|
284
211
|
},
|
|
285
|
-
|
|
212
|
+
);
|