github-mobile-reader 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # 📖 github-mobile-reader
2
2
 
3
- > **Stop squinting at code on your phone.**
4
3
  > `github-mobile-reader` transforms raw git diffs into clean, vertically-scrollable Markdown — no more pinch-zooming or swiping left and right to read a single line.
5
4
 
6
5
  [![npm version](https://img.shields.io/npm/v/github-mobile-reader.svg)](https://www.npmjs.com/package/github-mobile-reader)
@@ -15,15 +14,17 @@ GitHub's mobile web view renders code in a fixed-width monospace block. Long lin
15
14
 
16
15
  ## The Solution
17
16
 
18
- `github-mobile-reader` parses a git diff and produces a **Logical Flow** — a compact tree that shows *what the code does*, not just what characters changed. The result is a Markdown document that reads top-to-bottom on any screen width.
17
+ `github-mobile-reader` parses a git diff and produces a **Logical Flow** — a compact tree that shows _what the code does_, not just what characters changed. The result is a Markdown document that reads top-to-bottom on any screen width.
19
18
 
20
19
  **Before** (raw diff, mobile web):
20
+
21
21
  ```
22
22
  ← swipe → swipe → swipe →
23
23
  + const result = data.map(item => item.value).filter(v => v > 10).reduce((a,b) => a+b, 0)
24
24
  ```
25
25
 
26
26
  **After** (Reader Markdown):
27
+
27
28
  ```
28
29
  data
29
30
  └─ map(item → value)
@@ -37,9 +38,13 @@ data
37
38
 
38
39
  - **Zero-dependency core** — the parser runs anywhere Node.js ≥ 18 is available
39
40
  - **Dual output format** — CJS (`require`) and ESM (`import`) with full TypeScript types
41
+ - **CLI** — `npx github-mobile-reader --repo owner/repo --pr 42` fetches and converts any PR instantly
40
42
  - **GitHub Action** — drop one YAML block into any repo and get auto-generated Reader docs on every PR
41
- - **Tracks both sides of a diff** shows added *and* removed code in separate sections
43
+ - **File-by-file output** each changed JS/TS file gets its own independent section in the output
44
+ - **JSX/Tailwind aware** — `.jsx`/`.tsx` files get a component tree (`🎨 JSX Structure`) and a Tailwind class diff (`💅 Style Changes`) instead of one unreadable blob
45
+ - **Tracks both sides of a diff** — shows added _and_ removed code in separate sections
42
46
  - **Conservative by design** — when a pattern is ambiguous, the library shows less rather than showing something wrong
47
+ - **Secure by default** — token is read from `$GITHUB_TOKEN` only; no flag that leaks to shell history or `ps`
43
48
 
44
49
  ---
45
50
 
@@ -47,13 +52,14 @@ data
47
52
 
48
53
  1. [Quick Start](#quick-start)
49
54
  2. [Language Support](#language-support)
50
- 3. [GitHub Action (recommended)](#github-action-recommended)
51
- 4. [npm Library Usage](#npm-library-usage)
52
- 5. [Output Format](#output-format)
53
- 6. [API Reference](#api-reference)
54
- 7. [How the Parser Works](#how-the-parser-works)
55
- 8. [Contributing](#contributing)
56
- 9. [License](#license)
55
+ 3. [CLI Usage](#cli-usage)
56
+ 4. [GitHub Action (recommended)](#github-action-recommended)
57
+ 5. [npm Library Usage](#npm-library-usage)
58
+ 6. [Output Format](#output-format)
59
+ 7. [API Reference](#api-reference)
60
+ 8. [How the Parser Works](#how-the-parser-works)
61
+ 9. [Contributing](#contributing)
62
+ 10. [License](#license)
57
63
 
58
64
  ---
59
65
 
@@ -63,17 +69,17 @@ The parser is built on regex-based pattern matching, so it can technically recei
63
69
 
64
70
  ### Current support (v0.1)
65
71
 
66
- | Language | Extensions | Flow Quality | Notes |
67
- |----------|-----------|:------------:|-------|
68
- | **JavaScript** | `.js` `.mjs` `.cjs` | ✅ Full | Baseline target language |
69
- | **TypeScript** | `.ts` | ✅ Full | JS superset — all patterns apply |
70
- | **React JSX** | `.jsx` | ✅ Full | Same syntax as JS |
71
- | **React TSX** | `.tsx` | ✅ Full | Same syntax as TS |
72
- | **Next.js** | `.js` `.ts` `.jsx` `.tsx` | ✅ Full | Framework on top of JS/TS |
73
- | **Java** | `.java` | ⚠️ Partial (~55%) | `if/for/while` and dot-chaining work; function declarations missed (no `const/let/var`) |
74
- | **C#** | `.cs` | ⚠️ Partial (~35%) | LINQ chaining (`.Where().Select()`) works; `using`/`namespace`/`class` not detected |
75
- | **C** | `.c` `.h` | ❌ Minimal (~15%) | No matching keywords; pointer syntax (`->`, `*`) not understood |
76
- | **Python, Go, Rust, etc.** | — | 🔜 Planned | See roadmap below |
72
+ | Language | Extensions | Flow Quality | Notes |
73
+ | -------------------------- | ------------------------- | :---------------: | --------------------------------------------------------------------------------------- |
74
+ | **JavaScript** | `.js` `.mjs` `.cjs` | ✅ Full | Baseline target language |
75
+ | **TypeScript** | `.ts` | ✅ Full | JS superset — all patterns apply |
76
+ | **React JSX** | `.jsx` | ✅ Full | Same syntax as JS |
77
+ | **React TSX** | `.tsx` | ✅ Full | Same syntax as TS |
78
+ | **Next.js** | `.js` `.ts` `.jsx` `.tsx` | ✅ Full | Framework on top of JS/TS |
79
+ | **Java** | `.java` | ⚠️ Partial (~55%) | `if/for/while` and dot-chaining work; function declarations missed (no `const/let/var`) |
80
+ | **C#** | `.cs` | ⚠️ Partial (~35%) | LINQ chaining (`.Where().Select()`) works; `using`/`namespace`/`class` not detected |
81
+ | **C** | `.c` `.h` | ❌ Minimal (~15%) | No matching keywords; pointer syntax (`->`, `*`) not understood |
82
+ | **Python, Go, Rust, etc.** | — | 🔜 Planned | See roadmap below |
77
83
 
78
84
  > **Note:** Java, C#, and C files are not processed by the GitHub Action by default.
79
85
  > The Action only scans `.js .jsx .ts .tsx .mjs .cjs` files ([`src/action.ts` line 66](src/action.ts)).
@@ -86,8 +92,8 @@ All four share the same underlying syntax. The parser recognises:
86
92
  - **Method chaining** — line starting with `.` after a line ending with `)` or `}`
87
93
  ```ts
