From c2fea3ebb5203b6bd8dc666468ee9df51efe25c0 Mon Sep 17 00:00:00 2001 From: Paul Doho Date: Mon, 21 Sep 2026 22:58:17 +0100 Subject: [PATCH 1/2] feat: add TypeScript language adapter --- README.md | 10 +- docs/map/index.html | 6 +- docs/map/map.json | 16 +- docs/reference.md | 3 +- map/model.py | 8 +- pyproject.toml | 6 +- src/systemap/change.py | 58 ++--- src/systemap/cli.py | 12 +- src/systemap/config.py | 45 +++- src/systemap/extract.py | 106 ++++++++- src/systemap/language.py | 68 ++++++ src/systemap/scaffold.py | 22 +- src/systemap/typescript.py | 470 +++++++++++++++++++++++++++++++++++++ tests/test_extract.py | 18 ++ tests/test_screenshots.py | 5 +- tests/test_typescript.py | 121 ++++++++++ uv.lock | 217 +++++++++++++++++ 17 files changed, 1116 insertions(+), 75 deletions(-) create mode 100644 src/systemap/language.py create mode 100644 src/systemap/typescript.py create mode 100644 tests/test_typescript.py diff --git a/README.md b/README.md index 6e624f2b..8ff22dbc 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,12 @@ the version on PyPI the Python versions it runs on the licence - how many dependencies it has + how many dependencies it has

