oxlint-plugin-effect 0.2.2 → 0.2.3

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.
Files changed (35) hide show
  1. package/README.md +82 -0
  2. package/dist/index.d.ts +14 -2
  3. package/dist/index.js +13 -1
  4. package/dist/presets/core.d.ts +6 -0
  5. package/dist/presets/core.js +7 -1
  6. package/dist/presets/full.d.ts +7 -0
  7. package/dist/presets/full.js +8 -1
  8. package/dist/presets/functional.d.ts +1 -0
  9. package/dist/presets/functional.js +2 -1
  10. package/dist/presets/strict.d.ts +7 -0
  11. package/dist/presets/strict.js +8 -1
  12. package/dist/rules/index.d.ts +9 -2
  13. package/dist/rules/index.js +15 -1
  14. package/dist/rules/no-dynamic-imports.d.ts +6 -0
  15. package/dist/rules/no-dynamic-imports.js +90 -0
  16. package/dist/rules/no-hand-rolled-tagged-union.d.ts +6 -0
  17. package/dist/rules/no-hand-rolled-tagged-union.js +70 -0
  18. package/dist/rules/no-make-unsafe.d.ts +6 -0
  19. package/dist/rules/no-make-unsafe.js +30 -0
  20. package/dist/rules/no-promise-control-flow-in-tests.d.ts +6 -0
  21. package/dist/rules/no-promise-control-flow-in-tests.js +94 -0
  22. package/dist/rules/no-schema-struct.d.ts +6 -0
  23. package/dist/rules/no-schema-struct.js +28 -0
  24. package/dist/rules/no-sleep-in-tests.d.ts +6 -0
  25. package/dist/rules/no-sleep-in-tests.js +55 -0
  26. package/dist/rules/no-spread.d.ts +6 -0
  27. package/dist/rules/no-spread.js +26 -0
  28. package/dist/vendor/effect-oxlint/Rule.d.ts +6 -6
  29. package/dist/vendor/effect-oxlint/RuleContext.d.ts +1 -1
  30. package/dist/vendor/effect-oxlint/RuleContext.js +1 -1
  31. package/dist/vendor/effect-oxlint/Testing.js +4 -1
  32. package/dist/vendor/effect-oxlint/Visitor.d.ts +1 -1
  33. package/dist/vendor/effect-oxlint/index.d.ts +7 -1
  34. package/dist/vendor/effect-oxlint/index.js +24 -1
  35. package/package.json +23 -10
