Skip to content

Releases: cevr/effect-oxlint

v0.29.0

Choose a tag to compare

@github-actions github-actions released this 16 Oct 01:02
a5bbbf8

Minor Changes

  • #64 f348e20 Thanks @cevr! - requireForceKillAfter reads a parameter that holds an array as the command's arguments, not its options: make(cmd, args) with args a rest parameter, or a parameter typed T[], readonly T[], Array<T> or ReadonlyArray<T>, names no forceKillAfter and is reported, as make(cmd, ["arg"]) is. An untyped parameter may hold the options and is still left alone.

  • #64 f348e20 Thanks @cevr! - requireForceKillAfter names the signal the spawner sends on interrupt or timeout: SIGTERM when the command names no killSignal, else the command's killSignal as written, or "the command's killSignal" when the rule cannot read it. A command whose killSignal is "SIGKILL" (or 9) cannot be ignored and is not reported.

  • #64 f348e20 Thanks @cevr! - noGlobals follows every hop of the global object to itself: a page's top, parent and frames, a chain of its own names (globalThis.window, window.top), and document.defaultView, so parent.fetch() and document.defaultView.setTimeout() are reported. A watched global held where the walk cannot follow it ({ w: window }, [document], () => performance, mine ?? globalThis, a return) is now reported as a use; handing it to a call or a new stays allowed, alone, inside an object or array literal, or as what a function handed to one gives back, by an arrow's body or a return (createClient({ fetch }), new Client({ fetch }), take([window]), Reference({ defaultValue: () => globalThis.fetch }), createClient({ getFetch() { return fetch; } })), since that is how an adapter is built over it; a return from a function that is not handed to a call is still a hold. A held global object is reported only while a ban on some member is active, so builtins: false with no members, or with a global whose bans name no member (Bun: []), reports none. members takes a list of bans for one global, so its members can name different replacements, and a configured ban's use is named before the built-in one.

    New recommended rule noPointFreeLog: a logger that prints every argument it is given (console.log, info, warn, error, debug and trace, effect's Console members of the same names, and Effect.log, logFatal, logError, logWarning, logInfo, logDebug and logTrace) handed point-free, in a callback's place, to a call that passes its callback an index prints the index after each line; call it in a callback, (line) => Console.log(line). The calls the rule reads are Effect.forEach, Effect.filter and Effect.partition, effect's Array module, and an array literal's methods, each at its callback's place only (a thisArg or a seed is not called). A reducer (reduce, reduceRight) is passed the accumulator first, and its report says so. The rule does not read effect's other modules (Exit, Option, Stream, Chunk, Iterable): a logger handed to one of their members is not reported. A method of an array's name on a receiver the rule cannot see (xs.map(console.log)) is reported with what prints if xs is an array. console.count, time, table and dir take a label, a column filter or options and are not reported.

    New opt-in rule requireSpawnBudget: a test (it, test and their members, such as it.effect, it.skip and effect-bun-test's it.effect.layer(L), from bun:test, effect-bun-test, vitest, @effect/vitest or the global) whose body calls a process starter (Bun's spawn/spawnSync, node:child_process) or effect's ChildProcess.make, itself or through a function of the same file, names its timeout: a number after its body, or { timeout } in its options before the body (Vitest's it(name, { timeout }, body)) or after it; a suite's { timeout } is its tests' budget too. A suite (describe, suite, test.describe), @effect/vitest's layer group (it.layer(L)(…), layer(L)(…)), a fixture builder (test.extend, test.override) and a hook (beforeEach and the rest) is not a test; a test on a group factory's parameter is judged on its own. The report names the call the test makes, and the function of the file it makes it through; for ChildProcess.make it says the command starts a process when a platform spawner runs it. minimum also reports a timeout the rule reads as a number below that minimum.

Patch Changes

  • #64 f348e20 Thanks @cevr! - noInterruptibleMemo leaves the recommended preset and is opt-in, and its message now says what each memoizer does on each effect 4.x. Effect 4.0.0 runs a cached effect in its first caller's fiber and keeps the exit it reaches, so later callers get that caller's interruption back while the entry lives: for Effect.cached from then on, for cachedWithTTL and cachedInvalidateWithTTL until the time to live given for that exit ends or the entry is invalidated. From 4.0.1 the run is a fiber of its own: a run that every caller has left is interrupted and never cached, and the next caller starts a new one, so a run an interruption cannot stop (a promise with no signal, an engine's start) goes on beside it. The fix it names is the same for both: fork the run once into a scope that owns it and Fiber.join it in each caller. It no longer names Cache, which also gives up a lookup its callers leave. A namespace named Effect imported from another module is not effect's and is not reported. The devDependency on effect is 4.0.5, and tests hold the message to what each memoizer does on effect 4.0.0 and on 4.0.5.

