github-mobile-reader 0.1.3 → 0.1.5
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.ko.md +73 -64
- package/README.md +72 -65
- package/dist/action.js +27 -11
- package/dist/cli.js +27 -11
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +27 -11
- package/dist/index.mjs +27 -11
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -44,7 +44,7 @@ data
|
|
|
44
44
|
- **GitHub Action** — 레포에 YAML 파일 하나만 추가하면 PR마다 Reader 문서가 자동 생성됩니다
|
|
45
45
|
- **파일별 분리 출력** — 변경된 JS/TS 파일마다 독립적인 섹션으로 출력
|
|
46
46
|
- **JSX/Tailwind 인식** — `.jsx`/`.tsx` 파일은 컴포넌트 트리(`🎨 JSX Structure`)와 Tailwind 클래스 diff(`💅 Style Changes`)를 별도 섹션으로 분리 출력
|
|
47
|
-
-
|
|
47
|
+
- **심볼 단위 요약** — 전체 코드 블록을 덤프하는 대신, 어떤 함수/컴포넌트가 추가·삭제·수정됐는지 목록으로 표시
|
|
48
48
|
- **보수적 설계** — 패턴이 애매할 때는 잘못된 정보를 보여주는 대신 덜 보여줍니다
|
|
49
49
|
- **보안 기본값** — 토큰은 `$GITHUB_TOKEN` 환경변수로만 읽음 — 셸 히스토리나 `ps` 목록에 노출되는 `--token` 플래그 없음
|
|
50
50
|
|
|
@@ -108,18 +108,20 @@ npx github-mobile-reader --repo owner/repo --all
|
|
|
108
108
|
|
|
109
109
|
PR마다 `reader-output/pr-<번호>.md` 파일 하나가 생성됩니다.
|
|
110
110
|
|
|
111
|
-
JSX/TSX 파일은 추가 섹션이 생성됩니다:
|
|
111
|
+
변경 요약이 없는 파일은 자동으로 생략됩니다. JSX/TSX 파일은 추가 섹션이 생성됩니다:
|
|
112
112
|
|
|
113
113
|
```
|
|
114
114
|
# 📖 PR #42 — My Feature
|
|
115
115
|
|
|
116
116
|
## 📄 `src/App.tsx`
|
|
117
117
|
|
|
118
|
-
###
|
|
118
|
+
### 변경된 함수 / 컴포넌트
|
|
119
|
+
- ✅ `parseArgs()` — added
|
|
120
|
+
- ✏️ `processPR()` — modified
|
|
121
|
+
- ❌ `oldHelper()` — removed
|
|
122
|
+
|
|
119
123
|
### 🎨 JSX Structure ← 컴포넌트 계층 구조 (JSX/TSX 전용)
|
|
120
124
|
### 💅 Style Changes ← 추가/제거된 Tailwind 클래스 (JSX/TSX 전용)
|
|
121
|
-
### ✅ Added Code
|
|
122
|
-
### ❌ Removed Code
|
|
123
125
|
```
|
|
124
126
|
|
|
125
127
|
> **참고:** `reader-output/`는 기본적으로 `.gitignore`에 포함되어 있습니다 — 생성된 파일은 로컬에만 저장되며 레포지토리에 커밋되지 않습니다.
|
|
@@ -220,8 +222,9 @@ src/languages/
|
|
|
220
222
|
이 라이브러리를 사용하는 가장 쉬운 방법입니다. 매 PR마다 자동으로:
|
|
221
223
|
|
|
222
224
|
1. 변경된 `.js` / `.jsx` / `.ts` / `.tsx` / `.mjs` / `.cjs` 파일의 diff를 파싱
|
|
223
|
-
2.
|
|
224
|
-
|
|
225
|
+
2. Reader Markdown 요약을 **PR 코멘트**로 게시 (파일 커밋 불필요)
|
|
226
|
+
|
|
227
|
+
> **브랜치 보호 규칙과 호환** — 워크플로우는 `pull-requests: write` 권한만 필요합니다. 커밋을 push하지 않으므로 엄격한 브랜치 보호 규칙이 설정된 레포에서도 동작합니다.
|
|
225
228
|
|
|
226
229
|
### Step 1 — 워크플로우 파일 추가
|
|
227
230
|
|
|
@@ -235,7 +238,6 @@ on:
|
|
|
235
238
|
types: [opened, synchronize, reopened]
|
|
236
239
|
|
|
237
240
|
permissions:
|
|
238
|
-
contents: write # .md 파일 커밋
|
|
239
241
|
pull-requests: write # PR 코멘트 작성
|
|
240
242
|
|
|
241
243
|
jobs:
|
|
@@ -247,44 +249,45 @@ jobs:
|
|
|
247
249
|
- name: Checkout
|
|
248
250
|
uses: actions/checkout@v4
|
|
249
251
|
with:
|
|
250
|
-
fetch-depth: 0
|
|
252
|
+
fetch-depth: 0
|
|
251
253
|
|
|
252
|
-
- name:
|
|
253
|
-
uses:
|
|
254
|
+
- name: Setup Node
|
|
255
|
+
uses: actions/setup-node@v4
|
|
254
256
|
with:
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
257
|
+
node-version: '20'
|
|
258
|
+
|
|
259
|
+
- name: Generate Reader Markdown
|
|
260
|
+
run: npx github-mobile-reader@latest --repo ${{ github.repository }} --pr ${{ github.event.pull_request.number }} --out ./reader-output
|
|
258
261
|
env:
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
- name:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
262
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
263
|
+
|
|
264
|
+
- name: Post PR Comment
|
|
265
|
+
uses: actions/github-script@v7
|
|
266
|
+
with:
|
|
267
|
+
script: |
|
|
268
|
+
const fs = require('fs');
|
|
269
|
+
const path = './reader-output/pr-${{ github.event.pull_request.number }}.md';
|
|
270
|
+
if (!fs.existsSync(path)) { console.log('생성된 파일 없음.'); return; }
|
|
271
|
+
const body = fs.readFileSync(path, 'utf8');
|
|
272
|
+
const comments = await github.rest.issues.listComments({
|
|
273
|
+
owner: context.repo.owner, repo: context.repo.repo,
|
|
274
|
+
issue_number: ${{ github.event.pull_request.number }},
|
|
275
|
+
});
|
|
276
|
+
const prev = comments.data.find(c =>
|
|
277
|
+
c.user.login === 'github-actions[bot]' && c.body.startsWith('# 📖 PR #')
|
|
278
|
+
);
|
|
279
|
+
if (prev) await github.rest.issues.deleteComment({
|
|
280
|
+
owner: context.repo.owner, repo: context.repo.repo, comment_id: prev.id,
|
|
281
|
+
});
|
|
282
|
+
await github.rest.issues.createComment({
|
|
283
|
+
owner: context.repo.owner, repo: context.repo.repo,
|
|
284
|
+
issue_number: ${{ github.event.pull_request.number }}, body,
|
|
285
|
+
});
|
|
272
286
|
```
|
|
273
287
|
|
|
274
288
|
### Step 2 — PR 열기
|
|
275
289
|
|
|
276
|
-
이게 전부입니다. 이후 모든 PR에
|
|
277
|
-
|
|
278
|
-
- `docs/reader/pr-<번호>.md` 파일 생성
|
|
279
|
-
- 생성된 파일 링크가 담긴 PR 코멘트 자동 게시
|
|
280
|
-
|
|
281
|
-
### Action 입력값
|
|
282
|
-
|
|
283
|
-
| 입력값 | 필수 | 기본값 | 설명 |
|
|
284
|
-
| -------------- | ---- | ------------- | ---------------------------------- |
|
|
285
|
-
| `github_token` | ✅ | — | `${{ secrets.GITHUB_TOKEN }}` 사용 |
|
|
286
|
-
| `base_branch` | ❌ | `main` | PR이 머지되는 대상 브랜치 |
|
|
287
|
-
| `output_dir` | ❌ | `docs/reader` | 생성된 `.md` 파일 저장 경로 |
|
|
290
|
+
이게 전부입니다. 이후 모든 PR에 `github-actions[bot]`이 Reader Markdown 코멘트를 자동으로 달아줍니다. 새 커밋을 push할 때마다 코멘트가 업데이트됩니다 (중복 게시 없음).
|
|
288
291
|
|
|
289
292
|
---
|
|
290
293
|
|
|
@@ -363,47 +366,53 @@ console.log(treeLines.join("\n"));
|
|
|
363
366
|
|
|
364
367
|
## 출력 형식
|
|
365
368
|
|
|
366
|
-
생성된 Reader Markdown
|
|
369
|
+
생성된 Reader Markdown 코멘트는 다음 구조로 작성됩니다:
|
|
367
370
|
|
|
368
|
-
|
|
369
|
-
# 📖
|
|
371
|
+
```markdown
|
|
372
|
+
# 📖 PR #42 — My Feature
|
|
370
373
|
|
|
371
|
-
> Generated by **github-mobile-reader**
|
|
372
374
|
> Repository: my-org/my-repo
|
|
373
|
-
> Pull Request: #42
|
|
374
375
|
> Commit: `a1b2c3d`
|
|
375
|
-
>
|
|
376
|
+
> 변경된 JS/TS 파일: 2개
|
|
376
377
|
|
|
377
378
|
---
|
|
378
379
|
|
|
379
|
-
##
|
|
380
|
+
## 📄 `src/api/users.ts`
|
|
380
381
|
|
|
381
|
-
|
|
382
|
-
getData()
|
|
383
|
-
└─ filter(callback)
|
|
384
|
-
└─ map(item → value)
|
|
385
|
-
└─ reduce(callback)
|
|
386
|
-
```
|
|
382
|
+
### 변경된 함수 / 컴포넌트
|
|
387
383
|
|
|
388
|
-
|
|
384
|
+
- ✅ `getUser()` — added
|
|
385
|
+
- ✏️ `updateUser()` — modified
|
|
386
|
+
- ❌ `legacyFetch()` — removed
|
|
389
387
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
```
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## 📄 `src/components/UserCard.tsx`
|
|
391
|
+
|
|
392
|
+
### 변경된 함수 / 컴포넌트
|
|
396
393
|
|
|
397
|
-
|
|
394
|
+
- ✏️ `UserCard()` — modified
|
|
398
395
|
|
|
399
|
-
|
|
400
|
-
|
|
396
|
+
### 🎨 JSX Structure
|
|
397
|
+
|
|
398
|
+
```
|
|
399
|
+
div
|
|
400
|
+
header
|
|
401
|
+
section
|
|
402
|
+
UserAvatar
|
|
403
|
+
UserName
|
|
401
404
|
```
|
|
402
405
|
|
|
406
|
+
### 💅 Style Changes
|
|
407
|
+
|
|
408
|
+
**div**
|
|
409
|
+
+ dark:bg-gray-900 rounded-xl
|
|
410
|
+
- bg-white
|
|
411
|
+
|
|
403
412
|
---
|
|
404
413
|
|
|
405
|
-
🛠 Auto-generated by github-mobile-reader.
|
|
406
|
-
|
|
414
|
+
🛠 Auto-generated by github-mobile-reader.
|
|
415
|
+
```
|
|
407
416
|
|
|
408
417
|
---
|
|
409
418
|
|
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ data
|
|
|
42
42
|
- **GitHub Action** — drop one YAML block into any repo and get auto-generated Reader docs on every PR
|
|
43
43
|
- **File-by-file output** — each changed JS/TS file gets its own independent section in the output
|
|
44
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
|
-
- **
|
|
45
|
+
- **Symbol-level summary** — instead of dumping entire code blocks, the output lists which functions/components were added, removed, or modified
|
|
46
46
|
- **Conservative by design** — when a pattern is ambiguous, the library shows less rather than showing something wrong
|
|
47
47
|
- **Secure by default** — token is read from `$GITHUB_TOKEN` only; no flag that leaks to shell history or `ps`
|
|
48
48
|
|
|
@@ -177,18 +177,20 @@ Token: read from `$GITHUB_TOKEN` environment variable (60 req/hr unauthenticated
|
|
|
177
177
|
|
|
178
178
|
Each PR produces one file: `reader-output/pr-<number>.md`.
|
|
179
179
|
|
|
180
|
-
Inside that file, every changed JS/TS file gets its own section.
|
|
180
|
+
Inside that file, every changed JS/TS file gets its own section. Files with no detectable symbol changes are automatically skipped.
|
|
181
181
|
|
|
182
182
|
```
|
|
183
183
|
# 📖 PR #42 — My Feature
|
|
184
184
|
|
|
185
185
|
## 📄 `src/App.tsx`
|
|
186
186
|
|
|
187
|
-
###
|
|
187
|
+
### 변경된 함수 / 컴포넌트
|
|
188
|
+
- ✅ `parseArgs()` — added
|
|
189
|
+
- ✏️ `processPR()` — modified
|
|
190
|
+
- ❌ `oldHelper()` — removed
|
|
191
|
+
|
|
188
192
|
### 🎨 JSX Structure ← component hierarchy (JSX/TSX only)
|
|
189
193
|
### 💅 Style Changes ← added/removed Tailwind classes (JSX/TSX only)
|
|
190
|
-
### ✅ Added Code
|
|
191
|
-
### ❌ Removed Code
|
|
192
194
|
```
|
|
193
195
|
|
|
194
196
|
> **Note:** `reader-output/` is gitignored by default — the generated files are local only and not committed to your repository.
|
|
@@ -218,8 +220,9 @@ console.log(markdown);
|
|
|
218
220
|
The easiest way to use this library is as a GitHub Action. On every pull request it will:
|
|
219
221
|
|
|
220
222
|
1. Parse the diff of all changed `.js` / `.jsx` / `.ts` / `.tsx` / `.mjs` / `.cjs` files
|
|
221
|
-
2.
|
|
222
|
-
|
|
223
|
+
2. Post a Reader Markdown summary as a **PR comment** (no file commits required)
|
|
224
|
+
|
|
225
|
+
> **Branch protection compatible** — the workflow only needs `pull-requests: write` permission. It does not push any commits, so it works even with strict branch protection rules.
|
|
223
226
|
|
|
224
227
|
### Step 1 — Add the workflow file
|
|
225
228
|
|
|
@@ -233,8 +236,7 @@ on:
|
|
|
233
236
|
types: [opened, synchronize, reopened]
|
|
234
237
|
|
|
235
238
|
permissions:
|
|
236
|
-
|
|
237
|
-
pull-requests: write # post the PR comment
|
|
239
|
+
pull-requests: write # post PR comment
|
|
238
240
|
|
|
239
241
|
jobs:
|
|
240
242
|
generate-reader:
|
|
@@ -245,44 +247,45 @@ jobs:
|
|
|
245
247
|
- name: Checkout
|
|
246
248
|
uses: actions/checkout@v4
|
|
247
249
|
with:
|
|
248
|
-
fetch-depth: 0
|
|
250
|
+
fetch-depth: 0
|
|
249
251
|
|
|
250
|
-
- name:
|
|
251
|
-
uses:
|
|
252
|
+
- name: Setup Node
|
|
253
|
+
uses: actions/setup-node@v4
|
|
252
254
|
with:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
255
|
+
node-version: '20'
|
|
256
|
+
|
|
257
|
+
- name: Generate Reader Markdown
|
|
258
|
+
run: npx github-mobile-reader@latest --repo ${{ github.repository }} --pr ${{ github.event.pull_request.number }} --out ./reader-output
|
|
256
259
|
env:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
- name:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
260
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
261
|
+
|
|
262
|
+
- name: Post PR Comment
|
|
263
|
+
uses: actions/github-script@v7
|
|
264
|
+
with:
|
|
265
|
+
script: |
|
|
266
|
+
const fs = require('fs');
|
|
267
|
+
const path = './reader-output/pr-${{ github.event.pull_request.number }}.md';
|
|
268
|
+
if (!fs.existsSync(path)) { console.log('No reader file generated.'); return; }
|
|
269
|
+
const body = fs.readFileSync(path, 'utf8');
|
|
270
|
+
const comments = await github.rest.issues.listComments({
|
|
271
|
+
owner: context.repo.owner, repo: context.repo.repo,
|
|
272
|
+
issue_number: ${{ github.event.pull_request.number }},
|
|
273
|
+
});
|
|
274
|
+
const prev = comments.data.find(c =>
|
|
275
|
+
c.user.login === 'github-actions[bot]' && c.body.startsWith('# 📖 PR #')
|
|
276
|
+
);
|
|
277
|
+
if (prev) await github.rest.issues.deleteComment({
|
|
278
|
+
owner: context.repo.owner, repo: context.repo.repo, comment_id: prev.id,
|
|
279
|
+
});
|
|
280
|
+
await github.rest.issues.createComment({
|
|
281
|
+
owner: context.repo.owner, repo: context.repo.repo,
|
|
282
|
+
issue_number: ${{ github.event.pull_request.number }}, body,
|
|
283
|
+
});
|
|
270
284
|
```
|
|
271
285
|
|
|
272
286
|
### Step 2 — Open a PR
|
|
273
287
|
|
|
274
|
-
That's it. Every subsequent PR will automatically get
|
|
275
|
-
|
|
276
|
-
- A Reader Markdown file at `docs/reader/pr-<number>.md`
|
|
277
|
-
- A comment on the PR linking to the generated file
|
|
278
|
-
|
|
279
|
-
### Action Inputs
|
|
280
|
-
|
|
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 |
|
|
288
|
+
That's it. Every subsequent PR will automatically get a Reader Markdown comment posted by `github-actions[bot]`. The comment is updated (not duplicated) on every new push.
|
|
286
289
|
|
|
287
290
|
---
|
|
288
291
|
|
|
@@ -361,49 +364,53 @@ console.log(treeLines.join("\n"));
|
|
|
361
364
|
|
|
362
365
|
## Output Format
|
|
363
366
|
|
|
364
|
-
A generated Reader Markdown
|
|
367
|
+
A generated Reader Markdown comment has the following structure:
|
|
365
368
|
|
|
366
369
|
```markdown
|
|
367
|
-
# 📖
|
|
370
|
+
# 📖 PR #42 — My Feature
|
|
368
371
|
|
|
369
|
-
> Generated by **github-mobile-reader**
|
|
370
372
|
> Repository: my-org/my-repo
|
|
371
|
-
> Pull Request: #42
|
|
372
373
|
> Commit: `a1b2c3d`
|
|
373
|
-
>
|
|
374
|
+
> 변경된 JS/TS 파일: 2개
|
|
374
375
|
|
|
375
376
|
---
|
|
376
377
|
|
|
377
|
-
##
|
|
378
|
-
```
|
|
378
|
+
## 📄 `src/api/users.ts`
|
|
379
379
|
|
|
380
|
-
|
|
381
|
-
└─ filter(callback)
|
|
382
|
-
└─ map(item → value)
|
|
383
|
-
└─ reduce(callback)
|
|
380
|
+
### 변경된 함수 / 컴포넌트
|
|
384
381
|
|
|
385
|
-
|
|
382
|
+
- ✅ `getUser()` — added
|
|
383
|
+
- ✏️ `updateUser()` — modified
|
|
384
|
+
- ❌ `legacyFetch()` — removed
|
|
386
385
|
|
|
387
|
-
|
|
386
|
+
---
|
|
388
387
|
|
|
389
|
-
|
|
390
|
-
const result = getData()
|
|
391
|
-
.filter(item => item.active)
|
|
392
|
-
.map(item => item.value)
|
|
393
|
-
.reduce((a, b) => a + b, 0)
|
|
394
|
-
````
|
|
388
|
+
## 📄 `src/components/UserCard.tsx`
|
|
395
389
|
|
|
396
|
-
|
|
390
|
+
### 변경된 함수 / 컴포넌트
|
|
397
391
|
|
|
398
|
-
|
|
399
|
-
|
|
392
|
+
- ✏️ `UserCard()` — modified
|
|
393
|
+
|
|
394
|
+
### 🎨 JSX Structure
|
|
395
|
+
|
|
396
|
+
```
|
|
397
|
+
div
|
|
398
|
+
header
|
|
399
|
+
section
|
|
400
|
+
UserAvatar
|
|
401
|
+
UserName
|
|
400
402
|
```
|
|
401
403
|
|
|
402
|
-
|
|
404
|
+
### 💅 Style Changes
|
|
403
405
|
|
|
404
|
-
|
|
406
|
+
**div**
|
|
407
|
+
+ dark:bg-gray-900 rounded-xl
|
|
408
|
+
- bg-white
|
|
405
409
|
|
|
406
|
-
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
🛠 Auto-generated by github-mobile-reader.
|
|
413
|
+
```
|
|
407
414
|
|
|
408
415
|
---
|
|
409
416
|
|
package/dist/action.js
CHANGED
|
@@ -32,7 +32,9 @@ function isJSXFile(filename) {
|
|
|
32
32
|
return /\.(jsx|tsx)$/.test(filename);
|
|
33
33
|
}
|
|
34
34
|
function hasJSXContent(lines) {
|
|
35
|
-
return lines.some(
|
|
35
|
+
return lines.some(
|
|
36
|
+
(l) => /<[A-Z][A-Za-z]*[\s/>]/.test(l) || /return\s*\(/.test(l)
|
|
37
|
+
);
|
|
36
38
|
}
|
|
37
39
|
function isClassNameOnlyLine(line) {
|
|
38
40
|
return /^className=/.test(line.trim());
|
|
@@ -40,7 +42,9 @@ function isClassNameOnlyLine(line) {
|
|
|
40
42
|
function extractClassName(line) {
|
|
41
43
|
const staticMatch = line.match(/className="([^"]*)"/);
|
|
42
44
|
if (staticMatch) return staticMatch[1];
|
|
43
|
-
const ternaryMatch = line.match(
|
|
45
|
+
const ternaryMatch = line.match(
|
|
46
|
+
/className=\{[^?]+\?\s*"([^"]*)"\s*:\s*"([^"]*)"\}/
|
|
47
|
+
);
|
|
44
48
|
if (ternaryMatch) return `${ternaryMatch[1]} ${ternaryMatch[2]}`;
|
|
45
49
|
const templateMatch = line.match(/className=\{`([^`]*)`\}/);
|
|
46
50
|
if (templateMatch) {
|
|
@@ -62,14 +66,16 @@ function parseClassNameChanges(addedLines, removedLines) {
|
|
|
62
66
|
const cls = extractClassName(line);
|
|
63
67
|
const comp = extractComponentFromLine(line);
|
|
64
68
|
if (!cls) continue;
|
|
65
|
-
if (!componentMap.has(comp))
|
|
69
|
+
if (!componentMap.has(comp))
|
|
70
|
+
componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
66
71
|
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).added.add(c));
|
|
67
72
|
}
|
|
68
73
|
for (const line of removedLines.filter((l) => /className=/.test(l))) {
|
|
69
74
|
const cls = extractClassName(line);
|
|
70
75
|
const comp = extractComponentFromLine(line);
|
|
71
76
|
if (!cls) continue;
|
|
72
|
-
if (!componentMap.has(comp))
|
|
77
|
+
if (!componentMap.has(comp))
|
|
78
|
+
componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
73
79
|
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).removed.add(c));
|
|
74
80
|
}
|
|
75
81
|
const changes = [];
|
|
@@ -87,7 +93,8 @@ function renderStyleChanges(changes) {
|
|
|
87
93
|
for (const change of changes) {
|
|
88
94
|
lines.push(`**${change.component}**`);
|
|
89
95
|
if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
|
|
90
|
-
if (change.removed.length > 0)
|
|
96
|
+
if (change.removed.length > 0)
|
|
97
|
+
lines.push(` - ${change.removed.join(" ")}`);
|
|
91
98
|
}
|
|
92
99
|
return lines;
|
|
93
100
|
}
|
|
@@ -156,8 +163,12 @@ function parseJSXToFlowTree(lines) {
|
|
|
156
163
|
}
|
|
157
164
|
function filterDiffLines(diffText) {
|
|
158
165
|
const lines = diffText.split("\n");
|
|
159
|
-
const added = lines.filter(
|
|
160
|
-
|
|
166
|
+
const added = lines.filter(
|
|
167
|
+
(l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+"
|
|
168
|
+
).map((l) => l.substring(1));
|
|
169
|
+
const removed = lines.filter(
|
|
170
|
+
(l) => l.startsWith("-") && !l.startsWith("---") && l.trim() !== "-"
|
|
171
|
+
).map((l) => l.substring(1));
|
|
161
172
|
return { added, removed };
|
|
162
173
|
}
|
|
163
174
|
function getIndentDepth(line) {
|
|
@@ -166,14 +177,14 @@ function getIndentDepth(line) {
|
|
|
166
177
|
return Math.floor(match[1].length / 2);
|
|
167
178
|
}
|
|
168
179
|
function extractChangedSymbols(addedLines, removedLines) {
|
|
169
|
-
const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s
|
|
180
|
+
const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s+)?\(?|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*[a-z]\w+\s*[<(]/;
|
|
170
181
|
const COMPONENT_RE = /^(?:export\s+)?(?:default\s+)?(?:function|const)\s+([A-Z][a-z][A-Za-z0-9]*)/;
|
|
171
182
|
const extract = (lines) => {
|
|
172
183
|
const names = /* @__PURE__ */ new Set();
|
|
173
184
|
for (const line of lines) {
|
|
174
185
|
const cm = line.match(COMPONENT_RE) || line.match(FUNC_RE);
|
|
175
186
|
if (cm) {
|
|
176
|
-
const name = cm[1] || cm[2];
|
|
187
|
+
const name = cm[1] || cm[2] || cm[3];
|
|
177
188
|
if (name) names.add(name);
|
|
178
189
|
}
|
|
179
190
|
}
|
|
@@ -185,7 +196,10 @@ function extractChangedSymbols(addedLines, removedLines) {
|
|
|
185
196
|
const seen = /* @__PURE__ */ new Set();
|
|
186
197
|
for (const name of addedNames) {
|
|
187
198
|
seen.add(name);
|
|
188
|
-
results.push({
|
|
199
|
+
results.push({
|
|
200
|
+
name,
|
|
201
|
+
status: removedNames.has(name) ? "modified" : "added"
|
|
202
|
+
});
|
|
189
203
|
}
|
|
190
204
|
for (const name of removedNames) {
|
|
191
205
|
if (!seen.has(name)) {
|
|
@@ -246,7 +260,9 @@ function generateReaderMarkdown(diffText, meta = {}) {
|
|
|
246
260
|
sections.push("");
|
|
247
261
|
}
|
|
248
262
|
sections.push("---");
|
|
249
|
-
sections.push(
|
|
263
|
+
sections.push(
|
|
264
|
+
"\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/3rdflr/github-mobile-reader). Do not edit manually."
|
|
265
|
+
);
|
|
250
266
|
return sections.join("\n");
|
|
251
267
|
}
|
|
252
268
|
|
package/dist/cli.js
CHANGED
|
@@ -32,7 +32,9 @@ function isJSXFile(filename) {
|
|
|
32
32
|
return /\.(jsx|tsx)$/.test(filename);
|
|
33
33
|
}
|
|
34
34
|
function hasJSXContent(lines) {
|
|
35
|
-
return lines.some(
|
|
35
|
+
return lines.some(
|
|
36
|
+
(l) => /<[A-Z][A-Za-z]*[\s/>]/.test(l) || /return\s*\(/.test(l)
|
|
37
|
+
);
|
|
36
38
|
}
|
|
37
39
|
function isClassNameOnlyLine(line) {
|
|
38
40
|
return /^className=/.test(line.trim());
|
|
@@ -40,7 +42,9 @@ function isClassNameOnlyLine(line) {
|
|
|
40
42
|
function extractClassName(line) {
|
|
41
43
|
const staticMatch = line.match(/className="([^"]*)"/);
|
|
42
44
|
if (staticMatch) return staticMatch[1];
|
|
43
|
-
const ternaryMatch = line.match(
|
|
45
|
+
const ternaryMatch = line.match(
|
|
46
|
+
/className=\{[^?]+\?\s*"([^"]*)"\s*:\s*"([^"]*)"\}/
|
|
47
|
+
);
|
|
44
48
|
if (ternaryMatch) return `${ternaryMatch[1]} ${ternaryMatch[2]}`;
|
|
45
49
|
const templateMatch = line.match(/className=\{`([^`]*)`\}/);
|
|
46
50
|
if (templateMatch) {
|
|
@@ -62,14 +66,16 @@ function parseClassNameChanges(addedLines, removedLines) {
|
|
|
62
66
|
const cls = extractClassName(line);
|
|
63
67
|
const comp = extractComponentFromLine(line);
|
|
64
68
|
if (!cls) continue;
|
|
65
|
-
if (!componentMap.has(comp))
|
|
69
|
+
if (!componentMap.has(comp))
|
|
70
|
+
componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
66
71
|
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).added.add(c));
|
|
67
72
|
}
|
|
68
73
|
for (const line of removedLines.filter((l) => /className=/.test(l))) {
|
|
69
74
|
const cls = extractClassName(line);
|
|
70
75
|
const comp = extractComponentFromLine(line);
|
|
71
76
|
if (!cls) continue;
|
|
72
|
-
if (!componentMap.has(comp))
|
|
77
|
+
if (!componentMap.has(comp))
|
|
78
|
+
componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
73
79
|
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).removed.add(c));
|
|
74
80
|
}
|
|
75
81
|
const changes = [];
|
|
@@ -87,7 +93,8 @@ function renderStyleChanges(changes) {
|
|
|
87
93
|
for (const change of changes) {
|
|
88
94
|
lines.push(`**${change.component}**`);
|
|
89
95
|
if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
|
|
90
|
-
if (change.removed.length > 0)
|
|
96
|
+
if (change.removed.length > 0)
|
|
97
|
+
lines.push(` - ${change.removed.join(" ")}`);
|
|
91
98
|
}
|
|
92
99
|
return lines;
|
|
93
100
|
}
|
|
@@ -156,8 +163,12 @@ function parseJSXToFlowTree(lines) {
|
|
|
156
163
|
}
|
|
157
164
|
function filterDiffLines(diffText) {
|
|
158
165
|
const lines = diffText.split("\n");
|
|
159
|
-
const added = lines.filter(
|
|
160
|
-
|
|
166
|
+
const added = lines.filter(
|
|
167
|
+
(l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+"
|
|
168
|
+
).map((l) => l.substring(1));
|
|
169
|
+
const removed = lines.filter(
|
|
170
|
+
(l) => l.startsWith("-") && !l.startsWith("---") && l.trim() !== "-"
|
|
171
|
+
).map((l) => l.substring(1));
|
|
161
172
|
return { added, removed };
|
|
162
173
|
}
|
|
163
174
|
function getIndentDepth(line) {
|
|
@@ -166,14 +177,14 @@ function getIndentDepth(line) {
|
|
|
166
177
|
return Math.floor(match[1].length / 2);
|
|
167
178
|
}
|
|
168
179
|
function extractChangedSymbols(addedLines, removedLines) {
|
|
169
|
-
const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s
|
|
180
|
+
const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s+)?\(?|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*[a-z]\w+\s*[<(]/;
|
|
170
181
|
const COMPONENT_RE = /^(?:export\s+)?(?:default\s+)?(?:function|const)\s+([A-Z][a-z][A-Za-z0-9]*)/;
|
|
171
182
|
const extract = (lines) => {
|
|
172
183
|
const names = /* @__PURE__ */ new Set();
|
|
173
184
|
for (const line of lines) {
|
|
174
185
|
const cm = line.match(COMPONENT_RE) || line.match(FUNC_RE);
|
|
175
186
|
if (cm) {
|
|
176
|
-
const name = cm[1] || cm[2];
|
|
187
|
+
const name = cm[1] || cm[2] || cm[3];
|
|
177
188
|
if (name) names.add(name);
|
|
178
189
|
}
|
|
179
190
|
}
|
|
@@ -185,7 +196,10 @@ function extractChangedSymbols(addedLines, removedLines) {
|
|
|
185
196
|
const seen = /* @__PURE__ */ new Set();
|
|
186
197
|
for (const name of addedNames) {
|
|
187
198
|
seen.add(name);
|
|
188
|
-
results.push({
|
|
199
|
+
results.push({
|
|
200
|
+
name,
|
|
201
|
+
status: removedNames.has(name) ? "modified" : "added"
|
|
202
|
+
});
|
|
189
203
|
}
|
|
190
204
|
for (const name of removedNames) {
|
|
191
205
|
if (!seen.has(name)) {
|
|
@@ -246,7 +260,9 @@ function generateReaderMarkdown(diffText, meta = {}) {
|
|
|
246
260
|
sections.push("");
|
|
247
261
|
}
|
|
248
262
|
sections.push("---");
|
|
249
|
-
sections.push(
|
|
263
|
+
sections.push(
|
|
264
|
+
"\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/3rdflr/github-mobile-reader). Do not edit manually."
|
|
265
|
+
);
|
|
250
266
|
return sections.join("\n");
|
|
251
267
|
}
|
|
252
268
|
|
package/dist/index.d.mts
CHANGED
|
@@ -14,7 +14,7 @@ declare enum Priority {
|
|
|
14
14
|
OTHER = 5
|
|
15
15
|
}
|
|
16
16
|
interface FlowNode {
|
|
17
|
-
type:
|
|
17
|
+
type: "root" | "chain" | "condition" | "loop" | "function" | "call";
|
|
18
18
|
name: string;
|
|
19
19
|
children: FlowNode[];
|
|
20
20
|
depth: number;
|
|
@@ -74,7 +74,7 @@ declare function parseDiffToLogicalFlow(diffText: string): ParseResult;
|
|
|
74
74
|
*/
|
|
75
75
|
declare function extractChangedSymbols(addedLines: string[], removedLines: string[]): {
|
|
76
76
|
name: string;
|
|
77
|
-
status:
|
|
77
|
+
status: "added" | "removed" | "modified";
|
|
78
78
|
}[];
|
|
79
79
|
/**
|
|
80
80
|
* Render JSX tree as a single compact line: div > header > button(onClick)
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ declare enum Priority {
|
|
|
14
14
|
OTHER = 5
|
|
15
15
|
}
|
|
16
16
|
interface FlowNode {
|
|
17
|
-
type:
|
|
17
|
+
type: "root" | "chain" | "condition" | "loop" | "function" | "call";
|
|
18
18
|
name: string;
|
|
19
19
|
children: FlowNode[];
|
|
20
20
|
depth: number;
|
|
@@ -74,7 +74,7 @@ declare function parseDiffToLogicalFlow(diffText: string): ParseResult;
|
|
|
74
74
|
*/
|
|
75
75
|
declare function extractChangedSymbols(addedLines: string[], removedLines: string[]): {
|
|
76
76
|
name: string;
|
|
77
|
-
status:
|
|
77
|
+
status: "added" | "removed" | "modified";
|
|
78
78
|
}[];
|
|
79
79
|
/**
|
|
80
80
|
* Render JSX tree as a single compact line: div > header > button(onClick)
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,9 @@ function isJSXFile(filename) {
|
|
|
54
54
|
return /\.(jsx|tsx)$/.test(filename);
|
|
55
55
|
}
|
|
56
56
|
function hasJSXContent(lines) {
|
|
57
|
-
return lines.some(
|
|
57
|
+
return lines.some(
|
|
58
|
+
(l) => /<[A-Z][A-Za-z]*[\s/>]/.test(l) || /return\s*\(/.test(l)
|
|
59
|
+
);
|
|
58
60
|
}
|
|
59
61
|
function isClassNameOnlyLine(line) {
|
|
60
62
|
return /^className=/.test(line.trim());
|
|
@@ -62,7 +64,9 @@ function isClassNameOnlyLine(line) {
|
|
|
62
64
|
function extractClassName(line) {
|
|
63
65
|
const staticMatch = line.match(/className="([^"]*)"/);
|
|
64
66
|
if (staticMatch) return staticMatch[1];
|
|
65
|
-
const ternaryMatch = line.match(
|
|
67
|
+
const ternaryMatch = line.match(
|
|
68
|
+
/className=\{[^?]+\?\s*"([^"]*)"\s*:\s*"([^"]*)"\}/
|
|
69
|
+
);
|
|
66
70
|
if (ternaryMatch) return `${ternaryMatch[1]} ${ternaryMatch[2]}`;
|
|
67
71
|
const templateMatch = line.match(/className=\{`([^`]*)`\}/);
|
|
68
72
|
if (templateMatch) {
|
|
@@ -84,14 +88,16 @@ function parseClassNameChanges(addedLines, removedLines) {
|
|
|
84
88
|
const cls = extractClassName(line);
|
|
85
89
|
const comp = extractComponentFromLine(line);
|
|
86
90
|
if (!cls) continue;
|
|
87
|
-
if (!componentMap.has(comp))
|
|
91
|
+
if (!componentMap.has(comp))
|
|
92
|
+
componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
88
93
|
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).added.add(c));
|
|
89
94
|
}
|
|
90
95
|
for (const line of removedLines.filter((l) => /className=/.test(l))) {
|
|
91
96
|
const cls = extractClassName(line);
|
|
92
97
|
const comp = extractComponentFromLine(line);
|
|
93
98
|
if (!cls) continue;
|
|
94
|
-
if (!componentMap.has(comp))
|
|
99
|
+
if (!componentMap.has(comp))
|
|
100
|
+
componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
95
101
|
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).removed.add(c));
|
|
96
102
|
}
|
|
97
103
|
const changes = [];
|
|
@@ -109,7 +115,8 @@ function renderStyleChanges(changes) {
|
|
|
109
115
|
for (const change of changes) {
|
|
110
116
|
lines.push(`**${change.component}**`);
|
|
111
117
|
if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
|
|
112
|
-
if (change.removed.length > 0)
|
|
118
|
+
if (change.removed.length > 0)
|
|
119
|
+
lines.push(` - ${change.removed.join(" ")}`);
|
|
113
120
|
}
|
|
114
121
|
return lines;
|
|
115
122
|
}
|
|
@@ -178,8 +185,12 @@ function parseJSXToFlowTree(lines) {
|
|
|
178
185
|
}
|
|
179
186
|
function filterDiffLines(diffText) {
|
|
180
187
|
const lines = diffText.split("\n");
|
|
181
|
-
const added = lines.filter(
|
|
182
|
-
|
|
188
|
+
const added = lines.filter(
|
|
189
|
+
(l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+"
|
|
190
|
+
).map((l) => l.substring(1));
|
|
191
|
+
const removed = lines.filter(
|
|
192
|
+
(l) => l.startsWith("-") && !l.startsWith("---") && l.trim() !== "-"
|
|
193
|
+
).map((l) => l.substring(1));
|
|
183
194
|
return { added, removed };
|
|
184
195
|
}
|
|
185
196
|
function normalizeCode(lines) {
|
|
@@ -360,14 +371,14 @@ function parseDiffToLogicalFlow(diffText) {
|
|
|
360
371
|
};
|
|
361
372
|
}
|
|
362
373
|
function extractChangedSymbols(addedLines, removedLines) {
|
|
363
|
-
const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s
|
|
374
|
+
const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s+)?\(?|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*[a-z]\w+\s*[<(]/;
|
|
364
375
|
const COMPONENT_RE = /^(?:export\s+)?(?:default\s+)?(?:function|const)\s+([A-Z][a-z][A-Za-z0-9]*)/;
|
|
365
376
|
const extract = (lines) => {
|
|
366
377
|
const names = /* @__PURE__ */ new Set();
|
|
367
378
|
for (const line of lines) {
|
|
368
379
|
const cm = line.match(COMPONENT_RE) || line.match(FUNC_RE);
|
|
369
380
|
if (cm) {
|
|
370
|
-
const name = cm[1] || cm[2];
|
|
381
|
+
const name = cm[1] || cm[2] || cm[3];
|
|
371
382
|
if (name) names.add(name);
|
|
372
383
|
}
|
|
373
384
|
}
|
|
@@ -379,7 +390,10 @@ function extractChangedSymbols(addedLines, removedLines) {
|
|
|
379
390
|
const seen = /* @__PURE__ */ new Set();
|
|
380
391
|
for (const name of addedNames) {
|
|
381
392
|
seen.add(name);
|
|
382
|
-
results.push({
|
|
393
|
+
results.push({
|
|
394
|
+
name,
|
|
395
|
+
status: removedNames.has(name) ? "modified" : "added"
|
|
396
|
+
});
|
|
383
397
|
}
|
|
384
398
|
for (const name of removedNames) {
|
|
385
399
|
if (!seen.has(name)) {
|
|
@@ -440,7 +454,9 @@ function generateReaderMarkdown(diffText, meta = {}) {
|
|
|
440
454
|
sections.push("");
|
|
441
455
|
}
|
|
442
456
|
sections.push("---");
|
|
443
|
-
sections.push(
|
|
457
|
+
sections.push(
|
|
458
|
+
"\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/3rdflr/github-mobile-reader). Do not edit manually."
|
|
459
|
+
);
|
|
444
460
|
return sections.join("\n");
|
|
445
461
|
}
|
|
446
462
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.mjs
CHANGED
|
@@ -11,7 +11,9 @@ function isJSXFile(filename) {
|
|
|
11
11
|
return /\.(jsx|tsx)$/.test(filename);
|
|
12
12
|
}
|
|
13
13
|
function hasJSXContent(lines) {
|
|
14
|
-
return lines.some(
|
|
14
|
+
return lines.some(
|
|
15
|
+
(l) => /<[A-Z][A-Za-z]*[\s/>]/.test(l) || /return\s*\(/.test(l)
|
|
16
|
+
);
|
|
15
17
|
}
|
|
16
18
|
function isClassNameOnlyLine(line) {
|
|
17
19
|
return /^className=/.test(line.trim());
|
|
@@ -19,7 +21,9 @@ function isClassNameOnlyLine(line) {
|
|
|
19
21
|
function extractClassName(line) {
|
|
20
22
|
const staticMatch = line.match(/className="([^"]*)"/);
|
|
21
23
|
if (staticMatch) return staticMatch[1];
|
|
22
|
-
const ternaryMatch = line.match(
|
|
24
|
+
const ternaryMatch = line.match(
|
|
25
|
+
/className=\{[^?]+\?\s*"([^"]*)"\s*:\s*"([^"]*)"\}/
|
|
26
|
+
);
|
|
23
27
|
if (ternaryMatch) return `${ternaryMatch[1]} ${ternaryMatch[2]}`;
|
|
24
28
|
const templateMatch = line.match(/className=\{`([^`]*)`\}/);
|
|
25
29
|
if (templateMatch) {
|
|
@@ -41,14 +45,16 @@ function parseClassNameChanges(addedLines, removedLines) {
|
|
|
41
45
|
const cls = extractClassName(line);
|
|
42
46
|
const comp = extractComponentFromLine(line);
|
|
43
47
|
if (!cls) continue;
|
|
44
|
-
if (!componentMap.has(comp))
|
|
48
|
+
if (!componentMap.has(comp))
|
|
49
|
+
componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
45
50
|
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).added.add(c));
|
|
46
51
|
}
|
|
47
52
|
for (const line of removedLines.filter((l) => /className=/.test(l))) {
|
|
48
53
|
const cls = extractClassName(line);
|
|
49
54
|
const comp = extractComponentFromLine(line);
|
|
50
55
|
if (!cls) continue;
|
|
51
|
-
if (!componentMap.has(comp))
|
|
56
|
+
if (!componentMap.has(comp))
|
|
57
|
+
componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
52
58
|
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).removed.add(c));
|
|
53
59
|
}
|
|
54
60
|
const changes = [];
|
|
@@ -66,7 +72,8 @@ function renderStyleChanges(changes) {
|
|
|
66
72
|
for (const change of changes) {
|
|
67
73
|
lines.push(`**${change.component}**`);
|
|
68
74
|
if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
|
|
69
|
-
if (change.removed.length > 0)
|
|
75
|
+
if (change.removed.length > 0)
|
|
76
|
+
lines.push(` - ${change.removed.join(" ")}`);
|
|
70
77
|
}
|
|
71
78
|
return lines;
|
|
72
79
|
}
|
|
@@ -135,8 +142,12 @@ function parseJSXToFlowTree(lines) {
|
|
|
135
142
|
}
|
|
136
143
|
function filterDiffLines(diffText) {
|
|
137
144
|
const lines = diffText.split("\n");
|
|
138
|
-
const added = lines.filter(
|
|
139
|
-
|
|
145
|
+
const added = lines.filter(
|
|
146
|
+
(l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+"
|
|
147
|
+
).map((l) => l.substring(1));
|
|
148
|
+
const removed = lines.filter(
|
|
149
|
+
(l) => l.startsWith("-") && !l.startsWith("---") && l.trim() !== "-"
|
|
150
|
+
).map((l) => l.substring(1));
|
|
140
151
|
return { added, removed };
|
|
141
152
|
}
|
|
142
153
|
function normalizeCode(lines) {
|
|
@@ -317,14 +328,14 @@ function parseDiffToLogicalFlow(diffText) {
|
|
|
317
328
|
};
|
|
318
329
|
}
|
|
319
330
|
function extractChangedSymbols(addedLines, removedLines) {
|
|
320
|
-
const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s
|
|
331
|
+
const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s+)?\(?|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*[a-z]\w+\s*[<(]/;
|
|
321
332
|
const COMPONENT_RE = /^(?:export\s+)?(?:default\s+)?(?:function|const)\s+([A-Z][a-z][A-Za-z0-9]*)/;
|
|
322
333
|
const extract = (lines) => {
|
|
323
334
|
const names = /* @__PURE__ */ new Set();
|
|
324
335
|
for (const line of lines) {
|
|
325
336
|
const cm = line.match(COMPONENT_RE) || line.match(FUNC_RE);
|
|
326
337
|
if (cm) {
|
|
327
|
-
const name = cm[1] || cm[2];
|
|
338
|
+
const name = cm[1] || cm[2] || cm[3];
|
|
328
339
|
if (name) names.add(name);
|
|
329
340
|
}
|
|
330
341
|
}
|
|
@@ -336,7 +347,10 @@ function extractChangedSymbols(addedLines, removedLines) {
|
|
|
336
347
|
const seen = /* @__PURE__ */ new Set();
|
|
337
348
|
for (const name of addedNames) {
|
|
338
349
|
seen.add(name);
|
|
339
|
-
results.push({
|
|
350
|
+
results.push({
|
|
351
|
+
name,
|
|
352
|
+
status: removedNames.has(name) ? "modified" : "added"
|
|
353
|
+
});
|
|
340
354
|
}
|
|
341
355
|
for (const name of removedNames) {
|
|
342
356
|
if (!seen.has(name)) {
|
|
@@ -397,7 +411,9 @@ function generateReaderMarkdown(diffText, meta = {}) {
|
|
|
397
411
|
sections.push("");
|
|
398
412
|
}
|
|
399
413
|
sections.push("---");
|
|
400
|
-
sections.push(
|
|
414
|
+
sections.push(
|
|
415
|
+
"\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/3rdflr/github-mobile-reader). Do not edit manually."
|
|
416
|
+
);
|
|
401
417
|
return sections.join("\n");
|
|
402
418
|
}
|
|
403
419
|
export {
|
package/package.json
CHANGED