Ranger architecture

6 — What may import what

Written: 2026-08-15 · Established by: extracting every cross-directory from '../…' import across src/ outside src/__tests__/ at commit 93a9a67, then reading each edge that ran against the layering.

This describes the dependency direction that exists, not one that is enforced. No lint rule constrains imports between directories — eslint.config.js restricts only node:* builtins. So this diagram is a measurement, and the four edges called out below are the ones the measurement found going the wrong way.

Three layers, and one dotted arrow that should not be there.

issue 80

src/App.tsx

React layer

src/viewer/utils.ts

src/store

Engine layer

The solid arrows are the layering: React reads the store, the store drives the engine, and the engine reaches neither back. The dotted pair is the exception, and it is the whole finding — four engine-layer directories reach up into the render layer for geometry that was never render-specific.

Directory Depends on Notes
src/parser src/types The bottom of the graph
src/types src/parser Type-only; see below
src/checks src/parser, src/transforms, src/utils, src/types
src/transforms src/parser, src/utils, src/types Pure core; its dialogs are React
src/sim src/parser, src/utils, src/types, src/viewer
src/utils src/parser, src/types, src/viewer
src/workers src/sim, src/parser, src/utils, src/types
src/corpus src/parser, src/utils, src/types
src/store src/parser, src/sim, src/transforms, src/utils, src/viewer
src/viewer src/store, src/sim, src/transforms, src/parser, src/utils
src/sidebar src/store and most of the rest The widest importer
src/codemirror src/sidebar, src/types One symbol; see below
src/export src/store, src/transforms, src/codemirror
src/playback src/store, src/viewer, src/parser
src/commands src/viewer, src/utils
src/compare src/store, src/export, src/transforms
src/bug-report src/store, src/parser, src/utils
src/components src/utils Leaf
src/dev src/store, src/sim, src/viewer Dev-only
src/__tests__ everything pure, dom, scene, scripts

The four edges that run against the layering

src/sim, src/utils, src/store and src/transforms all import src/viewer/utils.ts. Nine files outside src/viewer/ import it, and between them they take ten symbols: arcGeometry, arcLength, arcSegmentCount, arcExtremePoints, chordTolerance, computeBounds, unionBounds, buildAllSegmentPositions, buildTransitionMarkers and the TransitionMarker type. The first seven are arc geometry and bounds with nothing to do with rendering, sitting in the render layer because that is where they were first needed. Issue #80 is open against exactly this and proposes one geometry layer below the viewer.

src/types/gcode.ts imports six types from src/parser. So src/parser is the bottom of the graph, not src/types: the shared vocabulary is defined where each construct is detected — Units, SubprogramInfo, CannedCycleInfo, WcsAlignmentResult, ToolTableEntry, G10Offset — and gcode.ts re-exports them. These are type-only imports and cost nothing at runtime.

src/checks/fixes/ imports src/transforms. Two symbols only, applyEdits and the Edit type. A guided fix produces the same edit shape a cleanup tool does, so this is reuse of the splice machinery rather than a check reaching into the transform UI.

src/codemirror/check-marks.ts imports src/sidebar/severity.ts. One symbol, SEVERITY_LABEL. The editor's gutter marks and the checks panel have to name a severity the same way.

Where the store is genuinely absent

The engine subgraph reaches no React and no store, and that is what makes src/__tests__/pure/ the project most of the suite lives in — it runs in a node environment with no jsdom setup, in roughly a quarter of the whole suite's time. src/transforms straddles the line: its .ts core is pure, and its *Dialog.tsx components import the store. Only useDeferredTransform.ts does so among the .ts files, and it is a React hook.

What this map does not tell you

It is directory-level, so it hides the shape inside each box. src/viewer alone holds 33 plain .ts modules next to its scene components. It also draws every edge at equal weight, which is why the load path, the write path and the simulation pipeline get their own diagrams rather than being read off this one.

Drag to pan · scroll to zoom · Esc to close