88
94
  data
89
- .filter(item => item.active) // detected as P1 chain
90
- .map(item => item.value) // detected as P1 chain
95
+ .filter((item) => item.active) // detected as P1 chain
96
+ .map((item) => item.value); // detected as P1 chain
91
97
  ```
92
98
  - **Function declarations** — `const`, `let`, `var`, `function`, `async`
93
99
  - **Conditionals** — `if / else / switch`
@@ -98,12 +104,12 @@ All four share the same underlying syntax. The parser recognises:
98
104
 
99
105
  These languages use different conventions for the patterns above:
100
106
 
101
- | Concept | JS/TS (✅ detected) | Java / C# / C (❌ missed) |
102
- |---------|---------------------|--------------------------|
103
- | Variable declaration | `const x = …` | `int x = …` / `String x = …` |
104
- | Arrow callbacks | `x => x.value` | Lambdas differ per language |
105
- | Noise imports | `import` / `export` | `using` / `#include` / `package` |
106
- | Async functions | `async function foo()` | `async Task<T> Foo()` |
107
+ | Concept | JS/TS (✅ detected) | Java / C# / C (❌ missed) |
108
+ | -------------------- | ---------------------- | -------------------------------- |
109
+ | Variable declaration | `const x = …` | `int x = …` / `String x = …` |
110
+ | Arrow callbacks | `x => x.value` | Lambdas differ per language |
111
+ | Noise imports | `import` / `export` | `using` / `#include` / `package` |
112
+ | Async functions | `async function foo()` | `async Task<T> Foo()` |
107
113
 
108
114
  ### Roadmap — Language Adapter system (v0.2)
109
115
 
