@thethracian/oxlint-config 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +125 -19
- package/dist/codemod-fix/cli.d.ts +3 -0
- package/dist/codemod-fix/cli.d.ts.map +1 -0
- package/dist/codemod-fix/cli.js +22 -0
- package/dist/codemod-fix/cli.js.map +1 -0
- package/dist/codemod-fix/index.d.ts +44 -0
- package/dist/codemod-fix/index.d.ts.map +1 -0
- package/dist/codemod-fix/index.js +110 -0
- package/dist/codemod-fix/index.js.map +1 -0
- package/dist/codemods/arrow-body-style.d.ts +7 -0
- package/dist/codemods/arrow-body-style.d.ts.map +1 -0
- package/dist/codemods/arrow-body-style.js +106 -0
- package/dist/codemods/arrow-body-style.js.map +1 -0
- package/dist/codemods/comment-format.d.ts +24 -0
- package/dist/codemods/comment-format.d.ts.map +1 -0
- package/dist/codemods/comment-format.js +100 -0
- package/dist/codemods/comment-format.js.map +1 -0
- package/dist/codemods/explicit-return-types.d.ts +7 -0
- package/dist/codemods/explicit-return-types.d.ts.map +1 -0
- package/dist/codemods/explicit-return-types.js +350 -0
- package/dist/codemods/explicit-return-types.js.map +1 -0
- package/dist/codemods/format-file-header.d.ts +7 -0
- package/dist/codemods/format-file-header.d.ts.map +1 -0
- package/dist/codemods/format-file-header.js +163 -0
- package/dist/codemods/format-file-header.js.map +1 -0
- package/dist/codemods/format-jsdoc-comments.d.ts +7 -0
- package/dist/codemods/format-jsdoc-comments.d.ts.map +1 -0
- package/dist/codemods/format-jsdoc-comments.js +129 -0
- package/dist/codemods/format-jsdoc-comments.js.map +1 -0
- package/dist/codemods/function-declarations.d.ts +7 -0
- package/dist/codemods/function-declarations.d.ts.map +1 -0
- package/dist/codemods/function-declarations.js +202 -0
- package/dist/codemods/function-declarations.js.map +1 -0
- package/dist/codemods/inline-export-lists.d.ts +7 -0
- package/dist/codemods/inline-export-lists.d.ts.map +1 -0
- package/dist/codemods/inline-export-lists.js +168 -0
- package/dist/codemods/inline-export-lists.js.map +1 -0
- package/dist/codemods/internal-export-docs.d.ts +7 -0
- package/dist/codemods/internal-export-docs.d.ts.map +1 -0
- package/dist/codemods/internal-export-docs.js +93 -0
- package/dist/codemods/internal-export-docs.js.map +1 -0
- package/dist/codemods/no-ternary-branch-initializers.d.ts +14 -0
- package/dist/codemods/no-ternary-branch-initializers.d.ts.map +1 -0
- package/dist/codemods/no-ternary-branch-initializers.js +240 -0
- package/dist/codemods/no-ternary-branch-initializers.js.map +1 -0
- package/dist/codemods/no-ternary-variable-initializers.d.ts +14 -0
- package/dist/codemods/no-ternary-variable-initializers.d.ts.map +1 -0
- package/dist/codemods/no-ternary-variable-initializers.js +235 -0
- package/dist/codemods/no-ternary-variable-initializers.js.map +1 -0
- package/dist/codemods/no-ternary.d.ts +7 -0
- package/dist/codemods/no-ternary.d.ts.map +1 -0
- package/dist/codemods/no-ternary.js +215 -0
- package/dist/codemods/no-ternary.js.map +1 -0
- package/dist/codemods/rename-acronyms.d.ts +8 -0
- package/dist/codemods/rename-acronyms.d.ts.map +1 -0
- package/dist/codemods/rename-acronyms.js +193 -0
- package/dist/codemods/rename-acronyms.js.map +1 -0
- package/dist/codemods/run.d.ts +2 -0
- package/dist/codemods/run.d.ts.map +1 -0
- package/dist/codemods/run.js +14 -0
- package/dist/codemods/run.js.map +1 -0
- package/dist/codemods/sort-imports.d.ts +7 -0
- package/dist/codemods/sort-imports.d.ts.map +1 -0
- package/dist/codemods/sort-imports.js +235 -0
- package/dist/codemods/sort-imports.js.map +1 -0
- package/dist/index.d.ts +30 -96
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +157 -95
- package/dist/index.js.map +1 -1
- package/dist/rules/acronym-case.d.ts +7 -3
- package/dist/rules/acronym-case.d.ts.map +1 -1
- package/dist/rules/acronym-case.js +129 -120
- package/dist/rules/acronym-case.js.map +1 -1
- package/dist/rules/acronyms-1.d.ts +3 -0
- package/dist/rules/acronyms-1.d.ts.map +1 -0
- package/dist/rules/acronyms-1.js +407 -0
- package/dist/rules/acronyms-1.js.map +1 -0
- package/dist/rules/acronyms-2.d.ts +3 -0
- package/dist/rules/acronyms-2.d.ts.map +1 -0
- package/dist/rules/acronyms-2.js +407 -0
- package/dist/rules/acronyms-2.js.map +1 -0
- package/dist/rules/acronyms-3.d.ts +3 -0
- package/dist/rules/acronyms-3.d.ts.map +1 -0
- package/dist/rules/acronyms-3.js +407 -0
- package/dist/rules/acronyms-3.js.map +1 -0
- package/dist/rules/acronyms-4.d.ts +3 -0
- package/dist/rules/acronyms-4.d.ts.map +1 -0
- package/dist/rules/acronyms-4.js +377 -0
- package/dist/rules/acronyms-4.js.map +1 -0
- package/dist/rules/acronyms.d.ts +1 -8
- package/dist/rules/acronyms.d.ts.map +1 -1
- package/dist/rules/acronyms.js +14 -1578
- package/dist/rules/acronyms.js.map +1 -1
- package/dist/rules/boolean-prefix.d.ts +11 -2
- package/dist/rules/boolean-prefix.d.ts.map +1 -1
- package/dist/rules/boolean-prefix.js +95 -59
- package/dist/rules/boolean-prefix.js.map +1 -1
- package/dist/rules/camel-case-identifiers.d.ts +3 -4
- package/dist/rules/camel-case-identifiers.d.ts.map +1 -1
- package/dist/rules/camel-case-identifiers.js +59 -40
- package/dist/rules/camel-case-identifiers.js.map +1 -1
- package/dist/rules/char-class.d.ts +26 -13
- package/dist/rules/char-class.d.ts.map +1 -1
- package/dist/rules/char-class.js +56 -24
- package/dist/rules/char-class.js.map +1 -1
- package/dist/rules/diagnostic-guidance.d.ts +91 -0
- package/dist/rules/diagnostic-guidance.d.ts.map +1 -0
- package/dist/rules/diagnostic-guidance.js +243 -0
- package/dist/rules/diagnostic-guidance.js.map +1 -0
- package/dist/rules/effect-default-ast.d.ts +89 -0
- package/dist/rules/effect-default-ast.d.ts.map +1 -0
- package/dist/rules/effect-default-ast.js +208 -0
- package/dist/rules/effect-default-ast.js.map +1 -0
- package/dist/rules/effect-default-compat-rules.d.ts +76 -0
- package/dist/rules/effect-default-compat-rules.d.ts.map +1 -0
- package/dist/rules/effect-default-compat-rules.js +270 -0
- package/dist/rules/effect-default-compat-rules.js.map +1 -0
- package/dist/rules/effect-default-env-rules.d.ts +60 -0
- package/dist/rules/effect-default-env-rules.d.ts.map +1 -0
- package/dist/rules/effect-default-env-rules.js +259 -0
- package/dist/rules/effect-default-env-rules.js.map +1 -0
- package/dist/rules/effect-default-fiber-helpers.d.ts +13 -0
- package/dist/rules/effect-default-fiber-helpers.d.ts.map +1 -0
- package/dist/rules/effect-default-fiber-helpers.js +76 -0
- package/dist/rules/effect-default-fiber-helpers.js.map +1 -0
- package/dist/rules/effect-default-floating-helpers.d.ts +7 -0
- package/dist/rules/effect-default-floating-helpers.d.ts.map +1 -0
- package/dist/rules/effect-default-floating-helpers.js +113 -0
- package/dist/rules/effect-default-floating-helpers.js.map +1 -0
- package/dist/rules/effect-default-helpers.d.ts +8 -32
- package/dist/rules/effect-default-helpers.d.ts.map +1 -1
- package/dist/rules/effect-default-helpers.js +11 -458
- package/dist/rules/effect-default-helpers.js.map +1 -1
- package/dist/rules/effect-default-resource-helpers.d.ts +18 -4
- package/dist/rules/effect-default-resource-helpers.d.ts.map +1 -1
- package/dist/rules/effect-default-resource-helpers.js +30 -13
- package/dist/rules/effect-default-resource-helpers.js.map +1 -1
- package/dist/rules/effect-default-safety-helpers.d.ts +43 -0
- package/dist/rules/effect-default-safety-helpers.d.ts.map +1 -0
- package/dist/rules/effect-default-safety-helpers.js +126 -0
- package/dist/rules/effect-default-safety-helpers.js.map +1 -0
- package/dist/rules/effect-default-scan-helpers.d.ts +43 -0
- package/dist/rules/effect-default-scan-helpers.d.ts.map +1 -0
- package/dist/rules/effect-default-scan-helpers.js +141 -0
- package/dist/rules/effect-default-scan-helpers.js.map +1 -0
- package/dist/rules/effect-default-schema-helpers.d.ts +37 -0
- package/dist/rules/effect-default-schema-helpers.d.ts.map +1 -0
- package/dist/rules/effect-default-schema-helpers.js +104 -0
- package/dist/rules/effect-default-schema-helpers.js.map +1 -0
- package/dist/rules/effect-default-test-helpers.d.ts +18 -4
- package/dist/rules/effect-default-test-helpers.d.ts.map +1 -1
- package/dist/rules/effect-default-test-helpers.js +41 -20
- package/dist/rules/effect-default-test-helpers.js.map +1 -1
- package/dist/rules/effect-default-workflow-helpers.d.ts +91 -0
- package/dist/rules/effect-default-workflow-helpers.d.ts.map +1 -0
- package/dist/rules/effect-default-workflow-helpers.js +247 -0
- package/dist/rules/effect-default-workflow-helpers.js.map +1 -0
- package/dist/rules/effect-default.d.ts +1 -1
- package/dist/rules/effect-default.d.ts.map +1 -1
- package/dist/rules/effect-default.js +198 -637
- package/dist/rules/effect-default.js.map +1 -1
- package/dist/rules/effect-exported-declarations.d.ts +19 -0
- package/dist/rules/effect-exported-declarations.d.ts.map +1 -0
- package/dist/rules/effect-exported-declarations.js +237 -0
- package/dist/rules/effect-exported-declarations.js.map +1 -0
- package/dist/rules/effect-path-options.d.ts +49 -11
- package/dist/rules/effect-path-options.d.ts.map +1 -1
- package/dist/rules/effect-path-options.js +85 -52
- package/dist/rules/effect-path-options.js.map +1 -1
- package/dist/rules/effect-rule-aliases.d.ts +55 -0
- package/dist/rules/effect-rule-aliases.d.ts.map +1 -0
- package/dist/rules/effect-rule-aliases.js +278 -0
- package/dist/rules/effect-rule-aliases.js.map +1 -0
- package/dist/rules/effect-rule-core.d.ts +46 -24
- package/dist/rules/effect-rule-core.d.ts.map +1 -1
- package/dist/rules/effect-rule-core.js +263 -303
- package/dist/rules/effect-rule-core.js.map +1 -1
- package/dist/rules/effect-rule-names.d.ts +12 -3
- package/dist/rules/effect-rule-names.d.ts.map +1 -1
- package/dist/rules/effect-rule-names.js +15 -3
- package/dist/rules/effect-rule-names.js.map +1 -1
- package/dist/rules/effect-source-comments.d.ts +7 -0
- package/dist/rules/effect-source-comments.d.ts.map +1 -0
- package/dist/rules/effect-source-comments.js +227 -0
- package/dist/rules/effect-source-comments.js.map +1 -0
- package/dist/rules/effect-source-helpers.d.ts +4 -9
- package/dist/rules/effect-source-helpers.d.ts.map +1 -1
- package/dist/rules/effect-source-helpers.js +7 -333
- package/dist/rules/effect-source-helpers.js.map +1 -1
- package/dist/rules/effect-source-navigation.d.ts +25 -0
- package/dist/rules/effect-source-navigation.d.ts.map +1 -0
- package/dist/rules/effect-source-navigation.js +244 -0
- package/dist/rules/effect-source-navigation.js.map +1 -0
- package/dist/rules/effect-source-scan.d.ts +30 -7
- package/dist/rules/effect-source-scan.d.ts.map +1 -1
- package/dist/rules/effect-source-scan.js +377 -297
- package/dist/rules/effect-source-scan.js.map +1 -1
- package/dist/rules/effect-strict-ast-specs.d.ts +8 -0
- package/dist/rules/effect-strict-ast-specs.d.ts.map +1 -0
- package/dist/rules/effect-strict-ast-specs.js +166 -0
- package/dist/rules/effect-strict-ast-specs.js.map +1 -0
- package/dist/rules/effect-strict-boundary-helpers.d.ts +25 -0
- package/dist/rules/effect-strict-boundary-helpers.d.ts.map +1 -0
- package/dist/rules/effect-strict-boundary-helpers.js +127 -0
- package/dist/rules/effect-strict-boundary-helpers.js.map +1 -0
- package/dist/rules/effect-strict-core-specs.d.ts +8 -0
- package/dist/rules/effect-strict-core-specs.d.ts.map +1 -0
- package/dist/rules/effect-strict-core-specs.js +334 -0
- package/dist/rules/effect-strict-core-specs.js.map +1 -0
- package/dist/rules/effect-strict-external-helpers.d.ts +25 -0
- package/dist/rules/effect-strict-external-helpers.d.ts.map +1 -0
- package/dist/rules/effect-strict-external-helpers.js +87 -0
- package/dist/rules/effect-strict-external-helpers.js.map +1 -0
- package/dist/rules/effect-strict-helpers.d.ts +75 -33
- package/dist/rules/effect-strict-helpers.d.ts.map +1 -1
- package/dist/rules/effect-strict-helpers.js +124 -246
- package/dist/rules/effect-strict-helpers.js.map +1 -1
- package/dist/rules/effect-strict-internals.d.ts +198 -0
- package/dist/rules/effect-strict-internals.d.ts.map +1 -0
- package/dist/rules/effect-strict-internals.js +477 -0
- package/dist/rules/effect-strict-internals.js.map +1 -0
- package/dist/rules/effect-strict-segment-helpers.d.ts +79 -0
- package/dist/rules/effect-strict-segment-helpers.d.ts.map +1 -0
- package/dist/rules/effect-strict-segment-helpers.js +308 -0
- package/dist/rules/effect-strict-segment-helpers.js.map +1 -0
- package/dist/rules/effect-strict.d.ts +1 -1
- package/dist/rules/effect-strict.d.ts.map +1 -1
- package/dist/rules/effect-strict.js +10 -661
- package/dist/rules/effect-strict.js.map +1 -1
- package/dist/rules/max-import-depth.d.ts +2 -2
- package/dist/rules/max-import-depth.d.ts.map +1 -1
- package/dist/rules/max-import-depth.js +52 -17
- package/dist/rules/max-import-depth.js.map +1 -1
- package/dist/rules/max-line-length.d.ts +9 -2
- package/dist/rules/max-line-length.d.ts.map +1 -1
- package/dist/rules/max-line-length.js +62 -7
- package/dist/rules/max-line-length.js.map +1 -1
- package/dist/rules/no-commented-out-code.d.ts +3 -7
- package/dist/rules/no-commented-out-code.d.ts.map +1 -1
- package/dist/rules/no-commented-out-code.js +157 -102
- package/dist/rules/no-commented-out-code.js.map +1 -1
- package/dist/rules/pascal-case-types.d.ts +1 -1
- package/dist/rules/pascal-case-types.d.ts.map +1 -1
- package/dist/rules/pascal-case-types.js +65 -46
- package/dist/rules/pascal-case-types.js.map +1 -1
- package/dist/rules/plugin-commented-out-code-rule.d.ts +32 -0
- package/dist/rules/plugin-commented-out-code-rule.d.ts.map +1 -0
- package/dist/rules/plugin-commented-out-code-rule.js +135 -0
- package/dist/rules/plugin-commented-out-code-rule.js.map +1 -0
- package/dist/rules/plugin.d.ts +3 -121
- package/dist/rules/plugin.d.ts.map +1 -1
- package/dist/rules/plugin.js +195 -201
- package/dist/rules/plugin.js.map +1 -1
- package/dist/rules/private-underscore.d.ts +1 -1
- package/dist/rules/private-underscore.d.ts.map +1 -1
- package/dist/rules/private-underscore.js +11 -4
- package/dist/rules/private-underscore.js.map +1 -1
- package/dist/rules/require-file-doc.d.ts +5 -6
- package/dist/rules/require-file-doc.d.ts.map +1 -1
- package/dist/rules/require-file-doc.js +127 -78
- package/dist/rules/require-file-doc.js.map +1 -1
- package/dist/rules/require-function-doc-ignored-text.d.ts +7 -0
- package/dist/rules/require-function-doc-ignored-text.d.ts.map +1 -0
- package/dist/rules/require-function-doc-ignored-text.js +67 -0
- package/dist/rules/require-function-doc-ignored-text.js.map +1 -0
- package/dist/rules/require-function-doc-local-exports.d.ts +7 -0
- package/dist/rules/require-function-doc-local-exports.d.ts.map +1 -0
- package/dist/rules/require-function-doc-local-exports.js +114 -0
- package/dist/rules/require-function-doc-local-exports.js.map +1 -0
- package/dist/rules/require-function-doc.d.ts +3 -18
- package/dist/rules/require-function-doc.d.ts.map +1 -1
- package/dist/rules/require-function-doc.js +404 -218
- package/dist/rules/require-function-doc.js.map +1 -1
- package/dist/rules/source-cache.d.ts +19 -0
- package/dist/rules/source-cache.d.ts.map +1 -0
- package/dist/rules/source-cache.js +63 -0
- package/dist/rules/source-cache.js.map +1 -0
- package/package.json +15 -10
|
@@ -1,132 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (nodeType(node) !== 'MemberExpression') {
|
|
31
|
-
return {};
|
|
32
|
-
}
|
|
33
|
-
const member = node;
|
|
34
|
-
return {
|
|
35
|
-
objectName: identifierName(member.object),
|
|
36
|
-
propertyName: identifierName(member.property),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function isEffectMember(node, source, methods) {
|
|
40
|
-
const { objectName, propertyName } = memberParts(node);
|
|
41
|
-
return Boolean(objectName &&
|
|
42
|
-
propertyName &&
|
|
43
|
-
effectImportAliases(source).includes(objectName) &&
|
|
44
|
-
methods.has(propertyName));
|
|
45
|
-
}
|
|
46
|
-
function isEffectFunctionCall(callee, source, names) {
|
|
47
|
-
const calleeName = identifierName(callee);
|
|
48
|
-
return Boolean(calleeName &&
|
|
49
|
-
[...names].some((name) => effectFunctionAliases(source, 'Effect', name).includes(calleeName)));
|
|
50
|
-
}
|
|
51
|
-
function propertyKeyName(node) {
|
|
52
|
-
const value = literalValue(node);
|
|
53
|
-
return identifierName(node) ?? (typeof value === 'string' ? value : undefined);
|
|
54
|
-
}
|
|
55
|
-
function typeReferenceName(node) {
|
|
56
|
-
if (nodeType(node) !== 'TSTypeReference') {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
const { typeName } = node;
|
|
60
|
-
if (nodeType(typeName) === 'Identifier') {
|
|
61
|
-
return identifierName(typeName);
|
|
62
|
-
}
|
|
63
|
-
if (nodeType(typeName) !== 'TSQualifiedName') {
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
const qualified = typeName;
|
|
67
|
-
const leftName = identifierName(qualified.left);
|
|
68
|
-
const rightName = identifierName(qualified.right);
|
|
69
|
-
return leftName && rightName ? `${leftName}.${rightName}` : undefined;
|
|
70
|
-
}
|
|
71
|
-
function firstTypeArgumentName(node) {
|
|
72
|
-
const params = node.typeArguments?.params;
|
|
73
|
-
return typeReferenceName(params?.[0]);
|
|
74
|
-
}
|
|
75
|
-
function effectServiceSelfName(superClass, source) {
|
|
76
|
-
if (nodeType(superClass) !== 'CallExpression') {
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
|
-
const outer = superClass;
|
|
80
|
-
const outerSelf = typeReferenceName(outer.typeArguments?.params?.[0]);
|
|
81
|
-
const inner = outer.callee;
|
|
82
|
-
if (nodeType(inner) !== 'CallExpression') {
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
const innerCall = inner;
|
|
86
|
-
const { objectName, propertyName } = memberParts(innerCall.callee);
|
|
87
|
-
if (objectName === 'Context' && propertyName === 'Tag') {
|
|
88
|
-
return outerSelf;
|
|
89
|
-
}
|
|
90
|
-
if (objectName &&
|
|
91
|
-
propertyName === 'Service' &&
|
|
92
|
-
effectImportAliases(source).includes(objectName)) {
|
|
93
|
-
return firstTypeArgumentName(inner);
|
|
94
|
-
}
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
|
-
function hasMixedEffectImportStyles(source) {
|
|
98
|
-
const hasNamedRootEffectImport = /import\s*{[^}]*\bEffect\b[^}]*}\s*from\s*['"]effect['"]/.test(source);
|
|
99
|
-
const hasNamespaceEffectImport = /import\s+\*\s+as\s+[A-Za-z_$][\w$]*\s+from\s*['"]effect(?:\/Effect)?['"]/.test(source);
|
|
100
|
-
return hasNamedRootEffectImport && hasNamespaceEffectImport;
|
|
101
|
-
}
|
|
102
|
-
function escapeRegExp(value) {
|
|
103
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
104
|
-
}
|
|
105
|
-
function hasTryCatchInEffectGen(source) {
|
|
106
|
-
const code = stripCommentsAndStrings(source);
|
|
107
|
-
const aliases = effectImportAliases(source).map(escapeRegExp).join('|');
|
|
108
|
-
if (!aliases) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
for (const match of code.matchAll(new RegExp(`\\b(?:${aliases})\\.gen\\s*\\(`, 'g'))) {
|
|
112
|
-
const openParenIndex = code.indexOf('(', match.index);
|
|
113
|
-
if (openParenIndex === -1) {
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
const bodyStart = openParenIndex + 1;
|
|
117
|
-
const body = code.slice(bodyStart, findBalancedCallEnd(code, openParenIndex));
|
|
118
|
-
const tryCatchMatch = /\btry\s*{[\s\S]*?\bcatch\s*\(/.exec(body);
|
|
119
|
-
if (tryCatchMatch?.index !== undefined) {
|
|
120
|
-
return bodyStart + tryCatchMatch.index;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
function hasStringErrorFailure(source) {
|
|
1
|
+
/* -------------------------------------------------------------------------- */
|
|
2
|
+
/* Always-on custom Effect lint rule definitions. */
|
|
3
|
+
/* -------------------------------------------------------------------------- */
|
|
4
|
+
import { arrayValue, effectCallPredicate, identifierName, isStringLikeLiteral, memberParts, nodeType, objectValue, propertyKeyName, reportAST, } from './effect-default-ast.js';
|
|
5
|
+
import { exportedCallableDeclarationSegments, exportedDeclarationSegments, stripCommentsAndStrings, } from './effect-source-helpers.js';
|
|
6
|
+
import { hasAsyncAwaitInEffect, hasBroadCatchAllWithoutRethrow, hasEffectInArrayForEach, hasEffectInPromiseCallback, hasErrorMappingWithoutCause, hasFloatingEffect, hasMultipleCatchTagsInOnePipe, hasNestedFlatMap, hasRecursiveEffectWithoutSuspend, hasReturnEffectInGen, hasRunForkWithoutObserver, hasRuntimeInEffect, hasSyncForPromise, hasSyncForThrowingOPS, hasThrowInEffect, hasTryPromiseWithoutTypedCatch, hasUnloggedIgnore, hasUnobservedFork, hasUnsafeLazyEvaluation, hasYieldWithoutStarInGen, } from './effect-default-helpers.js';
|
|
7
|
+
import { hasEffectSignal, makeRules } from './effect-rule-core.js';
|
|
8
|
+
import { effectDefaultCompatibilitySpecs } from './effect-default-compat-rules.js';
|
|
9
|
+
import { effectDefaultEnvironmentSpecs } from './effect-default-env-rules.js';
|
|
10
|
+
import { strictPathOptionsSchema } from './effect-path-options.js';
|
|
11
|
+
const effectDefaultRuleTokens = [
|
|
12
|
+
'Effect',
|
|
13
|
+
'Schema',
|
|
14
|
+
'Config',
|
|
15
|
+
'Context',
|
|
16
|
+
'Queue',
|
|
17
|
+
'Stream',
|
|
18
|
+
'TestClock',
|
|
19
|
+
'from "effect"',
|
|
20
|
+
"from 'effect'",
|
|
21
|
+
'@effect/',
|
|
22
|
+
'"effect/',
|
|
23
|
+
"'effect/",
|
|
24
|
+
'it.effect',
|
|
25
|
+
'describe.effect',
|
|
26
|
+
'JSON.parse',
|
|
27
|
+
'response.json',
|
|
28
|
+
];
|
|
29
|
+
const hasStringErrorFailure = (source) => {
|
|
126
30
|
const match = /\bEffect\.fail\s*\(\s*["'`]/.exec(stripCommentsAndStrings(source));
|
|
127
31
|
return match?.index ?? false;
|
|
128
|
-
}
|
|
129
|
-
|
|
32
|
+
};
|
|
33
|
+
const hasUntaggedErrorFailure = (source) => {
|
|
130
34
|
const code = stripCommentsAndStrings(source);
|
|
131
35
|
const nativeErrorMatch = /\bEffect\.fail\s*\(\s*new\s+Error\s*\(/.exec(code);
|
|
132
36
|
if (nativeErrorMatch) {
|
|
@@ -134,613 +38,270 @@ function hasUntaggedErrorFailure(source) {
|
|
|
134
38
|
}
|
|
135
39
|
const objectErrorMatch = /\bEffect\.fail\s*\(\s*{(?![^}]*\b_tag\s*:)/.exec(code);
|
|
136
40
|
return objectErrorMatch?.index ?? false;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const match = /\bclass\s+[A-Z][\w$]*\s+extends\s+Error\b/.exec(stripCommentsAndStrings(source));
|
|
143
|
-
return match?.index ?? false;
|
|
144
|
-
}
|
|
145
|
-
function hasUnsafeEffectTypeAssertion(source) {
|
|
146
|
-
const code = stripCommentsAndStrings(source);
|
|
147
|
-
const match = /\bas\s+Effect\.Effect\s*<[^>]*>/.exec(code);
|
|
148
|
-
return match?.index ?? false;
|
|
149
|
-
}
|
|
150
|
-
function hasServiceSelfMismatch(source) {
|
|
151
|
-
const code = stripCommentsAndStrings(source);
|
|
152
|
-
const legacyPattern = /\bclass\s+([A-Z][\w$]*)\s+extends\s+(?:Context\.Tag|Effect\.Service|Effect\.Tag)\s*\([^)]*\)\s*<\s*([A-Z][\w$]*)\b/g;
|
|
153
|
-
for (const match of code.matchAll(legacyPattern)) {
|
|
154
|
-
const [, className, selfName] = match;
|
|
155
|
-
if (className !== selfName) {
|
|
156
|
-
return match.index;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const servicePattern = /\bclass\s+([A-Z][\w$]*)\s+extends\s+Effect\.Service\s*<\s*([A-Z][\w$]*)\s*>\s*\(\s*\)/g;
|
|
160
|
-
for (const match of code.matchAll(servicePattern)) {
|
|
161
|
-
const [, className, selfName] = match;
|
|
162
|
-
if (className !== selfName) {
|
|
163
|
-
return match.index;
|
|
164
|
-
}
|
|
41
|
+
};
|
|
42
|
+
const isUntaggedErrorFailureArgument = (firstArg) => {
|
|
43
|
+
if (nodeType(firstArg) === 'NewExpression' &&
|
|
44
|
+
identifierName(objectValue(firstArg, 'callee')) === 'Error') {
|
|
45
|
+
return true;
|
|
165
46
|
}
|
|
166
|
-
return
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const code = stripCommentsAndStrings(source);
|
|
170
|
-
const match = /\bEffect\.fn(?:Untraced|UntracedEager)?\s*\([\s\S]*?\)\s*\([\s\S]*?\)\s*\(/.exec(code);
|
|
171
|
-
return match?.index ?? false;
|
|
172
|
-
}
|
|
47
|
+
return (nodeType(firstArg) === 'ObjectExpression' &&
|
|
48
|
+
!arrayValue(objectValue(firstArg, 'properties')).some((property) => propertyKeyName(objectValue(property, 'key')) === '_tag'));
|
|
49
|
+
};
|
|
173
50
|
const effectDefaultSpecs = [
|
|
174
51
|
{
|
|
175
|
-
name: 'effect-no-floating-effect',
|
|
176
|
-
message: 'Return, yield, assign, or compose Effect values; bare Effect calls never execute.',
|
|
177
52
|
check: hasFloatingEffect,
|
|
53
|
+
message: 'Return, yield, assign, or compose Effect values; bare Effect calls never execute.',
|
|
54
|
+
name: 'effect-no-floating-effect',
|
|
178
55
|
},
|
|
179
56
|
{
|
|
180
|
-
name: 'effect-require-yield-star',
|
|
181
|
-
message: 'Use yield* inside Effect.gen so generator composition unwraps the Effect value.',
|
|
182
57
|
check: hasYieldWithoutStarInGen,
|
|
58
|
+
message: 'Use yield* inside Effect.gen so generator composition unwraps the Effect value.',
|
|
59
|
+
name: 'effect-require-yield-star',
|
|
60
|
+
tokenGroups: [['gen'], ['yield']],
|
|
183
61
|
},
|
|
184
62
|
{
|
|
185
|
-
name: 'effect-require-return-yield-star',
|
|
186
|
-
message: 'Do not return an Effect from Effect.gen; return a value or return yield* the Effect.',
|
|
187
63
|
check: hasReturnEffectInGen,
|
|
64
|
+
message: 'Do not return an Effect from Effect.gen; return a value or return yield* the Effect.',
|
|
65
|
+
name: 'effect-require-return-yield-star',
|
|
66
|
+
tokenGroups: [['gen'], ['return']],
|
|
188
67
|
},
|
|
189
68
|
{
|
|
190
|
-
name: 'effect-prefer-gen-for-nested-flatmap',
|
|
191
|
-
message: 'Replace nested Effect.flatMap callbacks with Effect.gen for readable sequencing.',
|
|
192
69
|
check: hasNestedFlatMap,
|
|
70
|
+
message: 'Replace nested Effect.flatMap callbacks with Effect.gen for readable sequencing.',
|
|
71
|
+
name: 'effect-prefer-gen-for-nested-flatmap',
|
|
72
|
+
tokens: ['flatMap'],
|
|
193
73
|
},
|
|
194
74
|
{
|
|
195
|
-
name: 'effect-no-function-returning-gen',
|
|
196
|
-
message: 'Use Effect.fn for exported effectful functions instead of returning Effect.gen.',
|
|
197
75
|
check: (source) => exportedCallableDeclarationSegments(source).some((segment) => /(?:^|\breturn\s+)Effect\.gen\s*\(/.test(segment.trim())),
|
|
76
|
+
message: 'Use Effect.fn for exported effectful functions instead of returning Effect.gen.',
|
|
77
|
+
name: 'effect-no-function-returning-gen',
|
|
78
|
+
tokens: ['gen'],
|
|
198
79
|
},
|
|
199
80
|
{
|
|
200
|
-
name: 'effect-prefer-effect-fn-for-exported-effects',
|
|
201
|
-
message: 'Exported effectful functions should use Effect.fn for tracing and stable contracts.',
|
|
202
81
|
check: (source) => exportedCallableDeclarationSegments(source).some((segment) => /(?:^|\breturn\s+)Effect\.(?!fn\b|gen\b|isEffect\b|serviceFunction\b|runPromise\b)/.test(segment.trim())),
|
|
82
|
+
message: 'Exported effectful functions should use Effect.fn for tracing and stable contracts.',
|
|
83
|
+
name: 'effect-prefer-effect-fn-for-exported-effects',
|
|
84
|
+
tokens: ['export'],
|
|
203
85
|
},
|
|
204
86
|
{
|
|
205
|
-
name: 'effect-no-unnecessary-gen',
|
|
206
87
|
message: 'Do not wrap a single Effect in Effect.gen when direct composition is clearer.',
|
|
88
|
+
name: 'effect-no-unnecessary-gen',
|
|
207
89
|
patterns: [/Effect\.gen\s*\(\s*function\*\s*\([^)]*\)\s*{\s*return\s+yield\*\s+Effect\./],
|
|
90
|
+
tokenGroups: [['gen'], ['yield']],
|
|
208
91
|
},
|
|
209
92
|
{
|
|
210
|
-
name: 'effect-no-effect-in-array-foreach',
|
|
211
|
-
message: 'Use Effect.forEach instead of Array.forEach with Effect-returning callbacks.',
|
|
212
93
|
check: hasEffectInArrayForEach,
|
|
94
|
+
message: 'Use Effect.forEach instead of Array.forEach with Effect-returning callbacks.',
|
|
95
|
+
name: 'effect-no-effect-in-array-foreach',
|
|
96
|
+
tokens: ['forEach'],
|
|
213
97
|
},
|
|
214
98
|
{
|
|
215
|
-
name: 'effect-no-effect-in-promise-callback',
|
|
216
|
-
message: 'Do not create Effect values inside Promise callbacks; compose at the Effect boundary.',
|
|
217
99
|
check: hasEffectInPromiseCallback,
|
|
100
|
+
message: 'Do not create Effect values inside Promise callbacks; compose at the Effect boundary.',
|
|
101
|
+
name: 'effect-no-effect-in-promise-callback',
|
|
102
|
+
tokens: ['.then', '.catch'],
|
|
218
103
|
},
|
|
219
104
|
{
|
|
220
|
-
name: 'effect-no-floating-fiber',
|
|
221
|
-
message: 'Forked fibers must be joined, interrupted, scoped, supervised, or returned.',
|
|
222
105
|
check: hasUnobservedFork,
|
|
106
|
+
message: 'Forked fibers must be joined, interrupted, scoped, supervised, or returned.',
|
|
107
|
+
name: 'effect-no-floating-fiber',
|
|
108
|
+
tokens: ['fork'],
|
|
223
109
|
},
|
|
224
110
|
{
|
|
225
|
-
name: 'effect-require-suspend-for-recursion',
|
|
226
|
-
message: 'Recursive Effect construction must be wrapped in Effect.suspend.',
|
|
227
111
|
check: hasRecursiveEffectWithoutSuspend,
|
|
112
|
+
message: 'Recursive Effect construction must be wrapped in Effect.suspend.',
|
|
113
|
+
name: 'effect-require-suspend-for-recursion',
|
|
114
|
+
tokens: ['function', '=>'],
|
|
228
115
|
},
|
|
229
116
|
{
|
|
230
|
-
name: 'effect-require-suspend-for-lazy-evaluation',
|
|
231
|
-
message: 'Use Effect.suspend when Effect construction must defer eager JavaScript work.',
|
|
232
117
|
check: hasUnsafeLazyEvaluation,
|
|
118
|
+
message: 'Use Effect.suspend when Effect construction must defer eager JavaScript work.',
|
|
119
|
+
name: 'effect-require-suspend-for-lazy-evaluation',
|
|
120
|
+
tokens: ['Date.now', 'Math.random', 'new Date', 'JSON.parse'],
|
|
233
121
|
},
|
|
234
122
|
{
|
|
235
|
-
name: 'effect-no-async-await-in-effect',
|
|
236
|
-
message: 'Use Effect.tryPromise or Effect.promise boundaries instead of async/await in Effect code.',
|
|
237
123
|
check: hasAsyncAwaitInEffect,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
124
|
+
message: 'Use Effect.tryPromise or Effect.promise boundaries instead of async/await in Effect code.',
|
|
125
|
+
name: 'effect-no-async-await-in-effect',
|
|
126
|
+
tokens: ['async', 'await'],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
ast: (context, source) => {
|
|
130
|
+
const isEffectModule = hasEffectSignal(source);
|
|
131
|
+
return {
|
|
132
|
+
CallExpression(node) {
|
|
133
|
+
if (!isEffectModule) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const { propertyName } = memberParts(objectValue(node, 'callee'));
|
|
137
|
+
if (propertyName === 'then' || propertyName === 'catch') {
|
|
138
|
+
reportAST(context, 'Use Effect combinators instead of Promise then/catch chains in Effect modules.', node);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
},
|
|
241
143
|
message: 'Use Effect combinators instead of Promise then/catch chains in Effect modules.',
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (!hasEffectSignal(source)) {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
const { callee } = node;
|
|
248
|
-
const { propertyName } = memberParts(callee);
|
|
249
|
-
if (propertyName === 'then' || propertyName === 'catch') {
|
|
250
|
-
reportAst(context, 'Use Effect combinators instead of Promise then/catch chains in Effect modules.', node);
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
}),
|
|
144
|
+
name: 'effect-no-promise-then-in-effect',
|
|
145
|
+
tokens: ['.then', '.catch'],
|
|
254
146
|
},
|
|
255
147
|
{
|
|
256
|
-
name: 'effect-no-throw',
|
|
257
|
-
message: 'Use typed Effect failures instead of throw inside Effect workflows.',
|
|
258
148
|
check: hasThrowInEffect,
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
149
|
+
message: 'Use typed Effect failures instead of throw inside Effect workflows.',
|
|
150
|
+
name: 'effect-no-throw',
|
|
151
|
+
tokens: ['throw'],
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
ast: (context, source) => {
|
|
155
|
+
const isEffectFail = effectCallPredicate(source, ['fail']);
|
|
156
|
+
return {
|
|
157
|
+
CallExpression(node) {
|
|
158
|
+
const callArguments = arrayValue(objectValue(node, 'arguments'));
|
|
159
|
+
if (isEffectFail(objectValue(node, 'callee')) && isStringLikeLiteral(callArguments[0])) {
|
|
160
|
+
reportAST(context, 'Use structured tagged errors instead of string failures.', node);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
},
|
|
263
165
|
check: hasStringErrorFailure,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
166
|
+
message: 'Use structured tagged errors instead of string failures.',
|
|
167
|
+
name: 'effect-no-string-errors',
|
|
168
|
+
tokens: ['fail'],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
ast: (context, source) => {
|
|
172
|
+
const isEffectFail = effectCallPredicate(source, ['fail']);
|
|
173
|
+
return {
|
|
174
|
+
CallExpression(node) {
|
|
175
|
+
const callArguments = arrayValue(objectValue(node, 'arguments'));
|
|
176
|
+
const [firstArg] = callArguments;
|
|
177
|
+
if (!isEffectFail(objectValue(node, 'callee'))) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (!firstArg) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (isUntaggedErrorFailureArgument(firstArg)) {
|
|
184
|
+
reportAST(context, 'Use tagged/data/schema errors in Effect.fail instead of global Error values.', node);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
},
|
|
278
189
|
check: hasUntaggedErrorFailure,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const firstArg = call.arguments?.[0];
|
|
283
|
-
if (!isEffectMember(call.callee, source, new Set(['fail'])) &&
|
|
284
|
-
!isEffectFunctionCall(call.callee, source, new Set(['fail']))) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
if (!firstArg) {
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
if (nodeType(firstArg) === 'NewExpression' && identifierName(firstArg.callee) === 'Error') {
|
|
291
|
-
reportAst(context, 'Use tagged/data/schema errors in Effect.fail instead of global Error values.', node);
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
if (nodeType(firstArg) === 'ObjectExpression' &&
|
|
295
|
-
!firstArg.properties?.some((property) => propertyKeyName(property.key) === '_tag')) {
|
|
296
|
-
reportAst(context, 'Use tagged/data/schema errors in Effect.fail instead of global Error values.', node);
|
|
297
|
-
}
|
|
298
|
-
},
|
|
299
|
-
}),
|
|
190
|
+
message: 'Use tagged/data/schema errors in Effect.fail instead of global Error values.',
|
|
191
|
+
name: 'effect-no-untagged-errors',
|
|
192
|
+
tokens: ['fail'],
|
|
300
193
|
},
|
|
301
194
|
{
|
|
302
|
-
name: 'effect-no-silent-error-swallowing',
|
|
303
195
|
message: 'Do not erase Effect failures without recovery, logging, or explicit typed replacement.',
|
|
196
|
+
name: 'effect-no-silent-error-swallowing',
|
|
304
197
|
patterns: [
|
|
305
198
|
/Effect\.(?:catchAll|ignore)\s*\([\s\S]*?(?:Effect\.void|Effect\.succeed\s*\(\s*undefined|undefined)/,
|
|
306
199
|
],
|
|
200
|
+
tokens: ['catchAll', 'ignore'],
|
|
307
201
|
},
|
|
308
202
|
{
|
|
309
|
-
name: 'effect-require-typed-error-in-trypromise',
|
|
310
|
-
message: 'Use Effect.tryPromise({ try, catch }) so Promise failures become typed Effect errors.',
|
|
311
203
|
check: hasTryPromiseWithoutTypedCatch,
|
|
204
|
+
message: 'Use Effect.tryPromise({ try, catch }) so Promise failures become typed Effect errors.',
|
|
205
|
+
name: 'effect-require-typed-error-in-trypromise',
|
|
206
|
+
tokens: ['tryPromise'],
|
|
312
207
|
},
|
|
313
208
|
{
|
|
314
|
-
name: 'effect-prefer-catchTag-over-catchAll',
|
|
315
|
-
message: 'Prefer catchTag or catchTags over broad catchAll recovery.',
|
|
316
209
|
check: hasBroadCatchAllWithoutRethrow,
|
|
210
|
+
message: 'Prefer catchTag or catchTags over broad catchAll recovery.',
|
|
211
|
+
name: 'effect-prefer-catchTag-over-catchAll',
|
|
212
|
+
tokens: ['catchAll'],
|
|
317
213
|
},
|
|
318
214
|
{
|
|
319
|
-
name: 'effect-no-catchAll-with-mapError',
|
|
320
215
|
message: 'Use mapError directly instead of catchAll when only transforming the error.',
|
|
216
|
+
name: 'effect-no-catchAll-with-mapError',
|
|
321
217
|
patterns: [/Effect\.catchAll\s*\([\s\S]*?=>\s*Effect\.fail\s*\(/],
|
|
218
|
+
tokens: ['catchAll'],
|
|
322
219
|
},
|
|
323
220
|
{
|
|
324
|
-
name: 'effect-prefer-mapError-over-catchAll-rethrow',
|
|
325
221
|
message: 'Use Effect.mapError instead of catchAll followed by fail.',
|
|
222
|
+
name: 'effect-prefer-mapError-over-catchAll-rethrow',
|
|
326
223
|
patterns: [/(?:^|[^\w$.])catchAll\s*\([\s\S]*?=>\s*Effect\.fail\s*\(/],
|
|
224
|
+
tokens: ['catchAll'],
|
|
327
225
|
},
|
|
328
226
|
{
|
|
329
|
-
name: 'effect-require-error-cause-preserved',
|
|
330
|
-
message: 'Preserve the original cause when mapping or wrapping Effect errors.',
|
|
331
227
|
check: hasErrorMappingWithoutCause,
|
|
228
|
+
message: 'Preserve the original cause when mapping or wrapping Effect errors.',
|
|
229
|
+
name: 'effect-require-error-cause-preserved',
|
|
230
|
+
tokens: ['mapError', 'catchAll'],
|
|
332
231
|
},
|
|
333
232
|
{
|
|
334
|
-
name: 'effect-prefer-ignore-logged',
|
|
335
|
-
message: 'Ignored Effect failures must be logged or otherwise observable.',
|
|
336
233
|
check: hasUnloggedIgnore,
|
|
234
|
+
message: 'Ignored Effect failures must be logged or otherwise observable.',
|
|
235
|
+
name: 'effect-prefer-ignore-logged',
|
|
236
|
+
tokens: ['ignore'],
|
|
337
237
|
},
|
|
338
238
|
{
|
|
339
|
-
name: 'effect-prefer-catchTags-for-multiple-tags',
|
|
340
|
-
message: 'Use Effect.catchTags for multiple tagged Effect recoveries.',
|
|
341
239
|
check: hasMultipleCatchTagsInOnePipe,
|
|
240
|
+
message: 'Use Effect.catchTags for multiple tagged Effect recoveries.',
|
|
241
|
+
name: 'effect-prefer-catchTags-for-multiple-tags',
|
|
242
|
+
tokens: ['catchTag'],
|
|
342
243
|
},
|
|
343
244
|
{
|
|
344
|
-
name: 'effect-no-error-channel-widening-to-unknown',
|
|
345
245
|
message: 'Do not widen Effect error channels to unknown.',
|
|
246
|
+
name: 'effect-no-error-channel-widening-to-unknown',
|
|
346
247
|
patterns: [/Effect\s*<[^>]*,\s*unknown\b/, /Effect\.fail\s*<\s*unknown\b/],
|
|
248
|
+
tokens: ['unknown'],
|
|
347
249
|
},
|
|
348
250
|
{
|
|
349
|
-
name: 'effect-no-run-inside-effect',
|
|
350
|
-
message: 'Do not run an Effect from inside another Effect; yield or compose it instead.',
|
|
351
251
|
check: hasRuntimeInEffect,
|
|
252
|
+
message: 'Do not run an Effect from inside another Effect; yield or compose it instead.',
|
|
253
|
+
name: 'effect-no-run-inside-effect',
|
|
254
|
+
tokens: ['run'],
|
|
352
255
|
},
|
|
353
256
|
{
|
|
354
|
-
name: 'effect-no-runpromise-in-exported-api',
|
|
355
|
-
message: 'Exported APIs should expose Effect values instead of hiding execution behind Promise.',
|
|
356
257
|
check: (source) => exportedDeclarationSegments(source).some((segment) => /Effect\.runPromise\s*\(/.test(segment)),
|
|
258
|
+
message: 'Exported APIs should expose Effect values instead of hiding execution behind Promise.',
|
|
259
|
+
name: 'effect-no-runpromise-in-exported-api',
|
|
260
|
+
tokens: ['runPromise'],
|
|
357
261
|
},
|
|
358
262
|
{
|
|
359
|
-
name: 'effect-no-runfork-without-observer',
|
|
360
|
-
message: 'Do not call runFork without explicit observation, supervision, or interruption.',
|
|
361
263
|
check: hasRunForkWithoutObserver,
|
|
264
|
+
message: 'Do not call runFork without explicit observation, supervision, or interruption.',
|
|
265
|
+
name: 'effect-no-runfork-without-observer',
|
|
266
|
+
tokens: ['runFork'],
|
|
362
267
|
},
|
|
363
268
|
{
|
|
364
|
-
name: 'effect-no-sync-for-promise',
|
|
365
|
-
message: 'Use Effect.tryPromise for Promise-returning code instead of Effect.sync.',
|
|
366
269
|
check: hasSyncForPromise,
|
|
270
|
+
message: 'Use Effect.tryPromise for Promise-returning code instead of Effect.sync.',
|
|
271
|
+
name: 'effect-no-sync-for-promise',
|
|
272
|
+
tokenGroups: [['sync'], ['async', 'fetch', 'Promise.']],
|
|
367
273
|
},
|
|
368
274
|
{
|
|
369
|
-
|
|
275
|
+
check: hasSyncForThrowingOPS,
|
|
370
276
|
message: 'Use Effect.try for synchronous code that can throw instead of Effect.sync.',
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
277
|
+
name: 'effect-no-sync-for-throwing-ops',
|
|
278
|
+
tokenGroups: [['sync'], ['throw', 'JSON.parse']],
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
ast: (context, source) => {
|
|
282
|
+
const isEffectModule = hasEffectSignal(source);
|
|
283
|
+
return {
|
|
284
|
+
CallExpression(node) {
|
|
285
|
+
const { objectName, propertyName } = memberParts(objectValue(node, 'callee'));
|
|
286
|
+
if (isEffectModule &&
|
|
287
|
+
objectName === 'console' &&
|
|
288
|
+
propertyName &&
|
|
289
|
+
['debug', 'error', 'info', 'log', 'warn'].includes(propertyName)) {
|
|
290
|
+
reportAST(context, 'Use Effect logging APIs instead of console logging in Effect code.', node);
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
},
|
|
375
295
|
message: 'Use Effect logging APIs instead of console logging in Effect code.',
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const { objectName, propertyName } = memberParts(node.callee);
|
|
379
|
-
if (hasEffectSignal(source) &&
|
|
380
|
-
objectName === 'console' &&
|
|
381
|
-
propertyName &&
|
|
382
|
-
['debug', 'error', 'info', 'log', 'warn'].includes(propertyName)) {
|
|
383
|
-
reportAst(context, 'Use Effect logging APIs instead of console logging in Effect code.', node);
|
|
384
|
-
}
|
|
385
|
-
},
|
|
386
|
-
}),
|
|
387
|
-
},
|
|
388
|
-
{
|
|
389
|
-
name: 'effect-no-process-env-in-effect-code',
|
|
390
|
-
message: 'Use Effect Config instead of process.env in Effect code.',
|
|
391
|
-
ast: (context, source) => ({
|
|
392
|
-
MemberExpression(node) {
|
|
393
|
-
const { objectName, propertyName } = memberParts(node);
|
|
394
|
-
const processEnv = objectName === 'process' && propertyName === 'env';
|
|
395
|
-
if (hasEffectSignal(source) && !isConfiguredPath(context, 'configLayers') && processEnv) {
|
|
396
|
-
reportAst(context, 'Use Effect Config instead of process.env in Effect code.', node);
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
}),
|
|
400
|
-
},
|
|
401
|
-
{
|
|
402
|
-
name: 'effect-no-date-now-in-effect-code',
|
|
403
|
-
message: 'Use Effect Clock instead of Date.now in Effect code.',
|
|
404
|
-
ast: (context, source) => ({
|
|
405
|
-
CallExpression(node) {
|
|
406
|
-
const { objectName, propertyName } = memberParts(node.callee);
|
|
407
|
-
if (hasEffectSignal(source) &&
|
|
408
|
-
!isConfiguredPath(context, 'adapterLayers') &&
|
|
409
|
-
objectName === 'Date' &&
|
|
410
|
-
propertyName === 'now') {
|
|
411
|
-
reportAst(context, 'Use Effect Clock instead of Date.now in Effect code.', node);
|
|
412
|
-
}
|
|
413
|
-
},
|
|
414
|
-
}),
|
|
415
|
-
},
|
|
416
|
-
{
|
|
417
|
-
name: 'effect-no-math-random-in-effect-code',
|
|
418
|
-
message: 'Use Effect Random instead of Math.random in Effect code.',
|
|
419
|
-
ast: (context, source) => ({
|
|
420
|
-
CallExpression(node) {
|
|
421
|
-
const { objectName, propertyName } = memberParts(node.callee);
|
|
422
|
-
if (hasEffectSignal(source) &&
|
|
423
|
-
!isConfiguredPath(context, 'adapterLayers') &&
|
|
424
|
-
objectName === 'Math' &&
|
|
425
|
-
propertyName === 'random') {
|
|
426
|
-
reportAst(context, 'Use Effect Random instead of Math.random in Effect code.', node);
|
|
427
|
-
}
|
|
428
|
-
},
|
|
429
|
-
}),
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
name: 'effect-no-json-parse-cast',
|
|
433
|
-
message: 'Decode external JSON with Schema instead of casting parsed unknown data.',
|
|
434
|
-
patterns: [
|
|
435
|
-
/\bJSON\.parse\s*\([^)]*\)\s+as\s+[A-Za-z_$][\w$]*/,
|
|
436
|
-
/\.json\s*\(\s*\)\s+as\s+[A-Za-z_$][\w$]*/,
|
|
437
|
-
],
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
name: 'effect-schema-prefer-decodeUnknown-effect',
|
|
441
|
-
message: 'Use Schema.decodeUnknown to decode unknown input into the Effect error channel.',
|
|
442
|
-
check: hasSchemaPromiseDecode,
|
|
443
|
-
},
|
|
444
|
-
{
|
|
445
|
-
name: 'effect-schema-require-parse-error-handling',
|
|
446
|
-
message: 'Schema parsing must expose parse errors through typed Effect handling.',
|
|
447
|
-
check: hasUnhandledSchemaEffectDecode,
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
name: 'effect-schema-use-decodeUnknown-for-external-data',
|
|
451
|
-
message: 'External data must enter through Schema.decodeUnknown.',
|
|
452
|
-
check: hasExternalJsonWithoutDecodeUnknown,
|
|
453
|
-
},
|
|
454
|
-
{
|
|
455
|
-
name: 'effect-schema-no-unsafe-sync-decode-in-effect-code',
|
|
456
|
-
message: 'Do not use throwing synchronous Schema decoders in Effect modules.',
|
|
457
|
-
check: hasSchemaSyncDecodeInEffectWorkflow,
|
|
458
|
-
},
|
|
459
|
-
{
|
|
460
|
-
name: 'effect-schema-require-parseJson-for-json-strings',
|
|
461
|
-
message: 'Use Schema.parseJson when decoding JSON strings with Schema.',
|
|
462
|
-
check: hasJsonParsedBeforeSchemaStringDecode,
|
|
463
|
-
},
|
|
464
|
-
{
|
|
465
|
-
name: 'effect-schema-correct-number-type-for-parsed-json',
|
|
466
|
-
message: 'Use the correct Schema number type for already-parsed JSON numbers.',
|
|
467
|
-
check: hasParsedJsonNumberFromString,
|
|
468
|
-
},
|
|
469
|
-
{
|
|
470
|
-
name: 'effect-schema-prefer-taggedClass-over-manual-tag',
|
|
471
|
-
message: 'Prefer Schema.TaggedClass over hand-written _tag fields.',
|
|
472
|
-
patterns: [/Schema\.Struct\s*\(\s*{[\s\S]*?_tag\s*:\s*Schema\.Literal/],
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
name: 'effect-schema-avoid-old-type-names',
|
|
476
|
-
message: 'Use current Effect Schema API names instead of obsolete lowercase helpers.',
|
|
477
|
-
patterns: [/\bSchema\.(?:string|number|boolean|array|object)\s*\(/],
|
|
478
|
-
},
|
|
479
|
-
{
|
|
480
|
-
name: 'effect-schema-no-cast-after-decode',
|
|
481
|
-
message: 'Do not cast after Schema decoding; let the schema provide the type.',
|
|
482
|
-
check: hasCastAfterSchemaDecode,
|
|
483
|
-
},
|
|
484
|
-
{
|
|
485
|
-
name: 'effect-require-acquire-release',
|
|
486
|
-
message: 'Resource acquisition must use acquireRelease, scoped, or equivalent finalization.',
|
|
487
|
-
check: hasUnreleasedAcquire,
|
|
488
|
-
},
|
|
489
|
-
{
|
|
490
|
-
name: 'effect-require-scoped-for-acquireRelease',
|
|
491
|
-
message: 'Use Effect.scoped around acquireRelease when exposing acquired resources.',
|
|
492
|
-
check: hasUnscopedAcquireRelease,
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
name: 'effect-require-scoped-for-resources',
|
|
496
|
-
message: 'Resourceful workflows must be scoped.',
|
|
497
|
-
check: hasUnscopedResourceWorkflow,
|
|
498
|
-
},
|
|
499
|
-
{
|
|
500
|
-
name: 'effect-no-fork-daemon-without-cleanup',
|
|
501
|
-
message: 'Daemon fibers must have cleanup, interruption, or supervision.',
|
|
502
|
-
check: hasForkDaemonWithoutCleanup,
|
|
503
|
-
},
|
|
504
|
-
{
|
|
505
|
-
name: 'effect-prefer-fork-scoped-for-listeners',
|
|
506
|
-
message: 'Long-running listeners should use forkScoped so they follow Scope lifetime.',
|
|
507
|
-
patterns: [
|
|
508
|
-
/Effect\.fork\s*\([\s\S]*?\b(?:listen[A-Z]\w*|subscribe[A-Z]\w*|watch[A-Z]\w*|listen|subscribe|watch)\b/,
|
|
509
|
-
],
|
|
510
|
-
},
|
|
511
|
-
{
|
|
512
|
-
name: 'effect-require-restore-for-fork-in-uninterruptible',
|
|
513
|
-
message: 'Use restore when forking inside uninterruptible regions.',
|
|
514
|
-
check: hasForkInUninterruptibleWithoutRestore,
|
|
515
|
-
},
|
|
516
|
-
{
|
|
517
|
-
name: 'effect-require-bounded-concurrency',
|
|
518
|
-
message: 'Concurrent Effect traversal must declare an explicit concurrency bound.',
|
|
519
|
-
check: hasUnboundedEffectConcurrency,
|
|
520
|
-
},
|
|
521
|
-
{
|
|
522
|
-
name: 'effect-require-bounded-flatMap-concurrency',
|
|
523
|
-
message: 'Concurrent Effect.flatMap usage must declare a bounded concurrency value.',
|
|
524
|
-
check: hasUnboundedFlatMapConcurrency,
|
|
525
|
-
},
|
|
526
|
-
{
|
|
527
|
-
name: 'effect-no-unbounded-queue',
|
|
528
|
-
message: 'Use bounded, sliding, or dropping queues instead of unbounded queues.',
|
|
529
|
-
patterns: [/\bQueue\.unbounded\s*\(/],
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
name: 'effect-no-unbounded-stream-buffer',
|
|
533
|
-
message: 'Stream buffers must be explicitly bounded.',
|
|
534
|
-
patterns: [
|
|
535
|
-
/\bStream\.(?:buffer|fromQueue|async|asyncPush)\s*\([^)]*\b(?:Infinity|unbounded)\b/,
|
|
536
|
-
],
|
|
537
|
-
},
|
|
538
|
-
{
|
|
539
|
-
name: 'effect-test-no-runpromise',
|
|
540
|
-
message: 'Use @effect/vitest it.effect instead of manually running Effects in tests.',
|
|
541
|
-
check: (source, context) => isEffectTestPath(context) && hasRuntimeCall(source),
|
|
542
|
-
},
|
|
543
|
-
{
|
|
544
|
-
name: 'effect-prefer-it-effect-for-unit-tests',
|
|
545
|
-
message: 'Use it.effect for tests that exercise Effect programs.',
|
|
546
|
-
check: (source, context) => isEffectTestPath(context) &&
|
|
547
|
-
!hasRuntimeCall(source) &&
|
|
548
|
-
/\bit\s*\([\s\S]*?Effect\./.test(stripCommentsAndStrings(source)),
|
|
549
|
-
},
|
|
550
|
-
{
|
|
551
|
-
name: 'effect-testClock-requires-fork',
|
|
552
|
-
message: 'Fork time-dependent work before adjusting TestClock.',
|
|
553
|
-
check: (source) => /TestClock\.adjust\s*\(/.test(stripCommentsAndStrings(source)) &&
|
|
554
|
-
!hasForkBeforeTestClockAdjust(source),
|
|
555
|
-
},
|
|
556
|
-
{
|
|
557
|
-
name: 'effect-testClock-requires-testContext',
|
|
558
|
-
message: 'Use Effect test context when using TestClock.',
|
|
559
|
-
check: (source, context) => isEffectTestPath(context) && hasTestClockWithoutEffectContext(source),
|
|
560
|
-
},
|
|
561
|
-
{
|
|
562
|
-
name: 'effect-no-real-sleep-in-tests',
|
|
563
|
-
message: 'Use TestClock instead of real sleeps in Effect tests.',
|
|
564
|
-
check: (source, context) => isEffectTestPath(context) && hasRealSleepWithoutTestClock(source),
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
name: 'effect-use-exit-for-failure-tests',
|
|
568
|
-
message: 'Use Effect.exit inside it.effect when asserting Effect failures.',
|
|
569
|
-
check: (source, context) => isEffectTestPath(context) &&
|
|
570
|
-
!hasRuntimeCall(source) &&
|
|
571
|
-
/\b(?:rejects|toThrow)\b/.test(stripCommentsAndStrings(source)) &&
|
|
572
|
-
/\bEffect\./.test(stripCommentsAndStrings(source)),
|
|
573
|
-
},
|
|
574
|
-
{
|
|
575
|
-
name: 'effect-no-focused-effect-tests',
|
|
576
|
-
message: 'Focused Effect tests must not be committed.',
|
|
577
|
-
check: (source, context) => isEffectTestPath(context) &&
|
|
578
|
-
/\b(?:it|describe)\.effect\.only\s*\(/.test(stripCommentsAndStrings(source)),
|
|
579
|
-
},
|
|
580
|
-
{
|
|
581
|
-
name: 'effect-no-skipped-effect-tests',
|
|
582
|
-
message: 'Skipped Effect tests must not be committed.',
|
|
583
|
-
check: (source, context) => isEffectTestPath(context) &&
|
|
584
|
-
/\b(?:it|describe)\.effect\.skip\s*\(/.test(stripCommentsAndStrings(source)),
|
|
585
|
-
},
|
|
586
|
-
{
|
|
587
|
-
name: 'effect-no-obsolete-imports',
|
|
588
|
-
message: 'Import Effect APIs from the main effect package; deprecated split packages are blocked.',
|
|
589
|
-
patterns: [/from\s+['"]@effect\/(?:io|data)['"]/],
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
name: 'effect-no-known-fake-api',
|
|
593
|
-
message: 'This is not a known Effect API for the configured version.',
|
|
594
|
-
check: (source) => /\bEffect\.(?:fromPromise|tryCatch|bracket|fromEither)\s*\(/.test(stripCommentsAndStrings(source)),
|
|
595
|
-
},
|
|
596
|
-
{
|
|
597
|
-
name: 'effect-prefer-gen-over-do',
|
|
598
|
-
message: 'Prefer Effect.gen over Effect.Do for agent-readable sequential workflows.',
|
|
599
|
-
patterns: [/\bEffect\.Do\b/],
|
|
600
|
-
},
|
|
601
|
-
{
|
|
602
|
-
name: 'effect-prefer-direct-yield-star',
|
|
603
|
-
message: 'Use direct yield* effect style instead of adapter-style Effect.gen helpers.',
|
|
604
|
-
patterns: [/Effect\.gen\s*\(\s*function\*\s*\(\s*\$\s*\)/],
|
|
605
|
-
},
|
|
606
|
-
{
|
|
607
|
-
name: 'effect-prefer-config-redacted',
|
|
608
|
-
message: 'Use Config.redacted for sensitive config values so secrets stay redacted in logs and errors.',
|
|
609
|
-
patterns: [/Config\.(?:secret|Secret)\s*\(/],
|
|
610
|
-
},
|
|
611
|
-
{
|
|
612
|
-
name: 'effect-no-deprecated-schema-package',
|
|
613
|
-
message: 'Use Schema from effect/Schema instead of @effect/schema.',
|
|
614
|
-
patterns: [/from\s+['"]@effect\/schema['"]/],
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
name: 'effect-no-deprecated-context-tag-function',
|
|
618
|
-
message: 'Use the current Context.Tag class/service pattern instead of deprecated tag helpers.',
|
|
619
|
-
patterns: [
|
|
620
|
-
/\b(?:const|let|var)\s+[A-Z][\w$]*\s*=\s*Context\.Tag(?:<[^>]+>)?\s*\(\s*['"][^'"]+['"]\s*\)/,
|
|
621
|
-
/(?:^|[;\n]\s*)Context\.Tag(?:<[^>]+>)?\s*\(\s*['"][^'"]+['"]\s*\)/,
|
|
622
|
-
],
|
|
623
|
-
},
|
|
624
|
-
{
|
|
625
|
-
name: 'effect-no-global-error-channel',
|
|
626
|
-
message: 'Use domain-specific tagged errors instead of the global Error type channel.',
|
|
627
|
-
patterns: [/Effect\.Effect\s*<[^,>]+,\s*Error\s*[,>]/],
|
|
628
|
-
},
|
|
629
|
-
{
|
|
630
|
-
name: 'effect-use-duration-constructors',
|
|
631
|
-
message: 'Use Duration constructors or string durations instead of naked millisecond numbers.',
|
|
632
|
-
patterns: [
|
|
633
|
-
/Effect\.(?:sleep|timeout|delay)\s*\(\s*\d+\s*\)/,
|
|
634
|
-
/Effect\.(?:timeout|delay)\s*\([^,]+,\s*\d+\s*\)/,
|
|
635
|
-
],
|
|
636
|
-
},
|
|
637
|
-
{
|
|
638
|
-
name: 'effect-no-mixed-effect-import-styles',
|
|
639
|
-
message: 'Use one Effect import style per file.',
|
|
640
|
-
check: hasMixedEffectImportStyles,
|
|
641
|
-
},
|
|
642
|
-
{
|
|
643
|
-
name: 'effect-prefer-effect-is',
|
|
644
|
-
message: 'Use Effect.isEffect for Effect type checks.',
|
|
645
|
-
patterns: [/\b[A-Za-z_$][\w$]*\s+instanceof\s+Effect\b/, /\._op\s*===\s*['"]Effect['"]/],
|
|
646
|
-
},
|
|
647
|
-
{
|
|
648
|
-
name: 'effect-no-try-catch-in-effect-gen',
|
|
649
|
-
message: 'Use Effect error combinators instead of try/catch inside Effect.gen.',
|
|
650
|
-
ast: (context, source) => ({
|
|
651
|
-
TryStatement(node) {
|
|
652
|
-
const { start } = node;
|
|
653
|
-
if (typeof start !== 'number') {
|
|
654
|
-
return;
|
|
655
|
-
}
|
|
656
|
-
const index = hasTryCatchInEffectGen(source);
|
|
657
|
-
if (typeof index === 'number' && index === start) {
|
|
658
|
-
reportAst(context, 'Use Effect error combinators instead of try/catch inside Effect.gen.', node);
|
|
659
|
-
}
|
|
660
|
-
},
|
|
661
|
-
}),
|
|
662
|
-
},
|
|
663
|
-
{
|
|
664
|
-
name: 'effect-no-new-promise',
|
|
665
|
-
message: 'Use Effect.async, Effect.promise, or Effect.tryPromise instead of new Promise.',
|
|
666
|
-
ast: (context, source) => ({
|
|
667
|
-
NewExpression(node) {
|
|
668
|
-
if (hasEffectSignal(source) && identifierName(node.callee) === 'Promise') {
|
|
669
|
-
reportAst(context, 'Use Effect.async, Effect.promise, or Effect.tryPromise instead of new Promise.', node);
|
|
670
|
-
}
|
|
671
|
-
},
|
|
672
|
-
}),
|
|
673
|
-
},
|
|
674
|
-
{
|
|
675
|
-
name: 'effect-no-global-timers',
|
|
676
|
-
message: 'Use Effect.sleep, Schedule, or Clock instead of global timers in Effect modules.',
|
|
677
|
-
ast: (context, source) => ({
|
|
678
|
-
CallExpression(node) {
|
|
679
|
-
const calleeName = identifierName(node.callee);
|
|
680
|
-
if (hasEffectSignal(source) &&
|
|
681
|
-
calleeName &&
|
|
682
|
-
['clearInterval', 'clearTimeout', 'setInterval', 'setTimeout'].includes(calleeName)) {
|
|
683
|
-
reportAst(context, 'Use Effect.sleep, Schedule, or Clock instead of global timers in Effect modules.', node);
|
|
684
|
-
}
|
|
685
|
-
},
|
|
686
|
-
}),
|
|
687
|
-
},
|
|
688
|
-
{
|
|
689
|
-
name: 'effect-no-native-error-classes',
|
|
690
|
-
message: 'Use tagged/data/schema errors instead of classes extending native Error.',
|
|
691
|
-
check: hasNativeErrorClassInEffectModule,
|
|
692
|
-
ast: (context, source) => ({
|
|
693
|
-
ClassDeclaration(node) {
|
|
694
|
-
if (hasEffectSignal(source) && identifierName(node.superClass) === 'Error') {
|
|
695
|
-
reportAst(context, 'Use tagged/data/schema errors instead of classes extending native Error.', node);
|
|
696
|
-
}
|
|
697
|
-
},
|
|
698
|
-
}),
|
|
699
|
-
},
|
|
700
|
-
{
|
|
701
|
-
name: 'effect-no-unsafe-effect-type-assertion',
|
|
702
|
-
message: 'Do not assert Effect error or requirement channels with type casts.',
|
|
703
|
-
check: hasUnsafeEffectTypeAssertion,
|
|
704
|
-
ast: (context) => ({
|
|
705
|
-
TSAsExpression(node) {
|
|
706
|
-
if (typeReferenceName(node.typeAnnotation) === 'Effect.Effect') {
|
|
707
|
-
reportAst(context, 'Do not assert Effect error or requirement channels with type casts.', node);
|
|
708
|
-
}
|
|
709
|
-
},
|
|
710
|
-
}),
|
|
711
|
-
},
|
|
712
|
-
{
|
|
713
|
-
name: 'effect-require-service-self-match',
|
|
714
|
-
message: 'Effect service/tag self type must match the declaring class name.',
|
|
715
|
-
check: hasServiceSelfMismatch,
|
|
716
|
-
ast: (context, source) => ({
|
|
717
|
-
ClassDeclaration(node) {
|
|
718
|
-
const className = identifierName(node.id);
|
|
719
|
-
const selfName = effectServiceSelfName(node.superClass, source);
|
|
720
|
-
if (className && selfName && className !== selfName) {
|
|
721
|
-
reportAst(context, 'Effect service/tag self type must match the declaring class name.', node);
|
|
722
|
-
}
|
|
723
|
-
},
|
|
724
|
-
}),
|
|
725
|
-
},
|
|
726
|
-
{
|
|
727
|
-
name: 'effect-no-effect-fn-iife',
|
|
728
|
-
message: 'Do not call Effect.fn as an IIFE; use Effect.gen for local one-shot workflows.',
|
|
729
|
-
check: hasEffectFnIife,
|
|
730
|
-
ast: (context, source) => ({
|
|
731
|
-
CallExpression(node) {
|
|
732
|
-
const outerCallee = node.callee;
|
|
733
|
-
const middleCallee = outerCallee?.callee;
|
|
734
|
-
const innerCallee = middleCallee?.callee;
|
|
735
|
-
if (nodeType(outerCallee) === 'CallExpression' &&
|
|
736
|
-
nodeType(middleCallee) === 'CallExpression' &&
|
|
737
|
-
isEffectMember(innerCallee, source, new Set(['fn', 'fnUntraced', 'fnUntracedEager']))) {
|
|
738
|
-
reportAst(context, 'Do not call Effect.fn as an IIFE; use Effect.gen for local one-shot workflows.', node);
|
|
739
|
-
}
|
|
740
|
-
},
|
|
741
|
-
}),
|
|
296
|
+
name: 'effect-no-console-log-in-effect-code',
|
|
297
|
+
tokens: ['console'],
|
|
742
298
|
},
|
|
299
|
+
...effectDefaultEnvironmentSpecs,
|
|
300
|
+
...effectDefaultCompatibilitySpecs,
|
|
743
301
|
];
|
|
744
|
-
const effectDefaultRules = makeRules(effectDefaultSpecs, {
|
|
302
|
+
const effectDefaultRules = makeRules(effectDefaultSpecs, {
|
|
303
|
+
defaultTokens: effectDefaultRuleTokens,
|
|
304
|
+
schema: strictPathOptionsSchema,
|
|
305
|
+
});
|
|
745
306
|
export default effectDefaultRules;
|
|
746
307
|
//# sourceMappingURL=effect-default.js.map
|