Your coding agent writes code faster than you can read it. You review the diff, you merge, and one day you notice you are no longer sure how the pieces -of your own Python project fit together. +of your own Python or TypeScript project fit together. **systemap gives you one page that shows how they fit.** Your agent draws it from your code. A checker then refuses to let that page go out of date, and @@ -186,8 +186,10 @@ features that failed their test were written down rather than shipped ## What is systemap not? -It reads Python and only Python. It is not a call graph: the map shows the -lines your agent declared and defended, not every function call. It is not a +It reads Python, TypeScript and TSX. TypeScript support reads exported names, +imports, tests and package binaries; framework-specific routes and configured +path aliases are not read yet. It is not a call graph: the map shows the lines +your agent declared and defended, not every function call. It is not a dependency diagram: modules are not parts, and the map shows parts. It is not a UML tool: one picture, one layout, and nothing to learn beyond card, line and journey. diff --git a/docs/map/index.html b/docs/map/index.html index 56962c24..89b89c0e 100644 --- a/docs/map/index.html +++ b/docs/map/index.html @@ -177,7 +177,7 @@ .systemap-panel{font-family:var(--fs);font-size:13px;line-height:1.45;color:var(--ink-2);background:var(--surface);border:1px solid var(--line);border-radius:8px;padding:.9rem 1rem 1rem;min-height:3rem}.systemap-panel:empty::before{content:'Click a component to read it.';color:var(--ink-3)}.systemap-f__plain{font-size:19px;font-weight:600;color:var(--ink);letter-spacing:-.01em;line-height:1.2;margin:0}.systemap-f__code{font-family:var(--fm);font-size:12px;color:var(--accent);margin:.3rem 0 0;display:flex;flex-wrap:wrap;gap:.2rem .7rem;align-items:baseline}.systemap-f__kind{color:var(--ink-3);font-size:11px;letter-spacing:.06em;text-transform:uppercase}.systemap-f__does{margin:.6rem 0 .2rem;font-size:13px;color:var(--ink-2)}.systemap-f__iface{margin:.3rem 0 .2rem;font-family:var(--fm);font-size:11.5px;color:var(--ink-2);word-break:break-word}.systemap-f__note{margin:.5rem 0 .2rem;padding:.4rem .6rem;font-size:12.5px;color:var(--ink);border-left:3px solid var(--warn);background:var(--raised);border-radius:0 6px 6px 0}.systemap-f__wheel{margin:.4rem 0 0}.systemap-f__wheel svg{width:100%;height:auto;display:block;margin:0 auto}.systemap-f__say{margin:.2rem 0 .6rem;padding:.55rem .7rem;font-size:13px;line-height:1.45;color:var(--ink);border-left:3px solid var(--accent);background:var(--raised);border-radius:0 6px 6px 0;min-height:2.6rem}.systemap-f__say.muted{color:var(--ink-3);border-left-color:var(--line-2)}.systemap-f__evidence{margin:-.3rem 0 .6rem;font-family:var(--fm);font-size:11px;color:var(--ink-3);min-height:1em}.systemap-f__evidence.declared{color:var(--warn)}.systemap-f__chips{display:flex;flex-wrap:wrap;gap:.35rem;margin:.5rem 0 0}.systemap-chip{display:inline-flex;align-items:center;gap:.35em;min-height:24px;padding:0 .55em;border-radius:4px;font-family:var(--fm);font-size:11px;letter-spacing:.04em;background:var(--raised);color:var(--ink-3);border:1px solid var(--line)}.systemap-chip--built{color:var(--good);border-color:transparent}.systemap-chip--actor{color:var(--ink-3);background:none;border:1px dashed var(--line-2)}.systemap-chip a{color:var(--accent);text-decoration:none}.systemap-chip a:hover{text-decoration:underline}.systemap-chip--rule{color:var(--violet);cursor:help;min-width:24px;justify-content:center}.systemap-f__lives{margin:.6rem 0 0;font-family:var(--fm);font-size:11px;color:var(--ink-3)}.systemap-f__lives b{font-weight:400;color:var(--ink-3)}.systemap-f__entry{margin:.4rem 0 0;font-family:var(--fm);font-size:11px;color:var(--ink-3)}.systemap-f__entry b{font-weight:400;color:var(--ink-2)}.systemap-f__opens{margin:.4rem 0 0;font-family:var(--fm);font-size:11px;color:var(--ink-3)}.systemap-f__opens b{font-weight:400;color:var(--ink-2)}.systemap-f__preview{margin:.5rem 0 0;border:1px solid var(--line);border-radius:6px;overflow:hidden;background:var(--bg)}.systemap-f__preview svg{width:100%;height:auto;display:block}.systemap-f__open{appearance:none;display:block;margin:.5rem 0 0;min-height:30px;padding:0 .8rem;border-radius:6px;border:1px solid var(--accent);background:none;color:var(--accent);font-family:var(--fs);font-size:12.5px;cursor:pointer}.systemap-f__open:hover{background:var(--raised)}.systemap-w__spoke{cursor:pointer;outline:none}.systemap-w__hit{stroke:transparent;stroke-width:26;fill:none;pointer-events:stroke}.systemap-w__line{fill:none;stroke-width:1.5;transition:stroke-width .12s ease}.systemap-w__verb{font-family:var(--fs);font-size:11px;font-weight:500;text-anchor:middle;paint-order:stroke;stroke:var(--surface);stroke-width:5;stroke-linejoin:round;pointer-events:none}.systemap-w__name{font-family:var(--fm);font-size:11px;font-weight:600;fill:var(--ink);pointer-events:none}.systemap-w__centre rect{fill:var(--raised);stroke:var(--accent);stroke-width:1.6}.systemap-w__centre text{font-family:var(--fm);font-size:12px;font-weight:600;fill:var(--ink);text-anchor:middle}.systemap-w__spoke.peek .systemap-w__line,.systemap-w__spoke:hover .systemap-w__line,.systemap-w__spoke:focus-visible .systemap-w__line{stroke-width:3.2}.systemap-w__spoke.peek .systemap-w__name,.systemap-w__spoke:hover .systemap-w__name,.systemap-w__spoke:focus-visible .systemap-w__name{fill:var(--accent)}.systemap-w__empty{font-family:var(--fs);font-size:12px;fill:var(--ink-3);text-anchor:middle}

systemap map

-

A generated map of what the parts of systemap are and what they are to each other: 16 components and 4 actors, 67 flows, five layers. Facts from 10b4b6c232.

+

A generated map of what the parts of systemap are and what they are to each other: 16 components and 4 actors, 67 flows, five layers. Facts from 83aabb8180.