v0.28.1

Choose a tag to compare

@github-actions github-actions released this 10 Oct 21:00
5ae7b98

Patch Changes

  • #62 f905b3b Thanks @cevr! - preferEffectFn reports a spanned Effect.gen only where a function returns it (an arrow body or a return). A traced generator held as a value (a variable, a property, a yield* operand) is no longer reported. Effect.fn could only name it by being invoked on the spot, which @effect/tsgo's effectFnIife rejects.

v0.28.0

Choose a tag to compare

@github-actions github-actions released this 09 Oct 16:29
80f2b04

Minor Changes

  • #60 78594b7 Thanks @cevr! - Add the recommended preferTaggedConstructors rule to report raw _tag object construction and prefer Schema.TaggedUnion case constructors or Schema.TaggedStruct constructors. Data.taggedEnum and existing domain constructors remain valid. Recognize static keys, type-only wrappers, and const tag aliases while allowing schema declarations.

v0.27.1

Choose a tag to compare

@github-actions github-actions released this 02 Oct 13:03
ca2ce6a

Patch Changes

  • #58 ce08e15 Thanks @cevr! - Recognize literal string and static template member keys in noNodeBuiltinImport. Imported process and crypto capabilities and configured module members now receive the same diagnostics as dot access, while dynamic keys and unmatched members remain allowed.

v0.27.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 22:14
c9daddb

Minor Changes

  • 606708c Thanks @cevr! - Report cached* named imports and remove unsafe exemptions for uninterruptible inputs and function-shaped TTLs. Protecting the input does not protect cache settlement, and a function alone does not establish interruption's lifetime.

    Remove the opt-in noCodeUnitPadding rule. Display width belongs to the terminal application's policy; Gent now owns a stricter rule with proven ASCII receiver and padding checks.

v0.26.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 19:39
a98e0d3

Minor Changes

  • 88f6561 Thanks @cevr! - New opt-in rule noCodeUnitPadding: it reports padStart and padEnd on any receiver but a number rendered as ASCII (String(n), n.toString(…), toFixed, toPrecision, toExponential). Both count UTF-16 code units, so a wide, combining or emoji character misaligns a padded column; pad by display width instead. A project enables it for the files that draw columns (a terminal UI, a CLI table) with an override.

  • 0e6728d Thanks @cevr! - New recommended rule noInterruptibleMemo: it reports Effect.cached, Effect.cachedWithTTL and Effect.cachedInvalidateWithTTL, data-first or as a .pipe(...)/pipe(...) operator. Each runs the memoized effect in its first caller's fiber and keeps the exit that fiber reaches, so once that caller is interrupted every later caller gets the interruption back until the TTL ends. The message names the fixes: memoize a started fiber (fork once, then Fiber.join in each caller) or use Cache, which runs each lookup in a fiber of its own. An effect whose last step is Effect.uninterruptible, and a TTL given as a function (it can give an interruption no lifetime), are not reported.

v0.25.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 12:00
06b12e8

Minor Changes

  • 0731395 Thanks @cevr! - noAs also reports the angle-bracket assertion <T>x of .ts files, with the same message and the same exemption: <const>x stays allowed like as const. Until now only typescript/no-unsafe-type-assertion held that spelling, so a project kept it on as a second owner of assertions.

  • 61301f1 Thanks @cevr! - New recommended rule noLocaleCompare: it reports localeCompare on any receiver (a.localeCompare(b), a["localeCompare"]) and Intl.Collator called or constructed through any spelling of the global (new Intl.Collator(), Intl.Collator(), globalThis.Intl.Collator, an alias or destructure of Intl). Both order by the locale and ICU data of the running process, so the same input sorts differently on another machine; output that must be stable, such as prompt bytes and listings, uses Order.String, which orders by UTF-16 code unit.

  • 504175a Thanks @cevr! - New recommended rule noModulePathFacts: a module's file path comes from the Effect Path service's Path.fromFileUrl(new URL("./x.ts", import.meta.url)). It reports the host path facts import.meta.dirname, import.meta.filename, import.meta.dir and import.meta.path; .pathname read off a new URL(...) that takes import.meta.url as any argument, inline or held in a const; and .slice, .substring or .replace on import.meta.url or on such a URL's .href. A hand-read pathname keeps percent-escapes and, on Windows, a leading /C:. import.meta.url alone, import.meta.main, and passing the URL to any function stay allowed. Host adapter files turn the rule off with an override.

  • f4c4f6a Thanks @cevr! - New recommended rule noRepoTempDirectory: a test's temp directory lives in the system temp directory. A temp directory under the repo that a killed test leaves behind is linted and scanned as source, and parallel runs share it. In test code it reports a temp directory call (mkdtemp, mkdtempSync, makeTempDirectory, makeTempDirectoryScoped) whose argument, or options object, roots it in the repo (import.meta.dir, import.meta.dirname, __dirname, process.cwd(), a join/resolve of a relative literal, a relative directory literal, or a const/let bound to one); a node mkdtemp with a relative prefix such as mkdtempSync("case-"); and a repo path joined to a tmp/temp segment (join(import.meta.dir, ".tmp")).

  • 71b98b5 Thanks @cevr! - New recommended rule noSharedTestHome: a test's home, data and working directory are its own. A fixed path under the shared temp root (/tmp, /var/tmp, /private/tmp, /dev/shm, or tmpdir()) given to a home key is shared by every run and parallel suite, so a result depends on run order. The rule reads the value of an object property, JSX attribute, binding, parameter default, class field or assignment (process.env.HOME = "/tmp") named home, HOME, homeDir, homeDirectory, dataDir, cwd or any …Cwd, plus the names the keys option adds, and reports it unless the value makes a unique directory (mkdtemp*, makeTempDirectory*). Test code is read whole; other files only inside a test layer (static Test, a …TestLayer binding, a Test: key).

  • b34fd73 Thanks @cevr! - New recommended rule requireForceKillAfter: every ChildProcess.make command names forceKillAfter. When a child's scope closes, the platform spawner sends SIGTERM, waits one second, and then waits for exit with no bound unless forceKillAfter is set, so a child that ignores SIGTERM holds its scope open forever. The rule reads make from effect/process (ChildProcess.make, P.ChildProcess.make) and effect/process/ChildProcess (a namespace or the named make under any alias). It reports a command with no options, an options object literal (inline, held in a const, or spread from one) without forceKillAfter, and the bare template form make`cmd` , which cannot carry options; write make({ forceKillAfter: "5 seconds" })`cmd` . Options it cannot see, such as a parameter, stay allowed.

