oxlint-plugin-effect 0.5.0 → 0.6.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/README.md CHANGED
@@ -27,6 +27,7 @@ If the project uses JSON configuration, copy the exported rule map into `rules`;
27
27
 
28
28
  | Rule | Contract |
29
29
  | ----------------------------- | ----------------------------------------------------------------------------------------- |
30
+ | `effect/noAs` | Bans TypeScript `as` assertions; use `satisfies` |
30
31
  | `effect/noAsyncFunction` | Bans async functions and await expressions |
31
32
  | `effect/noTryCatch` | Bans every try/catch/finally statement |
32
33
  | `effect/noTestLifecycleHooks` | Bans `beforeEach`, `afterEach`, `beforeAll`, and `afterAll`; use Effect scopes instead |
@@ -37,7 +38,7 @@ If the project uses JSON configuration, copy the exported rule map into `rules`;
37
38
  | `effect/noDynamicImports` | Allows import() only behind a named lazy-loading boundary; bans require() |
38
39
  | `effect/noEffectDo` | Bans Effect.Do |
39
40
  | `effect/noEffectBind` | Bans Effect.bind |
40
- | `effect/noGlobals` | Bans ambient capabilities with direct Effect replacements |
41
+ | `effect/noGlobals` | Bans ambient capabilities with direct Effect replacements; allows `process.std*.isTTY` |
41
42
  | `effect/noNodeBuiltinImport` | Bans fully replaced Node modules and replaced operations from partial modules |
42
43
 
43
44
  The preset intentionally allows `Effect.as`, `Option.as`, `Effect.never`, `Effect.async`, ordinary `if` and `switch` statements, and runtime runners at explicit application boundaries.
@@ -1,6 +1,7 @@
1
1
  //#region src/presets/recommended.d.ts
2
2
  /** Strict, non-type-aware policy for Effect-native application code. */
