@checkdigit/eslint-plugin 7.13.0-PR.57-9e3a → 7.13.1-PR.120-6b16
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-mjs/index.mjs +2 -9
- package/dist-mjs/no-random-v4-uuid.mjs +24 -27
- package/dist-mjs/no-side-effects.mjs +87 -31
- package/dist-types/no-random-v4-uuid.d.ts +2 -1
- package/package.json +1 -1
- package/src/file-path-comment.ts +1 -1
- package/src/index.ts +1 -8
- package/src/no-random-v4-uuid.ts +49 -40
- package/src/no-side-effects.ts +161 -70
- package/dist-mjs/no-util.mjs +0 -47
- package/dist-types/no-util.d.ts +0 -5
- package/src/no-util.ts +0 -53
package/dist-mjs/index.mjs
CHANGED
|
@@ -23,7 +23,6 @@ import noEnum from "./no-enum.mjs";
|
|
|
23
23
|
import noSideEffects from "./no-side-effects.mjs";
|
|
24
24
|
import noRandomV4UUID from "./no-random-v4-uuid.mjs";
|
|
25
25
|
import noTestImport from "./no-test-import.mjs";
|
|
26
|
-
import noUtil from "./no-util.mjs";
|
|
27
26
|
import noUuid from "./no-uuid.mjs";
|
|
28
27
|
import noWallabyComment from "./no-wallaby-comment.mjs";
|
|
29
28
|
import objectLiteralResponse from "./object-literal-response.mjs";
|
|
@@ -38,7 +37,6 @@ var rules = {
|
|
|
38
37
|
"no-enum": noEnum,
|
|
39
38
|
"no-random-v4-uuid": noRandomV4UUID,
|
|
40
39
|
"no-status-code-assert": noStatusCodeAssert,
|
|
41
|
-
"no-util": noUtil,
|
|
42
40
|
"no-uuid": noUuid,
|
|
43
41
|
"require-assert-message": requireAssertMessage,
|
|
44
42
|
"require-strict-assert": requireStrictAssert,
|
|
@@ -75,16 +73,12 @@ var configs = {
|
|
|
75
73
|
"@checkdigit/file-path-comment": "error",
|
|
76
74
|
"@checkdigit/no-random-v4-uuid": "error",
|
|
77
75
|
"@checkdigit/no-status-code-assert": "error",
|
|
78
|
-
"@checkdigit/no-util": "error",
|
|
79
76
|
"@checkdigit/no-uuid": "error",
|
|
80
77
|
"@checkdigit/require-assert-message": "error",
|
|
81
78
|
"@checkdigit/require-strict-assert": "error",
|
|
82
79
|
"@checkdigit/require-ts-extension-imports-exports": "error",
|
|
83
80
|
"@checkdigit/no-wallaby-comment": "error",
|
|
84
|
-
"@checkdigit/no-side-effects":
|
|
85
|
-
"error",
|
|
86
|
-
{ excludedIdentifiers: ["assert", "debug", "log", "promisify", "Symbol.for"] }
|
|
87
|
-
],
|
|
81
|
+
"@checkdigit/no-side-effects": "error",
|
|
88
82
|
"@checkdigit/regular-expression-comment": "error",
|
|
89
83
|
"@checkdigit/require-assert-predicate-rejects-throws": "error",
|
|
90
84
|
"@checkdigit/object-literal-response": "error",
|
|
@@ -113,7 +107,6 @@ var configs = {
|
|
|
113
107
|
"@checkdigit/file-path-comment": "off",
|
|
114
108
|
"@checkdigit/no-random-v4-uuid": "error",
|
|
115
109
|
"@checkdigit/no-status-code-assert": "error",
|
|
116
|
-
"@checkdigit/no-util": "error",
|
|
117
110
|
"@checkdigit/no-uuid": "error",
|
|
118
111
|
"@checkdigit/require-assert-message": "error",
|
|
119
112
|
"@checkdigit/require-strict-assert": "error",
|
|
@@ -145,4 +138,4 @@ var src_default = defaultToExport;
|
|
|
145
138
|
export {
|
|
146
139
|
src_default as default
|
|
147
140
|
};
|
|
148
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
141
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2luZGV4LnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQVVBLE9BQU8sd0JBQXdCLFVBQVUsa0NBQWtDO0FBQzNFLE9BQU8sdUJBQXVCLFVBQVUsaUNBQWlDO0FBQ3pFLE9BQU8seUJBQXlCLFVBQVUsbUNBQW1DO0FBQzdFLE9BQU8sMkJBQTJCLFVBQVUscUNBQXFDO0FBQ2pGLE9BQU8sd0JBQXdCO0FBQy9CLE9BQU87QUFBQSxFQUNMLFVBQVU7QUFBQSxPQUNMO0FBQ1AsT0FBTztBQUFBLEVBQ0wsVUFBVTtBQUFBLE9BQ0w7QUFDUCxPQUFPO0FBQUEsRUFDTCxVQUFVO0FBQUEsT0FDTDtBQUNQLE9BQU8sa0JBQWtCLFVBQVUsNEJBQTRCO0FBQy9ELE9BQU87QUFBQSxFQUNMLFVBQVU7QUFBQSxPQUNMO0FBQ1AsT0FBTyxxQkFBcUI7QUFDNUIsT0FBTyxtQkFBbUI7QUFDMUIsT0FBTyxZQUFZO0FBQ25CLE9BQU8sbUJBQW1CO0FBQzFCLE9BQU8sb0JBQW9CO0FBQzNCLE9BQU8sa0JBQWtCO0FBQ3pCLE9BQU8sWUFBWTtBQUNuQixPQUFPLHNCQUFzQjtBQUM3QixPQUFPLDJCQUEyQjtBQUNsQyxPQUFPLGtCQUFrQjtBQUN6QixPQUFPLHlDQUF5QztBQUNoRCxPQUFPLHlCQUF5QjtBQUNoQyxPQUFPLDBCQUEwQjtBQUNqQyxPQUFPLHNDQUFzQztBQUU3QyxJQUFNLFFBQXNEO0FBQUEsRUFDMUQscUJBQXFCO0FBQUEsRUFDckIsbUJBQW1CO0FBQUEsRUFDbkIsV0FBVztBQUFBLEVBQ1gscUJBQXFCO0FBQUEsRUFDckIseUJBQXlCO0FBQUEsRUFDekIsV0FBVztBQUFBLEVBQ1gsMEJBQTBCO0FBQUEsRUFDMUIseUJBQXlCO0FBQUEsRUFDekIsd0NBQXdDO0FBQUEsRUFDeEMsa0JBQWtCO0FBQUEsRUFDbEIsc0JBQXNCO0FBQUEsRUFDdEIsbUJBQW1CO0FBQUEsRUFDbkIsOEJBQThCO0FBQUEsRUFDOUIsMkNBQTJDO0FBQUEsRUFDM0MsMkJBQTJCO0FBQUEsRUFDM0IsQ0FBQywwQkFBMEIsR0FBRztBQUFBLEVBQzlCLENBQUMsNkJBQTZCLEdBQUc7QUFBQSxFQUNqQyxDQUFDLDJCQUEyQixHQUFHO0FBQUEsRUFDL0IsQ0FBQyxnQ0FBZ0MsR0FBRztBQUFBLEVBQ3BDLENBQUMseUJBQXlCLEdBQUc7QUFBQSxFQUM3QixDQUFDLG9CQUFvQixHQUFHO0FBQUEsRUFDeEIsQ0FBQywyQ0FBMkMsR0FBRztBQUFBLEVBQy9DLENBQUMsZ0NBQWdDLEdBQUc7QUFBQSxFQUNwQyxDQUFDLHFDQUFxQyxHQUFHO0FBQzNDO0FBRUEsSUFBTSxTQUFxQztBQUFBLEVBQ3pDO0FBQ0Y7QUFFQSxJQUFNLFVBQXdEO0FBQUEsRUFDNUQsS0FBSztBQUFBLElBQ0g7QUFBQSxNQUNFLE9BQU8sQ0FBQyxTQUFTO0FBQUEsTUFDakIsU0FBUztBQUFBLFFBQ1AsZUFBZTtBQUFBLE1BQ2pCO0FBQUEsTUFDQSxPQUFPO0FBQUEsUUFDTCwrQkFBK0I7QUFBQSxRQUMvQix1QkFBdUI7QUFBQSxRQUN2QixpQ0FBaUM7QUFBQSxRQUNqQyxpQ0FBaUM7QUFBQSxRQUNqQyxxQ0FBcUM7QUFBQSxRQUNyQyx1QkFBdUI7QUFBQSxRQUN2QixzQ0FBc0M7QUFBQSxRQUN0QyxxQ0FBcUM7QUFBQSxRQUNyQyxvREFBb0Q7QUFBQSxRQUNwRCxrQ0FBa0M7QUFBQSxRQUNsQywrQkFBK0I7QUFBQSxRQUMvQiwwQ0FBMEM7QUFBQSxRQUMxQyx1REFBdUQ7QUFBQSxRQUN2RCx1Q0FBdUM7QUFBQSxRQUN2Qyw4QkFBOEI7QUFBQSxRQUM5QixDQUFDLGVBQWUsMEJBQTBCLEVBQUUsR0FBRztBQUFBLFFBQy9DLENBQUMsZUFBZSw2QkFBNkIsRUFBRSxHQUFHO0FBQUEsUUFDbEQsQ0FBQyxlQUFlLDJCQUEyQixFQUFFLEdBQUc7QUFBQSxRQUNoRCxDQUFDLGVBQWUsZ0NBQWdDLEVBQUUsR0FBRztBQUFBLFFBQ3JELENBQUMsZUFBZSx5QkFBeUIsRUFBRSxHQUFHO0FBQUEsUUFDOUMsQ0FBQyxlQUFlLGdDQUFnQyxFQUFFLEdBQUc7QUFBQSxRQUNyRCxDQUFDLGVBQWUscUNBQXFDLEVBQUUsR0FBRztBQUFBLFFBQzFELENBQUMsZUFBZSxvQkFBb0IsRUFBRSxHQUFHO0FBQUEsUUFDekMsQ0FBQyxlQUFlLDJDQUEyQyxFQUFFLEdBQUc7QUFBQSxNQUNsRTtBQUFBLElBQ0Y7QUFBQSxFQUNGO0FBQUEsRUFDQSxhQUFhO0FBQUEsSUFDWDtBQUFBLE1BQ0UsT0FBTyxDQUFDLFNBQVM7QUFBQSxNQUNqQixTQUFTO0FBQUEsUUFDUCxlQUFlO0FBQUEsTUFDakI7QUFBQSxNQUNBLE9BQU87QUFBQSxRQUNMLCtCQUErQjtBQUFBLFFBQy9CLHVCQUF1QjtBQUFBLFFBQ3ZCLGlDQUFpQztBQUFBLFFBQ2pDLGlDQUFpQztBQUFBLFFBQ2pDLHFDQUFxQztBQUFBLFFBQ3JDLHVCQUF1QjtBQUFBLFFBQ3ZCLHNDQUFzQztBQUFBLFFBQ3RDLHFDQUFxQztBQUFBLFFBQ3JDLG9EQUFvRDtBQUFBLFFBQ3BELGtDQUFrQztBQUFBLFFBQ2xDLCtCQUErQjtBQUFBLFFBQy9CLDBDQUEwQztBQUFBLFFBQzFDLHVEQUF1RDtBQUFBLFFBQ3ZELHVDQUF1QztBQUFBLFFBQ3ZDLDhCQUE4QjtBQUFBLFFBQzlCLENBQUMsZUFBZSwwQkFBMEIsRUFBRSxHQUFHO0FBQUEsUUFDL0MsQ0FBQyxlQUFlLDZCQUE2QixFQUFFLEdBQUc7QUFBQSxRQUNsRCxDQUFDLGVBQWUsMkJBQTJCLEVBQUUsR0FBRztBQUFBLFFBQ2hELENBQUMsZUFBZSxnQ0FBZ0MsRUFBRSxHQUFHO0FBQUEsUUFDckQsQ0FBQyxlQUFlLHlCQUF5QixFQUFFLEdBQUc7QUFBQSxRQUM5QyxDQUFDLGVBQWUsZ0NBQWdDLEVBQUUsR0FBRztBQUFBLFFBQ3JELENBQUMsZUFBZSxxQ0FBcUMsRUFBRSxHQUFHO0FBQUEsUUFDMUQsQ0FBQyxlQUFlLG9CQUFvQixFQUFFLEdBQUc7QUFBQSxRQUN6QyxDQUFDLGVBQWUsMkNBQTJDLEVBQUUsR0FBRztBQUFBLE1BQ2xFO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRjtBQUVBLElBQU0sa0JBRUY7QUFBQSxFQUNGLEdBQUc7QUFBQSxFQUNIO0FBQ0Y7QUFDQSxJQUFPLGNBQVE7IiwKICAibmFtZXMiOiBbXQp9Cg==
|
|
@@ -3,53 +3,50 @@ import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
|
3
3
|
import getDocumentationUrl from "./get-documentation-url.mjs";
|
|
4
4
|
var ruleId = "no-random-v4-uuid";
|
|
5
5
|
var NO_RANDOM_V4_UUID = "NO_RANDOM_V4_UUID";
|
|
6
|
+
var NO_UUID_MODULE_FOR_V4 = "NO_UUID_MODULE_FOR_V4";
|
|
6
7
|
var createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
|
|
7
|
-
var processImportDeclaration = (node,
|
|
8
|
-
let updatedUuid4Alias = uuid4Alias;
|
|
9
|
-
let updatedUuidDefaultAlias = uuidDefaultAlias;
|
|
8
|
+
var processImportDeclaration = (node, aliases) => {
|
|
10
9
|
node.specifiers.forEach((specifier) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
break;
|
|
10
|
+
if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {
|
|
11
|
+
if (node.source.value === "uuid" && specifier.imported.type === AST_NODE_TYPES.Identifier && specifier.imported.name === "v4") {
|
|
12
|
+
aliases.uuid4Alias = specifier.local.name;
|
|
13
|
+
} else if (node.source.value === "node:crypto" && specifier.imported.type === AST_NODE_TYPES.Identifier && specifier.imported.name === "randomUUID") {
|
|
14
|
+
aliases.nodeCryptoRandomUUIDAlias = specifier.local.name;
|
|
15
|
+
}
|
|
16
|
+
} else if (specifier.type === AST_NODE_TYPES.ImportDefaultSpecifier && node.source.value === "uuid") {
|
|
17
|
+
aliases.uuidDefaultAlias = specifier.local.name;
|
|
20
18
|
}
|
|
21
19
|
});
|
|
22
|
-
return { uuid4Alias: updatedUuid4Alias, uuidDefaultAlias: updatedUuidDefaultAlias };
|
|
23
20
|
};
|
|
24
|
-
var isUuid4Call = (node,
|
|
21
|
+
var isUuid4Call = (node, aliases) => node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === aliases.uuid4Alias || node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.object.type === AST_NODE_TYPES.Identifier && node.callee.object.name === aliases.uuidDefaultAlias && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === "v4";
|
|
22
|
+
var isCryptoRandomUUIDCall = (node, alias) => node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === alias || node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.object.type === AST_NODE_TYPES.Identifier && node.callee.object.name === "crypto" && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === "randomUUID";
|
|
25
23
|
var rule = createRule({
|
|
26
24
|
name: ruleId,
|
|
27
25
|
meta: {
|
|
28
26
|
type: "problem",
|
|
29
27
|
docs: {
|
|
30
|
-
description: "Disallow the use of `uuid.v4` for generating random v4 UUIDs"
|
|
28
|
+
description: "Disallow the use of `uuid.v4` and `crypto.randomUUID` for generating random v4 UUIDs, and suggest replacing `uuid` module usage with `crypto.randomUUID`."
|
|
31
29
|
},
|
|
32
30
|
schema: [],
|
|
33
31
|
messages: {
|
|
34
|
-
[NO_RANDOM_V4_UUID]: "Avoid using `
|
|
32
|
+
[NO_RANDOM_V4_UUID]: "Avoid using `crypto.randomUUID` for generating random v4 UUIDs.",
|
|
33
|
+
[NO_UUID_MODULE_FOR_V4]: "Avoid using the `uuid` module for v4 UUID generation. Use `crypto.randomUUID` instead."
|
|
35
34
|
}
|
|
36
35
|
},
|
|
37
36
|
defaultOptions: [],
|
|
38
37
|
create(context) {
|
|
39
|
-
|
|
40
|
-
let uuidDefaultAlias;
|
|
41
|
-
let hasUuidImport = false;
|
|
38
|
+
const aliases = {};
|
|
42
39
|
return {
|
|
43
40
|
ImportDeclaration(node) {
|
|
44
|
-
|
|
45
|
-
hasUuidImport = true;
|
|
46
|
-
const result = processImportDeclaration(node, uuid4Alias, uuidDefaultAlias);
|
|
47
|
-
uuid4Alias = result.uuid4Alias;
|
|
48
|
-
uuidDefaultAlias = result.uuidDefaultAlias;
|
|
49
|
-
}
|
|
41
|
+
processImportDeclaration(node, aliases);
|
|
50
42
|
},
|
|
51
43
|
CallExpression(node) {
|
|
52
|
-
if (
|
|
44
|
+
if (isUuid4Call(node, aliases)) {
|
|
45
|
+
context.report({
|
|
46
|
+
node,
|
|
47
|
+
messageId: NO_UUID_MODULE_FOR_V4
|
|
48
|
+
});
|
|
49
|
+
} else if (isCryptoRandomUUIDCall(node, aliases.nodeCryptoRandomUUIDAlias)) {
|
|
53
50
|
context.report({
|
|
54
51
|
node,
|
|
55
52
|
messageId: NO_RANDOM_V4_UUID
|
|
@@ -64,4 +61,4 @@ export {
|
|
|
64
61
|
no_random_v4_uuid_default as default,
|
|
65
62
|
ruleId
|
|
66
63
|
};
|
|
67
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
64
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL25vLXJhbmRvbS12NC11dWlkLnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQVFBLFNBQVMsZ0JBQWdCLG1CQUF1QztBQUNoRSxPQUFPLHlCQUF5QjtBQUV6QixJQUFNLFNBQVM7QUFDdEIsSUFBTSxvQkFBb0I7QUFDMUIsSUFBTSx3QkFBd0I7QUFFOUIsSUFBTSxhQUFhLFlBQVksWUFBWSxDQUFDLFNBQVMsb0JBQW9CLElBQUksQ0FBQztBQVE5RSxJQUFNLDJCQUEyQixDQUFDLE1BQWtDLFlBQXFCO0FBQ3ZGLE9BQUssV0FBVyxRQUFRLENBQUMsY0FBYztBQUNyQyxRQUFJLFVBQVUsU0FBUyxlQUFlLGlCQUFpQjtBQUNyRCxVQUNFLEtBQUssT0FBTyxVQUFVLFVBQ3RCLFVBQVUsU0FBUyxTQUFTLGVBQWUsY0FDM0MsVUFBVSxTQUFTLFNBQVMsTUFDNUI7QUFDQSxnQkFBUSxhQUFhLFVBQVUsTUFBTTtBQUFBLE1BQ3ZDLFdBQ0UsS0FBSyxPQUFPLFVBQVUsaUJBQ3RCLFVBQVUsU0FBUyxTQUFTLGVBQWUsY0FDM0MsVUFBVSxTQUFTLFNBQVMsY0FDNUI7QUFDQSxnQkFBUSw0QkFBNEIsVUFBVSxNQUFNO0FBQUEsTUFDdEQ7QUFBQSxJQUNGLFdBQVcsVUFBVSxTQUFTLGVBQWUsMEJBQTBCLEtBQUssT0FBTyxVQUFVLFFBQVE7QUFDbkcsY0FBUSxtQkFBbUIsVUFBVSxNQUFNO0FBQUEsSUFDN0M7QUFBQSxFQUNGLENBQUM7QUFDSDtBQUVBLElBQU0sY0FBYyxDQUFDLE1BQStCLFlBQ2pELEtBQUssT0FBTyxTQUFTLGVBQWUsY0FBYyxLQUFLLE9BQU8sU0FBUyxRQUFRLGNBQy9FLEtBQUssT0FBTyxTQUFTLGVBQWUsb0JBQ25DLEtBQUssT0FBTyxPQUFPLFNBQVMsZUFBZSxjQUMzQyxLQUFLLE9BQU8sT0FBTyxTQUFTLFFBQVEsb0JBQ3BDLEtBQUssT0FBTyxTQUFTLFNBQVMsZUFBZSxjQUM3QyxLQUFLLE9BQU8sU0FBUyxTQUFTO0FBRWxDLElBQU0seUJBQXlCLENBQUMsTUFBK0IsVUFDNUQsS0FBSyxPQUFPLFNBQVMsZUFBZSxjQUFjLEtBQUssT0FBTyxTQUFTLFNBQ3ZFLEtBQUssT0FBTyxTQUFTLGVBQWUsb0JBQ25DLEtBQUssT0FBTyxPQUFPLFNBQVMsZUFBZSxjQUMzQyxLQUFLLE9BQU8sT0FBTyxTQUFTLFlBQzVCLEtBQUssT0FBTyxTQUFTLFNBQVMsZUFBZSxjQUM3QyxLQUFLLE9BQU8sU0FBUyxTQUFTO0FBRWxDLElBQU0sT0FBcUYsV0FBVztBQUFBLEVBQ3BHLE1BQU07QUFBQSxFQUNOLE1BQU07QUFBQSxJQUNKLE1BQU07QUFBQSxJQUNOLE1BQU07QUFBQSxNQUNKLGFBQ0U7QUFBQSxJQUNKO0FBQUEsSUFDQSxRQUFRLENBQUM7QUFBQSxJQUNULFVBQVU7QUFBQSxNQUNSLENBQUMsaUJBQWlCLEdBQUc7QUFBQSxNQUNyQixDQUFDLHFCQUFxQixHQUFHO0FBQUEsSUFDM0I7QUFBQSxFQUNGO0FBQUEsRUFDQSxnQkFBZ0IsQ0FBQztBQUFBLEVBQ2pCLE9BQU8sU0FBUztBQUNkLFVBQU0sVUFBbUIsQ0FBQztBQUUxQixXQUFPO0FBQUEsTUFDTCxrQkFBa0IsTUFBa0M7QUFDbEQsaUNBQXlCLE1BQU0sT0FBTztBQUFBLE1BQ3hDO0FBQUEsTUFDQSxlQUFlLE1BQStCO0FBQzVDLFlBQUksWUFBWSxNQUFNLE9BQU8sR0FBRztBQUM5QixrQkFBUSxPQUFPO0FBQUEsWUFDYjtBQUFBLFlBQ0EsV0FBVztBQUFBLFVBQ2IsQ0FBQztBQUFBLFFBQ0gsV0FBVyx1QkFBdUIsTUFBTSxRQUFRLHlCQUF5QixHQUFHO0FBQzFFLGtCQUFRLE9BQU87QUFBQSxZQUNiO0FBQUEsWUFDQSxXQUFXO0FBQUEsVUFDYixDQUFDO0FBQUEsUUFDSDtBQUFBLE1BQ0Y7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUNGLENBQUM7QUFFRCxJQUFPLDRCQUFROyIsCiAgIm5hbWVzIjogW10KfQo=
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
// src/no-side-effects.ts
|
|
2
2
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
3
3
|
import { TSESTree } from "@typescript-eslint/typescript-estree";
|
|
4
|
+
import getDocumentationUrl from "./get-documentation-url.mjs";
|
|
4
5
|
var ruleId = "no-side-effects";
|
|
5
6
|
var NO_SIDE_EFFECTS = "NO_SIDE_EFFECTS";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
function isVariableDeclarationCallExpression(node, excludedIdentifiers) {
|
|
7
|
+
var isExpressionStatement = (node) => node.type === TSESTree.AST_NODE_TYPES.ExpressionStatement;
|
|
8
|
+
var isAwaitExpression = (statement) => isExpressionStatement(statement) && statement.expression.type === TSESTree.AST_NODE_TYPES.AwaitExpression;
|
|
9
|
+
var isVariableDeclarationAwaitExpression = (node) => node.type === TSESTree.AST_NODE_TYPES.VariableDeclaration && node.declarations.length > 0 && node.declarations[0]?.init?.type === TSESTree.AST_NODE_TYPES.AwaitExpression;
|
|
10
|
+
var isNotValidVariableDeclaration = (node) => node.type === TSESTree.AST_NODE_TYPES.VariableDeclaration && node.kind !== "const" && node.kind !== "using";
|
|
11
|
+
var isControlFlowStatement = (node) => [
|
|
12
|
+
TSESTree.AST_NODE_TYPES.TryStatement,
|
|
13
|
+
TSESTree.AST_NODE_TYPES.IfStatement,
|
|
14
|
+
TSESTree.AST_NODE_TYPES.SwitchStatement,
|
|
15
|
+
TSESTree.AST_NODE_TYPES.ForStatement,
|
|
16
|
+
TSESTree.AST_NODE_TYPES.WhileStatement,
|
|
17
|
+
TSESTree.AST_NODE_TYPES.DoWhileStatement
|
|
18
|
+
].includes(node.type);
|
|
19
|
+
var isAssignmentExpression = (node) => node.type === TSESTree.AST_NODE_TYPES.ExpressionStatement && node.expression.type === TSESTree.AST_NODE_TYPES.AssignmentExpression;
|
|
20
|
+
var isIdentifierCallee = (node, excludedIdentifiers) => node.callee.type === TSESTree.AST_NODE_TYPES.Identifier && !excludedIdentifiers.includes(node.callee.name);
|
|
21
|
+
var isMemberExpressionCallee = (node, excludedIdentifiers) => node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression && node.callee.object.type === TSESTree.AST_NODE_TYPES.Identifier && node.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier && !excludedIdentifiers.includes(`${node.callee.object.name}.${node.callee.property.name}`);
|
|
22
|
+
var isNonIdentifierObjectMemberExpressionCallee = (node) => node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression && node.callee.object.type !== TSESTree.AST_NODE_TYPES.Identifier;
|
|
23
|
+
var isCallExpressionCalleeMemberExpression = (statement, excludedIdentifiers) => isExpressionStatement(statement) && statement.expression.type === TSESTree.AST_NODE_TYPES.CallExpression && statement.expression.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression && statement.expression.callee.object.type === TSESTree.AST_NODE_TYPES.Identifier && statement.expression.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier && !excludedIdentifiers.includes(statement.expression.callee.object.name) && !excludedIdentifiers.includes(
|
|
24
|
+
`${statement.expression.callee.object.name}.${statement.expression.callee.property.name}`
|
|
25
|
+
);
|
|
26
|
+
var isVariableDeclarationCallExpression = (node, excludedIdentifiers) => {
|
|
28
27
|
if (node.type !== TSESTree.AST_NODE_TYPES.VariableDeclaration || node.declarations.length === 0) {
|
|
29
28
|
return false;
|
|
30
29
|
}
|
|
@@ -32,18 +31,71 @@ function isVariableDeclarationCallExpression(node, excludedIdentifiers) {
|
|
|
32
31
|
if (init?.type !== TSESTree.AST_NODE_TYPES.CallExpression) {
|
|
33
32
|
return false;
|
|
34
33
|
}
|
|
34
|
+
const callee = init.callee;
|
|
35
|
+
if (callee.type === TSESTree.AST_NODE_TYPES.ArrowFunctionExpression || callee.type === TSESTree.AST_NODE_TYPES.FunctionExpression) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
35
38
|
return isIdentifierCallee(init, excludedIdentifiers) || isMemberExpressionCallee(init, excludedIdentifiers) || isNonIdentifierObjectMemberExpressionCallee(init);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
39
|
+
};
|
|
40
|
+
var isExportNamedDeclarationWithSideEffects = (statement, excludedIdentifiers) => statement.type === TSESTree.AST_NODE_TYPES.ExportNamedDeclaration && statement.declaration !== null && (isVariableDeclarationAwaitExpression(statement.declaration) || isVariableDeclarationCallExpression(statement.declaration, excludedIdentifiers));
|
|
41
|
+
var isExpressionStatementWithSideEffects = (statement, excludedIdentifiers) => statement.type === TSESTree.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === TSESTree.AST_NODE_TYPES.CallExpression && (statement.expression.callee.type === TSESTree.AST_NODE_TYPES.Identifier && !excludedIdentifiers.includes(statement.expression.callee.name) || statement.expression.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression && statement.expression.callee.object.type === TSESTree.AST_NODE_TYPES.Identifier && statement.expression.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier && !excludedIdentifiers.includes(
|
|
42
|
+
`${statement.expression.callee.object.name}.${statement.expression.callee.property.name}`
|
|
43
|
+
));
|
|
44
|
+
var isVariableDeclarationNewExpression = (node) => {
|
|
45
|
+
if (node.type !== TSESTree.AST_NODE_TYPES.VariableDeclaration || node.declarations.length === 0) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const init = node.declarations[0]?.init;
|
|
49
|
+
return init?.type === TSESTree.AST_NODE_TYPES.NewExpression;
|
|
50
|
+
};
|
|
51
|
+
var isFunctionDeclarationAwaitExpression = (node) => node.type === TSESTree.AST_NODE_TYPES.FunctionDeclaration && node.body.body.some(
|
|
52
|
+
(statement) => statement.type === TSESTree.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === TSESTree.AST_NODE_TYPES.AwaitExpression
|
|
53
|
+
);
|
|
54
|
+
var hasSideEffects = (statement, excludedIdentifiers) => {
|
|
55
|
+
if (isAwaitExpression(statement)) {
|
|
56
|
+
return TSESTree.AST_NODE_TYPES.AwaitExpression;
|
|
57
|
+
}
|
|
58
|
+
if (isCallExpressionCalleeMemberExpression(statement, excludedIdentifiers)) {
|
|
59
|
+
return "CallExpressionCalleeMemberExpression";
|
|
60
|
+
}
|
|
61
|
+
if (isVariableDeclarationAwaitExpression(statement)) {
|
|
62
|
+
return "VariableDeclarationAwaitExpression";
|
|
63
|
+
}
|
|
64
|
+
if (isVariableDeclarationCallExpression(statement, excludedIdentifiers)) {
|
|
65
|
+
return "VariableDeclarationCallExpression";
|
|
66
|
+
}
|
|
67
|
+
if (isVariableDeclarationNewExpression(statement)) {
|
|
68
|
+
return "VariableDeclarationNewExpression";
|
|
69
|
+
}
|
|
70
|
+
if (isExportNamedDeclarationWithSideEffects(statement, excludedIdentifiers)) {
|
|
71
|
+
return "ExportNamedDeclarationWithSideEffects";
|
|
72
|
+
}
|
|
73
|
+
if (isExpressionStatementWithSideEffects(statement, excludedIdentifiers)) {
|
|
74
|
+
return "ExpressionStatementWithSideEffects";
|
|
75
|
+
}
|
|
76
|
+
if (isControlFlowStatement(statement)) {
|
|
77
|
+
return "ControlFlowStatement";
|
|
78
|
+
}
|
|
79
|
+
if (isNotValidVariableDeclaration(statement)) {
|
|
80
|
+
return "NotValidVariableDeclaration";
|
|
81
|
+
}
|
|
82
|
+
if (isAssignmentExpression(statement)) {
|
|
83
|
+
return TSESTree.AST_NODE_TYPES.AssignmentExpression;
|
|
84
|
+
}
|
|
85
|
+
if (isFunctionDeclarationAwaitExpression(statement)) {
|
|
86
|
+
return "FunctionDeclarationAwaitExpression";
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
};
|
|
90
|
+
var createRule = ESLintUtils.RuleCreator(
|
|
91
|
+
(name) => getDocumentationUrl(name)
|
|
92
|
+
);
|
|
41
93
|
var rule = createRule({
|
|
42
94
|
name: ruleId,
|
|
43
95
|
meta: {
|
|
44
96
|
type: "problem",
|
|
45
97
|
docs: {
|
|
46
|
-
description: "Ensure no side effects can occur at the module-level"
|
|
98
|
+
description: "Ensure no side effects can occur at the module-level only if exporting module"
|
|
47
99
|
},
|
|
48
100
|
schema: [
|
|
49
101
|
{
|
|
@@ -74,10 +126,14 @@ var rule = createRule({
|
|
|
74
126
|
return;
|
|
75
127
|
}
|
|
76
128
|
node.body.forEach((statement) => {
|
|
77
|
-
|
|
129
|
+
const sideEffectType = hasSideEffects(statement, excludedIdentifiers);
|
|
130
|
+
if (sideEffectType !== null) {
|
|
78
131
|
context.report({
|
|
79
132
|
node: statement,
|
|
80
|
-
messageId: NO_SIDE_EFFECTS
|
|
133
|
+
messageId: NO_SIDE_EFFECTS,
|
|
134
|
+
data: {
|
|
135
|
+
sideEffectType
|
|
136
|
+
}
|
|
81
137
|
});
|
|
82
138
|
}
|
|
83
139
|
});
|
|
@@ -90,4 +146,4 @@ export {
|
|
|
90
146
|
no_side_effects_default as default,
|
|
91
147
|
ruleId
|
|
92
148
|
};
|
|
93
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
149
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL25vLXNpZGUtZWZmZWN0cy50cyJdLAogICJtYXBwaW5ncyI6ICI7QUFRQSxTQUFTLG1CQUFtQjtBQUM1QixTQUFTLGdCQUFnQjtBQUN6QixPQUFPLHlCQUF5QjtBQU16QixJQUFNLFNBQVM7QUFDdEIsSUFBTSxrQkFBa0I7QUFLeEIsSUFBTSx3QkFBd0IsQ0FBQyxTQUM3QixLQUFLLFNBQVMsU0FBUyxlQUFlO0FBR3hDLElBQU0sb0JBQW9CLENBQUMsY0FDekIsc0JBQXNCLFNBQVMsS0FBSyxVQUFVLFdBQVcsU0FBUyxTQUFTLGVBQWU7QUFHNUYsSUFBTSx1Q0FBdUMsQ0FBQyxTQUM1QyxLQUFLLFNBQVMsU0FBUyxlQUFlLHVCQUN0QyxLQUFLLGFBQWEsU0FBUyxLQUMzQixLQUFLLGFBQWEsQ0FBQyxHQUFHLE1BQU0sU0FBUyxTQUFTLGVBQWU7QUFHL0QsSUFBTSxnQ0FBZ0MsQ0FBQyxTQUNyQyxLQUFLLFNBQVMsU0FBUyxlQUFlLHVCQUF1QixLQUFLLFNBQVMsV0FBVyxLQUFLLFNBQVM7QUFHdEcsSUFBTSx5QkFBeUIsQ0FBQyxTQUM5QjtBQUFBLEVBQ0UsU0FBUyxlQUFlO0FBQUEsRUFDeEIsU0FBUyxlQUFlO0FBQUEsRUFDeEIsU0FBUyxlQUFlO0FBQUEsRUFDeEIsU0FBUyxlQUFlO0FBQUEsRUFDeEIsU0FBUyxlQUFlO0FBQUEsRUFDeEIsU0FBUyxlQUFlO0FBQzFCLEVBQUUsU0FBUyxLQUFLLElBQUk7QUFHdEIsSUFBTSx5QkFBeUIsQ0FBQyxTQUM5QixLQUFLLFNBQVMsU0FBUyxlQUFlLHVCQUN0QyxLQUFLLFdBQVcsU0FBUyxTQUFTLGVBQWU7QUFLbkQsSUFBTSxxQkFBcUIsQ0FBQyxNQUErQix3QkFDekQsS0FBSyxPQUFPLFNBQVMsU0FBUyxlQUFlLGNBQWMsQ0FBQyxvQkFBb0IsU0FBUyxLQUFLLE9BQU8sSUFBSTtBQUczRyxJQUFNLDJCQUEyQixDQUFDLE1BQStCLHdCQUMvRCxLQUFLLE9BQU8sU0FBUyxTQUFTLGVB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
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
2
|
export declare const ruleId = "no-random-v4-uuid";
|
|
3
3
|
declare const NO_RANDOM_V4_UUID = "NO_RANDOM_V4_UUID";
|
|
4
|
-
declare const
|
|
4
|
+
declare const NO_UUID_MODULE_FOR_V4 = "NO_UUID_MODULE_FOR_V4";
|
|
5
|
+
declare const rule: TSESLint.RuleModule<typeof NO_RANDOM_V4_UUID | typeof NO_UUID_MODULE_FOR_V4>;
|
|
5
6
|
export default rule;
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@checkdigit/eslint-plugin","version":"7.13.
|
|
1
|
+
{"name":"@checkdigit/eslint-plugin","version":"7.13.1-PR.120-6b16","description":"Check Digit eslint plugins","keywords":["eslint","eslintplugin"],"homepage":"https://github.com/checkdigit/eslint-plugin#readme","bugs":{"url":"https://github.com/checkdigit/eslint-plugin/issues"},"repository":{"type":"git","url":"https://github.com/checkdigit/eslint-plugin"},"license":"MIT","author":"Check Digit, LLC","sideEffects":false,"type":"module","exports":{".":{"types":"./dist-types/index.d.ts","import":"./dist-mjs/index.mjs","default":"./dist-mjs/index.mjs"}},"files":["src","dist-types","dist-mjs","!src/**/test/**","!src/**/*.test.ts","!src/**/*.spec.ts","!dist-types/**/test/**","!dist-types/**/*.test.d.ts","!dist-types/**/*.spec.d.ts","!dist-mjs/**/test/**","!dist-mjs/**/*.test.mjs","!dist-mjs/**/*.spec.mjs","SECURITY.md"],"scripts":{"build:dist-mjs":"rimraf dist-mjs && npx builder --type=module --sourceMap --outDir=dist-mjs && node dist-mjs/index.mjs","build:dist-types":"rimraf dist-types && npx builder --type=types --outDir=dist-types","ci:compile":"tsc --noEmit","ci:coverage":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=true","ci:lint":"npm run lint","ci:style":"npm run prettier","ci:test":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=false","lint":"eslint --max-warnings 0 .","lint:fix":"eslint --max-warnings 0 --fix .","prepare":"","prepublishOnly":"npm run build:dist-types && npm run build:dist-mjs","prettier":"prettier --ignore-path .gitignore --list-different .","prettier:fix":"prettier --ignore-path .gitignore --write .","test":"npm run ci:compile && npm run ci:test && npm run ci:lint && npm run ci:style"},"prettier":"@checkdigit/prettier-config","jest":{"preset":"@checkdigit/jest-config"},"dependencies":{"@typescript-eslint/type-utils":"^8.23.0","@typescript-eslint/utils":"^8.23.0","http-status-codes":"^2.3.0","ts-api-utils":"^2.0.1"},"devDependencies":{"@checkdigit/jest-config":"^6.0.2","@checkdigit/prettier-config":"^6.1.0","@checkdigit/typescript-config":"^9.0.0","@eslint/js":"^9.19.0","@types/eslint":"^9.6.1","@types/eslint-config-prettier":"^6.11.3","@typescript-eslint/parser":"^8.23.0","@typescript-eslint/rule-tester":"^8.23.0","eslint":"^9.19.0","eslint-config-prettier":"^10.0.1","eslint-import-resolver-typescript":"^3.7.0","eslint-plugin-eslint-plugin":"^6.4.0","eslint-plugin-import":"^2.31.0","eslint-plugin-no-only-tests":"^3.3.0","eslint-plugin-no-secrets":"^2.2.1","eslint-plugin-node":"^11.1.0","eslint-plugin-sonarjs":"1.0.4","rimraf":"^6.0.1","typescript-eslint":"^8.23.0"},"peerDependencies":{"eslint":">=9 <10"},"engines":{"node":">=20.17"}}
|
package/src/file-path-comment.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -32,7 +32,6 @@ import noEnum from './no-enum.ts';
|
|
|
32
32
|
import noSideEffects from './no-side-effects.ts';
|
|
33
33
|
import noRandomV4UUID from './no-random-v4-uuid.ts';
|
|
34
34
|
import noTestImport from './no-test-import.ts';
|
|
35
|
-
import noUtil from './no-util.ts';
|
|
36
35
|
import noUuid from './no-uuid.ts';
|
|
37
36
|
import noWallabyComment from './no-wallaby-comment.ts';
|
|
38
37
|
import objectLiteralResponse from './object-literal-response.ts';
|
|
@@ -48,7 +47,6 @@ const rules: Record<string, TSESLint.LooseRuleDefinition> = {
|
|
|
48
47
|
'no-enum': noEnum,
|
|
49
48
|
'no-random-v4-uuid': noRandomV4UUID,
|
|
50
49
|
'no-status-code-assert': noStatusCodeAssert,
|
|
51
|
-
'no-util': noUtil,
|
|
52
50
|
'no-uuid': noUuid,
|
|
53
51
|
'require-assert-message': requireAssertMessage,
|
|
54
52
|
'require-strict-assert': requireStrictAssert,
|
|
@@ -87,16 +85,12 @@ const configs: Record<string, TSESLint.FlatConfig.Config[]> = {
|
|
|
87
85
|
'@checkdigit/file-path-comment': 'error',
|
|
88
86
|
'@checkdigit/no-random-v4-uuid': 'error',
|
|
89
87
|
'@checkdigit/no-status-code-assert': 'error',
|
|
90
|
-
'@checkdigit/no-util': 'error',
|
|
91
88
|
'@checkdigit/no-uuid': 'error',
|
|
92
89
|
'@checkdigit/require-assert-message': 'error',
|
|
93
90
|
'@checkdigit/require-strict-assert': 'error',
|
|
94
91
|
'@checkdigit/require-ts-extension-imports-exports': 'error',
|
|
95
92
|
'@checkdigit/no-wallaby-comment': 'error',
|
|
96
|
-
'@checkdigit/no-side-effects':
|
|
97
|
-
'error',
|
|
98
|
-
{ excludedIdentifiers: ['assert', 'debug', 'log', 'promisify', 'Symbol.for'] },
|
|
99
|
-
],
|
|
93
|
+
'@checkdigit/no-side-effects': 'error',
|
|
100
94
|
'@checkdigit/regular-expression-comment': 'error',
|
|
101
95
|
'@checkdigit/require-assert-predicate-rejects-throws': 'error',
|
|
102
96
|
'@checkdigit/object-literal-response': 'error',
|
|
@@ -125,7 +119,6 @@ const configs: Record<string, TSESLint.FlatConfig.Config[]> = {
|
|
|
125
119
|
'@checkdigit/file-path-comment': 'off',
|
|
126
120
|
'@checkdigit/no-random-v4-uuid': 'error',
|
|
127
121
|
'@checkdigit/no-status-code-assert': 'error',
|
|
128
|
-
'@checkdigit/no-util': 'error',
|
|
129
122
|
'@checkdigit/no-uuid': 'error',
|
|
130
123
|
'@checkdigit/require-assert-message': 'error',
|
|
131
124
|
'@checkdigit/require-strict-assert': 'error',
|
package/src/no-random-v4-uuid.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// no-random-v4-uuid.ts
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
* Copyright (c) 2022-
|
|
4
|
+
* Copyright (c) 2022-2025 Check Digit, LLC
|
|
5
5
|
*
|
|
6
6
|
* This code is licensed under the MIT license (see LICENSE.txt for details).
|
|
7
7
|
*/
|
|
@@ -11,74 +11,83 @@ import getDocumentationUrl from './get-documentation-url.ts';
|
|
|
11
11
|
|
|
12
12
|
export const ruleId = 'no-random-v4-uuid';
|
|
13
13
|
const NO_RANDOM_V4_UUID = 'NO_RANDOM_V4_UUID';
|
|
14
|
+
const NO_UUID_MODULE_FOR_V4 = 'NO_UUID_MODULE_FOR_V4';
|
|
14
15
|
|
|
15
16
|
const createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
let updatedUuidDefaultAlias = uuidDefaultAlias;
|
|
18
|
+
interface Aliases {
|
|
19
|
+
uuid4Alias?: string;
|
|
20
|
+
uuidDefaultAlias?: string;
|
|
21
|
+
nodeCryptoRandomUUIDAlias?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const processImportDeclaration = (node: TSESTree.ImportDeclaration, aliases: Aliases) => {
|
|
25
25
|
node.specifiers.forEach((specifier) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {
|
|
27
|
+
if (
|
|
28
|
+
node.source.value === 'uuid' &&
|
|
29
|
+
specifier.imported.type === AST_NODE_TYPES.Identifier &&
|
|
30
|
+
specifier.imported.name === 'v4'
|
|
31
|
+
) {
|
|
32
|
+
aliases.uuid4Alias = specifier.local.name;
|
|
33
|
+
} else if (
|
|
34
|
+
node.source.value === 'node:crypto' &&
|
|
35
|
+
specifier.imported.type === AST_NODE_TYPES.Identifier &&
|
|
36
|
+
specifier.imported.name === 'randomUUID'
|
|
37
|
+
) {
|
|
38
|
+
aliases.nodeCryptoRandomUUIDAlias = specifier.local.name;
|
|
39
|
+
}
|
|
40
|
+
} else if (specifier.type === AST_NODE_TYPES.ImportDefaultSpecifier && node.source.value === 'uuid') {
|
|
41
|
+
aliases.uuidDefaultAlias = specifier.local.name;
|
|
35
42
|
}
|
|
36
43
|
});
|
|
37
|
-
return { uuid4Alias: updatedUuid4Alias, uuidDefaultAlias: updatedUuidDefaultAlias };
|
|
38
44
|
};
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
node: TSESTree.CallExpression,
|
|
43
|
-
uuid4Alias: string | undefined,
|
|
44
|
-
uuidDefaultAlias: string | undefined,
|
|
45
|
-
): boolean =>
|
|
46
|
-
(node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === uuid4Alias) ||
|
|
46
|
+
const isUuid4Call = (node: TSESTree.CallExpression, aliases: Aliases): boolean =>
|
|
47
|
+
(node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === aliases.uuid4Alias) ||
|
|
47
48
|
(node.callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
48
49
|
node.callee.object.type === AST_NODE_TYPES.Identifier &&
|
|
49
|
-
node.callee.object.name === uuidDefaultAlias &&
|
|
50
|
+
node.callee.object.name === aliases.uuidDefaultAlias &&
|
|
50
51
|
node.callee.property.type === AST_NODE_TYPES.Identifier &&
|
|
51
52
|
node.callee.property.name === 'v4');
|
|
52
53
|
|
|
53
|
-
const
|
|
54
|
+
const isCryptoRandomUUIDCall = (node: TSESTree.CallExpression, alias?: string): boolean =>
|
|
55
|
+
(node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === alias) ||
|
|
56
|
+
(node.callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
57
|
+
node.callee.object.type === AST_NODE_TYPES.Identifier &&
|
|
58
|
+
node.callee.object.name === 'crypto' &&
|
|
59
|
+
node.callee.property.type === AST_NODE_TYPES.Identifier &&
|
|
60
|
+
node.callee.property.name === 'randomUUID');
|
|
61
|
+
|
|
62
|
+
const rule: TSESLint.RuleModule<typeof NO_RANDOM_V4_UUID | typeof NO_UUID_MODULE_FOR_V4> = createRule({
|
|
54
63
|
name: ruleId,
|
|
55
64
|
meta: {
|
|
56
65
|
type: 'problem',
|
|
57
66
|
docs: {
|
|
58
|
-
description:
|
|
67
|
+
description:
|
|
68
|
+
'Disallow the use of `uuid.v4` and `crypto.randomUUID` for generating random v4 UUIDs, and suggest replacing `uuid` module usage with `crypto.randomUUID`.',
|
|
59
69
|
},
|
|
60
70
|
schema: [],
|
|
61
71
|
messages: {
|
|
62
|
-
[NO_RANDOM_V4_UUID]: 'Avoid using `
|
|
72
|
+
[NO_RANDOM_V4_UUID]: 'Avoid using `crypto.randomUUID` for generating random v4 UUIDs.',
|
|
73
|
+
[NO_UUID_MODULE_FOR_V4]: 'Avoid using the `uuid` module for v4 UUID generation. Use `crypto.randomUUID` instead.',
|
|
63
74
|
},
|
|
64
75
|
},
|
|
65
76
|
defaultOptions: [],
|
|
66
77
|
create(context) {
|
|
67
|
-
|
|
68
|
-
let uuidDefaultAlias: string | undefined;
|
|
69
|
-
let hasUuidImport = false;
|
|
78
|
+
const aliases: Aliases = {};
|
|
70
79
|
|
|
71
80
|
return {
|
|
72
81
|
ImportDeclaration(node: TSESTree.ImportDeclaration) {
|
|
73
|
-
|
|
74
|
-
hasUuidImport = true;
|
|
75
|
-
const result = processImportDeclaration(node, uuid4Alias, uuidDefaultAlias);
|
|
76
|
-
uuid4Alias = result.uuid4Alias;
|
|
77
|
-
uuidDefaultAlias = result.uuidDefaultAlias;
|
|
78
|
-
}
|
|
82
|
+
processImportDeclaration(node, aliases);
|
|
79
83
|
},
|
|
80
84
|
CallExpression(node: TSESTree.CallExpression) {
|
|
81
|
-
if (
|
|
85
|
+
if (isUuid4Call(node, aliases)) {
|
|
86
|
+
context.report({
|
|
87
|
+
node,
|
|
88
|
+
messageId: NO_UUID_MODULE_FOR_V4,
|
|
89
|
+
});
|
|
90
|
+
} else if (isCryptoRandomUUIDCall(node, aliases.nodeCryptoRandomUUIDAlias)) {
|
|
82
91
|
context.report({
|
|
83
92
|
node,
|
|
84
93
|
messageId: NO_RANDOM_V4_UUID,
|
package/src/no-side-effects.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// no-side-effects.ts
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
* Copyright (c) 2022-
|
|
4
|
+
* Copyright (c) 2022-2025 Check Digit, LLC
|
|
5
5
|
*
|
|
6
6
|
* This code is licensed under the MIT license (see LICENSE.txt for details).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
10
10
|
import { TSESTree } from '@typescript-eslint/typescript-estree';
|
|
11
|
+
import getDocumentationUrl from './get-documentation-url.ts';
|
|
11
12
|
|
|
12
13
|
interface RuleOptions {
|
|
13
14
|
excludedIdentifiers: string[];
|
|
@@ -16,61 +17,74 @@ interface RuleOptions {
|
|
|
16
17
|
export const ruleId = 'no-side-effects';
|
|
17
18
|
const NO_SIDE_EFFECTS = 'NO_SIDE_EFFECTS';
|
|
18
19
|
|
|
19
|
-
// Type
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
20
|
+
// Type guards
|
|
21
|
+
|
|
22
|
+
// Checks if a node is an ExpressionStatement
|
|
23
|
+
const isExpressionStatement = (node: TSESTree.Node): node is TSESTree.ExpressionStatement =>
|
|
24
|
+
node.type === TSESTree.AST_NODE_TYPES.ExpressionStatement;
|
|
25
|
+
|
|
26
|
+
// Checks if a statement is an AwaitExpression
|
|
27
|
+
const isAwaitExpression = (statement: TSESTree.Node): boolean =>
|
|
28
|
+
isExpressionStatement(statement) && statement.expression.type === TSESTree.AST_NODE_TYPES.AwaitExpression;
|
|
29
|
+
|
|
30
|
+
// Checks if a node is a VariableDeclaration with an AwaitExpression
|
|
31
|
+
const isVariableDeclarationAwaitExpression = (node: TSESTree.Node): boolean =>
|
|
32
|
+
node.type === TSESTree.AST_NODE_TYPES.VariableDeclaration &&
|
|
33
|
+
node.declarations.length > 0 &&
|
|
34
|
+
node.declarations[0]?.init?.type === TSESTree.AST_NODE_TYPES.AwaitExpression;
|
|
35
|
+
|
|
36
|
+
// Checks if a node is a VariableDeclaration that is not const or using
|
|
37
|
+
const isNotValidVariableDeclaration = (node: TSESTree.Node): boolean =>
|
|
38
|
+
node.type === TSESTree.AST_NODE_TYPES.VariableDeclaration && node.kind !== 'const' && node.kind !== 'using';
|
|
39
|
+
|
|
40
|
+
// Checks if a node is a control flow statement
|
|
41
|
+
const isControlFlowStatement = (node: TSESTree.Node): boolean =>
|
|
42
|
+
[
|
|
43
|
+
TSESTree.AST_NODE_TYPES.TryStatement,
|
|
44
|
+
TSESTree.AST_NODE_TYPES.IfStatement,
|
|
45
|
+
TSESTree.AST_NODE_TYPES.SwitchStatement,
|
|
46
|
+
TSESTree.AST_NODE_TYPES.ForStatement,
|
|
47
|
+
TSESTree.AST_NODE_TYPES.WhileStatement,
|
|
48
|
+
TSESTree.AST_NODE_TYPES.DoWhileStatement,
|
|
49
|
+
].includes(node.type);
|
|
50
|
+
|
|
51
|
+
// Checks if a node is an AssignmentExpression
|
|
52
|
+
const isAssignmentExpression = (node: TSESTree.Node): boolean =>
|
|
53
|
+
node.type === TSESTree.AST_NODE_TYPES.ExpressionStatement &&
|
|
54
|
+
node.expression.type === TSESTree.AST_NODE_TYPES.AssignmentExpression;
|
|
55
|
+
|
|
56
|
+
// Helper functions
|
|
57
|
+
|
|
58
|
+
// Checks if the callee is an identifier and not excluded
|
|
59
|
+
const isIdentifierCallee = (node: TSESTree.CallExpression, excludedIdentifiers: string[]): boolean =>
|
|
60
|
+
node.callee.type === TSESTree.AST_NODE_TYPES.Identifier && !excludedIdentifiers.includes(node.callee.name);
|
|
61
|
+
|
|
62
|
+
// Checks if the callee is a member expression and not excluded
|
|
63
|
+
const isMemberExpressionCallee = (node: TSESTree.CallExpression, excludedIdentifiers: string[]): boolean =>
|
|
64
|
+
node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
65
|
+
node.callee.object.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
66
|
+
node.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
67
|
+
!excludedIdentifiers.includes(`${node.callee.object.name}.${node.callee.property.name}`);
|
|
68
|
+
|
|
69
|
+
// Checks if the callee is a member expression with a non-identifier object
|
|
70
|
+
const isNonIdentifierObjectMemberExpressionCallee = (node: TSESTree.CallExpression): boolean =>
|
|
71
|
+
node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
72
|
+
node.callee.object.type !== TSESTree.AST_NODE_TYPES.Identifier;
|
|
73
|
+
|
|
74
|
+
// Checks if a statement is a CallExpression with a member expression callee
|
|
75
|
+
const isCallExpressionCalleeMemberExpression = (statement: TSESTree.Node, excludedIdentifiers: string[]): boolean =>
|
|
76
|
+
isExpressionStatement(statement) &&
|
|
77
|
+
statement.expression.type === TSESTree.AST_NODE_TYPES.CallExpression &&
|
|
78
|
+
statement.expression.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
79
|
+
statement.expression.callee.object.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
80
|
+
statement.expression.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
81
|
+
!excludedIdentifiers.includes(statement.expression.callee.object.name) &&
|
|
82
|
+
!excludedIdentifiers.includes(
|
|
83
|
+
`${statement.expression.callee.object.name}.${statement.expression.callee.property.name}`,
|
|
37
84
|
);
|
|
38
|
-
}
|
|
39
85
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
node.type === TSESTree.AST_NODE_TYPES.VariableDeclaration &&
|
|
44
|
-
node.declarations.length > 0 &&
|
|
45
|
-
node.declarations[0]?.init?.type === TSESTree.AST_NODE_TYPES.AwaitExpression
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Helper function to check if the callee is an identifier and not excluded
|
|
50
|
-
function isIdentifierCallee(node: TSESTree.CallExpression, excludedIdentifiers: string[]): boolean {
|
|
51
|
-
return node.callee.type === TSESTree.AST_NODE_TYPES.Identifier && !excludedIdentifiers.includes(node.callee.name);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Helper function to check if the callee is a member expression and not excluded
|
|
55
|
-
function isMemberExpressionCallee(node: TSESTree.CallExpression, excludedIdentifiers: string[]): boolean {
|
|
56
|
-
return (
|
|
57
|
-
node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
58
|
-
node.callee.object.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
59
|
-
node.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
60
|
-
!excludedIdentifiers.includes(`${node.callee.object.name}.${node.callee.property.name}`)
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Helper function to check if the callee is a member expression with a non-identifier object
|
|
65
|
-
function isNonIdentifierObjectMemberExpressionCallee(node: TSESTree.CallExpression): boolean {
|
|
66
|
-
return (
|
|
67
|
-
node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
68
|
-
node.callee.object.type !== TSESTree.AST_NODE_TYPES.Identifier
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// To check if it is a variable declaration with a call expression i.e const configuration = new Class(); or Member Expressions i.e const server = http.createServer();
|
|
73
|
-
function isVariableDeclarationCallExpression(node: TSESTree.Node, excludedIdentifiers: string[]): boolean {
|
|
86
|
+
// Checks if a node is a VariableDeclaration with a CallExpression
|
|
87
|
+
const isVariableDeclarationCallExpression = (node: TSESTree.Node, excludedIdentifiers: string[]): boolean => {
|
|
74
88
|
if (node.type !== TSESTree.AST_NODE_TYPES.VariableDeclaration || node.declarations.length === 0) {
|
|
75
89
|
return false;
|
|
76
90
|
}
|
|
@@ -80,35 +94,108 @@ function isVariableDeclarationCallExpression(node: TSESTree.Node, excludedIdenti
|
|
|
80
94
|
return false;
|
|
81
95
|
}
|
|
82
96
|
|
|
97
|
+
const callee = init.callee;
|
|
98
|
+
if (
|
|
99
|
+
callee.type === TSESTree.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
100
|
+
callee.type === TSESTree.AST_NODE_TYPES.FunctionExpression
|
|
101
|
+
) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
83
105
|
return (
|
|
84
106
|
isIdentifierCallee(init, excludedIdentifiers) ||
|
|
85
107
|
isMemberExpressionCallee(init, excludedIdentifiers) ||
|
|
86
108
|
isNonIdentifierObjectMemberExpressionCallee(init)
|
|
87
109
|
);
|
|
88
|
-
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Checks if an ExportNamedDeclaration has side effects
|
|
113
|
+
const isExportNamedDeclarationWithSideEffects = (statement: TSESTree.Node, excludedIdentifiers: string[]): boolean =>
|
|
114
|
+
statement.type === TSESTree.AST_NODE_TYPES.ExportNamedDeclaration &&
|
|
115
|
+
statement.declaration !== null &&
|
|
116
|
+
(isVariableDeclarationAwaitExpression(statement.declaration) ||
|
|
117
|
+
isVariableDeclarationCallExpression(statement.declaration, excludedIdentifiers));
|
|
118
|
+
|
|
119
|
+
// Checks if an ExpressionStatement has side effects
|
|
120
|
+
const isExpressionStatementWithSideEffects = (statement: TSESTree.Node, excludedIdentifiers: string[]): boolean =>
|
|
121
|
+
statement.type === TSESTree.AST_NODE_TYPES.ExpressionStatement &&
|
|
122
|
+
statement.expression.type === TSESTree.AST_NODE_TYPES.CallExpression &&
|
|
123
|
+
((statement.expression.callee.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
124
|
+
!excludedIdentifiers.includes(statement.expression.callee.name)) ||
|
|
125
|
+
(statement.expression.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
126
|
+
statement.expression.callee.object.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
127
|
+
statement.expression.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
128
|
+
!excludedIdentifiers.includes(
|
|
129
|
+
`${statement.expression.callee.object.name}.${statement.expression.callee.property.name}`,
|
|
130
|
+
)));
|
|
131
|
+
|
|
132
|
+
// Checks if a node is a VariableDeclaration with a NewExpression
|
|
133
|
+
const isVariableDeclarationNewExpression = (node: TSESTree.Node): boolean => {
|
|
134
|
+
if (node.type !== TSESTree.AST_NODE_TYPES.VariableDeclaration || node.declarations.length === 0) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
89
137
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
138
|
+
const init = node.declarations[0]?.init;
|
|
139
|
+
return init?.type === TSESTree.AST_NODE_TYPES.NewExpression;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Checks if a node is a FunctionDeclaration with an AwaitExpression
|
|
143
|
+
const isFunctionDeclarationAwaitExpression = (node: TSESTree.Node): boolean =>
|
|
144
|
+
node.type === TSESTree.AST_NODE_TYPES.FunctionDeclaration &&
|
|
145
|
+
node.body.body.some(
|
|
146
|
+
(statement) =>
|
|
147
|
+
statement.type === TSESTree.AST_NODE_TYPES.ExpressionStatement &&
|
|
148
|
+
statement.expression.type === TSESTree.AST_NODE_TYPES.AwaitExpression,
|
|
101
149
|
);
|
|
102
|
-
}
|
|
103
150
|
|
|
104
|
-
|
|
151
|
+
// Update the hasSideEffects function to return a string indicating the type of side effect
|
|
152
|
+
const hasSideEffects = (statement: TSESTree.Node, excludedIdentifiers: string[]): string | null => {
|
|
153
|
+
if (isAwaitExpression(statement)) {
|
|
154
|
+
return TSESTree.AST_NODE_TYPES.AwaitExpression;
|
|
155
|
+
}
|
|
156
|
+
if (isCallExpressionCalleeMemberExpression(statement, excludedIdentifiers)) {
|
|
157
|
+
return 'CallExpressionCalleeMemberExpression';
|
|
158
|
+
}
|
|
159
|
+
if (isVariableDeclarationAwaitExpression(statement)) {
|
|
160
|
+
return 'VariableDeclarationAwaitExpression';
|
|
161
|
+
}
|
|
162
|
+
if (isVariableDeclarationCallExpression(statement, excludedIdentifiers)) {
|
|
163
|
+
return 'VariableDeclarationCallExpression';
|
|
164
|
+
}
|
|
165
|
+
if (isVariableDeclarationNewExpression(statement)) {
|
|
166
|
+
return 'VariableDeclarationNewExpression';
|
|
167
|
+
}
|
|
168
|
+
if (isExportNamedDeclarationWithSideEffects(statement, excludedIdentifiers)) {
|
|
169
|
+
return 'ExportNamedDeclarationWithSideEffects';
|
|
170
|
+
}
|
|
171
|
+
if (isExpressionStatementWithSideEffects(statement, excludedIdentifiers)) {
|
|
172
|
+
return 'ExpressionStatementWithSideEffects';
|
|
173
|
+
}
|
|
174
|
+
if (isControlFlowStatement(statement)) {
|
|
175
|
+
return 'ControlFlowStatement';
|
|
176
|
+
}
|
|
177
|
+
if (isNotValidVariableDeclaration(statement)) {
|
|
178
|
+
return 'NotValidVariableDeclaration';
|
|
179
|
+
}
|
|
180
|
+
if (isAssignmentExpression(statement)) {
|
|
181
|
+
return TSESTree.AST_NODE_TYPES.AssignmentExpression;
|
|
182
|
+
}
|
|
183
|
+
if (isFunctionDeclarationAwaitExpression(statement)) {
|
|
184
|
+
return 'FunctionDeclarationAwaitExpression';
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const createRule: ReturnType<typeof ESLintUtils.RuleCreator> = ESLintUtils.RuleCreator((name) =>
|
|
190
|
+
getDocumentationUrl(name),
|
|
191
|
+
);
|
|
105
192
|
|
|
106
193
|
const rule: ReturnType<typeof createRule> = createRule({
|
|
107
194
|
name: ruleId,
|
|
108
195
|
meta: {
|
|
109
196
|
type: 'problem',
|
|
110
197
|
docs: {
|
|
111
|
-
description: 'Ensure no side effects can occur at the module-level',
|
|
198
|
+
description: 'Ensure no side effects can occur at the module-level only if exporting module',
|
|
112
199
|
},
|
|
113
200
|
schema: [
|
|
114
201
|
{
|
|
@@ -144,10 +231,14 @@ const rule: ReturnType<typeof createRule> = createRule({
|
|
|
144
231
|
}
|
|
145
232
|
|
|
146
233
|
node.body.forEach((statement: TSESTree.Node) => {
|
|
147
|
-
|
|
234
|
+
const sideEffectType = hasSideEffects(statement, excludedIdentifiers);
|
|
235
|
+
if (sideEffectType !== null) {
|
|
148
236
|
context.report({
|
|
149
237
|
node: statement,
|
|
150
238
|
messageId: NO_SIDE_EFFECTS,
|
|
239
|
+
data: {
|
|
240
|
+
sideEffectType,
|
|
241
|
+
},
|
|
151
242
|
});
|
|
152
243
|
}
|
|
153
244
|
});
|
package/dist-mjs/no-util.mjs
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// src/no-util.ts
|
|
2
|
-
import { AST_TOKEN_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
3
|
-
var ruleId = "no-util";
|
|
4
|
-
var NO_UTIL = "NO_UTIL";
|
|
5
|
-
var createRule = ESLintUtils.RuleCreator((name) => name);
|
|
6
|
-
var rule = createRule({
|
|
7
|
-
name: ruleId,
|
|
8
|
-
meta: {
|
|
9
|
-
type: "problem",
|
|
10
|
-
docs: {
|
|
11
|
-
description: "Detects if file name is util"
|
|
12
|
-
},
|
|
13
|
-
schema: [],
|
|
14
|
-
messages: {
|
|
15
|
-
[NO_UTIL]: "File name '{{filename}}' contains banned 'util' pattern."
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
defaultOptions: [],
|
|
19
|
-
create(context) {
|
|
20
|
-
return {
|
|
21
|
-
Program() {
|
|
22
|
-
const filename = context.filename;
|
|
23
|
-
const utilRegex = /(?:^|[-_/])util(?=[-_./]|$)/iu;
|
|
24
|
-
if (utilRegex.test(filename)) {
|
|
25
|
-
const sourceCode = context.sourceCode;
|
|
26
|
-
const tokens = sourceCode.tokensAndComments;
|
|
27
|
-
const firstNonCommentToken = tokens.find(
|
|
28
|
-
(token) => token.type !== AST_TOKEN_TYPES.Block && token.type !== AST_TOKEN_TYPES.Line
|
|
29
|
-
);
|
|
30
|
-
if (firstNonCommentToken !== void 0) {
|
|
31
|
-
context.report({
|
|
32
|
-
messageId: NO_UTIL,
|
|
33
|
-
data: { filename },
|
|
34
|
-
node: firstNonCommentToken
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
var no_util_default = rule;
|
|
43
|
-
export {
|
|
44
|
-
no_util_default as default,
|
|
45
|
-
ruleId
|
|
46
|
-
};
|
|
47
|
-
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL25vLXV0aWwudHMiXSwKICAibWFwcGluZ3MiOiAiO0FBUUEsU0FBUyxpQkFBaUIsbUJBQW1CO0FBRXRDLElBQU0sU0FBUztBQUN0QixJQUFNLFVBQVU7QUFFaEIsSUFBTSxhQUFhLFlBQVksWUFBWSxDQUFDLFNBQVMsSUFBSTtBQUV6RCxJQUFNLE9BQStDLFdBQVc7QUFBQSxFQUM5RCxNQUFNO0FBQUEsRUFDTixNQUFNO0FBQUEsSUFDSixNQUFNO0FBQUEsSUFDTixNQUFNO0FBQUEsTUFDSixhQUFhO0FBQUEsSUFDZjtBQUFBLElBQ0EsUUFBUSxDQUFDO0FBQUEsSUFDVCxVQUFVO0FBQUEsTUFDUixDQUFDLE9BQU8sR0FBRztBQUFBLElBQ2I7QUFBQSxFQUNGO0FBQUEsRUFDQSxnQkFBZ0IsQ0FBQztBQUFBLEVBQ2pCLE9BQU8sU0FBUztBQUNkLFdBQU87QUFBQSxNQUNMLFVBQVU7QUFDUixjQUFNLFdBQVcsUUFBUTtBQUN6QixjQUFNLFlBQVk7QUFDbEIsWUFBSSxVQUFVLEtBQUssUUFBUSxHQUFHO0FBQzVCLGdCQUFNLGFBQWEsUUFBUTtBQUMzQixnQkFBTSxTQUFTLFdBQVc7QUFDMUIsZ0JBQU0sdUJBQXVCLE9BQU87QUFBQSxZQUNsQyxDQUFDLFVBQVUsTUFBTSxTQUFTLGdCQUFnQixTQUFTLE1BQU0sU0FBUyxnQkFBZ0I7QUFBQSxVQUNwRjtBQUNBLGNBQUkseUJBQXlCLFFBQVc7QUFDdEMsb0JBQVEsT0FBTztBQUFBLGNBQ2IsV0FBVztBQUFBLGNBQ1gsTUFBTSxFQUFFLFNBQVM7QUFBQSxjQUNqQixNQUFNO0FBQUEsWUFDUixDQUFDO0FBQUEsVUFDSDtBQUFBLFFBQ0Y7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRixDQUFDO0FBRUQsSUFBTyxrQkFBUTsiLAogICJuYW1lcyI6IFtdCn0K
|
package/dist-types/no-util.d.ts
DELETED
package/src/no-util.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// no-util.ts
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
* Copyright (c) 2021-2025 Check Digit, LLC
|
|
5
|
-
*
|
|
6
|
-
* This code is licensed under the MIT license (see LICENSE.txt for details).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { AST_TOKEN_TYPES, ESLintUtils } from '@typescript-eslint/utils';
|
|
10
|
-
|
|
11
|
-
export const ruleId = 'no-util';
|
|
12
|
-
const NO_UTIL = 'NO_UTIL';
|
|
13
|
-
|
|
14
|
-
const createRule = ESLintUtils.RuleCreator((name) => name);
|
|
15
|
-
|
|
16
|
-
const rule: ESLintUtils.RuleModule<typeof NO_UTIL> = createRule({
|
|
17
|
-
name: ruleId,
|
|
18
|
-
meta: {
|
|
19
|
-
type: 'problem',
|
|
20
|
-
docs: {
|
|
21
|
-
description: 'Detects if file name is util',
|
|
22
|
-
},
|
|
23
|
-
schema: [],
|
|
24
|
-
messages: {
|
|
25
|
-
[NO_UTIL]: "File name '{{filename}}' contains banned 'util' pattern.",
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
defaultOptions: [],
|
|
29
|
-
create(context) {
|
|
30
|
-
return {
|
|
31
|
-
Program() {
|
|
32
|
-
const filename = context.filename;
|
|
33
|
-
const utilRegex = /(?:^|[-_/])util(?=[-_./]|$)/iu;
|
|
34
|
-
if (utilRegex.test(filename)) {
|
|
35
|
-
const sourceCode = context.sourceCode;
|
|
36
|
-
const tokens = sourceCode.tokensAndComments;
|
|
37
|
-
const firstNonCommentToken = tokens.find(
|
|
38
|
-
(token) => token.type !== AST_TOKEN_TYPES.Block && token.type !== AST_TOKEN_TYPES.Line,
|
|
39
|
-
);
|
|
40
|
-
if (firstNonCommentToken !== undefined) {
|
|
41
|
-
context.report({
|
|
42
|
-
messageId: NO_UTIL,
|
|
43
|
-
data: { filename },
|
|
44
|
-
node: firstNonCommentToken,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
export default rule;
|