v0.24.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 09:21
efc2517

Minor Changes

  • 7402ac5 Thanks @cevr! - noGlobals takes a builtins option. builtins: false drops the built-in list of banned globals and keeps only the members bans, so a plain script that may use console, process.env and Bun directly can still ban specific members: { "builtins": false, "members": { "Bun": { "properties": ["Glob"], "use": "Effect FileSystem" } } } reports Bun.Glob in every spelling the rule follows (globalThis["Bun"].Glob, an alias) and nothing else. The default, true, keeps today's behavior.

v0.23.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 09:05
020e524

Minor Changes

  • 5d50805 Thanks @cevr! - noNewPromise follows the global Promise as noGlobals follows the globals it bans, through one shared walk. It reports new globalThis.Promise(), new globalThis["Promise"](), new self.Promise(), globalThis.Promise.all([]), Promise["resolve"](1), new (Promise as PromiseConstructor)(), an alias (const P = Promise; new P()) and a destructure (const { resolve } = Promise; resolve(1), const { Promise: { all } } = globalThis). A local binding named Promise is no longer reported, and an alias that is reassigned to another value is not followed. Code that passed only because of the spelling now reports.

Patch Changes

  • 0ff27ce Thanks @cevr! - noPlatformLayerOutsideEntry reports a non-layer member of a platform module that a file exports: export const socket = BunSocket.makeNet, export const { runMain } = BunRuntime, export default BunSocket.makeNet, a named import exported again (import { makeNet } from "@effect/platform-bun/BunSocket"; export const socket = makeNet), and each of these through a local alias or an export { name } list. Every importer of such an export reaches the platform package where no rule follows it. A member used in place (BunRuntime.runMain(program), BunSocket.makeNet(options)) is still not reported.

v0.22.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 07:47
1afcc22

Minor Changes

  • 6abf56f Thanks @cevr! - noGlobals follows a global's value instead of matching one syntax shape. It reports a banned global read through the global object (globalThis.fetch(), new globalThis.Date(), self.setTimeout(), globalThis.process.env), through a computed string member (process["env"], console[`log`]), through as/!/satisfies/?., and through an alias or destructure (const { env } = process, const p = process; p.env, const D = Date; new D(), const { process: p } = globalThis, const { process: { env } } = globalThis). An alias is followed only while every write to it stores the same global, so let p = process; p = local; p.env does not report, and an alias cycle ends. A global the members option bans whole ({ "Bun": { "use": "..." } }) is reported wherever its value leaves the rule's sight, such as use(Bun) or const { ...rest } = Bun. Code that passed only because of the spelling now reports.

Patch Changes

  • e4772e2 Thanks @cevr! - noPlatformLayerOutsideEntry reports a platform package or module exported as the value of a declaration: export const Fs = BunFileSystem, export let P = Platform, export const Fs = Platform.BunFileSystem, export const { BunPath } = Platform, and an exported alias of an alias. The exported binding has no reads in its own file, so the rule could not follow it to a layer read.