Commit Graph

4 Commits

Author SHA1 Message Date
Ignacy Łątka acfe80ab6b chore: add ESLint with typescript-eslint (#349)
## What

Adds type-aware linting to the workspace. Until now the only static gate
on JS/TS was Prettier (formatting) and `tsc` — there was no linter, so
the whole class of bugs `tsc` doesn't catch (floating promises, misused
promises, throwing non-Errors, unsafe stringification, dead code) went
unguarded across ~450 TS files.

- **`eslint.config.mjs`** — flat config. `recommendedTypeChecked` runs
on the TS sources via an explicit `project` list (each package keeps
tests in a separate `tsconfig.test.json` that `projectService` can't
auto-discover, so it's listed explicitly). Type-checked rules are
disabled for the loosely-typed plain-JS build/dev scripts. The
`argent-private` submodule and build artifacts are ignored (mirrors
`.prettierignore`).
- **`lint` / `lint:fix`** npm scripts.
- **`.github/workflows/lint.yml`** — builds the workspace (type-aware
rules need the referenced projects' declarations) then runs `eslint .`.
Node 24 (ESLint 10 requires `^20.19 || ^22.13 || >=24`).

## Rule calibration

This is an adoption PR, so it's deliberately calibrated rather than
maximal:

- **Kept on (bug-catchers):** `no-floating-promises`,
`no-misused-promises` (with `checksVoidReturn: false` to drop the noisy
callback variant), `only-throw-error`, `no-base-to-string`,
`prefer-promise-reject-errors`, `no-unused-vars` (`_`-prefix escape
hatch), plus the ESLint recommended set.
- **Parked off as documented debt:** the high-volume style/`any`-family
rules — `no-unsafe-*`, `require-await`, `no-unnecessary-type-assertion`,
`restrict-template-expressions`, `unbound-method`, `no-explicit-any`,
etc. These flag broad classes of pre-existing code and are out of scope
here; each is commented as a ratchet target for follow-up passes.

All findings from the kept-on rules are fixed in this PR (dead
code/imports, error-cause chaining, **3 genuine floating promises**,
non-Error throws, unsafe stringification). Stale unused `eslint-disable`
directives left over in the tree were removed by `--fix`.

## Verification

Locally, all green: `eslint .` (0 errors), `tsc --build`,
`typecheck:tests`/`typecheck:scripts`, the full `vitest` suite (no
behavior change from the fixes), and `prettier --check`.

## Not included (intentionally)

Other static-analysis gates discussed (CodeQL, dependency-audit,
ShellCheck, actionlint) are out of scope for this PR.
2026-06-17 12:20:55 +02:00
Ignacy Łątka ff49143d03 fix(debugger): make inspect-element work on old-arch RN (RN 0.81 bridge) (#344)
## Problem

`debugger-inspect-element` returned `{"error":"no host fiber"}` at every
coordinate on a React Native 0.81.5 app (Hermes, old architecture /
legacy bridge, React Compiler). Verified live on a real device.

## Root causes (three layers, each verified live)

1. **Host-fiber resolution** - `findHostFiber` only recognised old-arch
host fibers via `stateNode.canonical`, but RN 0.81 bridge host fibers
expose `stateNode._nativeTag` with no `canonical`, so it found nothing
and threw "no host fiber". Fixed by also matching `_nativeTag` (mirrors
the working `component-tree.ts` getHostInfo). On the real tree: 87/87
host fibers had `_nativeTag` and 0 had `canonical`.
2. **Wrong anchor** - even with the host fiber resolved, the first host
fiber is a sibling subtree that does not contain the touch point, so the
renderer's native `findSubviewIn` resolved nothing for *every*
coordinate (touchedViewTag undefined). Fixed by anchoring at the
**FiberRoot container** (its `containerTag`), the universal ancestor.
Fabric and older Paper (no containerTag) keep the host-fiber anchor.
3. **Changed data shape** - RN 0.81 no longer returns
`data.closestInstance`/`hierarchy`; it returns `data.componentStack` (a
React stack string). The old extraction produced empty results. Now
parsed into items; bundle source locations are symbolicated by the tool
layer exactly like `_debugStack` frames.

## Result

Live on a real RN 0.81 app, inspecting a point now returns the real
component hierarchy (170 frames: `SplashScreen`, `ErrorBoundary`,
`LoggedOut`, `NativeStackNavigator`, `HomeTabNavigator`, `SceneView`,
`NavigationProvider`, ...) with source locations.

## Safety / scope

- Fabric (new-arch) and iOS paths unchanged; canonical-publicInstance
handling is byte-for-byte preserved (incl. the lazily-null Fabric case).
- Distinct from #284 (symbolication fallback - a different, later
failure mode).
- Unit tests added for: `_nativeTag` host-fiber resolution, container
anchoring, componentStack parsing, and the Fabric null-publicInstance
no-regression case. The test harness evaluates the generated script, so
it exercises the real emitted code.
- Validated end-to-end against a live emulator, not only unit tests.
2026-06-16 16:20:50 +02:00
Filip131311 9267940391 style: prettier formatting in inspect-at-point-script test 2026-06-10 12:32:36 +02:00
Alexander Brokking d2071fafc5 fix(tool-server): select the correct renderer in debugger-inspect-element (#317)
## Summary

Companion to #316. `debugger-inspect-element` has the same
hardcoded-renderer assumption as the component-tree script, so it
inspects the wrong React renderer on multi-renderer apps.

## Root cause

`makeInspectScript`
(`packages/tool-server/src/utils/debugger/scripts/inspect-at-point.ts`)
picks:

```js
var renderer = Array.from(hook.renderers.values())[0];
var roots = hook.getFiberRoots(1);
var root = Array.from(roots)[0];
```

On React Native **New Architecture** apps that also use a secondary
React reconciler (`react-native-skia`, `react-native-svg`,
`react-three-fiber`, …), that library registers its own renderer and
frequently takes id `1`. `renderer` then points at the Skia/SVG
reconciler and `getFiberRoots(1)` returns only its roots, so
`renderer.rendererConfig.getInspectorDataForViewAtPoint(...)` runs
against the wrong tree and returns no app component at the touch point.

## Fix

Enumerate `hook.renderers`, walk each renderer's roots, and select the
**(renderer, root)** pair with the largest fiber subtree (the real app
UI) before inspecting — then call `getInspectorDataForViewAtPoint` on
that renderer. Falls back to the previous first-renderer behavior if
enumeration yields nothing.

Unlike the component-tree script, the Fabric layout path here was
already correct (it uses
`hostFiber.stateNode.canonical.publicInstance`), so no measurement
change is needed — this PR is purely the renderer/root selection.

## Testing

- Full `npm run build` (`tsc --build`) passes; the modified file
typechecks under `strict`.
- Same environment as #316: a React 19.2 / Fabric (bridgeless) app whose
DevTools hook exposes a `react-native-skia` renderer at id 1 alongside
the real `react-native` UI renderer. With the fix, inspection resolves
against the app renderer instead of the Skia one.

No change to the output schema or the Paper path.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:27:52 +02:00