@@ -118,6 +124,7 @@ src/languages/
118
124
  ```
119
125
 
120
126
  Each adapter will declare:
127
+
121
128
  - Supported file extensions
122
129
  - Function-declaration detection pattern
123
130
  - Keywords to ignore (noise list)
@@ -127,6 +134,67 @@ If you'd like to contribute an adapter for your language, see [Contributing](#co
127
134
 
128
135
  ---
129
136
 
137
+ ## CLI Usage
138
+
139
+ Run `github-mobile-reader` directly from your terminal — no setup, no config file. It fetches a PR diff from GitHub, converts it to mobile-friendly Markdown, and saves one file per PR to `./reader-output/`.
140
+
141
+ ### Authentication
142
+
143
+ Set your GitHub token as an environment variable **before** running the CLI:
144
+
145
+ ```bash
146
+ export GITHUB_TOKEN=ghp_xxxx
147
+ npx github-mobile-reader --repo owner/repo --pr 42
148
+ ```
149
+
150
+ > **Security note:** The CLI does not accept a `--token` flag. Passing secrets as command-line arguments exposes them in shell history and `ps` output. Always use the environment variable.
151
+
152
+ ### Single PR
153
+
154
+ ```bash
155
+ npx github-mobile-reader --repo owner/repo --pr 42
156
+ ```
157
+
158
+ ### All recent PRs
159
+
160
+ ```bash
161
+ npx github-mobile-reader --repo owner/repo --all
162
+ ```
163
+
164
+ ### Options
165
+
166
+ | Flag | Default | Description |
167
+ | --------- | ----------------- | ------------------------------------------------- |
168
+ | `--repo` | *(required)* | Repository in `owner/repo` format |
169
+ | `--pr` | — | Process a single PR by number |
170
+ | `--all` | — | Process all recent PRs (use with `--limit`) |
171
+ | `--out` | `./reader-output` | Output directory — relative paths only, no `..` |
172
+ | `--limit` | `10` | Max number of PRs to fetch when using `--all` |
173
+
174
+ Token: read from `$GITHUB_TOKEN` environment variable (60 req/hr unauthenticated, 5 000 req/hr authenticated).
175
+
176
+ ### Output
177
+
178
+ Each PR produces one file: `reader-output/pr-<number>.md`.
179
+
180
+ Inside that file, every changed JS/TS file gets its own section. JSX/TSX files get two extra sections:
181
+
182
+ ```
183
+ # 📖 PR #42 — My Feature
184
+
185
+ ## 📄 `src/App.tsx`
186
+
187
+ ### 🧠 Logical Flow ← JS logic tree
188
+ ### 🎨 JSX Structure ← component hierarchy (JSX/TSX only)
189
+ ### 💅 Style Changes ← added/removed Tailwind classes (JSX/TSX only)
190
+ ### ✅ Added Code
191
+ ### ❌ Removed Code
192
+ ```
193
+
194
+ > **Note:** `reader-output/` is gitignored by default — the generated files are local only and not committed to your repository.
195
+
196
+ ---
197
+
130
198
  ## Quick Start
131
199
 
132
200
  ```bash
@@ -134,13 +202,13 @@ npm install github-mobile-reader
134
202
  ```
135
203
 
136
204
  ```ts
137
- import { generateReaderMarkdown } from 'github-mobile-reader'
138
- import { execSync } from 'child_process'
205
+ import { generateReaderMarkdown } from "github-mobile-reader";
206
+ import { execSync } from "child_process";
139
207
 
140
- const diff = execSync('git diff HEAD~1', { encoding: 'utf8' })
141
- const markdown = generateReaderMarkdown(diff, { file: 'src/utils.ts' })
208
+ const diff = execSync("git diff HEAD~1", { encoding: "utf8" });
209
+ const markdown = generateReaderMarkdown(diff, { file: "src/utils.ts" });
142
210
 
