@simplysm/lint 14.0.42 → 14.0.43
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/eslint-plugin.d.ts +3 -0
- package/dist/eslint-plugin.d.ts.map +1 -1
- package/dist/eslint-plugin.js +2 -0
- package/dist/eslint-plugin.js.map +1 -1
- package/dist/eslint-recommended.d.ts.map +1 -1
- package/dist/eslint-recommended.js +19 -1
- package/dist/eslint-recommended.js.map +1 -1
- package/dist/rules/ng-template-no-strict-null-check.d.ts +12 -0
- package/dist/rules/ng-template-no-strict-null-check.d.ts.map +1 -0
- package/dist/rules/ng-template-no-strict-null-check.js +57 -0
- package/dist/rules/ng-template-no-strict-null-check.js.map +1 -0
- package/dist/rules/ng-template-no-todo-comments.js +1 -1
- package/dist/rules/ng-template-no-todo-comments.js.map +1 -1
- package/package.json +1 -1
- package/src/eslint-plugin.ts +2 -0
- package/src/eslint-recommended.ts +19 -1
- package/src/rules/ng-template-no-strict-null-check.ts +75 -0
- package/src/rules/ng-template-no-todo-comments.ts +1 -1
package/dist/eslint-plugin.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
declare const _default: {
|
|
2
2
|
rules: {
|
|
3
|
+
"ng-template-no-strict-null-check": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noStrictNullCheck", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
3
6
|
"ng-template-no-todo-comments": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noTodo", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
4
7
|
name: string;
|
|
5
8
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-plugin.d.ts","sourceRoot":"","sources":["../src/eslint-plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"eslint-plugin.d.ts","sourceRoot":"","sources":["../src/eslint-plugin.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,wBAWE"}
|
package/dist/eslint-plugin.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ngTemplateNoStrictNullCheck from "./rules/ng-template-no-strict-null-check.js";
|
|
1
2
|
import ngTemplateNoTodoComments from "./rules/ng-template-no-todo-comments.js";
|
|
2
3
|
import ngTemplateSdRequireBindingAttrs from "./rules/ng-template-sd-require-binding-attrs.js";
|
|
3
4
|
import noHardPrivate from "./rules/no-hard-private.js";
|
|
@@ -7,6 +8,7 @@ import tsNoUnusedInjects from "./rules/ts-no-unused-injects.js";
|
|
|
7
8
|
import tsNoUnusedProtectedReadonly from "./rules/ts-no-unused-protected-readonly.js";
|
|
8
9
|
export default {
|
|
9
10
|
rules: {
|
|
11
|
+
"ng-template-no-strict-null-check": ngTemplateNoStrictNullCheck,
|
|
10
12
|
"ng-template-no-todo-comments": ngTemplateNoTodoComments,
|
|
11
13
|
"ng-template-sd-require-binding-attrs": ngTemplateSdRequireBindingAttrs,
|
|
12
14
|
"no-hard-private": noHardPrivate,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-plugin.js","sourceRoot":"","sources":["../src/eslint-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,wBAAwB,MAAM,sCAAsC,CAAC;AAC5E,OAAO,+BAA+B,MAAM,8CAA8C,CAAC;AAC3F,OAAO,aAAa,MAAM,yBAAyB,CAAC;AACpD,OAAO,4BAA4B,MAAM,0CAA0C,CAAC;AACpF,OAAO,4BAA4B,MAAM,2CAA2C,CAAC;AACrF,OAAO,iBAAiB,MAAM,8BAA8B,CAAC;AAC7D,OAAO,2BAA2B,MAAM,yCAAyC,CAAC;AAElF,eAAe;IACb,KAAK,EAAE;QACL,8BAA8B,EAAE,wBAAwB;QACxD,sCAAsC,EAAE,+BAA+B;QACvE,iBAAiB,EAAE,aAAa;QAChC,kCAAkC,EAAE,4BAA4B;QAChE,mCAAmC,EAAE,4BAA4B;QACjE,sBAAsB,EAAE,iBAAiB;QACzC,iCAAiC,EAAE,2BAA2B;KAC/D;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"eslint-plugin.js","sourceRoot":"","sources":["../src/eslint-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,MAAM,0CAA0C,CAAC;AACnF,OAAO,wBAAwB,MAAM,sCAAsC,CAAC;AAC5E,OAAO,+BAA+B,MAAM,8CAA8C,CAAC;AAC3F,OAAO,aAAa,MAAM,yBAAyB,CAAC;AACpD,OAAO,4BAA4B,MAAM,0CAA0C,CAAC;AACpF,OAAO,4BAA4B,MAAM,2CAA2C,CAAC;AACrF,OAAO,iBAAiB,MAAM,8BAA8B,CAAC;AAC7D,OAAO,2BAA2B,MAAM,yCAAyC,CAAC;AAElF,eAAe;IACb,KAAK,EAAE;QACL,kCAAkC,EAAE,2BAA2B;QAC/D,8BAA8B,EAAE,wBAAwB;QACxD,sCAAsC,EAAE,+BAA+B;QACvE,iBAAiB,EAAE,aAAa;QAChC,kCAAkC,EAAE,4BAA4B;QAChE,mCAAmC,EAAE,4BAA4B;QACjE,sBAAsB,EAAE,iBAAiB;QACzC,iCAAiC,EAAE,2BAA2B;KAC/D;CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-recommended.d.ts","sourceRoot":"","sources":["../src/eslint-recommended.ts"],"names":[],"mappings":"AAAA,OAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;;
|
|
1
|
+
{"version":3,"file":"eslint-recommended.d.ts","sourceRoot":"","sources":["../src/eslint-recommended.ts"],"names":[],"mappings":"AAAA,OAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;;AAkG9D,wBA4KE"}
|
|
@@ -8,7 +8,7 @@ import { ESLint } from "eslint";
|
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
const commonRules = {
|
|
10
10
|
"no-warning-comments": "warn",
|
|
11
|
-
"eqeqeq": ["error", "always", { null: "
|
|
11
|
+
"eqeqeq": ["error", "always", { null: "never" }],
|
|
12
12
|
"no-self-compare": "error",
|
|
13
13
|
"array-callback-return": "error",
|
|
14
14
|
};
|
|
@@ -59,6 +59,22 @@ const noDirectEnvAccessRules = {
|
|
|
59
59
|
selector: 'CallExpression[callee.name="env"][arguments.0.value="NODE_ENV"]',
|
|
60
60
|
message: "NODE_ENV 환경변수는 사용할 수 없습니다.",
|
|
61
61
|
},
|
|
62
|
+
{
|
|
63
|
+
selector: 'BinaryExpression[operator="==="][right.type="Identifier"][right.name="undefined"]',
|
|
64
|
+
message: "`== null`를 사용하지 마세요. `== null`을 사용하세요.",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
selector: 'BinaryExpression[operator="==="][left.type="Identifier"][left.name="undefined"]',
|
|
68
|
+
message: "`== null`를 사용하지 마세요. `== null`을 사용하세요.",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
selector: 'BinaryExpression[operator="!=="][right.type="Identifier"][right.name="undefined"]',
|
|
72
|
+
message: "`!= null`를 사용하지 마세요. `!= null`을 사용하세요.",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
selector: 'BinaryExpression[operator="!=="][left.type="Identifier"][left.name="undefined"]',
|
|
76
|
+
message: "`!= null`를 사용하지 마세요. `!= null`을 사용하세요.",
|
|
77
|
+
},
|
|
62
78
|
],
|
|
63
79
|
};
|
|
64
80
|
const unusedImportsRules = {
|
|
@@ -208,8 +224,10 @@ export default tseslint.config({
|
|
|
208
224
|
"@simplysm": plugin,
|
|
209
225
|
},
|
|
210
226
|
rules: {
|
|
227
|
+
"@simplysm/ng-template-no-strict-null-check": "error",
|
|
211
228
|
"@simplysm/ng-template-no-todo-comments": "warn",
|
|
212
229
|
"@simplysm/ng-template-sd-require-binding-attrs": "error",
|
|
230
|
+
"@angular-eslint/template/eqeqeq": ["error", { allowNullOrUndefined: true }],
|
|
213
231
|
"@angular-eslint/template/label-has-associated-control": "off",
|
|
214
232
|
},
|
|
215
233
|
}, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-recommended.js","sourceRoot":"","sources":["../src/eslint-recommended.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAmB,MAAM,mBAAmB,CAAC;AAC9D,OAAO,OAAO,MAAM,gBAAgB,CAAC;AACrC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,MAAM,MAAM,iBAAiB,CAAC;AACrC,OAAO,YAAY,MAAM,sBAAsB,CAAC;AAChD,OAAO,mBAAmB,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,WAAW,GAAqB;IACpC,qBAAqB,EAAE,MAAM;IAC7B,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"eslint-recommended.js","sourceRoot":"","sources":["../src/eslint-recommended.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Angular 템플릿에서 `=== null`, `!== null`, `=== undefined`, `!== undefined` 사용을 금지하는 ESLint 규칙.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* `== null` / `!= null`로 통일하도록 강제합니다.
|
|
6
|
+
* 인라인 템플릿에서 offset 매핑 문제로 autofix는 제공하지 않습니다.
|
|
7
|
+
*/
|
|
8
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noStrictNullCheck", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
|
12
|
+
//# sourceMappingURL=ng-template-no-strict-null-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-template-no-strict-null-check.d.ts","sourceRoot":"","sources":["../../src/rules/ng-template-no-strict-null-check.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;;;;AACH,wBAqDG"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getTemplateParserServices } from "@angular-eslint/utils";
|
|
2
|
+
import { createRule } from "../utils/create-rule.js";
|
|
3
|
+
/**
|
|
4
|
+
* Angular 템플릿에서 `=== null`, `!== null`, `=== undefined`, `!== undefined` 사용을 금지하는 ESLint 규칙.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* `== null` / `!= null`로 통일하도록 강제합니다.
|
|
8
|
+
* 인라인 템플릿에서 offset 매핑 문제로 autofix는 제공하지 않습니다.
|
|
9
|
+
*/
|
|
10
|
+
export default createRule({
|
|
11
|
+
name: "ng-template-no-strict-null-check",
|
|
12
|
+
meta: {
|
|
13
|
+
type: "problem",
|
|
14
|
+
docs: {
|
|
15
|
+
description: "Angular 템플릿에서 `=== null`, `!== null`, `=== undefined`, `!== undefined`를 금지합니다. `== null` / `!= null`을 사용하세요.",
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
messages: {
|
|
19
|
+
noStrictNullCheck: '`{{actual}}`을 사용하지 마세요. `{{replacement}}`를 사용하세요.',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultOptions: [],
|
|
23
|
+
create(context) {
|
|
24
|
+
const parserServices = getTemplateParserServices(context);
|
|
25
|
+
const sourceCode = context.sourceCode;
|
|
26
|
+
return {
|
|
27
|
+
'Binary[operation=/^(===|!==)$/]'(node) {
|
|
28
|
+
const { left, right, operation } = node;
|
|
29
|
+
const leftIsNil = isNilValue(left);
|
|
30
|
+
const rightIsNil = isNilValue(right);
|
|
31
|
+
if (!leftIsNil && !rightIsNil)
|
|
32
|
+
return;
|
|
33
|
+
const looseOp = operation === "===" ? "==" : "!=";
|
|
34
|
+
const nilSide = leftIsNil ? left : right;
|
|
35
|
+
const otherSide = leftIsNil ? right : left;
|
|
36
|
+
const actualText = `${getNodeText(otherSide, sourceCode)} ${operation} ${getNodeText(nilSide, sourceCode)}`;
|
|
37
|
+
const replacementText = `${getNodeText(otherSide, sourceCode)} ${looseOp} null`;
|
|
38
|
+
const loc = parserServices.convertNodeSourceSpanToLoc(node.sourceSpan);
|
|
39
|
+
context.report({
|
|
40
|
+
loc,
|
|
41
|
+
messageId: "noStrictNullCheck",
|
|
42
|
+
data: {
|
|
43
|
+
actual: actualText,
|
|
44
|
+
replacement: replacementText,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
function isNilValue(node) {
|
|
52
|
+
return "value" in node && node.value == null;
|
|
53
|
+
}
|
|
54
|
+
function getNodeText(node, sourceCode) {
|
|
55
|
+
return sourceCode.getText().slice(node.span.start, node.span.end);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=ng-template-no-strict-null-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-template-no-strict-null-check.js","sourceRoot":"","sources":["../../src/rules/ng-template-no-strict-null-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD;;;;;;GAMG;AACH,eAAe,UAAU,CAAC;IACxB,IAAI,EAAE,kCAAkC;IACxC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,8GAA8G;SACjH;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,iBAAiB,EACf,mDAAmD;SACtD;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,cAAc,GAAG,yBAAyB,CAAC,OAAgB,CAAC,CAAC;QACnE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QAEtC,OAAO;YACL,iCAAiC,CAC/B,IAKC;gBAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;gBACxC,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;gBACnC,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;gBACrC,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU;oBAAE,OAAO;gBAEtC,MAAM,OAAO,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;gBAClD,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;gBACzC,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;gBAE3C,MAAM,UAAU,GAAG,GAAG,WAAW,CAAC,SAAS,EAAE,UAAU,CAAC,IAAI,SAAS,IAAI,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;gBAC5G,MAAM,eAAe,GAAG,GAAG,WAAW,CAAC,SAAS,EAAE,UAAU,CAAC,IAAI,OAAO,OAAO,CAAC;gBAEhF,MAAM,GAAG,GAAG,cAAc,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAmB,CAAC,CAAC;gBAEhF,OAAO,CAAC,MAAM,CAAC;oBACb,GAAG;oBACH,SAAS,EAAE,mBAAmB;oBAC9B,IAAI,EAAE;wBACJ,MAAM,EAAE,UAAU;wBAClB,WAAW,EAAE,eAAe;qBAC7B;iBACF,CAAC,CAAC;YACL,CAAC;SACO,CAAC;IACb,CAAC;CACF,CAAC,CAAC;AAEH,SAAS,UAAU,CAAC,IAAyB;IAC3C,OAAO,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC;AAC/C,CAAC;AAED,SAAS,WAAW,CAClB,IAA8C,EAC9C,UAAiC;IAEjC,OAAO,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpE,CAAC"}
|
|
@@ -24,7 +24,7 @@ export default createRule({
|
|
|
24
24
|
const source = sourceCode.getText();
|
|
25
25
|
const commentRegex = /<!--([\s\S]*?)-->/g;
|
|
26
26
|
let match;
|
|
27
|
-
while ((match = commentRegex.exec(source))
|
|
27
|
+
while ((match = commentRegex.exec(source)) != null) {
|
|
28
28
|
const commentContent = match[1];
|
|
29
29
|
const todoIndex = commentContent.indexOf("TODO:");
|
|
30
30
|
if (todoIndex < 0)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-template-no-todo-comments.js","sourceRoot":"","sources":["../../src/rules/ng-template-no-todo-comments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD;;;;;;GAMG;AACH,eAAe,UAAU,CAAC;IACxB,IAAI,EAAE,8BAA8B;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,4BAA4B;SAC1C;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,MAAM,EAAE,aAAa;SACtB;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACtC,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,oBAAoB,CAAC;QAC1C,IAAI,KAAK,CAAC;QACV,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"ng-template-no-todo-comments.js","sourceRoot":"","sources":["../../src/rules/ng-template-no-todo-comments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD;;;;;;GAMG;AACH,eAAe,UAAU,CAAC;IACxB,IAAI,EAAE,8BAA8B;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,4BAA4B;SAC1C;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,MAAM,EAAE,aAAa;SACtB;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACtC,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,oBAAoB,CAAC;QAC1C,IAAI,KAAK,CAAC;QACV,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;YACnD,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAClD,IAAI,SAAS,GAAG,CAAC;gBAAE,SAAS;YAE5B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC1B,MAAM,GAAG,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACpC,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3D,MAAM,GAAG,GAAG,UAAU,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;YAE/C,OAAO,CAAC,MAAM,CAAC;gBACb,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE;gBAChC,SAAS,EAAE,QAAQ;gBACnB,IAAI,EAAE,EAAE,OAAO,EAAE;aAClB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC,CAAC"}
|
package/package.json
CHANGED
package/src/eslint-plugin.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ngTemplateNoStrictNullCheck from "./rules/ng-template-no-strict-null-check";
|
|
1
2
|
import ngTemplateNoTodoComments from "./rules/ng-template-no-todo-comments";
|
|
2
3
|
import ngTemplateSdRequireBindingAttrs from "./rules/ng-template-sd-require-binding-attrs";
|
|
3
4
|
import noHardPrivate from "./rules/no-hard-private";
|
|
@@ -8,6 +9,7 @@ import tsNoUnusedProtectedReadonly from "./rules/ts-no-unused-protected-readonly
|
|
|
8
9
|
|
|
9
10
|
export default {
|
|
10
11
|
rules: {
|
|
12
|
+
"ng-template-no-strict-null-check": ngTemplateNoStrictNullCheck,
|
|
11
13
|
"ng-template-no-todo-comments": ngTemplateNoTodoComments,
|
|
12
14
|
"ng-template-sd-require-binding-attrs": ngTemplateSdRequireBindingAttrs,
|
|
13
15
|
"no-hard-private": noHardPrivate,
|
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from "url";
|
|
|
9
9
|
|
|
10
10
|
const commonRules: FlatConfig.Rules = {
|
|
11
11
|
"no-warning-comments": "warn",
|
|
12
|
-
"eqeqeq": ["error", "always", { null: "
|
|
12
|
+
"eqeqeq": ["error", "always", { null: "never" }],
|
|
13
13
|
"no-self-compare": "error",
|
|
14
14
|
"array-callback-return": "error",
|
|
15
15
|
};
|
|
@@ -64,6 +64,22 @@ const noDirectEnvAccessRules: FlatConfig.Rules = {
|
|
|
64
64
|
selector: 'CallExpression[callee.name="env"][arguments.0.value="NODE_ENV"]',
|
|
65
65
|
message: "NODE_ENV 환경변수는 사용할 수 없습니다.",
|
|
66
66
|
},
|
|
67
|
+
{
|
|
68
|
+
selector: 'BinaryExpression[operator="==="][right.type="Identifier"][right.name="undefined"]',
|
|
69
|
+
message: "`== null`를 사용하지 마세요. `== null`을 사용하세요.",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
selector: 'BinaryExpression[operator="==="][left.type="Identifier"][left.name="undefined"]',
|
|
73
|
+
message: "`== null`를 사용하지 마세요. `== null`을 사용하세요.",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
selector: 'BinaryExpression[operator="!=="][right.type="Identifier"][right.name="undefined"]',
|
|
77
|
+
message: "`!= null`를 사용하지 마세요. `!= null`을 사용하세요.",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
selector: 'BinaryExpression[operator="!=="][left.type="Identifier"][left.name="undefined"]',
|
|
81
|
+
message: "`!= null`를 사용하지 마세요. `!= null`을 사용하세요.",
|
|
82
|
+
},
|
|
67
83
|
],
|
|
68
84
|
};
|
|
69
85
|
|
|
@@ -231,8 +247,10 @@ export default tseslint.config(
|
|
|
231
247
|
"@simplysm": plugin as unknown as ESLint.Plugin,
|
|
232
248
|
},
|
|
233
249
|
rules: {
|
|
250
|
+
"@simplysm/ng-template-no-strict-null-check": "error",
|
|
234
251
|
"@simplysm/ng-template-no-todo-comments": "warn",
|
|
235
252
|
"@simplysm/ng-template-sd-require-binding-attrs": "error",
|
|
253
|
+
"@angular-eslint/template/eqeqeq": ["error", { allowNullOrUndefined: true }],
|
|
236
254
|
"@angular-eslint/template/label-has-associated-control": "off",
|
|
237
255
|
},
|
|
238
256
|
},
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { getTemplateParserServices } from "@angular-eslint/utils";
|
|
2
|
+
import { createRule } from "../utils/create-rule";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Angular 템플릿에서 `=== null`, `!== null`, `=== undefined`, `!== undefined` 사용을 금지하는 ESLint 규칙.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* `== null` / `!= null`로 통일하도록 강제합니다.
|
|
9
|
+
* 인라인 템플릿에서 offset 매핑 문제로 autofix는 제공하지 않습니다.
|
|
10
|
+
*/
|
|
11
|
+
export default createRule({
|
|
12
|
+
name: "ng-template-no-strict-null-check",
|
|
13
|
+
meta: {
|
|
14
|
+
type: "problem",
|
|
15
|
+
docs: {
|
|
16
|
+
description:
|
|
17
|
+
"Angular 템플릿에서 `=== null`, `!== null`, `=== undefined`, `!== undefined`를 금지합니다. `== null` / `!= null`을 사용하세요.",
|
|
18
|
+
},
|
|
19
|
+
schema: [],
|
|
20
|
+
messages: {
|
|
21
|
+
noStrictNullCheck:
|
|
22
|
+
'`{{actual}}`을 사용하지 마세요. `{{replacement}}`를 사용하세요.',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultOptions: [],
|
|
26
|
+
create(context) {
|
|
27
|
+
const parserServices = getTemplateParserServices(context as never);
|
|
28
|
+
const sourceCode = context.sourceCode;
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
'Binary[operation=/^(===|!==)$/]'(
|
|
32
|
+
node: {
|
|
33
|
+
left: { value?: unknown; span: { start: number; end: number } };
|
|
34
|
+
right: { value?: unknown; span: { start: number; end: number } };
|
|
35
|
+
operation: "===" | "!==";
|
|
36
|
+
sourceSpan: { start: number; end: number };
|
|
37
|
+
},
|
|
38
|
+
) {
|
|
39
|
+
const { left, right, operation } = node;
|
|
40
|
+
const leftIsNil = isNilValue(left);
|
|
41
|
+
const rightIsNil = isNilValue(right);
|
|
42
|
+
if (!leftIsNil && !rightIsNil) return;
|
|
43
|
+
|
|
44
|
+
const looseOp = operation === "===" ? "==" : "!=";
|
|
45
|
+
const nilSide = leftIsNil ? left : right;
|
|
46
|
+
const otherSide = leftIsNil ? right : left;
|
|
47
|
+
|
|
48
|
+
const actualText = `${getNodeText(otherSide, sourceCode)} ${operation} ${getNodeText(nilSide, sourceCode)}`;
|
|
49
|
+
const replacementText = `${getNodeText(otherSide, sourceCode)} ${looseOp} null`;
|
|
50
|
+
|
|
51
|
+
const loc = parserServices.convertNodeSourceSpanToLoc(node.sourceSpan as never);
|
|
52
|
+
|
|
53
|
+
context.report({
|
|
54
|
+
loc,
|
|
55
|
+
messageId: "noStrictNullCheck",
|
|
56
|
+
data: {
|
|
57
|
+
actual: actualText,
|
|
58
|
+
replacement: replacementText,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
} as never;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
function isNilValue(node: { value?: unknown }): boolean {
|
|
67
|
+
return "value" in node && node.value == null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getNodeText(
|
|
71
|
+
node: { span: { start: number; end: number } },
|
|
72
|
+
sourceCode: { getText(): string },
|
|
73
|
+
): string {
|
|
74
|
+
return sourceCode.getText().slice(node.span.start, node.span.end);
|
|
75
|
+
}
|
|
@@ -25,7 +25,7 @@ export default createRule({
|
|
|
25
25
|
const source = sourceCode.getText();
|
|
26
26
|
const commentRegex = /<!--([\s\S]*?)-->/g;
|
|
27
27
|
let match;
|
|
28
|
-
while ((match = commentRegex.exec(source))
|
|
28
|
+
while ((match = commentRegex.exec(source)) != null) {
|
|
29
29
|
const commentContent = match[1];
|
|
30
30
|
const todoIndex = commentContent.indexOf("TODO:");
|
|
31
31
|
if (todoIndex < 0) continue;
|