Repository navigation
Releases: cevr/effect-oxlint
Release list
v0.29.0
Minor Changes
-
#64
f348e20Thanks @cevr! -requireForceKillAfterreads a parameter that holds an array as the command's arguments, not its options:make(cmd, args)withargsa rest parameter, or a parameter typedT[],readonly T[],Array<T>orReadonlyArray<T>, names noforceKillAfterand is reported, asmake(cmd, ["arg"])is. An untyped parameter may hold the options and is still left alone. -
#64
f348e20Thanks @cevr! -requireForceKillAfternames the signal the spawner sends on interrupt or timeout: SIGTERM when the command names nokillSignal, else the command'skillSignalas written, or "the command's killSignal" when the rule cannot read it. A command whosekillSignalis"SIGKILL"(or9) cannot be ignored and is not reported. -
#64
f348e20Thanks @cevr! -noGlobalsfollows every hop of the global object to itself: a page'stop,parentandframes, a chain of its own names (globalThis.window,window.top), anddocument.defaultView, soparent.fetch()anddocument.defaultView.setTimeout()are reported. A watched global held where the walk cannot follow it ({ w: window },[document],() => performance,mine ?? globalThis, areturn) is now reported as a use; handing it to a call or anewstays allowed, alone, inside an object or array literal, or as what a function handed to one gives back, by an arrow's body or areturn(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; areturnfrom 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, sobuiltins: falsewith nomembers, or with a global whose bans name no member (Bun: []), reports none.memberstakes a list of bans for one global, so its members can name different replacements, and a configured ban'suseis named before the built-in one.New recommended rule
noPointFreeLog: a logger that prints every argument it is given (console.log,info,warn,error,debugandtrace, effect'sConsolemembers of the same names, andEffect.log,logFatal,logError,logWarning,logInfo,logDebugandlogTrace) 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 areEffect.forEach,Effect.filterandEffect.partition, effect'sArraymodule, and an array literal's methods, each at its callback's place only (athisArgor 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 ifxsis an array.console.count,time,tableanddirtake a label, a column filter or options and are not reported.New opt-in rule
requireSpawnBudget: a test (it,testand their members, such asit.effect,it.skipand effect-bun-test'sit.effect.layer(L), frombun:test,effect-bun-test,vitest,@effect/vitestor the global) whose body calls a process starter (Bun'sspawn/spawnSync,node:child_process) or effect'sChildProcess.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'sit(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 (beforeEachand 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; forChildProcess.makeit says the command starts a process when a platform spawner runs it.minimumalso reports a timeout the rule reads as a number below that minimum.
Patch Changes
- #64
f348e20Thanks @cevr! -noInterruptibleMemoleaves 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: forEffect.cachedfrom then on, forcachedWithTTLandcachedInvalidateWithTTLuntil 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 andFiber.joinit in each caller. It no longer namesCache, which also gives up a lookup its callers leave. A namespace namedEffectimported 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
Patch Changes
- #62
f905b3bThanks @cevr! -preferEffectFnreports a spannedEffect.genonly where a function returns it (an arrow body or areturn). A traced generator held as a value (a variable, a property, ayield*operand) is no longer reported.Effect.fncould only name it by being invoked on the spot, which@effect/tsgo'seffectFnIiferejects.
v0.28.0
Minor Changes
- #60
78594b7Thanks @cevr! - Add the recommendedpreferTaggedConstructorsrule to report raw_tagobject construction and preferSchema.TaggedUnioncase constructors orSchema.TaggedStructconstructors.Data.taggedEnumand existing domain constructors remain valid. Recognize static keys, type-only wrappers, and const tag aliases while allowing schema declarations.
v0.27.1
v0.27.0
Minor Changes
-
606708cThanks @cevr! - Reportcached*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
noCodeUnitPaddingrule. 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
Minor Changes
-
88f6561Thanks @cevr! - New opt-in rulenoCodeUnitPadding: it reportspadStartandpadEndon 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. -
0e6728dThanks @cevr! - New recommended rulenoInterruptibleMemo: it reportsEffect.cached,Effect.cachedWithTTLandEffect.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, thenFiber.joinin each caller) or useCache, which runs each lookup in a fiber of its own. An effect whose last step isEffect.uninterruptible, and a TTL given as a function (it can give an interruption no lifetime), are not reported.
v0.25.0
Minor Changes
-
0731395Thanks @cevr! -noAsalso reports the angle-bracket assertion<T>xof.tsfiles, with the same message and the same exemption:<const>xstays allowed likeas const. Until now onlytypescript/no-unsafe-type-assertionheld that spelling, so a project kept it on as a second owner of assertions. -
61301f1Thanks @cevr! - New recommended rulenoLocaleCompare: it reportslocaleCompareon any receiver (a.localeCompare(b),a["localeCompare"]) andIntl.Collatorcalled or constructed through any spelling of the global (new Intl.Collator(),Intl.Collator(),globalThis.Intl.Collator, an alias or destructure ofIntl). 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, usesOrder.String, which orders by UTF-16 code unit. -
504175aThanks @cevr! - New recommended rulenoModulePathFacts: a module's file path comes from the EffectPathservice'sPath.fromFileUrl(new URL("./x.ts", import.meta.url)). It reports the host path factsimport.meta.dirname,import.meta.filename,import.meta.dirandimport.meta.path;.pathnameread off anew URL(...)that takesimport.meta.urlas any argument, inline or held in aconst; and.slice,.substringor.replaceonimport.meta.urlor on such a URL's.href. A hand-readpathnamekeeps percent-escapes and, on Windows, a leading/C:.import.meta.urlalone,import.meta.main, and passing the URL to any function stay allowed. Host adapter files turn the rule off with an override. -
f4c4f6aThanks @cevr! - New recommended rulenoRepoTempDirectory: 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(), ajoin/resolveof a relative literal, a relativedirectoryliteral, or aconst/letbound to one); a nodemkdtempwith a relative prefix such asmkdtempSync("case-"); and a repo path joined to atmp/tempsegment (join(import.meta.dir, ".tmp")). -
71b98b5Thanks @cevr! - New recommended rulenoSharedTestHome: 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, ortmpdir()) 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") namedhome,HOME,homeDir,homeDirectory,dataDir,cwdor any…Cwd, plus the names thekeysoption 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…TestLayerbinding, aTest:key). -
b34fd73Thanks @cevr! - New recommended rulerequireForceKillAfter: everyChildProcess.makecommand namesforceKillAfter. When a child's scope closes, the platform spawner sends SIGTERM, waits one second, and then waits for exit with no bound unlessforceKillAfteris set, so a child that ignores SIGTERM holds its scope open forever. The rule readsmakefromeffect/process(ChildProcess.make,P.ChildProcess.make) andeffect/process/ChildProcess(a namespace or the namedmakeunder any alias). It reports a command with no options, an options object literal (inline, held in aconst, or spread from one) withoutforceKillAfter, and the bare template formmake`cmd`, which cannot carry options; writemake({ forceKillAfter: "5 seconds" })`cmd`. Options it cannot see, such as a parameter, stay allowed.
v0.24.0
Minor Changes
7402ac5Thanks @cevr! -noGlobalstakes abuiltinsoption.builtins: falsedrops the built-in list of banned globals and keeps only themembersbans, so a plain script that may useconsole,process.envandBundirectly can still ban specific members:{ "builtins": false, "members": { "Bun": { "properties": ["Glob"], "use": "Effect FileSystem" } } }reportsBun.Globin every spelling the rule follows (globalThis["Bun"].Glob, an alias) and nothing else. The default,true, keeps today's behavior.
v0.23.0
Minor Changes
5d50805Thanks @cevr! -noNewPromisefollows the globalPromiseasnoGlobalsfollows the globals it bans, through one shared walk. It reportsnew 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 namedPromiseis 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
0ff27ceThanks @cevr! -noPlatformLayerOutsideEntryreports 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 anexport { 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
Minor Changes
6abf56fThanks @cevr! -noGlobalsfollows 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`]), throughas/!/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, solet p = process; p = local; p.envdoes not report, and an alias cycle ends. A global themembersoption bans whole ({ "Bun": { "use": "..." } }) is reported wherever its value leaves the rule's sight, such asuse(Bun)orconst { ...rest } = Bun. Code that passed only because of the spelling now reports.
Patch Changes
e4772e2Thanks @cevr! -noPlatformLayerOutsideEntryreports 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 alayerread.