3
3
  declare const recommended: {
4
+ readonly "effect/noAs": "error";
4
5
  readonly "effect/noAsyncFunction": "error";
5
6
  readonly "effect/noDynamicImports": "error";
6
7
  readonly "effect/noEffectBind": "error";
@@ -1,6 +1,7 @@
1
1
  //#region src/presets/recommended.ts
2
2
  /** Strict, non-type-aware policy for Effect-native application code. */
3
3
  const recommended = {
4
+ "effect/noAs": "error",
4
5
  "effect/noAsyncFunction": "error",
5
6
  "effect/noDynamicImports": "error",
6
7
  "effect/noEffectBind": "error",
@@ -1,5 +1,6 @@
1
1
  import { noEffectBind } from "./no-effect-bind.js";
2
2
  import { noEffectDo } from "./no-effect-do.js";
3
+ import { noAs } from "./no-as.js";
3
4
  import { noAsyncFunction } from "./no-async-function.js";
4
5
  import { noDynamicImports } from "./no-dynamic-imports.js";
5
6
  import { noGlobals } from "./no-globals.js";
@@ -13,7 +14,7 @@ import { noTryCatch } from "./no-try-catch.js";
13
14
 
14
15
  //#region src/rules/index.d.ts
15
16
  declare namespace index_d_exports {
16
- export { noAsyncFunction, noDynamicImports, noEffectBind, noEffectDo, noGlobals, noNewError, noNewPromise, noNodeBuiltinImport, noTernary, noTestLifecycleHooks, noThrowStatement, noTryCatch };
17
+ export { noAs, noAsyncFunction, noDynamicImports, noEffectBind, noEffectDo, noGlobals, noNewError, noNewPromise, noNodeBuiltinImport, noTernary, noTestLifecycleHooks, noThrowStatement, noTryCatch };
17
18
  }
18
19
  //#endregion
19
- export { index_d_exports, noAsyncFunction, noDynamicImports, noEffectBind, noEffectDo, noGlobals, noNewError, noNewPromise, noNodeBuiltinImport, noTernary, noTestLifecycleHooks, noThrowStatement, noTryCatch };
20
+ export { index_d_exports, noAs, noAsyncFunction, noDynamicImports, noEffectBind, noEffectDo, noGlobals, noNewError, noNewPromise, noNodeBuiltinImport, noTernary, noTestLifecycleHooks, noThrowStatement, noTryCatch };
@@ -1,6 +1,7 @@
1
1
  import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
2
  import { noEffectBind } from "./no-effect-bind.js";
3
3
  import { noEffectDo } from "./no-effect-do.js";
4
+ import { noAs } from "./no-as.js";
4
5
  import { noAsyncFunction } from "./no-async-function.js";
5
6
  import { noDynamicImports } from "./no-dynamic-imports.js";
6
7
  import { noGlobals } from "./no-globals.js";
@@ -13,6 +14,7 @@ import { noThrowStatement } from "./no-throw-statement.js";
13
14
  import { noTryCatch } from "./no-try-catch.js";
14
15
  //#region src/rules/index.ts
15
16
  var rules_exports = /* @__PURE__ */ __exportAll({
17
+ noAs: () => noAs,
16
18
  noAsyncFunction: () => noAsyncFunction,
17
19
  noDynamicImports: () => noDynamicImports,
18
20
  noEffectBind: () => noEffectBind,
@@ -27,4 +29,4 @@ var rules_exports = /* @__PURE__ */ __exportAll({
27
29
  noTryCatch: () => noTryCatch
28
30
  });
29
31
  //#endregion
30
- export { noAsyncFunction, noDynamicImports, noEffectBind, noEffectDo, noGlobals, noNewError, noNewPromise, noNodeBuiltinImport, noTernary, noTestLifecycleHooks, noThrowStatement, noTryCatch, rules_exports };
32
+ export { noAs, noAsyncFunction, noDynamicImports, noEffectBind, noEffectDo, noGlobals, noNewError, noNewPromise, noNodeBuiltinImport, noTernary, noTestLifecycleHooks, noThrowStatement, noTryCatch, rules_exports };
@@ -0,0 +1,6 @@
1
+ import * as _$_oxlint_plugins0 from "@oxlint/plugins";
2
+
3
+ //#region src/rules/no-as.d.ts
4
+ declare const noAs: _$_oxlint_plugins0.CreateRule;
5
+ //#endregion
6
+ export { noAs };
@@ -0,0 +1,22 @@
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-as.ts
6
+ /** Ban TypeScript `as` assertions. */
7
+ const noAs = define({
8
+ name: "no-as",
9
+ meta: meta({
10
+ type: "problem",
11
+ description: "Use satisfies instead of an as assertion."
12
+ }),
13
+ create: function* () {
14
+ const ctx = yield* RuleContext;
15
+ return { TSAsExpression: (node) => ctx.report(make({
16
+ node,
17
+ message: "Avoid as assertions. Use satisfies instead."
18
+ })) };
19
+ }
20
+ });
21
+ //#endregion
22
+ export { noAs };
@@ -148,6 +148,16 @@ const isUnshadowedGlobal = (ctx, node, name) => Option.match(findVariableUp(ctx.
148
148
  onSome: (variable) => variable.defs.length === 0
149
149
  });
150
150
  const staticMember = (node) => Option.getOrUndefined(memberNames(node));
151
+ const processStreams = new Set([
152
+ "stderr",
153
+ "stdin",
154
+ "stdout"
155
+ ]);
156
+ const isTtyRead = (node) => {
157
+ const parent = node.parent;
158
+ if (parent?.type !== "MemberExpression" || parent.computed) return false;
159
+ return parent.object === node && parent.property.type === "Identifier" && parent.property.name === "isTTY";
160
+ };
151
161
  const isCryptoDigest = (node) => {
152
162
  if (node.computed || node.property.type !== "Identifier" || node.property.name !== "digest") return false;
153
163
  const object = node.object;
@@ -175,7 +185,10 @@ const noGlobals = define({
175
185
  if (names === void 0) return Effect.void;
176
186
  const [object, property] = names;
177
187
  if (!isUnshadowedGlobal(ctx, memberExpression, object)) return Effect.void;
178
- for (const [bannedObject, properties, alternative] of memberBans) if (object === bannedObject && properties.has(property)) return report(memberExpression, `${object}.${property}`, alternative);
188
+ for (const [bannedObject, properties, alternative] of memberBans) if (object === bannedObject && properties.has(property)) {
189
+ if (object === "process" && processStreams.has(property) && isTtyRead(memberExpression)) return Effect.void;
190
+ return report(memberExpression, `${object}.${property}`, alternative);
191
+ }
179
192
  return Effect.void;
180
193
  },
181
194
  CallExpression: (node) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-effect",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "oxlint plugin with Effect-specific lint rules",
5
5
  "license": "MIT",
6
6
  "repository": {