143
- console.log(markdown)
211
+ console.log(markdown);
144
212
  ```
145
213
 
146
214
  ---
@@ -165,8 +233,8 @@ on:
165
233
  types: [opened, synchronize, reopened]
166
234
 
167
235
  permissions:
168
- contents: write # commit the generated .md file
169
- pull-requests: write # post the PR comment
236
+ contents: write # commit the generated .md file
237
+ pull-requests: write # post the PR comment
170
238
 
171
239
  jobs:
172
240
  generate-reader:
@@ -177,7 +245,7 @@ jobs:
177
245
  - name: Checkout
178
246
  uses: actions/checkout@v4
179
247
  with:
180
- fetch-depth: 0 # full history required for git diff
248
+ fetch-depth: 0 # full history required for git diff
181
249
 
182
250
  - name: Generate Reader Markdown
183
251
  uses: 3rdflr/github-mobile-reader@v1
@@ -210,11 +278,11 @@ That's it. Every subsequent PR will automatically get:
210
278
 
211
279
  ### Action Inputs
212
280
 
213
- | Input | Required | Default | Description |
214
- |-------|----------|---------|-------------|
215
- | `github_token` | ✅ | — | Use `${{ secrets.GITHUB_TOKEN }}` |
216
- | `base_branch` | ❌ | `main` | The branch the PR is merging into |
217
- | `output_dir` | ❌ | `docs/reader` | Directory for generated `.md` files |
281
+ | Input | Required | Default | Description |
282
+ | -------------- | -------- | ------------- | ----------------------------------- |
283
+ | `github_token` | ✅ | — | Use `${{ secrets.GITHUB_TOKEN }}` |
284
+ | `base_branch` | ❌ | `main` | The branch the PR is merging into |
285
+ | `output_dir` | ❌ | `docs/reader` | Directory for generated `.md` files |
218
286
 
219
287
  ---
220
288
 
@@ -238,35 +306,38 @@ yarn add github-mobile-reader
238
306
  ### CommonJS
239
307
 
240
308
  ```js
241
- const { generateReaderMarkdown } = require('github-mobile-reader')
309
+ const { generateReaderMarkdown } = require("github-mobile-reader");
242
310
  ```
243
311
 
244
312
  ### ESM / TypeScript
245
313
 
246
314
  ```ts
247
- import { generateReaderMarkdown, parseDiffToLogicalFlow } from 'github-mobile-reader'
315
+ import {
316
+ generateReaderMarkdown,
317
+ parseDiffToLogicalFlow,
318
+ } from "github-mobile-reader";
248
319
  ```
249
320
 
250
321
  ### Basic Example
251
322
 
252
323
  ```ts
253
- import { generateReaderMarkdown } from 'github-mobile-reader'
254
- import { execSync } from 'child_process'
255
- import { writeFileSync } from 'fs'
324
+ import { generateReaderMarkdown } from "github-mobile-reader";
325
+ import { execSync } from "child_process";
326
+ import { writeFileSync } from "fs";
256
327
 
257
328
  // Get the diff for the last commit
258
- const diff = execSync('git diff HEAD~1 HEAD', { encoding: 'utf8' })
329
+ const diff = execSync("git diff HEAD~1 HEAD", { encoding: "utf8" });
259
330
 
260
331
  // Generate Reader Markdown with metadata
261
332
  const markdown = generateReaderMarkdown(diff, {
262
- pr: '42',
263
- commit: 'a1b2c3d',
264
- file: 'src/api/users.ts',
265
- repo: 'my-org/my-repo',
266
- })
333
+ pr: "42",
334
+ commit: "a1b2c3d",
335
+ file: "src/api/users.ts",
336
+ repo: "my-org/my-repo",
337
+ });
267
338
 
268
339
  // Write to a file or post to Slack / Discord / GitHub
269
- writeFileSync('reader.md', markdown, 'utf8')
340
+ writeFileSync("reader.md", markdown, "utf8");
270
341
  ```
271
342
 
272
343
  ### Low-level API Example
@@ -274,16 +345,16 @@ writeFileSync('reader.md', markdown, 'utf8')
274
345
  If you only need the parsed tree (e.g. to build your own renderer):
275
346
 
276
347
  ```ts
277
- import { parseDiffToLogicalFlow, renderFlowTree } from 'github-mobile-reader'
348
+ import { parseDiffToLogicalFlow, renderFlowTree } from "github-mobile-reader";
278
349
 
279
- const { root, rawCode, removedCode } = parseDiffToLogicalFlow(diff)
350
+ const { root, rawCode, removedCode } = parseDiffToLogicalFlow(diff);
280
351
 
281
352
  // root → FlowNode[] (the logical tree)
282
353
  // rawCode → string (added lines, joined)
283
354
  // removedCode → string (removed lines, joined)
284
355
 
285
- const treeLines = renderFlowTree(root)
286
- console.log(treeLines.join('\n'))
356
+ const treeLines = renderFlowTree(root);
357
+ console.log(treeLines.join("\n"));
287
358
  ```
288
359
 
289
360
  ---
@@ -304,13 +375,14 @@ A generated Reader Markdown document has four sections:
304
375
  ---
305
376
 
306
377
  ## 🧠 Logical Flow
307
-
308
378
  ```
379
+
309
380
  getData()