package/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # oxlint-plugin-effect
2
+
3
+ Oxlint plugin for [Effect](https://effect.website) codebases.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ bun add -D oxlint oxlint-plugin-effect
9
+ ```
10
+
11
+ `.oxlintrc.json`:
12
+
13
+ ```json
14
+ {
15
+ "jsPlugins": ["oxlint-plugin-effect/plugin"],
16
+ "rules": {
17
+ "effect/noEffectDo": "error",
18
+ "effect/noNestedPipe": "error",
19
+ "effect/noThrowStatement": "error",
20
+ "effect/noGlobals": "warn"
21
+ },
22
+ "overrides": [
23
+ {
24
+ "files": ["**/*.test.ts", "**/*.test.tsx", "**/tests/**", "**/test/**"],
25
+ "rules": {
26
+ "effect/noInlineProvide": "off"
27
+ }
28
+ }
29
+ ]
30
+ }
31
+ ```
32
+
33
+ The `overrides` entry turns off `noInlineProvide` in test files. The rule's intent is "provide layers at the boundary, not scattered through production code" — in tests, `it.effect(() => Effect.gen(function*() { ... }).pipe(Effect.provide(TestLayer)))` is the boundary.
34
+
35
+ ```bash
36
+ oxlint
37
+ ```
38
+
39
+ ## Rule Authoring
40
+
41
+ The package also exports the Effect-first rule authoring bindings:
42
+
43
+ ```ts
44
+ import { Diagnostic, Rule, RuleContext } from "oxlint-plugin-effect/rule-bindings";
45
+
46
+ export const noThing = Rule.define({
47
+ name: "no-thing",
48
+ meta: Rule.meta({
49
+ type: "problem",
50
+ description: "Avoid thing.",
51
+ }),
52
+ create: function* () {
53
+ const ctx = yield* RuleContext;
54
+ return {
55
+ Identifier: (node) => ctx.report(Diagnostic.make({ node, message: "Avoid thing." })),
56
+ };
57
+ },
58
+ });
59
+ ```
60
+
61
+ The same bindings are available from the root export for convenience:
62
+
63
+ ```ts
64
+ import { Rule, Diagnostic, RuleContext } from "oxlint-plugin-effect";
65
+ ```
66
+
67
+ ## Rules
68
+
69
+ 66 AST-only rules across categories: API bans, global bans, import bans, statement bans, AST patterns, and Effect-context rules. Five presets are available: `core`, `full`, `effect-native`, `functional`, and `strict`.
70
+
71
+ ## Development
72
+
73
+ ```bash
74
+ bun install
75
+ bun run gate
76
+ bun run codegen
77
+ bun run add-rule <name> [--context]
78
+ ```
79
+
80
+ ## License
81
+
82
+ MIT
package/dist/index.d.ts CHANGED
@@ -1,4 +1,16 @@
1
+ import { AST_d_exports } from "./vendor/effect-oxlint/AST.js";
2
+ import { Comment_d_exports } from "./vendor/effect-oxlint/Comment.js";
3
+ import { Diagnostic_d_exports } from "./vendor/effect-oxlint/Diagnostic.js";
4
+ import { Plugin_d_exports } from "./vendor/effect-oxlint/Plugin.js";
5
+ import { RuleContext, ast, cwd, filename, id, report, sourceCode, text } from "./vendor/effect-oxlint/RuleContext.js";
6
+ import { Visitor_d_exports } from "./vendor/effect-oxlint/Visitor.js";
7
+ import { Rule_d_exports } from "./vendor/effect-oxlint/Rule.js";
8
+ import { Scope_d_exports } from "./vendor/effect-oxlint/Scope.js";
9
+ import { SourceCode_d_exports } from "./vendor/effect-oxlint/SourceCode.js";
10
+ import { Testing_d_exports } from "./vendor/effect-oxlint/Testing.js";
11
+ import { Token_d_exports } from "./vendor/effect-oxlint/Token.js";
12
+ import { Context, CreateOnceRule, CreateRule, Definition, DefinitionType, ESTree, Fix, FixFn, Fixer, LineColumn, Location, OxlintComment, OxlintPlugin, OxlintRule, OxlintScope, OxlintSourceCode, OxlintToken, OxlintVisitor, Range, Ranged, Reference, RuleDocs, RuleMeta, ScopeManager, ScopeType, Settings, Span, Suggestion, Variable, index_d_exports as index_d_exports$1 } from "./vendor/effect-oxlint/index.js";
1
13
  import _default from "./plugin.js";
2
- import { index_d_exports as index_d_exports$1 } from "./rules/index.js";
14
+ import { index_d_exports as index_d_exports$2 } from "./rules/index.js";
3
15
  import { index_d_exports } from "./presets/index.js";
4
- export { _default as plugin, index_d_exports as presets, index_d_exports$1 as rules };
16
+ export { AST_d_exports as AST, Comment_d_exports as Comment, Context, CreateOnceRule, CreateRule, Definition, DefinitionType, Diagnostic_d_exports as Diagnostic, ESTree, Fix, FixFn, Fixer, LineColumn, Location, OxlintComment, OxlintPlugin, OxlintRule, OxlintScope, OxlintSourceCode, OxlintToken, OxlintVisitor, Plugin_d_exports as Plugin, Range, Ranged, Reference, Rule_d_exports as Rule, RuleContext, RuleDocs, RuleMeta, Scope_d_exports as Scope, ScopeManager, ScopeType, Settings, SourceCode_d_exports as SourceCode, Span, Suggestion, Testing_d_exports as Testing, Token_d_exports as Token, Variable, Visitor_d_exports as Visitor, ast, cwd, filename, id, _default as plugin, index_d_exports as presets, report, index_d_exports$1 as ruleBindings, index_d_exports$2 as rules, sourceCode, text };
package/dist/index.js CHANGED
@@ -1,4 +1,16 @@
1
+ import { AST_exports } from "./vendor/effect-oxlint/AST.js";
2
+ import { Comment_exports } from "./vendor/effect-oxlint/Comment.js";
3
+ import { Diagnostic_exports } from "./vendor/effect-oxlint/Diagnostic.js";
4
+ import { Plugin_exports } from "./vendor/effect-oxlint/Plugin.js";
5
+ import { RuleContext, ast, cwd, filename, id, report, sourceCode, text } from "./vendor/effect-oxlint/RuleContext.js";
6
+ import { Visitor_exports } from "./vendor/effect-oxlint/Visitor.js";
7
+ import { Rule_exports } from "./vendor/effect-oxlint/Rule.js";
8
+ import { Scope_exports } from "./vendor/effect-oxlint/Scope.js";
9
+ import { SourceCode_exports } from "./vendor/effect-oxlint/SourceCode.js";
10
+ import { Testing_exports } from "./vendor/effect-oxlint/Testing.js";
11
+ import { Token_exports } from "./vendor/effect-oxlint/Token.js";
12
+ import { effect_oxlint_exports } from "./vendor/effect-oxlint/index.js";
1
13
  import { rules_exports } from "./rules/index.js";
2
14
  import plugin_default from "./plugin.js";
3
15
  import { presets_exports } from "./presets/index.js";
4
- export { plugin_default as plugin, presets_exports as presets, rules_exports as rules };
16
+ export { AST_exports as AST, Comment_exports as Comment, Diagnostic_exports as Diagnostic, Plugin_exports as Plugin, Rule_exports as Rule, RuleContext, Scope_exports as Scope, SourceCode_exports as SourceCode, Testing_exports as Testing, Token_exports as Token, Visitor_exports as Visitor, ast, cwd, filename, id, plugin_default as plugin, presets_exports as presets, report, effect_oxlint_exports as ruleBindings, rules_exports as rules, sourceCode, text };
@@ -27,10 +27,14 @@ declare const core: {
27
27
  readonly "effect/noUnnecessaryPipeChain": "error";
28
28
  readonly "effect/noMultipleEffectProvide": "error";
29
29
  readonly "effect/noSchemaUnionOfLiterals": "warn";
30
+ readonly "effect/noSchemaStruct": "error";
30
31
  readonly "effect/noSchemaStructWithTag": "warn";
31
32
  readonly "effect/noRedundantSchemaTagIdentifier": "warn";
32
33
  readonly "effect/noEffectMapFlatten": "warn";
33
34
  readonly "effect/noPositionalLogError": "error";
35
+ readonly "effect/noMakeUnsafe": "error";
36
+ readonly "effect/noHandRolledTaggedUnion": "error";
37
+ readonly "effect/noDynamicImports": "error";
34
38
  readonly "effect/noThrowStatement": "error";
35
39
  readonly "effect/noTryCatch": "error";
36
40
  readonly "effect/noNewPromise": "error";
@@ -38,6 +42,8 @@ declare const core: {
38
42
  readonly "effect/noReturnNullish": "warn";
39
43
  readonly "effect/noGlobals": "error";
40
44
  readonly "effect/noInlineProvide": "error";
45
+ readonly "effect/noPromiseControlFlowInTests": "error";
46
+ readonly "effect/noSleepInTests": "error";
41
47
  };
42
48
  //#endregion
43
49
  export { core };
@@ -27,17 +27,23 @@ const core = {
27
27
  "effect/noUnnecessaryPipeChain": "error",
28
28
  "effect/noMultipleEffectProvide": "error",
29
29
  "effect/noSchemaUnionOfLiterals": "warn",
30
+ "effect/noSchemaStruct": "error",
30
31
  "effect/noSchemaStructWithTag": "warn",
31
32
  "effect/noRedundantSchemaTagIdentifier": "warn",
32
33
  "effect/noEffectMapFlatten": "warn",
33
34
  "effect/noPositionalLogError": "error",
35
+ "effect/noMakeUnsafe": "error",
36
+ "effect/noHandRolledTaggedUnion": "error",
37
+ "effect/noDynamicImports": "error",
34
38
  "effect/noThrowStatement": "error",
35
39
  "effect/noTryCatch": "error",
36
40
  "effect/noNewPromise": "error",
37
41
  "effect/noNewError": "error",
38
42
  "effect/noReturnNullish": "warn",
39
43
  "effect/noGlobals": "error",
40
- "effect/noInlineProvide": "error"
44
+ "effect/noInlineProvide": "error",
45
+ "effect/noPromiseControlFlowInTests": "error",
46
+ "effect/noSleepInTests": "error"
41
47
  };
42
48
  //#endregion
43
49
  export { core };
@@ -14,6 +14,7 @@ declare const full: {
14
14
  readonly "effect/noOptionAs": "error";
15
15
  readonly "effect/noRunInEffectGen": "error";
16
16
  readonly "effect/noNodeBuiltinImport": "warn";
17
+ readonly "effect/noSpread": "error";
17
18
  readonly "effect/noNestedPipe": "error";
18
19
  readonly "effect/noNestedEffectGen": "error";
19
20
  readonly "effect/noUnnecessaryEffectGen": "error";
@@ -42,12 +43,16 @@ declare const full: {
42
43
  readonly "effect/noUnnecessaryPipeChain": "error";
43
44
  readonly "effect/noMultipleEffectProvide": "error";
44
45
  readonly "effect/noSchemaUnionOfLiterals": "warn";
46
+ readonly "effect/noSchemaStruct": "error";
45
47
  readonly "effect/noSchemaStructWithTag": "warn";
46
48
  readonly "effect/noRedundantSchemaTagIdentifier": "warn";
47
49
  readonly "effect/noEffectMapFlatten": "warn";
48
50
  readonly "effect/noGlobalErrorInFailure": "error";
49
51
  readonly "effect/noGlobalErrorInCatch": "error";
50
52
  readonly "effect/noPositionalLogError": "error";
53
+ readonly "effect/noMakeUnsafe": "error";
54
+ readonly "effect/noHandRolledTaggedUnion": "error";
55
+ readonly "effect/noDynamicImports": "error";
51
56
  readonly "effect/noThrowStatement": "error";
52
57
  readonly "effect/noTryCatch": "error";
53
58
  readonly "effect/noNewPromise": "error";
@@ -55,6 +60,8 @@ declare const full: {
55
60
  readonly "effect/noReturnNullish": "warn";
56
61
  readonly "effect/noGlobals": "error";
57
62
  readonly "effect/noInlineProvide": "error";
63
+ readonly "effect/noPromiseControlFlowInTests": "error";
64
+ readonly "effect/noSleepInTests": "error";
58
65
  };
59
66
  //#endregion
60
67
  export { full };
@@ -14,6 +14,7 @@ const full = {
14
14
  "effect/noOptionAs": "error",
15
15
  "effect/noRunInEffectGen": "error",
16
16
  "effect/noNodeBuiltinImport": "warn",
17
+ "effect/noSpread": "error",
17
18
  "effect/noNestedPipe": "error",
18
19
  "effect/noNestedEffectGen": "error",
19
20
  "effect/noUnnecessaryEffectGen": "error",
@@ -42,19 +43,25 @@ const full = {
42
43
  "effect/noUnnecessaryPipeChain": "error",
43
44
  "effect/noMultipleEffectProvide": "error",
44
45
  "effect/noSchemaUnionOfLiterals": "warn",
46
+ "effect/noSchemaStruct": "error",
45
47
  "effect/noSchemaStructWithTag": "warn",
46
48
  "effect/noRedundantSchemaTagIdentifier": "warn",
47
49
  "effect/noEffectMapFlatten": "warn",
48
50
  "effect/noGlobalErrorInFailure": "error",
49
51
  "effect/noGlobalErrorInCatch": "error",
50
52
  "effect/noPositionalLogError": "error",
53
+ "effect/noMakeUnsafe": "error",
54
+ "effect/noHandRolledTaggedUnion": "error",
55
+ "effect/noDynamicImports": "error",
51
56
  "effect/noThrowStatement": "error",
52
57
  "effect/noTryCatch": "error",
53
58
  "effect/noNewPromise": "error",
54
59
  "effect/noNewError": "error",
55
60
  "effect/noReturnNullish": "warn",
56
61
  "effect/noGlobals": "error",
57
- "effect/noInlineProvide": "error"
62
+ "effect/noInlineProvide": "error",
63
+ "effect/noPromiseControlFlowInTests": "error",
64
+ "effect/noSleepInTests": "error"
58
65
  };
59
66
  //#endregion
60
67
  export { full };
@@ -16,6 +16,7 @@ declare const functional: {
16
16
  readonly "effect/noMatchEffectBranch": "error";
17
17
  readonly "effect/noOptionBooleanNormalization": "error";
18
18
  readonly "effect/noArrowLadder": "error";
19
+ readonly "effect/noSpread": "error";
19
20
  };
20
21
  //#endregion
21
22
  export { functional };
@@ -15,7 +15,8 @@ const functional = {
15
15
  "effect/noMatchVoidBranch": "error",
16
16
  "effect/noMatchEffectBranch": "error",
17
17
  "effect/noOptionBooleanNormalization": "error",
18
- "effect/noArrowLadder": "error"
18
+ "effect/noArrowLadder": "error",
19
+ "effect/noSpread": "error"
19
20
  };
20
21
  //#endregion
21
22
  export { functional };
@@ -17,6 +17,7 @@ declare const strict: {
17
17
  readonly "effect/noReturnInArrow": "error";
18
18
  readonly "effect/noStringSentinelConst": "error";
19
19
  readonly "effect/noArrowLadder": "error";
20
+ readonly "effect/noSpread": "error";
20
21
  readonly "effect/noNodeBuiltinImport": "error";
21
22
  readonly "effect/noNestedPipe": "error";
22
23
  readonly "effect/noNestedEffectGen": "error";
@@ -46,12 +47,16 @@ declare const strict: {
46
47
  readonly "effect/noUnnecessaryPipeChain": "error";
47
48
  readonly "effect/noMultipleEffectProvide": "error";
48
49
  readonly "effect/noSchemaUnionOfLiterals": "error";
50
+ readonly "effect/noSchemaStruct": "error";
49
51
  readonly "effect/noSchemaStructWithTag": "error";
50
52
  readonly "effect/noRedundantSchemaTagIdentifier": "error";
51
53
  readonly "effect/noEffectMapFlatten": "error";
52
54
  readonly "effect/noGlobalErrorInFailure": "error";
53
55
  readonly "effect/noGlobalErrorInCatch": "error";
54
56
  readonly "effect/noPositionalLogError": "error";
57
+ readonly "effect/noMakeUnsafe": "error";
58
+ readonly "effect/noHandRolledTaggedUnion": "error";
59
+ readonly "effect/noDynamicImports": "error";
55
60
  readonly "effect/noThrowStatement": "error";
56
61
  readonly "effect/noTryCatch": "error";
57
62
  readonly "effect/noNewPromise": "error";
@@ -59,6 +64,8 @@ declare const strict: {
59
64
  readonly "effect/noReturnNullish": "error";
60
65
  readonly "effect/noGlobals": "error";
61
66
  readonly "effect/noInlineProvide": "error";
67
+ readonly "effect/noPromiseControlFlowInTests": "error";
68
+ readonly "effect/noSleepInTests": "error";
62
69
  };
63
70
  //#endregion
64
71
  export { strict };
@@ -17,6 +17,7 @@ const strict = {
17
17
  "effect/noReturnInArrow": "error",
18
18
  "effect/noStringSentinelConst": "error",
19
19
  "effect/noArrowLadder": "error",
20
+ "effect/noSpread": "error",
20
21
  "effect/noNodeBuiltinImport": "error",
21
22
  "effect/noNestedPipe": "error",
22
23
  "effect/noNestedEffectGen": "error",
@@ -46,19 +47,25 @@ const strict = {
46
47
  "effect/noUnnecessaryPipeChain": "error",
47
48
  "effect/noMultipleEffectProvide": "error",
48
49
  "effect/noSchemaUnionOfLiterals": "error",
50
+ "effect/noSchemaStruct": "error",
49
51
  "effect/noSchemaStructWithTag": "error",
50
52
  "effect/noRedundantSchemaTagIdentifier": "error",
51
53
  "effect/noEffectMapFlatten": "error",
52
54
  "effect/noGlobalErrorInFailure": "error",
53
55
  "effect/noGlobalErrorInCatch": "error",
54
56
  "effect/noPositionalLogError": "error",
57
+ "effect/noMakeUnsafe": "error",
58
+ "effect/noHandRolledTaggedUnion": "error",
59
+ "effect/noDynamicImports": "error",
55
60
  "effect/noThrowStatement": "error",
56
61
  "effect/noTryCatch": "error",
57
62
  "effect/noNewPromise": "error",
58
63
  "effect/noNewError": "error",
59
64
  "effect/noReturnNullish": "error",
60
65
  "effect/noGlobals": "error",
61
- "effect/noInlineProvide": "error"
66
+ "effect/noInlineProvide": "error",
67
+ "effect/noPromiseControlFlowInTests": "error",
68
+ "effect/noSleepInTests": "error"
62
69
  };
63
70
  //#endregion
64
71
  export { strict };
@@ -10,6 +10,7 @@ import { noSwitchStatement } from "./no-switch-statement.js";
10
10
  import { noArrowLadder } from "./no-arrow-ladder.js";
11
11
  import { noAsyncFunction } from "./no-async-function.js";
12
12
  import { noCatchAllToMapError } from "./no-catch-all-to-map-error.js";
13
+ import { noDynamicImports } from "./no-dynamic-imports.js";
13
14
  import { noEffectFnGenerator } from "./no-effect-fn-generator.js";
14
15
  import { noEffectGenAdapter } from "./no-effect-gen-adapter.js";
15
16
  import { noEffectMapFlatten } from "./no-effect-map-flatten.js";
@@ -27,8 +28,10 @@ import { noFlatmapLadder } from "./no-flatmap-ladder.js";
27
28
  import { noFromNullableCoalesce } from "./no-fromnullable-coalesce.js";
28
29
  import { noGlobalErrorInCatch } from "./no-global-error-in-catch.js";
29
30
  import { noGlobalErrorInFailure } from "./no-global-error-in-failure.js";
31
+ import { noHandRolledTaggedUnion } from "./no-hand-rolled-tagged-union.js";
30
32
  import { noIifeWrapper } from "./no-iife-wrapper.js";
31
33
  import { noInstanceofSchema } from "./no-instanceof-schema.js";
34
+ import { noMakeUnsafe } from "./no-make-unsafe.js";
32
35
  import { noManualEffectChannels } from "./no-manual-effect-channels.js";
33
36
  import { noMatchEffectBranch } from "./no-match-effect-branch.js";
34
37
  import { noMatchVoidBranch } from "./no-match-void-branch.js";
@@ -39,10 +42,14 @@ import { noNestedPipe } from "./no-nested-pipe.js";
39
42
  import { noNodeBuiltinImport } from "./no-node-builtin-import.js";
40
43
  import { noOptionBooleanNormalization } from "./no-option-boolean-normalization.js";
41
44
  import { noPositionalLogError } from "./no-positional-log-error.js";
45
+ import { noPromiseControlFlowInTests } from "./no-promise-control-flow-in-tests.js";
42
46
  import { noRedundantSchemaTagIdentifier } from "./no-redundant-schema-tag-identifier.js";
43
47
  import { noReturnInArrow } from "./no-return-in-arrow.js";
44
48
  import { noSchemaStructWithTag } from "./no-schema-struct-with-tag.js";
49
+ import { noSchemaStruct } from "./no-schema-struct.js";
45
50
  import { noSchemaUnionOfLiterals } from "./no-schema-union-of-literals.js";
51
+ import { noSleepInTests } from "./no-sleep-in-tests.js";
52
+ import { noSpread } from "./no-spread.js";
46
53
  import { noStringSentinelConst } from "./no-string-sentinel-const.js";
47
54
  import { noTernary } from "./no-ternary.js";
48
55
  import { noUnnecessaryArrowBlock } from "./no-unnecessary-arrow-block.js";
@@ -60,7 +67,7 @@ import { noTryCatch } from "./no-try-catch.js";
60
67
 
61
68
  //#region src/rules/index.d.ts
62
69
  declare namespace index_d_exports {
63
- export { noArrowLadder, noAsyncFunction, noCatchAllToMapError, noEffectAs, noEffectAsync, noEffectBind, noEffectDo, noEffectFnGenerator, noEffectGenAdapter, noEffectMapFlatten, noEffectMapVoid, noEffectNever, noEffectOrElseLadder, noEffectSucceedString, noEffectSucceedVariable, noEffectSucceedVoid, noEffectSyncConsole, noEffectSyncWrapper, noEffectTypeAlias, noEffectWrapperAlias, noExtendsNativeError, noFlatmapLadder, noFromNullableCoalesce, noGlobalErrorInCatch, noGlobalErrorInFailure, noGlobals, noIfStatement, noIifeWrapper, noInlineProvide, noInstanceofSchema, noManualEffectChannels, noMatchEffectBranch, noMatchVoidBranch, noMultipleEffectProvide, noNestedEffectCall, noNestedEffectGen, noNestedPipe, noNewError, noNewPromise, noNodeBuiltinImport, noOptionAs, noOptionBooleanNormalization, noPositionalLogError, noRedundantSchemaTagIdentifier, noReturnInArrow, noReturnNullish, noRunInEffectGen, noRuntimeRunFork, noSchemaStructWithTag, noSchemaUnionOfLiterals, noStringSentinelConst, noSwitchStatement, noTernary, noThrowStatement, noTryCatch, noUnnecessaryArrowBlock, noUnnecessaryEffectGen, noUnnecessaryPipe, noUnnecessaryPipeChain };
70
+ export { noArrowLadder, noAsyncFunction, noCatchAllToMapError, noDynamicImports, noEffectAs, noEffectAsync, noEffectBind, noEffectDo, noEffectFnGenerator, noEffectGenAdapter, noEffectMapFlatten, noEffectMapVoid, noEffectNever, noEffectOrElseLadder, noEffectSucceedString, noEffectSucceedVariable, noEffectSucceedVoid, noEffectSyncConsole, noEffectSyncWrapper, noEffectTypeAlias, noEffectWrapperAlias, noExtendsNativeError, noFlatmapLadder, noFromNullableCoalesce, noGlobalErrorInCatch, noGlobalErrorInFailure, noGlobals, noHandRolledTaggedUnion, noIfStatement, noIifeWrapper, noInlineProvide, noInstanceofSchema, noMakeUnsafe, noManualEffectChannels, noMatchEffectBranch, noMatchVoidBranch, noMultipleEffectProvide, noNestedEffectCall, noNestedEffectGen, noNestedPipe, noNewError, noNewPromise, noNodeBuiltinImport, noOptionAs, noOptionBooleanNormalization, noPositionalLogError, noPromiseControlFlowInTests, noRedundantSchemaTagIdentifier, noReturnInArrow, noReturnNullish, noRunInEffectGen, noRuntimeRunFork, noSchemaStruct, noSchemaStructWithTag, noSchemaUnionOfLiterals, noSleepInTests, noSpread, noStringSentinelConst, noSwitchStatement, noTernary, noThrowStatement, noTryCatch, noUnnecessaryArrowBlock, noUnnecessaryEffectGen, noUnnecessaryPipe, noUnnecessaryPipeChain };
64
71
  }
65
72
  //#endregion
66
- export { index_d_exports, noArrowLadder, noAsyncFunction, noCatchAllToMapError, noEffectAs, noEffectAsync, noEffectBind, noEffectDo, noEffectFnGenerator, noEffectGenAdapter, noEffectMapFlatten, noEffectMapVoid, noEffectNever, noEffectOrElseLadder, noEffectSucceedString, noEffectSucceedVariable, noEffectSucceedVoid, noEffectSyncConsole, noEffectSyncWrapper, noEffectTypeAlias, noEffectWrapperAlias, noExtendsNativeError, noFlatmapLadder, noFromNullableCoalesce, noGlobalErrorInCatch, noGlobalErrorInFailure, noGlobals, noIfStatement, noIifeWrapper, noInlineProvide, noInstanceofSchema, noManualEffectChannels, noMatchEffectBranch, noMatchVoidBranch, noMultipleEffectProvide, noNestedEffectCall, noNestedEffectGen, noNestedPipe, noNewError, noNewPromise, noNodeBuiltinImport, noOptionAs, noOptionBooleanNormalization, noPositionalLogError, noRedundantSchemaTagIdentifier, noReturnInArrow, noReturnNullish, noRunInEffectGen, noRuntimeRunFork, noSchemaStructWithTag, noSchemaUnionOfLiterals, noStringSentinelConst, noSwitchStatement, noTernary, noThrowStatement, noTryCatch, noUnnecessaryArrowBlock, noUnnecessaryEffectGen, noUnnecessaryPipe, noUnnecessaryPipeChain };
73
+ export { index_d_exports, noArrowLadder, noAsyncFunction, noCatchAllToMapError, noDynamicImports, noEffectAs, noEffectAsync, noEffectBind, noEffectDo, noEffectFnGenerator, noEffectGenAdapter, noEffectMapFlatten, noEffectMapVoid, noEffectNever, noEffectOrElseLadder, noEffectSucceedString, noEffectSucceedVariable, noEffectSucceedVoid, noEffectSyncConsole, noEffectSyncWrapper, noEffectTypeAlias, noEffectWrapperAlias, noExtendsNativeError, noFlatmapLadder, noFromNullableCoalesce, noGlobalErrorInCatch, noGlobalErrorInFailure, noGlobals, noHandRolledTaggedUnion, noIfStatement, noIifeWrapper, noInlineProvide, noInstanceofSchema, noMakeUnsafe, noManualEffectChannels, noMatchEffectBranch, noMatchVoidBranch, noMultipleEffectProvide, noNestedEffectCall, noNestedEffectGen, noNestedPipe, noNewError, noNewPromise, noNodeBuiltinImport, noOptionAs, noOptionBooleanNormalization, noPositionalLogError, noPromiseControlFlowInTests, noRedundantSchemaTagIdentifier, noReturnInArrow, noReturnNullish, noRunInEffectGen, noRuntimeRunFork, noSchemaStruct, noSchemaStructWithTag, noSchemaUnionOfLiterals, noSleepInTests, noSpread, noStringSentinelConst, noSwitchStatement, noTernary, noThrowStatement, noTryCatch, noUnnecessaryArrowBlock, noUnnecessaryEffectGen, noUnnecessaryPipe, noUnnecessaryPipeChain };
@@ -11,6 +11,7 @@ import { noSwitchStatement } from "./no-switch-statement.js";
11
11
  import { noArrowLadder } from "./no-arrow-ladder.js";
12
12
  import { noAsyncFunction } from "./no-async-function.js";
13
13
  import { noCatchAllToMapError } from "./no-catch-all-to-map-error.js";
14
+ import { noDynamicImports } from "./no-dynamic-imports.js";
14
15
  import { noEffectFnGenerator } from "./no-effect-fn-generator.js";
15
16
  import { noEffectGenAdapter } from "./no-effect-gen-adapter.js";
16
17
  import { noEffectMapFlatten } from "./no-effect-map-flatten.js";
@@ -28,8 +29,10 @@ import { noFlatmapLadder } from "./no-flatmap-ladder.js";
28
29
  import { noFromNullableCoalesce } from "./no-fromnullable-coalesce.js";
29
30
  import { noGlobalErrorInCatch } from "./no-global-error-in-catch.js";
30
31
  import { noGlobalErrorInFailure } from "./no-global-error-in-failure.js";
32
+ import { noHandRolledTaggedUnion } from "./no-hand-rolled-tagged-union.js";
31
33
  import { noIifeWrapper } from "./no-iife-wrapper.js";
32
34
  import { noInstanceofSchema } from "./no-instanceof-schema.js";
35
+ import { noMakeUnsafe } from "./no-make-unsafe.js";
33
36
  import { noManualEffectChannels } from "./no-manual-effect-channels.js";
34
37
  import { noMatchEffectBranch } from "./no-match-effect-branch.js";
35
38
  import { noMatchVoidBranch } from "./no-match-void-branch.js";
@@ -40,10 +43,14 @@ import { noNestedPipe } from "./no-nested-pipe.js";
40
43
  import { noNodeBuiltinImport } from "./no-node-builtin-import.js";
41
44
  import { noOptionBooleanNormalization } from "./no-option-boolean-normalization.js";
42
45
  import { noPositionalLogError } from "./no-positional-log-error.js";
46
+ import { noPromiseControlFlowInTests } from "./no-promise-control-flow-in-tests.js";
43
47
  import { noRedundantSchemaTagIdentifier } from "./no-redundant-schema-tag-identifier.js";
44
48
  import { noReturnInArrow } from "./no-return-in-arrow.js";
45
49
  import { noSchemaStructWithTag } from "./no-schema-struct-with-tag.js";
50
+ import { noSchemaStruct } from "./no-schema-struct.js";
46
51
  import { noSchemaUnionOfLiterals } from "./no-schema-union-of-literals.js";
52
+ import { noSleepInTests } from "./no-sleep-in-tests.js";
53
+ import { noSpread } from "./no-spread.js";
47
54
  import { noStringSentinelConst } from "./no-string-sentinel-const.js";
48
55
  import { noTernary } from "./no-ternary.js";
49
56
  import { noUnnecessaryArrowBlock } from "./no-unnecessary-arrow-block.js";
@@ -63,6 +70,7 @@ var rules_exports = /* @__PURE__ */ __exportAll({
63
70
  noArrowLadder: () => noArrowLadder,
64
71
  noAsyncFunction: () => noAsyncFunction,
65
72
  noCatchAllToMapError: () => noCatchAllToMapError,
73
+ noDynamicImports: () => noDynamicImports,
66
74
  noEffectAs: () => noEffectAs,
67
75
  noEffectAsync: () => noEffectAsync,
68
76
  noEffectBind: () => noEffectBind,
@@ -86,10 +94,12 @@ var rules_exports = /* @__PURE__ */ __exportAll({
86
94
  noGlobalErrorInCatch: () => noGlobalErrorInCatch,
87
95
  noGlobalErrorInFailure: () => noGlobalErrorInFailure,
88
96
  noGlobals: () => noGlobals,
97
+ noHandRolledTaggedUnion: () => noHandRolledTaggedUnion,
89
98
  noIfStatement: () => noIfStatement,
90
99
  noIifeWrapper: () => noIifeWrapper,
91
100
  noInlineProvide: () => noInlineProvide,
92
101
  noInstanceofSchema: () => noInstanceofSchema,
102
+ noMakeUnsafe: () => noMakeUnsafe,
93
103
  noManualEffectChannels: () => noManualEffectChannels,
94
104
  noMatchEffectBranch: () => noMatchEffectBranch,
95
105
  noMatchVoidBranch: () => noMatchVoidBranch,
@@ -103,13 +113,17 @@ var rules_exports = /* @__PURE__ */ __exportAll({
103
113
  noOptionAs: () => noOptionAs,
104
114
  noOptionBooleanNormalization: () => noOptionBooleanNormalization,
105
115
  noPositionalLogError: () => noPositionalLogError,
116
+ noPromiseControlFlowInTests: () => noPromiseControlFlowInTests,
106
117
  noRedundantSchemaTagIdentifier: () => noRedundantSchemaTagIdentifier,
107
118
  noReturnInArrow: () => noReturnInArrow,
108
119
  noReturnNullish: () => noReturnNullish,
109
120
  noRunInEffectGen: () => noRunInEffectGen,
110
121
  noRuntimeRunFork: () => noRuntimeRunFork,
122
+ noSchemaStruct: () => noSchemaStruct,
111
123
  noSchemaStructWithTag: () => noSchemaStructWithTag,
112
124
  noSchemaUnionOfLiterals: () => noSchemaUnionOfLiterals,
125
+ noSleepInTests: () => noSleepInTests,
126
+ noSpread: () => noSpread,
113
127
  noStringSentinelConst: () => noStringSentinelConst,
114
128
  noSwitchStatement: () => noSwitchStatement,
115
129
  noTernary: () => noTernary,
@@ -121,4 +135,4 @@ var rules_exports = /* @__PURE__ */ __exportAll({
121
135
  noUnnecessaryPipeChain: () => noUnnecessaryPipeChain
122
136
  });
123
137
  //#endregion
124
- export { noArrowLadder, noAsyncFunction, noCatchAllToMapError, noEffectAs, noEffectAsync, noEffectBind, noEffectDo, noEffectFnGenerator, noEffectGenAdapter, noEffectMapFlatten, noEffectMapVoid, noEffectNever, noEffectOrElseLadder, noEffectSucceedString, noEffectSucceedVariable, noEffectSucceedVoid, noEffectSyncConsole, noEffectSyncWrapper, noEffectTypeAlias, noEffectWrapperAlias, noExtendsNativeError, noFlatmapLadder, noFromNullableCoalesce, noGlobalErrorInCatch, noGlobalErrorInFailure, noGlobals, noIfStatement, noIifeWrapper, noInlineProvide, noInstanceofSchema, noManualEffectChannels, noMatchEffectBranch, noMatchVoidBranch, noMultipleEffectProvide, noNestedEffectCall, noNestedEffectGen, noNestedPipe, noNewError, noNewPromise, noNodeBuiltinImport, noOptionAs, noOptionBooleanNormalization, noPositionalLogError, noRedundantSchemaTagIdentifier, noReturnInArrow, noReturnNullish, noRunInEffectGen, noRuntimeRunFork, noSchemaStructWithTag, noSchemaUnionOfLiterals, noStringSentinelConst, noSwitchStatement, noTernary, noThrowStatement, noTryCatch, noUnnecessaryArrowBlock, noUnnecessaryEffectGen, noUnnecessaryPipe, noUnnecessaryPipeChain, rules_exports };
138
+ export { noArrowLadder, noAsyncFunction, noCatchAllToMapError, noDynamicImports, noEffectAs, noEffectAsync, noEffectBind, noEffectDo, noEffectFnGenerator, noEffectGenAdapter, noEffectMapFlatten, noEffectMapVoid, noEffectNever, noEffectOrElseLadder, noEffectSucceedString, noEffectSucceedVariable, noEffectSucceedVoid, noEffectSyncConsole, noEffectSyncWrapper, noEffectTypeAlias, noEffectWrapperAlias, noExtendsNativeError, noFlatmapLadder, noFromNullableCoalesce, noGlobalErrorInCatch, noGlobalErrorInFailure, noGlobals, noHandRolledTaggedUnion, noIfStatement, noIifeWrapper, noInlineProvide, noInstanceofSchema, noMakeUnsafe, noManualEffectChannels, noMatchEffectBranch, noMatchVoidBranch, noMultipleEffectProvide, noNestedEffectCall, noNestedEffectGen, noNestedPipe, noNewError, noNewPromise, noNodeBuiltinImport, noOptionAs, noOptionBooleanNormalization, noPositionalLogError, noPromiseControlFlowInTests, noRedundantSchemaTagIdentifier, noReturnInArrow, noReturnNullish, noRunInEffectGen, noRuntimeRunFork, noSchemaStruct, noSchemaStructWithTag, noSchemaUnionOfLiterals, noSleepInTests, noSpread, noStringSentinelConst, noSwitchStatement, noTernary, noThrowStatement, noTryCatch, noUnnecessaryArrowBlock, noUnnecessaryEffectGen, noUnnecessaryPipe, noUnnecessaryPipeChain, rules_exports };
@@ -0,0 +1,6 @@
1
+ import * as _$_oxlint_plugins0 from "@oxlint/plugins";
2
+
3
+ //#region src/rules/no-dynamic-imports.d.ts
4
+ declare const noDynamicImports: _$_oxlint_plugins0.CreateRule;
5
+ //#endregion
6
+ export { noDynamicImports };
@@ -0,0 +1,90 @@
1
+ import { make } from "../vendor/effect-oxlint/Diagnostic.js";
2
+ import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
3
+ import { define, meta } from "../vendor/effect-oxlint/Rule.js";
4
+ import "../vendor/effect-oxlint/index.js";
5
+ import * as Effect from "effect/Effect";
6
+ //#region src/rules/no-dynamic-imports.ts
7
+ const allowPattern = /\beffect\/no-dynamic-imports:\s*allow\s+\S/;
8
+ const isAstNode = (value) => typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
9
+ const getNodeField = (node, field) => {
10
+ const value = node[field];
11
+ return isAstNode(value) ? value : void 0;
12
+ };
13
+ const getStringField = (node, field) => {
14
+ const value = node[field];
15
+ return typeof value === "string" ? value : void 0;
16
+ };
17
+ const getLine = (node, edge) => {
18
+ const loc = node["loc"];
19
+ if (typeof loc !== "object" || loc === null) return void 0;
20
+ const point = loc[edge];
21
+ if (typeof point !== "object" || point === null) return void 0;
22
+ const line = point["line"];
23
+ return typeof line === "number" ? line : void 0;
24
+ };
25
+ const hasAllowComment = (node, comments) => {
26
+ const startLine = getLine(node, "start");
27
+ if (startLine === void 0) return false;
28
+ return comments.some((comment) => {
29
+ const endLine = getLine(comment, "end");
30
+ if (endLine === void 0) return false;
31
+ if (endLine !== startLine - 1 && endLine !== startLine) return false;
32
+ return allowPattern.test(comment.value);
33
+ });
34
+ };
35
+ const createRequireAliasName = (node) => {
36
+ if (node.type !== "VariableDeclarator") return void 0;
37
+ const id = getNodeField(node, "id");
38
+ const init = getNodeField(node, "init");
39
+ if (id?.type !== "Identifier" || init?.type !== "CallExpression") return void 0;
40
+ const callee = getNodeField(init, "callee");
41
+ if (callee?.type !== "Identifier" || getStringField(callee, "name") !== "createRequire") return void 0;
42
+ return getStringField(id, "name");
43
+ };
44
+ const classifyDynamicLoadCall = (callee, createRequireAliases) => {
45
+ if (callee === void 0) return void 0;
46
+ if (callee.type === "Identifier") {
47
+ const name = getStringField(callee, "name");
48
+ if (name === "require") return "`require(...)` is forbidden. Use a top-level static import.";
49
+ if (name !== void 0 && createRequireAliases.has(name)) return "`createRequire(...)` aliases are forbidden. Use a top-level static import.";
50
+ }
51
+ if (callee.type === "MemberExpression") {
52
+ const object = getNodeField(callee, "object");
53
+ const property = getNodeField(callee, "property");
54
+ if (object?.type === "Identifier" && getStringField(object, "name") === "module" && property?.type === "Identifier" && getStringField(property, "name") === "require") return "`module.require(...)` is forbidden. Use a top-level static import.";
55
+ }
56
+ if (callee.type === "CallExpression") {
57
+ const inner = getNodeField(callee, "callee");
58
+ if (inner?.type === "Identifier" && getStringField(inner, "name") === "createRequire") return "`createRequire(...)(...)` is forbidden. Use a top-level static import.";
59
+ }
60
+ };
61
+ const noDynamicImports = define({
62
+ name: "no-dynamic-imports",
63
+ meta: meta({
64
+ type: "problem",
65
+ description: "Avoid dynamic import/require. Use top-level static imports."
66
+ }),
67
+ create: function* () {
68
+ const ctx = yield* RuleContext;
69
+ const createRequireAliases = /* @__PURE__ */ new Set();
70
+ const reportUnlessAllowed = (node, message) => hasAllowComment(node, ctx.sourceCode.getAllComments()) ? Effect.void : ctx.report(make({
71
+ node,
72
+ message
73
+ }));
74
+ return {
75
+ VariableDeclarator: (node) => {
76
+ const alias = createRequireAliasName(node);
77
+ if (alias === void 0) return Effect.void;
78
+ createRequireAliases.add(alias);
79
+ return reportUnlessAllowed(node, "`createRequire(...)` is forbidden. Use a top-level static import.");
80
+ },
81
+ ImportExpression: (node) => reportUnlessAllowed(node, "Dynamic `import(...)` is forbidden. Use a top-level static import."),
82
+ CallExpression: (node) => {
83
+ const message = classifyDynamicLoadCall(getNodeField(node, "callee"), createRequireAliases);
84
+ return message === void 0 ? Effect.void : reportUnlessAllowed(node, message);
85
+ }
86
+ };
87
+ }
88
+ });
89
+ //#endregion
90
+ export { noDynamicImports };
@@ -0,0 +1,6 @@
1
+ import * as _$_oxlint_plugins0 from "@oxlint/plugins";
2
+
3
+ //#region src/rules/no-hand-rolled-tagged-union.d.ts
4
+ declare const noHandRolledTaggedUnion: _$_oxlint_plugins0.CreateRule;
5
+ //#endregion
6
+ export { noHandRolledTaggedUnion };
@@ -0,0 +1,70 @@
1
+ import { make } from "../vendor/effect-oxlint/Diagnostic.js";
2
+ import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
3
+ import { define, meta } from "../vendor/effect-oxlint/Rule.js";
4
+ import "../vendor/effect-oxlint/index.js";
5
+ import * as Effect from "effect/Effect";
6
+ //#region src/rules/no-hand-rolled-tagged-union.ts
7
+ const isAstNode = (value) => typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
8
+ const getNodeField = (node, field) => {
9
+ const value = node[field];
10
+ return isAstNode(value) ? value : void 0;
11
+ };
12
+ const getStringField = (node, field) => {
13
+ const value = node[field];
14
+ return typeof value === "string" ? value : void 0;
15
+ };
16
+ const getNodeArrayField = (node, field) => {
17
+ const value = node[field];
18
+ return Array.isArray(value) ? value.filter(isAstNode) : void 0;
19
+ };
20
+ const getPropertyKeyName = (node) => {
21
+ if (node.type === "Identifier" || node.type === "StringLiteral" || node.type === "Literal") return getStringField(node, "name") ?? getStringField(node, "value");
22
+ };
23
+ const isPascalCaseTag = (value) => {
24
+ if (value.length === 0) return false;
25
+ const first = value.charAt(0);
26
+ return first === first.toUpperCase() && first !== first.toLowerCase();
27
+ };
28
+ const isReportableTagLiteral = (member) => {
29
+ if (member.type !== "TSPropertySignature") return false;
30
+ const key = getNodeField(member, "key");
31
+ if (key === void 0 || getPropertyKeyName(key) !== "_tag") return false;
32
+ const annotation = getNodeField(member, "typeAnnotation");
33
+ const inner = annotation === void 0 ? void 0 : getNodeField(annotation, "typeAnnotation");
34
+ if (inner?.type !== "TSLiteralType") return false;
35
+ const literal = getNodeField(inner, "literal");
36
+ if (literal === void 0) return false;
37
+ if (literal.type !== "StringLiteral" && literal.type !== "Literal") return false;
38
+ const value = getStringField(literal, "value");
39
+ return value !== void 0 && isPascalCaseTag(value);
40
+ };
41
+ const literalHasTag = (node) => {
42
+ if (node.type !== "TSTypeLiteral") return false;
43
+ const members = getNodeArrayField(node, "members");
44
+ return members !== void 0 && members.some(isReportableTagLiteral);
45
+ };
46
+ const noHandRolledTaggedUnion = define({
47
+ name: "no-hand-rolled-tagged-union",
48
+ meta: meta({
49
+ type: "suggestion",
50
+ description: "Use Schema.TaggedUnion, Schema.TaggedStruct, or Schema.TaggedErrorClass instead of inline _tag union literals."
51
+ }),
52
+ create: function* () {
53
+ const ctx = yield* RuleContext;
54
+ return { TSUnionType: (node) => {
55
+ const types = getNodeArrayField(node, "types");
56
+ if (types === void 0 || types.length < 2) return Effect.void;
57
+ let tagged = 0;
58
+ for (const type of types) {
59
+ if (literalHasTag(type)) tagged += 1;
60
+ if (tagged >= 2) return ctx.report(make({
61
+ node,
62
+ message: "Hand-rolled `_tag` discriminated union. Use Schema.TaggedUnion, Schema.TaggedStruct, or Schema.TaggedErrorClass instead."
63
+ }));
64
+ }
65
+ return Effect.void;
66
+ } };
67
+ }
68
+ });
69
+ //#endregion
70
+ export { noHandRolledTaggedUnion };
@@ -0,0 +1,6 @@
1
+ import * as _$_oxlint_plugins0 from "@oxlint/plugins";
2
+
3
+ //#region src/rules/no-make-unsafe.d.ts
4
+ declare const noMakeUnsafe: _$_oxlint_plugins0.CreateRule;
5
+ //#endregion
6
+ export { noMakeUnsafe };
@@ -0,0 +1,30 @@
1
+ import { memberNames } from "../vendor/effect-oxlint/AST.js";
2
+ import { make } from "../vendor/effect-oxlint/Diagnostic.js";
3
+ import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
4
+ import { define, meta } from "../vendor/effect-oxlint/Rule.js";
5
+ import "../vendor/effect-oxlint/index.js";
6
+ import * as Option from "effect/Option";
7
+ import * as Effect from "effect/Effect";
8
+ //#region src/rules/no-make-unsafe.ts
9
+ const noMakeUnsafe = define({
10
+ name: "no-make-unsafe",
11
+ meta: meta({
12
+ type: "problem",
13
+ description: "Avoid makeUnsafe constructors. Use safe Effectful or Option-returning construction."
14
+ }),
15
+ create: function* () {
16
+ const ctx = yield* RuleContext;
17
+ return { CallExpression: (node) => {
18
+ const call = node;
19
+ if (call.callee.type !== "MemberExpression") return Effect.void;
20
+ if (Option.isNone(memberNames(call.callee))) return Effect.void;
21
+ if (Option.getOrUndefined(memberNames(call.callee))?.[1] !== "makeUnsafe") return Effect.void;
22
+ return ctx.report(make({
23
+ node,
24
+ message: "Do not call `.makeUnsafe(...)`. Use safe Effectful/Option-returning construction instead of bypassing validation."
25
+ }));
26
+ } };
27
+ }
28
+ });
29
+ //#endregion
30
+ export { noMakeUnsafe };
@@ -0,0 +1,6 @@
1
+ import * as _$_oxlint_plugins0 from "@oxlint/plugins";
2
+
3
+ //#region src/rules/no-promise-control-flow-in-tests.d.ts
4
+ declare const noPromiseControlFlowInTests: _$_oxlint_plugins0.CreateRule;
5
+ //#endregion
6
+ export { noPromiseControlFlowInTests };
@@ -0,0 +1,94 @@
1
+ import { memberNames } from "../vendor/effect-oxlint/AST.js";
2
+ import { make } from "../vendor/effect-oxlint/Diagnostic.js";
3
+ import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
4
+ import { define, meta } from "../vendor/effect-oxlint/Rule.js";
5
+ import "../vendor/effect-oxlint/index.js";
6
+ import * as Option from "effect/Option";
7
+ import * as Effect from "effect/Effect";
8
+ //#region src/rules/no-promise-control-flow-in-tests.ts
9
+ const isTestFilename = (filename) => /\.test\.tsx?$/.test(filename) || /\/tests\/.*\.[cm]?tsx?$/.test(filename);
10
+ const promiseChainMethods = new Set([
11
+ "then",
12
+ "catch",
13
+ "finally"
14
+ ]);
15
+ const promiseStaticMethods = new Set([
16
+ "all",
17
+ "allSettled",
18
+ "any",
19
+ "race",
20
+ "resolve",
21
+ "reject"
22
+ ]);
23
+ const isAsyncFunction = (node) => "async" in node && node.async === true;
24
+ const promiseChainMethodName = (node) => {
25
+ if (node.callee.type !== "MemberExpression") return void 0;
26
+ const names = Option.getOrUndefined(memberNames(node.callee));
27
+ if (names === void 0) return void 0;
28
+ const [object, property] = names;
29
+ if (object === "Effect") return void 0;
30
+ return promiseChainMethods.has(property) ? property : void 0;
31
+ };
32
+ const noPromiseControlFlowInTests = define({
33
+ name: "no-promise-control-flow-in-tests",
34
+ meta: meta({
35
+ type: "problem",
36
+ description: "Keep test control flow in Effect instead of async/await, try/finally, or Promise chains."
37
+ }),
38
+ create: function* () {
39
+ const ctx = yield* RuleContext;
40
+ if (!isTestFilename(ctx.filename)) return {};
41
+ return {
42
+ TryStatement: (node) => {
43
+ if (!("finalizer" in node) || node.finalizer == null) return Effect.void;
44
+ return ctx.report(make({
45
+ node,
46
+ message: "Do not use `try/finally` cleanup in tests. Use Effect scopes."
47
+ }));
48
+ },
49
+ FunctionDeclaration: (node) => isAsyncFunction(node) ? ctx.report(make({
50
+ node,
51
+ message: "Do not use async test functions. Return an Effect from the test body."
52
+ })) : Effect.void,
53
+ FunctionExpression: (node) => isAsyncFunction(node) ? ctx.report(make({
54
+ node,
55
+ message: "Do not use async test functions. Return an Effect from the test body."
56
+ })) : Effect.void,
57
+ ArrowFunctionExpression: (node) => isAsyncFunction(node) ? ctx.report(make({
58
+ node,
59
+ message: "Do not use async test functions. Return an Effect from the test body."
60
+ })) : Effect.void,
61
+ AwaitExpression: (node) => ctx.report(make({
62
+ node,
63
+ message: "Do not use `await` in tests. Use `yield*` inside Effect.gen."
64
+ })),
65
+ NewExpression: (node) => {
66
+ const expression = node;
67
+ if (expression.callee.type !== "Identifier" || expression.callee.name !== "Promise") return Effect.void;
68
+ return ctx.report(make({
69
+ node,
70
+ message: "Do not construct Promise directly in tests. Use Effect.async/Effect.promise at real boundaries."
71
+ }));
72
+ },
73
+ CallExpression: (node) => {
74
+ const call = node;
75
+ const chainMethod = promiseChainMethodName(call);
76
+ if (chainMethod !== void 0) return ctx.report(make({
77
+ node,
78
+ message: `Do not use Promise.${chainMethod} style chains in tests. Keep control flow in Effect.`
79
+ }));
80
+ if (call.callee.type !== "MemberExpression") return Effect.void;
81
+ const names = Option.getOrUndefined(memberNames(call.callee));
82
+ if (names === void 0) return Effect.void;
83
+ const [object, property] = names;
84
+ if (object !== "Promise" || !promiseStaticMethods.has(property)) return Effect.void;
85
+ return ctx.report(make({
86
+ node,
87
+ message: `Do not use Promise.${property} in tests. Use Effect concurrency primitives.`
88
+ }));
89
+ }
90
+ };
91
+ }
92
+ });
93
+ //#endregion
94
+ export { noPromiseControlFlowInTests };
@@ -0,0 +1,6 @@
1
+ import * as _$_oxlint_plugins0 from "@oxlint/plugins";
2
+
3
+ //#region src/rules/no-schema-struct.d.ts
4
+ declare const noSchemaStruct: _$_oxlint_plugins0.CreateRule;
5
+ //#endregion
6
+ export { noSchemaStruct };
@@ -0,0 +1,28 @@
1
+ import { matchCallOf } from "../vendor/effect-oxlint/AST.js";
2
+ import { make } from "../vendor/effect-oxlint/Diagnostic.js";
3
+ import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
4
+ import { define, meta } from "../vendor/effect-oxlint/Rule.js";
5
+ import "../vendor/effect-oxlint/index.js";
6
+ import * as Option from "effect/Option";
7
+ import * as Effect from "effect/Effect";
8
+ //#region src/rules/no-schema-struct.ts
9
+ const noSchemaStruct = define({
10
+ name: "no-schema-struct",
11
+ meta: meta({
12
+ type: "suggestion",
13
+ description: "Use Schema.Class instead of Schema.Struct."
14
+ }),
15
+ create: function* () {
16
+ const ctx = yield* RuleContext;
17
+ return { CallExpression: (node) => {
18
+ const call = node;
19
+ if (Option.isNone(matchCallOf(call, "Schema", "Struct"))) return Effect.void;
20
+ return ctx.report(make({
21
+ node,
22
+ message: "Use Schema.Class instead of Schema.Struct."
23
+ }));
24
+ } };
25
+ }
26
+ });
27
+ //#endregion
28
+ export { noSchemaStruct };
@@ -0,0 +1,6 @@
1
+ import * as _$_oxlint_plugins0 from "@oxlint/plugins";
2
+
3
+ //#region src/rules/no-sleep-in-tests.d.ts
4
+ declare const noSleepInTests: _$_oxlint_plugins0.CreateRule;
5
+ //#endregion
6
+ export { noSleepInTests };
@@ -0,0 +1,55 @@
1
+ import { memberNames } from "../vendor/effect-oxlint/AST.js";
2
+ import { make } from "../vendor/effect-oxlint/Diagnostic.js";
3
+ import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
4
+ import { define, meta } from "../vendor/effect-oxlint/Rule.js";
5
+ import "../vendor/effect-oxlint/index.js";
6
+ import * as Option from "effect/Option";
7
+ import * as Effect from "effect/Effect";
8
+ //#region src/rules/no-sleep-in-tests.ts
9
+ const allowPattern = /\beffect\/no-sleep-in-tests:\s*allow\s+\S/;
10
+ const isTestFilename = (filename) => /\.test\.tsx?$/.test(filename) || /\/tests\/.*\.[cm]?tsx?$/.test(filename);
11
+ const getLine = (node, edge) => {
12
+ const loc = node["loc"];
13
+ if (typeof loc !== "object" || loc === null) return void 0;
14
+ const point = loc[edge];
15
+ if (typeof point !== "object" || point === null) return void 0;
16
+ const line = point["line"];
17
+ return typeof line === "number" ? line : void 0;
18
+ };
19
+ const hasAllowComment = (node, comments) => {
20
+ const startLine = getLine(node, "start");
21
+ if (startLine === void 0) return false;
22
+ return comments.some((comment) => {
23
+ const endLine = getLine(comment, "end");
24
+ if (endLine === void 0) return false;
25
+ if (endLine !== startLine - 1 && endLine !== startLine) return false;
26
+ return allowPattern.test(comment.value);
27
+ });
28
+ };
29
+ const noSleepInTests = define({
30
+ name: "no-sleep-in-tests",
31
+ meta: meta({
32
+ type: "problem",
33
+ description: "Avoid fixed sleeps in tests. Use deterministic synchronization."
34
+ }),
35
+ create: function* () {
36
+ const ctx = yield* RuleContext;
37
+ if (!isTestFilename(ctx.filename)) return {};
38
+ return { CallExpression: (node) => {
39
+ const call = node;
40
+ if (call.callee.type !== "MemberExpression") return Effect.void;
41
+ const names = Option.getOrUndefined(memberNames(call.callee));
42
+ if (names === void 0) return Effect.void;
43
+ const [object, property] = names;
44
+ if (property !== "sleep") return Effect.void;
45
+ if (object !== "Effect" && object !== "Bun") return Effect.void;
46
+ if (hasAllowComment(node, ctx.sourceCode.getAllComments())) return Effect.void;
47
+ return ctx.report(make({
48
+ node,
49
+ message: `Avoid ${object}.sleep(...) in tests. Wait on Deferred, polling helpers, or test controls instead of fixed delays.`
50
+ }));
51
+ } };
52
+ }
53
+ });
54
+ //#endregion
55
+ export { noSleepInTests };
@@ -0,0 +1,6 @@
1
+ import * as _$_oxlint_plugins0 from "@oxlint/plugins";
2
+
3
+ //#region src/rules/no-spread.d.ts
4
+ declare const noSpread: _$_oxlint_plugins0.CreateRule;
5
+ //#endregion
6
+ export { noSpread };
@@ -0,0 +1,26 @@
1
+ import { make } from "../vendor/effect-oxlint/Diagnostic.js";
2
+ import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
3
+ import { define, meta } from "../vendor/effect-oxlint/Rule.js";
4
+ import "../vendor/effect-oxlint/index.js";
5
+ //#region src/rules/no-spread.ts
6
+ const message = "Avoid spread syntax. Use explicit construction or a named transformation instead.";
7
+ const noSpread = define({
8
+ name: "no-spread",
9
+ meta: meta({
10
+ type: "suggestion",
11
+ description: message
12
+ }),
13
+ create: function* () {
14
+ const ctx = yield* RuleContext;
15
+ const reportSpread = (node) => ctx.report(make({
16
+ node,
17
+ message
18
+ }));
19
+ return {
20
+ SpreadElement: reportSpread,
21
+ JSXSpreadAttribute: reportSpread
22
+ };
23
+ }
24
+ });
25
+ //#endregion
26
+ export { noSpread };
@@ -58,7 +58,7 @@ declare const define: <Options = undefined>(config: RuleConfig<Options>) => Crea
58
58
  *
59
59
  * @since 0.2.0
60
60
  */
61
- type EffectVersion = 'v3' | 'v4' | 'both';
61
+ type EffectVersion = "v3" | "v4" | "both";
62
62
  /**
63
63
  * Build `RuleMeta` with sensible defaults.
64
64
  *
@@ -168,24 +168,24 @@ declare const banStatement: (nodeType: string, opts: {
168
168
  * @since 0.3.0
169
169
  */
170
170
  type BanSpec = {
171
- readonly type: 'member';
171
+ readonly type: "member";
172
172
  readonly object: string;
173
173
  readonly property: string | ReadonlyArray<string>;
174
174
  readonly message: string;
175
175
  } | {
176
- readonly type: 'call';
176
+ readonly type: "call";
177
177
  readonly name: string | ReadonlyArray<string>;
178
178
  readonly message: string;
179
179
  } | {
180
- readonly type: 'new';
180
+ readonly type: "new";
181
181
  readonly name: string | ReadonlyArray<string>;
182
182
  readonly message: string;
183
183
  } | {
184
- readonly type: 'import';
184
+ readonly type: "import";
185
185
  readonly source: string | ((source: string) => boolean);
186
186
  readonly message: string;
187
187
  } | {
188
- readonly type: 'statement';
188
+ readonly type: "statement";
189
189
  readonly nodeType: string;
190
190
  readonly message: string;
191
191
  };
@@ -3,7 +3,7 @@ import * as Context$1 from "effect/Context";
3
3
  import { Context, Diagnostic, ESTree, LanguageOptions, Options, Settings, SourceCode } from "@oxlint/plugins";
4
4
 
5
5
  //#region src/vendor/effect-oxlint/RuleContext.d.ts
6
- declare const RuleContext_base: Context$1.ServiceClass<RuleContext, "effect-oxlint/RuleContext", {
6
+ declare const RuleContext_base: Context$1.ServiceClass<RuleContext, "oxlint-plugin-effect/vendor/effect-oxlint/RuleContext", {
7
7
  /** Report a lint diagnostic for the current file. */readonly report: (diagnostic: Diagnostic) => Effect.Effect<void>; /** Rule ID in `plugin/rule` form. */
8
8
  readonly id: string; /** Absolute path of the file being linted. */
9
9
  readonly filename: string; /** Current working directory. */
@@ -9,7 +9,7 @@ import * as Context from "effect/Context";
9
9
  *
10
10
  * @since 0.1.0
11
11
  */
12
- var RuleContext = class extends Context.Service()("effect-oxlint/RuleContext") {};
12
+ var RuleContext = class extends Context.Service()("oxlint-plugin-effect/vendor/effect-oxlint/RuleContext") {};
13
13
  /**
14
14
  * Build a `RuleContext` value from the raw oxlint `Context`.
15
15
  *
@@ -866,7 +866,10 @@ const mockRuleContextLayer = (opts) => {
866
866
  *
867
867
  * @since 0.2.0
868
868
  */
869
- const withMockRuleContext = (effect, opts) => Effect.provide(effect, mockRuleContextLayer(opts));
869
+ const withMockRuleContext = (effect, opts) => {
870
+ const { context } = createMockContext(opts);
871
+ return Effect.provideService(effect, RuleContext, fromOxlintContext(context));
872
+ };
870
873
  /** @internal Call a visitor handler on a mock AST node. */
871
874
  const callHandler = (visitors, key, visitorNode) => {
872
875
  const handler = visitors[key];
@@ -18,7 +18,7 @@ declare namespace Visitor_d_exports {
18
18
  */
19
19
  type EffectHandler<N = ESTree.Node> = {
20
20
  bivarianceHack(node: N): Effect.Effect<void, never, RuleContext>;
21
- }['bivarianceHack'];
21
+ }["bivarianceHack"];
22
22
  /**
23
23
  * A map from AST node type names (and `"NodeType:exit"` variants) to
24
24
  * effectful handlers.
@@ -10,4 +10,10 @@ import { SourceCode_d_exports } from "./SourceCode.js";
10
10
  import { Testing_d_exports } from "./Testing.js";
11
11
  import { Token_d_exports } from "./Token.js";
12
12
  import { Comment as OxlintComment, Context, CreateOnceRule, CreateRule, Definition, DefinitionType, ESTree, Fix, FixFn, Fixer, LineColumn, Location, Plugin as OxlintPlugin, Range, Ranged, Reference, Rule as OxlintRule, RuleDocs, RuleMeta, Scope as OxlintScope, ScopeManager, ScopeType, Settings, SourceCode as OxlintSourceCode, Span, Suggestion, Token as OxlintToken, Variable, Visitor as OxlintVisitor } from "@oxlint/plugins";
13
- export { AST_d_exports as AST, Comment_d_exports as Comment, type Context, type CreateOnceRule, type CreateRule, type Definition, type DefinitionType, Diagnostic_d_exports as Diagnostic, type ESTree, type Fix, type FixFn, type Fixer, type LineColumn, type Location, type OxlintComment, type OxlintPlugin, type OxlintRule, type OxlintScope, type OxlintSourceCode, type OxlintToken, type OxlintVisitor, Plugin_d_exports as Plugin, type Range, type Ranged, type Reference, Rule_d_exports as Rule, RuleContext, type RuleDocs, type RuleMeta, Scope_d_exports as Scope, type ScopeManager, type ScopeType, type Settings, SourceCode_d_exports as SourceCode, type Span, type Suggestion, Testing_d_exports as Testing, Token_d_exports as Token, type Variable, Visitor_d_exports as Visitor, ast, cwd, filename, id, report, sourceCode, text };
13
+
14
+ //#region src/vendor/effect-oxlint/index.d.ts
15
+ declare namespace index_d_exports {
16
+ export { AST_d_exports as AST, Comment_d_exports as Comment, Context, CreateOnceRule, CreateRule, Definition, DefinitionType, Diagnostic_d_exports as Diagnostic, ESTree, Fix, FixFn, Fixer, LineColumn, Location, OxlintComment, OxlintPlugin, OxlintRule, OxlintScope, OxlintSourceCode, OxlintToken, OxlintVisitor, Plugin_d_exports as Plugin, Range, Ranged, Reference, Rule_d_exports as Rule, RuleContext, RuleDocs, RuleMeta, Scope_d_exports as Scope, ScopeManager, ScopeType, Settings, SourceCode_d_exports as SourceCode, Span, Suggestion, Testing_d_exports as Testing, Token_d_exports as Token, Variable, Visitor_d_exports as Visitor, ast, cwd, filename, id, report, sourceCode, text };
17
+ }
18
+ //#endregion
19
+ export { AST_d_exports as AST, Comment_d_exports as Comment, type Context, type CreateOnceRule, type CreateRule, type Definition, type DefinitionType, Diagnostic_d_exports as Diagnostic, type ESTree, type Fix, type FixFn, type Fixer, type LineColumn, type Location, type OxlintComment, type OxlintPlugin, type OxlintRule, type OxlintScope, type OxlintSourceCode, type OxlintToken, type OxlintVisitor, Plugin_d_exports as Plugin, type Range, type Ranged, type Reference, Rule_d_exports as Rule, RuleContext, type RuleDocs, type RuleMeta, Scope_d_exports as Scope, type ScopeManager, type ScopeType, type Settings, SourceCode_d_exports as SourceCode, type Span, type Suggestion, Testing_d_exports as Testing, Token_d_exports as Token, type Variable, Visitor_d_exports as Visitor, ast, cwd, filename, id, index_d_exports, report, sourceCode, text };
@@ -1,3 +1,4 @@
1
+ import { __exportAll } from "../../_virtual/_rolldown/runtime.js";
1
2
  import { AST_exports } from "./AST.js";
2
3
  import { Comment_exports } from "./Comment.js";
3
4
  import { Diagnostic_exports } from "./Diagnostic.js";
@@ -9,4 +10,26 @@ import { Scope_exports } from "./Scope.js";
9
10
  import { SourceCode_exports } from "./SourceCode.js";
10
11
  import { Testing_exports } from "./Testing.js";
11
12
  import { Token_exports } from "./Token.js";
12
- export { AST_exports as AST, Comment_exports as Comment, Diagnostic_exports as Diagnostic, Plugin_exports as Plugin, Rule_exports as Rule, RuleContext, Scope_exports as Scope, SourceCode_exports as SourceCode, Testing_exports as Testing, Token_exports as Token, Visitor_exports as Visitor, ast, cwd, filename, id, report, sourceCode, text };
13
+ //#region src/vendor/effect-oxlint/index.ts
14
+ var effect_oxlint_exports = /* @__PURE__ */ __exportAll({
15
+ AST: () => AST_exports,
16
+ Comment: () => Comment_exports,
17
+ Diagnostic: () => Diagnostic_exports,
18
+ Plugin: () => Plugin_exports,
19
+ Rule: () => Rule_exports,
20
+ RuleContext: () => RuleContext,
21
+ Scope: () => Scope_exports,
22
+ SourceCode: () => SourceCode_exports,
23
+ Testing: () => Testing_exports,
24
+ Token: () => Token_exports,
25
+ Visitor: () => Visitor_exports,
26
+ ast: () => ast,
27
+ cwd: () => cwd,
28
+ filename: () => filename,
29
+ id: () => id,
30
+ report: () => report,
31
+ sourceCode: () => sourceCode,
32
+ text: () => text
33
+ });
34
+ //#endregion
35
+ export { AST_exports as AST, Comment_exports as Comment, Diagnostic_exports as Diagnostic, Plugin_exports as Plugin, Rule_exports as Rule, RuleContext, Scope_exports as Scope, SourceCode_exports as SourceCode, Testing_exports as Testing, Token_exports as Token, Visitor_exports as Visitor, ast, cwd, effect_oxlint_exports, filename, id, report, sourceCode, text };
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-effect",
3
- "version": "0.2.2",
4
- "type": "module",
3
+ "version": "0.2.3",
5
4
  "description": "oxlint plugin with Effect-specific lint rules",
6
5
  "license": "MIT",
7
6
  "repository": {
@@ -11,6 +10,7 @@
11
10
  "files": [
12
11
  "dist"
13
12
  ],
13
+ "type": "module",
14
14
  "exports": {
15
15
  ".": {
16
16
  "import": {
@@ -24,6 +24,12 @@
24
24
  "default": "./dist/plugin.js"
25
25
  }
26
26
  },
27
+ "./rule-bindings": {
28
+ "import": {
29
+ "types": "./dist/vendor/effect-oxlint/index.d.ts",
30
+ "default": "./dist/vendor/effect-oxlint/index.js"
31
+ }
32
+ },
27
33
  "./presets/*": {
28
34
  "import": {
29
35
  "types": "./dist/presets/*.d.ts",
@@ -44,26 +50,33 @@
44
50
  "test": "bun test",
45
51
  "gate": "concurrently -n type,style,test -c blue,yellow,green \"bun run typecheck\" \"bun run lint\" \"bun run test\"",
46
52
  "codegen": "bun run scripts/codegen.ts",
47
- "add-rule": "bun run scripts/add-rule.ts"
53
+ "add-rule": "bun run scripts/add-rule.ts",
54
+ "version": "changeset version",
55
+ "release": "bun run build && changeset publish",
56
+ "prepare": "lefthook install"
48
57
  },
49
58
  "dependencies": {
50
- "@oxlint/plugins": "^1.60.0"
51
- },
52
- "peerDependencies": {
53
- "effect": ">=4.0.0-beta.49"
59
+ "@oxlint/plugins": "^1.64.0"
54
60
  },
55
61
  "devDependencies": {
62
+ "@changesets/changelog-github": "^0.7.0",
63
+ "@changesets/cli": "^2.31.0",
56
64
  "@effect/language-service": "latest",
57
65
  "@effect/vitest": "latest",
58
66
  "@types/bun": "latest",
59
67
  "@typescript/native-preview": "latest",
60
68
  "concurrently": "latest",
61
- "effect": "4.0.0-beta.49",
69
+ "effect": "4.0.0-beta.66",
62
70
  "lefthook": "latest",
63
71
  "oxfmt": "latest",
64
72
  "oxlint": "latest",
65
- "tsdown": "^0.21.7",
73
+ "tsdown": "^0.22.0",
66
74
  "typescript": "latest",
75
+ "unrun": "^0.3.0",
67
76
  "vitest": "latest"
68
- }
77
+ },
78
+ "peerDependencies": {
79
+ "effect": ">=4.0.0-beta.66 <5"
80
+ },
81
+ "packageManager": "bun@1.3.13"
69
82
  }