@deslop/coding-standards 0.1.1083 → 0.1.1085
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/dist/{MutableList-Cpkiw3Cl.mjs → MutableList-NTBI6AHW.mjs} +58 -1
- package/dist/install.js +2 -2
- package/dist/oxlint.d.ts +10 -0
- package/dist/oxlint.js +445 -137
- package/package.json +1 -1
- package/skills/design/SKILL.md +2 -2
- package/skills/engineering/SKILL.md +92 -77
package/package.json
CHANGED
package/skills/design/SKILL.md
CHANGED
|
@@ -35,7 +35,7 @@ Blur a capture: the primary content and next action still stand out.
|
|
|
35
35
|
- **Web components.** shadcn's primitives, used as shipped. The registry changes often, so list it before you choose: `vpx shadcn@latest list @shadcn -t registry:ui`, then `vpx shadcn@latest docs <name>` for the chosen one. When shadcn has the component, use it with its own variants, props and sizes; add a missing one with `shadcn add` and its defaults. Never a wrapper, a new variant, a restyle or a hand-built replacement; `className` only places a primitive in the layout. Needing heavy customization means the wrong primitive or pattern.
|
|
36
36
|
- **iOS.** The same shadcn look, carried by native iOS elements: the mobile theme's tokens for color, radius and type on SwiftUI primitives through `@expo/ui` (navigation, toolbars, search, lists, menus, swipe actions, sheets, SF Symbols) with their system sizes and behavior. Glass for controls and navigation, never content. Never draw a custom system control.
|
|
37
37
|
- **Surfaces and color** come from the theme's tokens and semantic colors only: no raw colors, shadows or radii. Group by proximity before borders; no nested cards; one border owner per edge.
|
|
38
|
-
- **Flat.** Never a gradient, glow or blurred shadow, anywhere: screens, pages, videos and images use flat theme colors and 1 px borders.
|
|
38
|
+
- **Flat.** Never a gradient, glow or blurred shadow, anywhere: screens, pages, videos and images use flat theme colors and even 1 px borders, never a colored stripe on one side.
|
|
39
39
|
|
|
40
40
|
```tsx
|
|
41
41
|
<div className="bg-card border" /> // good
|
|
@@ -65,7 +65,7 @@ Capture the result at the reference's size, theme, data and state, beside the re
|
|
|
65
65
|
|
|
66
66
|
1. **Structure.** Opens on current work; nothing repeated, hidden or missing; route, sections, controls, terms and copy match the canonical screen unless the request changed them.
|
|
67
67
|
2. **Behavior.** States, focus, errors, gestures and motion match the reference; replay transitions.
|
|
68
|
-
3. **Visual.** Hierarchy, alignment, spacing, type, color, icons, and these tells: any gradient or glow, every section a bordered card with an icon, equal prominence everywhere, a color per tile, decorative emphasized words, generic copy such as "seamless", content waiting on an entrance animation, and generated defaults: cream canvas with serif and clay accent, identical rounded cards, all-caps eyebrows, the hero, feature grid, logo row and email form skeleton.
|
|
68
|
+
3. **Visual.** Hierarchy, alignment, spacing, type, color, icons, and these tells: any gradient or glow, a colored stripe on one side of a card, quote or code block, every section a bordered card with an icon, equal prominence everywhere, a color per tile, decorative emphasized words, generic copy such as "seamless", content waiting on an entrance animation, and generated defaults: cream canvas with serif and clay accent, identical rounded cards, all-caps eyebrows, the hero, feature grid, logo row and email form skeleton.
|
|
69
69
|
|
|
70
70
|
Repair grouping, then hierarchy, alignment, copy and states; recapture with long names, Unicode, 0, 1 and many records, the narrow phone and dark mode.
|
|
71
71
|
|
|
@@ -15,6 +15,15 @@ Oxlint and Oxfmt check every change and stay authoritative; this skill holds wha
|
|
|
15
15
|
|
|
16
16
|
- **Normalize.** Run the repository's fix command at the root before you analyze or check; a read-only reviewer never runs it.
|
|
17
17
|
- **First pass.** Write the forms in [Lint rules](#lint-rules) from the start. Fix a diagnostic at its source with the form its message names; never a cast, a weakened rule or a rewrite that trades one diagnostic for another.
|
|
18
|
+
- **Fix the code, never the rule.** A diagnostic whose named form does not fit points at the model upstream: change the model, not the line that trips the rule.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// good: absence is an optional value, cleared by "All"
|
|
22
|
+
const [kind, setKind] = useState<Kind>()
|
|
23
|
+
// bad: a sentinel that only exists because `undefined` is banned as a type
|
|
24
|
+
const [kind, setKind] = useState<Kind | ''>('')
|
|
25
|
+
```
|
|
26
|
+
|
|
18
27
|
- **Type early.** Check and test the package you change after each small batch of edits; the repository's full gate runs at the root before every push. Read an unfamiliar API's types in the installed version's source first. Fix the first error in each file first: one broken import turns into hundreds of implicit `any` errors downstream.
|
|
19
28
|
- **Disables** need an unavoidable conflict with an outside contract and a `--` reason naming it. Never disable ordering or weaken file configuration.
|
|
20
29
|
|
|
@@ -24,7 +33,7 @@ Start from a real caller and the outcome, not a pattern. Trace one successful op
|
|
|
24
33
|
|
|
25
34
|
- **Direct.** The most direct code that meets today's request: extend the canonical example per [Patterns](#patterns), else a function or an existing service method. A new service only for a distinct capability or lifetime; reimplementing a library's job is a new mechanism. Keep the direct version unless a probe proves a concrete contract rules it out.
|
|
26
35
|
- **Callers.** Every function, parameter, option, export, Layer, check and config value has a caller that varies it. Handle reachable inputs only: no futureproofing or impossible cases. Explicit variants for genuinely different behaviors; no boolean combinations, registries, generic configuration or factories for hypothetical callers.
|
|
27
|
-
- **Reuse.** Search existing code, installed modules and Effect's modules by the domain concept, not only the request's words, before you write traversal, state, configuration or platform code; replace dependencies Effect covers. Identical logic twice (a computation, a query, an Effect pipeline) is one function in its owner's `lib/utils`. Similar code stays separate: three similar lines beat an abstraction, and a shared function never branches on its caller.
|
|
36
|
+
- **Reuse.** Search existing code, installed modules and Effect's modules by the domain concept, not only the request's words, before you write traversal, state, configuration or platform code; replace dependencies Effect covers. Before a hand-written type, guard, table or wrapper, list the owning module's exports in its source checkout per explore: `Schema.Literals([...]).pick([...])` replaces a second literal list, `Schema.TaggedUnion` a hand-written matcher. Identical logic twice (a computation, a query, an Effect pipeline) is one function in its owner's `lib/utils`. Similar code stays separate: three similar lines beat an abstraction, and a shared function never branches on its caller. Class lists and small expressions stay inline at each use, never in shared constants; a one-line literal read once stays in place, and a module literal read twice or spanning lines is an UPPER_SNAKE constant directly under the imports. Reuse lives at the component, service or function level. Compose with Effect's own combinators, pipelines, Layers and Schema instead of writing a wrapper.
|
|
28
37
|
- **One owner.** Derive values from their owner; never mirror server state, request state, caches or pending results in another store. A persisted value has one write boundary, a domain enum one schema, a cache key the full identity it caches. No duplicate, subset or superset fields, methods, options or exports; remove states no caller produces.
|
|
29
38
|
- **Lifetime.** Finite work stays in its request scope; a stream serves progress without a job system. Background ownership only when work must outlive its caller.
|
|
30
39
|
- **Failure.** Failures flow through the error channel with their cause; never turn one into empty success. No catch, retry, fallback, idempotency or defensive check without a contract that needs it.
|
|
@@ -104,7 +113,7 @@ export const Verbose = Context.Reference<boolean>('@app/ai/Verbose', {defaultVal
|
|
|
104
113
|
|
|
105
114
|
## Types and Schema
|
|
106
115
|
|
|
107
|
-
- **Derive** types from their source,
|
|
116
|
+
- **Derive** types from their source: the library's exported type first, else `ReturnType<typeof fromSdk>` in the signature that needs it, never a private alias; use schema types and indexed members in signatures. Annotate a return type only on a guard, assertion, recursive function or `Schema.suspend` thunk.
|
|
108
117
|
- **Schema pairs.** `export type X = typeof X.Type` directly before `export const X = Schema.Struct(...)`; reuse fields by spreading `.fields`. `Schema.optionalKey` for an absent field; `Schema.optional` only for an explicit `undefined`.
|
|
109
118
|
- **Construction.** `X.make` validates and throws: trusted values only. Input that can fail uses `X.makeEffect`, mapped to the domain error.
|
|
110
119
|
- **Error predicates** are static members of their tagged error; check a struct inline with `Schema.is`.
|
|
@@ -182,81 +191,87 @@ Once it works, reread the diff as a reviewer who knows only the request, and act
|
|
|
182
191
|
|
|
183
192
|
Lint rejects the right column without an autofix. Write the left column from the start.
|
|
184
193
|
|
|
185
|
-
| Write | Never
|
|
186
|
-
| -------------------------------------------------------------------------------------------------------------------- |
|
|
187
|
-
| An early `return`, `Boolean.match(x, {onFalse, onTrue})`, `Option.match` or a lookup record | `a ? b : c`, in JSX too
|
|
188
|
-
| `options?.root ?? '.'` | `root !== undefined ? root : '.'`
|
|
189
|
-
| `pipe(Match.value(event), Match.tag('Text', text), Match.exhaustive)` | `switch`
|
|
190
|
-
| `selected === true` on a value that can be `undefined`; `ref.current === null`, `editor === undefined` | `if (selected)` on an optional, `if (!ref.current)`, `if (editor)`
|
|
191
|
-
| `Array.filter` before the loop, an early `return` in a callback | `continue`
|
|
192
|
-
| At most 3 nested blocks, 6 nested callbacks and 3 nested `pipe` calls | Deeper nesting
|
|
193
|
-
| `pipe(value, f, g)` | `value.pipe(f, g)`
|
|
194
|
-
| `const total = Array.reduce(values, 0, Number.sum)` | `let` with a loop
|
|
195
|
-
| `Array.join(parts, ', ')`, `Array.map(items, f)`, `String.split(text, ',')`, `Record.keys(record)` | `parts.join(', ')`, `items.map(f)`, `text.split(',')`, `Object.keys(record)`
|
|
196
|
-
| `Predicate.isString(v)`, `Predicate.hasProperty(node, 'tag')`, `Predicate.isNotNullish(v)` | `typeof`, `'tag' in node`, `v !== null && v !== undefined`
|
|
197
|
-
| `Predicate.isNotNullish`, `Predicate.isObject`, `Predicate.isDate` passed directly | A hand-written guard `(v): v is T => v !== null`
|
|
198
|
-
| `Array.isReadonlyArrayEmpty(items)`, `String.isNonEmpty(text)` | `items.length === 0`, `text.length > 0`
|
|
199
|
-
| `Array.ensure(value)` | `Array.isArray(value) ? value : [value]`
|
|
200
|
-
| `Option.map(option, f)`, `Option.match`, `Option.getOrElse` | `option.value` after `Option.isSome`
|
|
201
|
-
| `undefined`, `Option.getOrUndefined` | `null`, `Option.getOrNull`, except for an outside contract
|
|
202
|
-
| `Number.parse(text)`, `Number.max`, `HashMap`, `HashSet`, `DateTime` | `parseInt`, `Math.max`, `Map`, `Set`, `Date`
|
|
203
|
-
| `Clock`, `Config`, `Console`, `Random`, `Effect.sleep`, `HttpClient` | `Date.now()`, `process.env`, `console`, `setTimeout`, `fetch`
|
|
204
|
-
| `FileSystem`, `Path`, `ChildProcess`, `Crypto`'s `randomUUIDv4` | `node:fs`, `node:path`, `node:child_process`, `crypto.randomUUID()`
|
|
205
|
-
| `#lib/utils` through the package's `imports` map, named imports, named exports | `../lib/utils`, `import * as utils from '#lib/utils'`, `export default` outside config files, a `namespace`
|
|
206
|
-
| `import {Folder} from 'lucide-react'` from the defining module | `export * from 'lucide-react'`, a file that only re-exports
|
|
207
|
-
| Kebab-case file names | `LedgerView.tsx`, `ledger_view.ts`
|
|
208
|
-
| A name an outer scope does not use | An inner `entry` shadowing an outer `entry`
|
|
209
|
-
| A new value | Assigning to a parameter or its properties
|
|
210
|
-
| `/pattern/u` | A regular expression without the `u` or `v` flag
|
|
211
|
-
| Strings, numbers and booleans in a template literal | An object, `Option` or `unknown` value interpolated
|
|
212
|
-
| A check only where the type allows both outcomes | `?.`, `??` or a condition on a value whose type rules one outcome out
|
|
213
|
-
| A type parameter that links two positions | A type parameter used once, `string \| 'id'`, `void` in a union or parameter
|
|
214
|
-
| `items => service.load(items)` | `service.load` passed unbound
|
|
215
|
-
| An Effect (`Effect.fn`, `Effect.tryPromise`); in `.tsx` only, an `async` handler that awaits | An `async` function in `.ts`, `async` without `await`, `void promise`, an unhandled promise
|
|
216
|
-
| The current API its deprecation names | A deprecated API
|
|
217
|
-
| Values decoded with Schema before use | Member access, calls, assignments, arguments or returns on `any`
|
|
218
|
-
| `function name() {}` for a named function; an arrow only in place | `const name = () => {}`
|
|
219
|
-
| `server => ({...server, warmup})`, `for (const schema of schemas)`, `pair => pair[0]` | `({host, port}) =>`, `for (const {name} of schemas)`
|
|
220
|
-
| `Array.sort(entries, Order.mapInput(Order.Number, (entry: Entry) => entry.tokens))` | `entries.sort((left, right) => left.tokens - right.tokens)`
|
|
221
|
-
| The source value where it is used | A one-line wrapper, alias or argument-free constant function
|
|
222
|
-
| `
|
|
223
|
-
|
|
|
224
|
-
|
|
|
225
|
-
|
|
|
226
|
-
| `
|
|
227
|
-
| `
|
|
228
|
-
| `
|
|
229
|
-
|
|
|
230
|
-
|
|
|
231
|
-
|
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
234
|
-
|
|
|
235
|
-
| `
|
|
236
|
-
|
|
|
237
|
-
|
|
|
238
|
-
| `
|
|
239
|
-
| `
|
|
240
|
-
|
|
|
241
|
-
| `
|
|
242
|
-
|
|
|
243
|
-
|
|
|
244
|
-
| `
|
|
245
|
-
|
|
|
246
|
-
| `
|
|
247
|
-
| `
|
|
248
|
-
|
|
|
249
|
-
|
|
|
250
|
-
| `
|
|
251
|
-
|
|
|
252
|
-
|
|
|
253
|
-
| `
|
|
254
|
-
|
|
|
255
|
-
|
|
|
256
|
-
|
|
|
257
|
-
|
|
|
258
|
-
|
|
|
259
|
-
|
|
|
194
|
+
| Write | Never | Rule |
|
|
195
|
+
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
196
|
+
| An early `return`, `Boolean.match(x, {onFalse, onTrue})`, `Option.match` or a lookup record | `a ? b : c`, in JSX too | `no-ternary` |
|
|
197
|
+
| `options?.root ?? '.'` | `root !== undefined ? root : '.'` | `no-nullish-ternary`, `prefer-nullish-coalescing` |
|
|
198
|
+
| `pipe(Match.value(event), Match.tag('Text', text), Match.exhaustive)` | `switch` | `no-switch-statement` |
|
|
199
|
+
| `selected === true` on a value that can be `undefined`; `ref.current === null`, `editor === undefined` | `if (selected)` on an optional, `if (!ref.current)`, `if (editor)` | `strict-boolean-expressions` |
|
|
200
|
+
| `Array.filter` before the loop, an early `return` in a callback | `continue` | `no-continue` |
|
|
201
|
+
| At most 3 nested blocks, 6 nested callbacks and 3 nested `pipe` calls | Deeper nesting | `max-depth`, `max-nested-callbacks`, `no-deep-pipe` |
|
|
202
|
+
| `pipe(value, f, g)` | `value.pipe(f, g)` | `no-restricted-properties` |
|
|
203
|
+
| `const total = Array.reduce(values, 0, Number.sum)` | `let` with a loop | `no-let` |
|
|
204
|
+
| `Array.join(parts, ', ')`, `Array.map(items, f)`, `String.split(text, ',')`, `Record.keys(record)` | `parts.join(', ')`, `items.map(f)`, `text.split(',')`, `Object.keys(record)` | `no-native-method-call`, `no-restricted-globals`; the `Path` service's `path.join` stays |
|
|
205
|
+
| `Predicate.isString(v)`, `Predicate.hasProperty(node, 'tag')`, `Predicate.isNotNullish(v)` | `typeof`, `'tag' in node`, `v !== null && v !== undefined` | `no-typeof`, `no-in-operator`, `no-double-nullish-check` |
|
|
206
|
+
| `Predicate.isNotNullish`, `Predicate.isObject`, `Predicate.isDate` passed directly | A hand-written guard `(v): v is T => v !== null` | `no-hand-written-guard` |
|
|
207
|
+
| `Array.isReadonlyArrayEmpty(items)`, `String.isNonEmpty(text)` | `items.length === 0`, `text.length > 0` | `no-native-emptiness-check` |
|
|
208
|
+
| `Array.ensure(value)` | `Array.isArray(value) ? value : [value]` | `no-array-wrap-ternary` |
|
|
209
|
+
| `Option.map(option, f)`, `Option.match`, `Option.getOrElse` | `option.value` after `Option.isSome` | `no-option-value-access` |
|
|
210
|
+
| `undefined`, `Option.getOrUndefined` | `null`, `Option.getOrNull`, except for an outside contract | `unicorn/no-null`, `no-restricted-properties` |
|
|
211
|
+
| `Number.parse(text)`, `Number.max`, `HashMap`, `HashSet`, `DateTime` | `parseInt`, `Math.max`, `Map`, `Set`, `Date` | `no-restricted-globals`, `no-restricted-properties`, `no-restricted-types` |
|
|
212
|
+
| `Clock`, `Config`, `Console`, `Random`, `Effect.sleep`, `HttpClient` | `Date.now()`, `process.env`, `console`, `setTimeout`, `fetch` | `effecttsgo/global-*`, `effecttsgo/process-env` |
|
|
213
|
+
| `FileSystem`, `Path`, `ChildProcess`, `Crypto`'s `randomUUIDv4` | `node:fs`, `node:path`, `node:child_process`, `crypto.randomUUID()` | `effecttsgo/node-builtin-import`, `effecttsgo/crypto-random-uuid` |
|
|
214
|
+
| `#lib/utils` through the package's `imports` map, named imports, named exports | `../lib/utils`, `import * as utils from '#lib/utils'`, `export default` outside config files, a `namespace` | `import/no-relative-parent-imports`, `import/no-namespace`, `import/no-default-export`, `typescript/no-namespace` |
|
|
215
|
+
| `import {Folder} from 'lucide-react'` from the defining module | `export * from 'lucide-react'`, a file that only re-exports | `no-barrel-module` |
|
|
216
|
+
| Kebab-case file names | `LedgerView.tsx`, `ledger_view.ts` | `unicorn/filename-case` |
|
|
217
|
+
| A name an outer scope does not use | An inner `entry` shadowing an outer `entry` | `no-shadow` |
|
|
218
|
+
| A new value | Assigning to a parameter or its properties | `no-param-reassign` |
|
|
219
|
+
| `/pattern/u` | A regular expression without the `u` or `v` flag | `require-unicode-regexp` |
|
|
220
|
+
| Strings, numbers and booleans in a template literal | An object, `Option` or `unknown` value interpolated | `typescript/restrict-template-expressions`, `typescript/no-base-to-string` |
|
|
221
|
+
| A check only where the type allows both outcomes | `?.`, `??` or a condition on a value whose type rules one outcome out | `typescript/no-unnecessary-condition` |
|
|
222
|
+
| A type parameter that links two positions | A type parameter used once, `string \| 'id'`, `void` in a union or parameter | `typescript/no-unnecessary-type-parameters`, `typescript/no-redundant-type-constituents`, `typescript/no-invalid-void-type` |
|
|
223
|
+
| `items => service.load(items)` | `service.load` passed unbound | `typescript/unbound-method` |
|
|
224
|
+
| An Effect (`Effect.fn`, `Effect.tryPromise`); in `.tsx` only, an `async` handler that awaits | An `async` function in `.ts`, `async` without `await`, `void promise`, an unhandled promise | `effecttsgo/async-function`, `typescript/require-await`, `no-void`, `typescript/no-floating-promises` |
|
|
225
|
+
| The current API its deprecation names | A deprecated API | `typescript/no-deprecated` |
|
|
226
|
+
| Values decoded with Schema before use | Member access, calls, assignments, arguments or returns on `any` | `typescript/no-unsafe-*` |
|
|
227
|
+
| `function name() {}` for a named function; an arrow only in place | `const name = () => {}` | `func-style` |
|
|
228
|
+
| `server => ({...server, warmup})`, `for (const schema of schemas)`, `pair => pair[0]` | `({host, port}) =>`, `for (const {name} of schemas)` | `no-destructured-parameter`; labeled tuples, React state and refs can destructure |
|
|
229
|
+
| `Array.sort(entries, Order.mapInput(Order.Number, (entry: Entry) => entry.tokens))` | `entries.sort((left, right) => left.tokens - right.tokens)` | `no-native-method-call`; a third-party object's own method stays |
|
|
230
|
+
| The source value where it is used | A one-line wrapper, alias or argument-free constant function | `no-trivial-indirection`, `no-constant-function` |
|
|
231
|
+
| `props.note.title` in place; an alias only for a value a check narrows and a callback reads | `const note = props.note`, `export const server = extra.server` | `no-property-alias` |
|
|
232
|
+
| `const MAX_WHISPER_SECONDS = 2 * 60 * 60` directly under the imports; a one-line literal read once in place | `const maxSize = 10` below a function, `const pattern = /x/u` read once | `module-constant`; the autofix renames |
|
|
233
|
+
| Definitions above their first use | A `const` read above its declaration | `no-use-before-define` |
|
|
234
|
+
| The element where it renders; a component such as `function DeleteButton(props)` when several places render it | `const remove = <Button label="Delete" />` | `no-stored-jsx` |
|
|
235
|
+
| The library's own type, such as `import type {AnyNode} from 'domhandler'`, or the value passed straight to its call | `type Node = Parameters<typeof cheerio.contains>[1]`, `type Options = Parameters<typeof make>[0]` kept in a module | `no-floating-derived-type` |
|
|
236
|
+
| `const Kind = Schema.Literals([...])` read as `typeof Kind.Type`; `Kind.pick([...])` for a subset | `(typeof kinds)[number]['kind']`, `keyof typeof table` | `no-type-from-value` |
|
|
237
|
+
| `import {Schema} from 'effect'` | `import {Schema as S} from 'effect'` | `no-renamed-import` |
|
|
238
|
+
| Properties built with `Effect.fn` or `Effect.fnUntraced` | `fetch: url => Effect.tryPromise(...)` as a property arrow | `no-effect-property-arrow` |
|
|
239
|
+
| Inferred return and variable types, `satisfies`, `Array.empty<T>()` | `function f(): T`, `const items: T[] = []` | `no-return-type`, `no-redundant-variable-annotation` |
|
|
240
|
+
| `satisfies T`, `as const` | `as T`, `any`, `value!` | `consistent-type-assertions`, `no-explicit-any`, `no-non-null-assertion` |
|
|
241
|
+
| `useRef(initial)` for lifecycle state | `const [box] = useState(() => ({current: initial}))` with no setter | `no-fake-ref-state` |
|
|
242
|
+
| `Ref.make(0)`, `useState(false)`, `useRef<HTMLDivElement>(null)` | `Ref.make<number>(0)`, `useRef<HTMLDivElement \| null>(null)` | `no-redundant-state-type`, `no-redundant-use-ref-null-type` |
|
|
243
|
+
| `T[]`, an optional property | `Array<T>`, `Promise<T>`, `x: T \| undefined` | `no-restricted-types` |
|
|
244
|
+
| `Effect.tryPromise({catch: cause => X.make({cause, message}), try})`, each failure typed by its tagged error | `unknown` or `any` in the error or requirements channel, a `catch` returning the raw cause | `effecttsgo/any-unknown-in-error-context`, `effecttsgo/unknown-in-effect-catch` |
|
|
245
|
+
| `Effect.mapError(cause => X.make({cause, message}))` | `Effect.mapError(failure => X.make({message}))`, `catch: failure => X.make({message})` | `preserve-error-cause` |
|
|
246
|
+
| The failure in the error channel; `Effect.option` for a value that is optional by design | `Effect.orElseSucceed(() => [])`, `Effect.catch(() => Effect.void)`, `Effect.succeed('')` in a catch | `no-empty-recovery` |
|
|
247
|
+
| `Effect.forEach(items, load, {concurrency: 'unbounded'})`; `{concurrency: 1}` when order or a resource matters | `Effect.forEach(items, load)`, `Effect.all([a, b])`, `Effect.all(effects, {discard: true})` | `explicit-effect-concurrency` |
|
|
248
|
+
| `Effect.fn('Service.method')` for a function returning an Effect | A plain function returning `Effect.gen` | `effecttsgo/effect-fn-opportunity` |
|
|
249
|
+
| Layers provided once at the entry point; `Layer.provide` between layers | `Effect.provide(layer)` inside services or per call | `effecttsgo/strict-effect-provide` |
|
|
250
|
+
| `Layer.provideMerge` when one layer needs another | `Layer.mergeAll` of layers that depend on each other | `effecttsgo/layer-merge-all-with-dependencies` |
|
|
251
|
+
| One `pipe(value, f, g, h)` | `pipe(pipe(value, f), g)`, `pipe(value)` | `effecttsgo/unnecessary-pipe-chain`, `effecttsgo/unnecessary-pipe` |
|
|
252
|
+
| Service keys `@<package>/<path>/<Name>`; an error's tag is its class name | A service key that does not match the package, path and class name | `effecttsgo/deterministic-keys` |
|
|
253
|
+
| `return yield* NotFound.make({...})` | `yield* Effect.fail(NotFound.make({...}))` | `effecttsgo/unnecessary-fail-yieldable-error` |
|
|
254
|
+
| `Schema.decodeEffect(S)(typed)`, `Schema.fromJsonString(S)` for JSON text | `Schema.decodeUnknownEffect` on typed input, `JSON.parse` in an Effect | `effecttsgo/prefer-typed-schema-decoder`, `prefer-schema-over-json` |
|
|
255
|
+
| `Schema.fromJsonString(Protocol)` with the protocol's schema | `Schema.UnknownFromJsonString`, `Schema.fromJsonString(Schema.Unknown)` | `no-unvalidated-json-decode` |
|
|
256
|
+
| `export type X = typeof X.Type` directly before `export const X`; an inferred schema | A type elsewhere, `Schema.Schema<X>` restated, an unannotated `suspend` thunk | `schema-type-pair` |
|
|
257
|
+
| `X.make(...)`, `Schema.is(X)(value)`, `Schema.decodeEffect` inside an Effect, `Schema.Finite` for numbers | `new X(...)` on a schema class, `instanceof X`, `Schema.decodeSync` in a generator, `Schema.Number` | `effecttsgo/new-schema-class`, `effecttsgo/instance-of-schema`, `effecttsgo/schema-sync-in-effect`, `effecttsgo/schema-number` |
|
|
258
|
+
| The Schema operation inline at its use | `const decode = Schema.decodeEffect(S)` stored for later | `no-stored-schema-operation` |
|
|
259
|
+
| `Schema.Struct`, `Schema.TaggedError`, `Schema.NonEmptyString` | `Schema.Class`, `Data.TaggedError`, `Error`, a rebuilt refinement | `no-restricted-properties`, `no-restricted-globals`, `no-reinvented-schema` |
|
|
260
|
+
| The failure mapped to the service's error; best-effort work as `Effect.ignore({log: 'Warn', message})` | A bare `Effect.ignore`, `Effect.orDie` outside tests | `no-discarded-failure` |
|
|
261
|
+
| `NodeRuntime.runMain(program)`; atoms in React | `Effect.runPromise`, `ManagedRuntime.make` | `no-restricted-properties` |
|
|
262
|
+
| `import {useState} from 'react'`; `const [open, setOpen] = useState(false)` | `React.useState`, `const state = useState(false)` | `no-restricted-properties`, `no-undestructured-use-state` |
|
|
263
|
+
| React Compiler memoization, a `ref` prop | `useCallback`, `useMemo`, `memo`, `forwardRef` | `no-restricted-imports` |
|
|
264
|
+
| Code the compiler accepts | `'use no memo'`, an inline disable of a React rule | `no-compiler-opt-out`, `react/rule-suppression` |
|
|
265
|
+
| `const countAtom = Atom.make(0)` at module level, `Atom.family(id => ...)` per prop | `Atom.make` inside a component or hook, `useState(() => Atom.make(0))` | `no-component-atom-allocation` |
|
|
266
|
+
| `<Editor key={draft.id} />` to reset a draft; a value derived in render | A `useEffect` that copies a prop into state | `react-doctor/no-adjust-state-on-prop-change`, `react-doctor/no-derived-state` |
|
|
267
|
+
| `<Label htmlFor="name">`, `aria-label` on a field without a visible label, a `<Button>` for a click | A placeholder as the only label, `<div onClick>` | `jsx-a11y/*`, `react-doctor/no-placeholder-only-field` |
|
|
268
|
+
| `bg-card`, `border` | `bg-linear-to-b from-muted`, `bg-[radial-gradient(...)]` | `no-gradient` |
|
|
269
|
+
| `duration-200`, `zoom-in-95`, `starting:scale-95`, `motion-safe:animate-spin` | `duration-500`, `zoom-in`, `starting:scale-50`, an ungated `animate-bounce` | `motion-limits`, `react-doctor/no-ungated-tailwind-animation` |
|
|
270
|
+
| Tailwind's scale and the theme's tokens: `w-64`, `text-sm`, `bg-primary`; `className` on a primitive for layout only | `w-[312px]`, `text-blue-500`, `style={{...}}`, an unknown class, a primitive restyled | `shadcn/no-arbitrary-values`, `shadcn/no-raw-colors`, `shadcn/no-inline-styles`, `shadcn/no-unknown-classes`, `shadcn/no-restyle` |
|
|
271
|
+
| The UI package's primitive | A styled native `<button>` or `<input>` | `no-native-control` |
|
|
272
|
+
| `assert` from `@effect/vitest`, the failure's identity | `expect`, `vi`, an assertion on message wording | `no-restricted-imports`, `no-error-message-assertion` |
|
|
273
|
+
| One comment line for an outside constraint | A multiline comment | `no-multiline-comment` |
|
|
274
|
+
| Object keys in ascending order; a blank line starts a new group | Unsorted keys, a `sort-keys` disable | `sort-keys`, `no-unexplained-disable` |
|
|
260
275
|
|
|
261
276
|
Forms that satisfy every rule where the obvious fix trades one diagnostic for another:
|
|
262
277
|
|