310
- └─ filter(callback)
311
- └─ map(item → value)
312
- └─ reduce(callback)
313
- ```
381
+ └─ filter(callback)
382
+ └─ map(item → value)
383
+ └─ reduce(callback)
384
+
385
+ ````
314
386
 
315
387
  ## ✅ Added Code
316
388
 
@@ -319,17 +391,19 @@ const result = getData()
319
391
  .filter(item => item.active)
320
392
  .map(item => item.value)
321
393
  .reduce((a, b) => a + b, 0)
322
- ```
394
+ ````
323
395
 
324
396
  ## ❌ Removed Code
325
397
 
326
398
  ```typescript
327
- const result = getData().map(item => item.value)
399
+ const result = getData().map((item) => item.value);
328
400
  ```
329
401
 
330
402
  ---
403
+
331
404
  🛠 Auto-generated by github-mobile-reader. Do not edit manually.
332
- ```
405
+
406
+ ````
333
407
 
334
408
  ---
335
409
 
@@ -363,7 +437,7 @@ interface ParseResult {
363
437
  rawCode: string // added lines joined with \n
364
438
  removedCode: string // removed lines joined with \n
365
439
  }
366
- ```
440
+ ````
367
441
 
368
442
  ---
369
443
 
@@ -372,7 +446,7 @@ interface ParseResult {
372
446
  Converts a `FlowNode[]` tree into an array of Markdown-safe text lines.
373
447
 
374
448
  ```ts
375
- const lines = renderFlowTree(root)
449
+ const lines = renderFlowTree(root);
376
450
  // [ 'getData()', ' └─ filter(callback)', ' └─ map(item → value)' ]
377
451
  ```
378
452
 
@@ -382,11 +456,11 @@ const lines = renderFlowTree(root)
382
456
 
383
457
  ```ts
384
458
  interface FlowNode {
385
- type: 'root' | 'chain' | 'condition' | 'loop' | 'function' | 'call'
386
- name: string
387
- children: FlowNode[]
388
- depth: number
389
- priority: Priority
459
+ type: "root" | "chain" | "condition" | "loop" | "function" | "call";
460
+ name: string;
461
+ children: FlowNode[];
462
+ depth: number;
463
+ priority: Priority;
390
464
  }
391
465
  ```
392
466
 
@@ -394,13 +468,13 @@ interface FlowNode {
394
468
 
395
469
  ### `Priority` (enum)
396
470
 
397
- | Value | Meaning |
398
- |-------|---------|
399
- | `CHAINING = 1` | Method chains (`.map()`, `.filter()`, …) — highest priority |
400
- | `CONDITIONAL = 2` | `if` / `else` / `switch` blocks |
401
- | `LOOP = 3` | `for` / `while` loops |
402
- | `FUNCTION = 4` | Function declarations |
403
- | `OTHER = 5` | Everything else |
471
+ | Value | Meaning |
472
+ | ----------------- | ----------------------------------------------------------- |
473
+ | `CHAINING = 1` | Method chains (`.map()`, `.filter()`, …) — highest priority |
474
+ | `CONDITIONAL = 2` | `if` / `else` / `switch` blocks |
475
+ | `LOOP = 3` | `for` / `while` loops |
476
+ | `FUNCTION = 4` | Function declarations |
477
+ | `OTHER = 5` | Everything else |
404
478
 
405
479
  ---
406
480
 
@@ -474,8 +548,10 @@ github-mobile-reader/
474
548
  ├── src/
475
549
  │ ├── parser.ts ← core diff → logical flow parser
476
550
  │ ├── index.ts ← public npm API surface
477
- └── action.ts ← GitHub Action entry point
551
+ ├── action.ts ← GitHub Action entry point
552
+ │ └── cli.ts ← CLI entry point (npx github-mobile-reader)
478
553
  ├── dist/ ← compiled output (auto-generated, do not edit)
554
+ ├── reader-output/ ← CLI output directory (gitignored)
479
555
  ├── .github/
480
556
  │ └── workflows/
481
557
  │ └── mobile-reader.yml ← example workflow for consumers
@@ -499,6 +575,3 @@ The parser currently relies on JS/TS syntax heuristics (dot-chaining, `const`/`l
499
575
  MIT © [3rdflr](https://github.com/3rdflr)
500
576
 
501
577
  ---
502
-
503
- > **"The era of per-device number crunching is over.
504
- > One logic. Every screen."**