@ox0/guards
v0.3.0
Published
Deterministic syntax-level guard runner for React and TypeScript projects.
Readme
@ox0/guards
Deterministic syntax-level guard runner for React and TypeScript projects.
Usage
Inside ox0:
pnpm run check:guardsInside another project where this package is installed:
pnpm exec ox0-guards --project .Rules
| Rule | What it catches | Fix |
| ---- | --------------- | --- |
| guard/missing-label | Interactive form elements without an accessible label or aria-label | Add <label>, aria-label, or aria-labelledby |
| guard/no-api-call-in-effects | Direct network calls inside useEffect/useLayoutEffect | Move data fetching to TanStack Query |
| guard/no-async-react-component | React component functions declared async | Remove async; use useEffect or useSuspenseQuery |
| guard/no-bare-intl | new Intl.DateTimeFormat(), new Intl.NumberFormat(), etc. outside the i18n package | Use react-intl formatting helpers |
| guard/no-css-import | Importing raw .css files outside approved entrypoints | Use design token primitives from @ox0/tokens |
| guard/no-direct-network-call | Direct fetch, axios, or XMLHttpRequest anywhere in the project | Route through a validated API layer |
| guard/no-effect-set-state-chain | State setter called from an effect that depends on that same state | Break the cycle; derive the value or use a ref |
| guard/no-presentation-attrs | className or style on raw HTML elements | Use component-library primitives |
| guard/no-raw-dom-jsx | <div>, <span>, <section>, <article>, <aside>, <main> in component code | Use semantic elements or layout primitives |
| guard/no-set-state-in-render | State setter called unconditionally during render | Move to an event handler or effect |
| guard/no-static-click-handler | onClick on non-interactive elements without role, tabIndex, and a keyboard handler | Use <button> or another semantic control |
| guard/no-unsafe-type-assertion | as cast on unvalidated external data | Validate with TypeBox before narrowing |
| guard/no-unstable-hook-deps | Inline objects, arrays, or functions in hook dependency arrays | Hoist, memoize, or stabilize the value |
| guard/no-unstable-hook-deps-transitive | Unstable props passed to a child that uses those props in hook deps | Stabilize before passing |
| guard/no-web-storage | Direct localStorage/sessionStorage in shared primitives | Use an approved storage abstraction |
| guard/require-effect-cleanup | Effects that register listeners, timers, or observers without a cleanup return | Return a cleanup function |
| guard/stable-provider-values | Inline object, array, or function literal in <Provider value={...}> | Memoize the value with useMemo |
Configuration
Drop an ox0-guards.config.ts (or .js, .mjs, .cjs) at the project root:
import { defineGuardConfig, createNoApiCallInEffectsGuardWith } from '@ox0/guards'
export default defineGuardConfig({
// Disable a built-in rule for this project
disable: ['guard/no-raw-dom-jsx'],
// Add custom or pre-configured guards
additionalGuards: [
// Replace the default no-api-call-in-effects with a version
// that also catches calls to helpers imported from API modules
createNoApiCallInEffectsGuardWith({
apiImportPattern: /schwab|\/api\//,
}),
],
})When using createNoApiCallInEffectsGuardWith, also add 'guard/no-api-call-in-effects' to disable to avoid running both the default and your configured version.
disable
Array of rule IDs to exclude from the run. Useful when a built-in rule does not fit the project's conventions.
additionalGuards
Array of GuardFactory values appended to the built-in set. Use this to add project-specific rules or to register a configured variant of a built-in guard.
CLI Flags
| Flag | Description |
| ---- | ----------- |
| --project <path> | Project root to scan (default: .) |
| --rule <rule-id> | Run only this rule; repeatable |
| --rules | Print all registered rule IDs and exit |
| --quiet | Suppress output when there are no findings |
| --color / --no-color | Force or suppress ANSI color |
Examples:
pnpm exec ox0-guards --project . --rules
pnpm exec ox0-guards --project . --rule guard/no-unstable-hook-deps
pnpm exec ox0-guards --project . --quietSuppressions
Suppress a single line:
// guard-disable-next-line guard/no-web-storage rationale here
localStorage.setItem('key', value)Suppress a block:
// guard-disable-start guard/no-web-storage reading legacy migration flag written before the abstraction existed
const legacy = localStorage.getItem('legacy-flag')
doMigration(legacy)
// guard-disable-end guard/no-web-storageBoth next-line and start require a rationale. Suppressions that reference unknown rule IDs are themselves flagged as findings.
LLM Guidance
For a dense, directive guide covering every rule, configuration, and suppression — written for language models — see ./guides/guards.md. Point your project's AGENTS.md at node_modules/@ox0/guards/guides/guards.md.
