1
0

try-repo.sh 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #!/usr/bin/env bash
  2. # Try the Steps / Screens / Entry-points pictures on a real project with the
  3. # CURRENT build: clone (shallow) a preset or any git URL, index it with
  4. # dist/bin/codegraph.js (a full rebuild, so new synthesizers and resolvers
  5. # apply), print what the index holds — routes, cross-tier edges, navigations —
  6. # and open the viewer on it.
  7. #
  8. # npm run build
  9. # scripts/try-repo.sh # list the presets and what each shows
  10. # scripts/try-repo.sh proshop # clone + index + open the viewer
  11. # scripts/try-repo.sh proshop 4750 # on a fixed port
  12. # scripts/try-repo.sh https://github.com/x/y.git
  13. # scripts/try-repo.sh ~/code/my-app # a local project (re-indexed with this build)
  14. #
  15. # Env: CODEGRAPH_TRY_DIR where clones live (default ~/.cache/codegraph-try)
  16. # CODEGRAPH_TRY_NO_OPEN=1 print the URL instead of opening a browser
  17. # CODEGRAPH_TRY_REINDEX=0 reuse an existing index instead of rebuilding
  18. set -euo pipefail
  19. HERE="$(cd "$(dirname "$0")/.." && pwd)"
  20. CLI="$HERE/dist/bin/codegraph.js"
  21. DIR="${CODEGRAPH_TRY_DIR:-$HOME/.cache/codegraph-try}"
  22. export CODEGRAPH_TELEMETRY=0 DO_NOT_TRACK=1
  23. # name|url|what to look at (hash URLs relative to the viewer)
  24. PRESETS='
  25. proshop|https://github.com/bradtraversy/proshop_mern.git|Express + React Router (MERN). Steps: #/steps?symbol=login&through=1 — login → ⇢ POST /api/users/login → User.findOne → 401 rows; #/steps?symbol=/payment — the bounce to /shipping WHEN !shippingAddress.address, the push to /placeorder, and the checkout nav tabs, each under the prop that enables it. Screens: #/screens — 19 pages wired by history.push and <LinkContainer to>. Entry points: 30 endpoints + 19 pages; the project reads as a web app.
  26. express-realworld|https://github.com/gothinkster/node-express-realworld-example-app.git|Express + Prisma (TypeScript). Steps: #/steps?symbol=POST%20/api/users/login — request → handler → prisma → response rows with their status codes.
  27. nest-samples|https://github.com/nestjs/nest.git|NestJS samples. Steps: #/steps?symbol=POST%20/audio/transcode (sample/26-queues: the job lands on @Process("transcode") as ⇠ transcode); the event emitter sample (30) pairs emit("order.created") with its @OnEvent listener; sample/02-gateways for @SubscribeMessage.
  28. nest-boilerplate|https://github.com/brocoders/nestjs-boilerplate.git|NestJS + TypeORM. Steps: #/steps?symbol=POST%20/api/v1/auth/email/login — guards on the class and method (FIRES FROM … after UseGuards), DI followed by declared type into the service, repository saves as data calls, thrown exceptions as response rows.
  29. tanstack|https://github.com/TanStack/router.git|TanStack Router: 477 example and e2e apps in ONE index — the app-root gating under load, where a link resolves within its own app and never into another. Look at examples/react/kitchen-sink-file-based (file-based: /profile from _auth.profile.tsx, /route-group from a (group) folder) and examples/react/basic (code-based: /posts/:postId composed through getParentRoute).
  30. next-saas-starter|https://github.com/leerob/next-saas-starter.git|Next.js App Router + server actions. Screens: #/screens — /sign-in → /dashboard via Login > signIn WHEN …, <Link>s, redirect(), NextResponse.redirect; Steps: #/steps?symbol=/dashboard&through=1 — FIRES FROM page load, handlers, useSWR("/api/team") → ⇢ GET /api/team.
  31. spring-petclinic|https://github.com/spring-projects/spring-petclinic.git|Spring (Java). Steps: #/steps?symbol=POST%20/owners/new — PreAuthorize-style guards, OwnerRepository owners → owners.save as the database, ResponseEntity / view replies with WHEN rows. Kotlin twin: spring-petclinic-kotlin.
  32. spring-petclinic-kotlin|https://github.com/spring-petclinic/spring-petclinic-kotlin.git|Spring (Kotlin). Same picture as spring-petclinic with Kotlin guards (if expressions, when).
  33. fastapi-template|https://github.com/fastapi/full-stack-fastapi-template.git|FastAPI + a TanStack Router frontend. Screens: #/screens — 8 frontend pages with their guards (/login and /signup bounce to / WHEN isLoggedIn(), /admin WHEN NOT user.is_superuser). Entry points: 23 routes named by path (APIRouter prefixes composed); Steps: #/steps?symbol=POST%20/items — Depends(...) as the chain, session.add/commit as the database, HTTPException rows with status_code. (settings.API_V1_STR is a computed prefix and is left off, on purpose.)
  34. dispatch|https://github.com/Netflix/dispatch.git|FastAPI, large. Steps on any router endpoint; expect guards and arguments for Python.
  35. clean-architecture|https://github.com/jasontaylordev/CleanArchitecture.git|ASP.NET Minimal API endpoint groups (C#). Entry points: 10 routes (POST /api/TodoItems, PUT /api/TodoItems/{id} …); Steps: #/steps?symbol=PUT%20/api/TodoItems/{id} — TypedResults replies as 204 · 400 rows with WHEN.
  36. eshoponweb|https://github.com/dotnet-architecture/eShopOnWeb.git|ASP.NET MVC + Minimal API (C#). Steps on a controller action or a MapGet endpoint; C# guards and arguments.
  37. bookstack|https://github.com/BookStackApp/BookStack.git|Laravel (PHP). Entry points: routes/web.php → controller methods; Steps draws handlers and effects (Eloquent, responses) but PHP has no WHEN / arguments / trigger rules yet — expect boxes without conditions.
  38. sveltekit-realworld|https://github.com/sveltejs/realworld.git|SvelteKit. Screens: #/screens — 10 pages wired by <a href>, goto() and redirect(status, path); /settings and /editor guard themselves in their +page.server.js loaders, drawn WHEN !locals.user. Entry points: file routes with load() edges; Steps on a load or an action.
  39. vue-realworld|https://github.com/gothinkster/vue-realworld-example-app.git|Vue Router. Screens: #/screens — 10 routes read from src/router/index.js, wired by router.push({ name }) and <router-link :to>, which navigate by route NAME rather than by path. Steps on a handler: template @click bindings, Pinia/Vuex channels.
  40. icecubes|https://github.com/Dimillian/IceCubesApp.git|SwiftUI (Swift). Steps on a view model method: Swift guards, network / storage effects; SwiftUI navigation is not a Screens picture yet.
  41. nowinandroid|https://github.com/android/nowinandroid.git|Jetpack Compose (Kotlin). Steps on a ViewModel method: Kotlin guards, DataStore / network effects; Compose navigation is not a Screens picture yet.
  42. '
  43. usage() {
  44. echo "usage: scripts/try-repo.sh <preset | git-url | local-path> [port]"
  45. echo
  46. echo "presets:"
  47. echo "$PRESETS" | awk -F'|' 'NF>=3 { printf " %-24s %s\n", $1, $3 }'
  48. }
  49. [ -f "$CLI" ] || { echo "no build at $CLI — run: npm run build"; exit 1; }
  50. [ $# -ge 1 ] || { usage; exit 0; }
  51. TARGET="$1"
  52. PORT="${2:-}"
  53. HINT=""
  54. if [ -d "$TARGET" ]; then
  55. REPO="$(cd "$TARGET" && pwd)"
  56. NAME="$(basename "$REPO")"
  57. else
  58. LINE="$(echo "$PRESETS" | awk -F'|' -v n="$TARGET" '$1==n { print; exit }')"
  59. if [ -n "$LINE" ]; then
  60. NAME="$TARGET"
  61. URL="$(echo "$LINE" | cut -d'|' -f2)"
  62. HINT="$(echo "$LINE" | cut -d'|' -f3-)"
  63. else
  64. URL="$TARGET"
  65. NAME="$(basename "${URL%.git}")"
  66. fi
  67. REPO="$DIR/$NAME"
  68. if [ ! -d "$REPO/.git" ]; then
  69. mkdir -p "$DIR"
  70. echo "cloning $URL → $REPO"
  71. git clone -q --depth 1 "$URL" "$REPO"
  72. fi
  73. fi
  74. cd "$REPO"
  75. if [ ! -d .codegraph ]; then
  76. echo "indexing $REPO (first time)"
  77. node "$CLI" init . 2>&1 | tail -2
  78. elif [ "${CODEGRAPH_TRY_REINDEX:-1}" != "0" ]; then
  79. echo "re-indexing $REPO with the current build"
  80. node "$CLI" index . 2>&1 | tail -2
  81. fi
  82. # What the index holds for the pictures: routes, cross-tier edges, navigations.
  83. node --disable-warning=ExperimentalWarning - <<'JS'
  84. const { DatabaseSync } = require('node:sqlite');
  85. const db = new DatabaseSync('.codegraph/codegraph.db', { readOnly: true });
  86. const routes = db.prepare("select name from nodes where kind='route' order by name").all().map((r) => r.name);
  87. const verbs = routes.filter((n) => /^[A-Z]+ /.test(n));
  88. const pages = routes.filter((n) => !/^[A-Z]+ /.test(n));
  89. console.log(`routes: ${routes.length} (${verbs.length} endpoints, ${pages.length} pages)`);
  90. if (routes.length) console.log(' ' + routes.slice(0, 24).join(' | ') + (routes.length > 24 ? ' | …' : ''));
  91. const navs = db.prepare("select count(*) as n from edges where kind='navigates'").get().n;
  92. const synth = db.prepare("select json_extract(metadata, '$.synthesizedBy') as by, count(*) as n from edges where provenance='heuristic' group by 1 order by 2 desc").all();
  93. console.log(`navigations: ${navs}${navs ? ' — the Screens tab is on' : ' — no screen navigation: the Screens tab stays hidden, Entry points is the list'}`);
  94. console.log('synthesized edges: ' + (synth.map((s) => `${s.by}=${s.n}`).join(', ') || 'none'));
  95. const tier = db.prepare("select s.name as s, t.name as t, e.metadata as m from edges e join nodes s on s.id=e.source join nodes t on t.id=e.target where json_extract(e.metadata, '$.synthesizedBy') in ('http-client', 'queue-job', 'event-bus') limit 12").all();
  96. for (const r of tier) { const m = JSON.parse(r.m); console.log(` ${m.synthesizedBy}: ${r.s} → ${r.t}${m.href ? ' (' + m.method + ' ' + m.href + ')' : m.event ? ' (' + m.event + ')' : ''}`); }
  97. JS
  98. echo
  99. [ -n "$HINT" ] && { echo "look at: $HINT"; echo; }
  100. ARGS=(ui)
  101. [ -n "$PORT" ] && ARGS+=(--port "$PORT")
  102. [ "${CODEGRAPH_TRY_NO_OPEN:-0}" = "1" ] && ARGS+=(--no-open)
  103. echo "tabs: #/entry (Entry points) #/screens #/steps (chooser) #/steps?symbol=<route or function name>&through=1"
  104. exec node "$CLI" "${ARGS[@]}" .