github-mobile-reader 0.1.1 → 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.ko.md +75 -7
- package/README.md +76 -8
- package/dist/action.js +156 -18
- package/dist/cli.js +552 -0
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +177 -11
- package/dist/index.mjs +167 -10
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -21,12 +21,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Priority: () => Priority,
|
|
24
|
+
extractClassName: () => extractClassName,
|
|
25
|
+
extractJSXComponentName: () => extractJSXComponentName,
|
|
24
26
|
filterDiffLines: () => filterDiffLines,
|
|
25
27
|
generateReaderMarkdown: () => generateReaderMarkdown,
|
|
28
|
+
hasJSXContent: () => hasJSXContent,
|
|
29
|
+
isClassNameOnlyLine: () => isClassNameOnlyLine,
|
|
30
|
+
isJSXElement: () => isJSXElement,
|
|
31
|
+
isJSXFile: () => isJSXFile,
|
|
26
32
|
normalizeCode: () => normalizeCode,
|
|
33
|
+
parseClassNameChanges: () => parseClassNameChanges,
|
|
27
34
|
parseDiffToLogicalFlow: () => parseDiffToLogicalFlow,
|
|
35
|
+
parseJSXToFlowTree: () => parseJSXToFlowTree,
|
|
28
36
|
parseToFlowTree: () => parseToFlowTree,
|
|
29
|
-
renderFlowTree: () => renderFlowTree
|
|
37
|
+
renderFlowTree: () => renderFlowTree,
|
|
38
|
+
renderStyleChanges: () => renderStyleChanges
|
|
30
39
|
});
|
|
31
40
|
module.exports = __toCommonJS(index_exports);
|
|
32
41
|
|
|
@@ -39,6 +48,131 @@ var Priority = /* @__PURE__ */ ((Priority2) => {
|
|
|
39
48
|
Priority2[Priority2["OTHER"] = 5] = "OTHER";
|
|
40
49
|
return Priority2;
|
|
41
50
|
})(Priority || {});
|
|
51
|
+
function isJSXFile(filename) {
|
|
52
|
+
return /\.(jsx|tsx)$/.test(filename);
|
|
53
|
+
}
|
|
54
|
+
function hasJSXContent(lines) {
|
|
55
|
+
return lines.some((l) => /<[A-Z][A-Za-z]*[\s/>]/.test(l) || /return\s*\(/.test(l));
|
|
56
|
+
}
|
|
57
|
+
function isClassNameOnlyLine(line) {
|
|
58
|
+
return /^className=/.test(line.trim());
|
|
59
|
+
}
|
|
60
|
+
function extractClassName(line) {
|
|
61
|
+
const staticMatch = line.match(/className="([^"]*)"/);
|
|
62
|
+
if (staticMatch) return staticMatch[1];
|
|
63
|
+
const ternaryMatch = line.match(/className=\{[^?]+\?\s*"([^"]*)"\s*:\s*"([^"]*)"\}/);
|
|
64
|
+
if (ternaryMatch) return `${ternaryMatch[1]} ${ternaryMatch[2]}`;
|
|
65
|
+
const templateMatch = line.match(/className=\{`([^`]*)`\}/);
|
|
66
|
+
if (templateMatch) {
|
|
67
|
+
const raw = templateMatch[1];
|
|
68
|
+
const literals = raw.replace(/\$\{[^}]*\}/g, " ").trim();
|
|
69
|
+
const exprStrings = [...raw.matchAll(/"([^"]*)"/g)].map((m) => m[1]);
|
|
70
|
+
return [literals, ...exprStrings].filter(Boolean).join(" ");
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function extractComponentFromLine(line) {
|
|
75
|
+
const tagMatch = line.match(/<([A-Za-z][A-Za-z0-9.]*)/);
|
|
76
|
+
if (tagMatch) return tagMatch[1];
|
|
77
|
+
return "unknown";
|
|
78
|
+
}
|
|
79
|
+
function parseClassNameChanges(addedLines, removedLines) {
|
|
80
|
+
const componentMap = /* @__PURE__ */ new Map();
|
|
81
|
+
for (const line of addedLines.filter((l) => /className=/.test(l))) {
|
|
82
|
+
const cls = extractClassName(line);
|
|
83
|
+
const comp = extractComponentFromLine(line);
|
|
84
|
+
if (!cls) continue;
|
|
85
|
+
if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
86
|
+
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).added.add(c));
|
|
87
|
+
}
|
|
88
|
+
for (const line of removedLines.filter((l) => /className=/.test(l))) {
|
|
89
|
+
const cls = extractClassName(line);
|
|
90
|
+
const comp = extractComponentFromLine(line);
|
|
91
|
+
if (!cls) continue;
|
|
92
|
+
if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
93
|
+
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).removed.add(c));
|
|
94
|
+
}
|
|
95
|
+
const changes = [];
|
|
96
|
+
for (const [comp, { added, removed }] of componentMap) {
|
|
97
|
+
const pureAdded = [...added].filter((c) => !removed.has(c));
|
|
98
|
+
const pureRemoved = [...removed].filter((c) => !added.has(c));
|
|
99
|
+
if (pureAdded.length === 0 && pureRemoved.length === 0) continue;
|
|
100
|
+
changes.push({ component: comp, added: pureAdded, removed: pureRemoved });
|
|
101
|
+
}
|
|
102
|
+
return changes;
|
|
103
|
+
}
|
|
104
|
+
function renderStyleChanges(changes) {
|
|
105
|
+
const lines = [];
|
|
106
|
+
for (const change of changes) {
|
|
107
|
+
lines.push(`**${change.component}**`);
|
|
108
|
+
if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
|
|
109
|
+
if (change.removed.length > 0) lines.push(` - ${change.removed.join(" ")}`);
|
|
110
|
+
}
|
|
111
|
+
return lines;
|
|
112
|
+
}
|
|
113
|
+
function isJSXElement(line) {
|
|
114
|
+
const t = line.trim();
|
|
115
|
+
return /^<[A-Za-z]/.test(t) || /^<\/[A-Za-z]/.test(t);
|
|
116
|
+
}
|
|
117
|
+
function isJSXClosing(line) {
|
|
118
|
+
return /^<\/[A-Za-z]/.test(line.trim());
|
|
119
|
+
}
|
|
120
|
+
function isJSXSelfClosing(line) {
|
|
121
|
+
return /\/>[\s]*$/.test(line.trim());
|
|
122
|
+
}
|
|
123
|
+
function extractJSXComponentName(line) {
|
|
124
|
+
const trimmed = line.trim();
|
|
125
|
+
const closingMatch = trimmed.match(/^<\/([A-Za-z][A-Za-z0-9.]*)/);
|
|
126
|
+
if (closingMatch) return `/${closingMatch[1]}`;
|
|
127
|
+
const nameMatch = trimmed.match(/^<([A-Za-z][A-Za-z0-9.]*)/);
|
|
128
|
+
if (!nameMatch) return trimmed;
|
|
129
|
+
const name = nameMatch[1];
|
|
130
|
+
const eventProps = [];
|
|
131
|
+
for (const m of trimmed.matchAll(/\b(on[A-Z]\w+)=/g)) {
|
|
132
|
+
eventProps.push(m[1]);
|
|
133
|
+
}
|
|
134
|
+
return eventProps.length > 0 ? `${name}(${eventProps.join(", ")})` : name;
|
|
135
|
+
}
|
|
136
|
+
function shouldIgnoreJSX(line) {
|
|
137
|
+
const t = line.trim();
|
|
138
|
+
return isClassNameOnlyLine(t) || /^style=/.test(t) || /^aria-/.test(t) || /^data-/.test(t) || /^strokeLinecap=/.test(t) || /^strokeLinejoin=/.test(t) || /^strokeWidth=/.test(t) || /^viewBox=/.test(t) || /^fill=/.test(t) || /^stroke=/.test(t) || /^d="/.test(t) || t === "{" || t === "}" || t === "(" || t === ")" || t === "<>" || t === "</>" || /^\{\/\*/.test(t);
|
|
139
|
+
}
|
|
140
|
+
function parseJSXToFlowTree(lines) {
|
|
141
|
+
const roots = [];
|
|
142
|
+
const stack = [];
|
|
143
|
+
for (const line of lines) {
|
|
144
|
+
if (!isJSXElement(line)) continue;
|
|
145
|
+
if (shouldIgnoreJSX(line)) continue;
|
|
146
|
+
const depth = getIndentDepth(line);
|
|
147
|
+
if (isJSXClosing(line)) {
|
|
148
|
+
while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
|
|
149
|
+
stack.pop();
|
|
150
|
+
}
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const name = extractJSXComponentName(line);
|
|
154
|
+
const selfClosing = isJSXSelfClosing(line);
|
|
155
|
+
const node = {
|
|
156
|
+
type: "call",
|
|
157
|
+
name,
|
|
158
|
+
children: [],
|
|
159
|
+
depth,
|
|
160
|
+
priority: 5 /* OTHER */
|
|
161
|
+
};
|
|
162
|
+
while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
|
|
163
|
+
stack.pop();
|
|
164
|
+
}
|
|
165
|
+
if (stack.length === 0) {
|
|
166
|
+
roots.push(node);
|
|
167
|
+
} else {
|
|
168
|
+
stack[stack.length - 1].node.children.push(node);
|
|
169
|
+
}
|
|
170
|
+
if (!selfClosing) {
|
|
171
|
+
stack.push({ node, depth });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return roots;
|
|
175
|
+
}
|
|
42
176
|
function filterDiffLines(diffText) {
|
|
43
177
|
const lines = diffText.split("\n");
|
|
44
178
|
const added = lines.filter((l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+").map((l) => l.substring(1));
|
|
@@ -223,8 +357,20 @@ function parseDiffToLogicalFlow(diffText) {
|
|
|
223
357
|
};
|
|
224
358
|
}
|
|
225
359
|
function generateReaderMarkdown(diffText, meta = {}) {
|
|
226
|
-
const
|
|
360
|
+
const { added, removed } = filterDiffLines(diffText);
|
|
361
|
+
const isJSX = Boolean(
|
|
362
|
+
meta.file && isJSXFile(meta.file) || hasJSXContent(added)
|
|
363
|
+
);
|
|
364
|
+
const addedForFlow = isJSX ? added.filter((l) => !isClassNameOnlyLine(l)) : added;
|
|
365
|
+
const normalizedAdded = normalizeCode(addedForFlow);
|
|
366
|
+
const flowTree = parseToFlowTree(normalizedAdded);
|
|
367
|
+
const rawCode = addedForFlow.join("\n");
|
|
368
|
+
const removedForCode = isJSX ? removed.filter((l) => !isClassNameOnlyLine(l)) : removed;
|
|
369
|
+
const removedCode = removedForCode.join("\n");
|
|
370
|
+
const classNameChanges = isJSX ? parseClassNameChanges(added, removed) : [];
|
|
371
|
+
const jsxTree = isJSX ? parseJSXToFlowTree(added) : [];
|
|
227
372
|
const sections = [];
|
|
373
|
+
const lang = isJSX ? "tsx" : "typescript";
|
|
228
374
|
sections.push("# \u{1F4D6} GitHub Reader View\n");
|
|
229
375
|
sections.push("> Generated by **github-mobile-reader**");
|
|
230
376
|
if (meta.repo) sections.push(`> Repository: ${meta.repo}`);
|
|
@@ -232,22 +378,33 @@ function generateReaderMarkdown(diffText, meta = {}) {
|
|
|
232
378
|
if (meta.commit) sections.push(`> Commit: \`${meta.commit}\``);
|
|
233
379
|
if (meta.file) sections.push(`> File: \`${meta.file}\``);
|
|
234
380
|
sections.push("\n");
|
|
235
|
-
if (
|
|
381
|
+
if (flowTree.length > 0) {
|
|
236
382
|
sections.push("## \u{1F9E0} Logical Flow\n");
|
|
237
383
|
sections.push("```");
|
|
238
|
-
sections.push(...renderFlowTree(
|
|
384
|
+
sections.push(...renderFlowTree(flowTree));
|
|
239
385
|
sections.push("```\n");
|
|
240
386
|
}
|
|
241
|
-
if (
|
|
387
|
+
if (isJSX && jsxTree.length > 0) {
|
|
388
|
+
sections.push("## \u{1F3A8} JSX Structure\n");
|
|
389
|
+
sections.push("```");
|
|
390
|
+
sections.push(...renderFlowTree(jsxTree));
|
|
391
|
+
sections.push("```\n");
|
|
392
|
+
}
|
|
393
|
+
if (isJSX && classNameChanges.length > 0) {
|
|
394
|
+
sections.push("## \u{1F485} Style Changes\n");
|
|
395
|
+
sections.push(...renderStyleChanges(classNameChanges));
|
|
396
|
+
sections.push("");
|
|
397
|
+
}
|
|
398
|
+
if (rawCode.trim()) {
|
|
242
399
|
sections.push("## \u2705 Added Code\n");
|
|
243
|
-
sections.push(
|
|
244
|
-
sections.push(
|
|
400
|
+
sections.push(`\`\`\`${lang}`);
|
|
401
|
+
sections.push(rawCode);
|
|
245
402
|
sections.push("```\n");
|
|
246
403
|
}
|
|
247
|
-
if (
|
|
404
|
+
if (removedCode.trim()) {
|
|
248
405
|
sections.push("## \u274C Removed Code\n");
|
|
249
|
-
sections.push(
|
|
250
|
-
sections.push(
|
|
406
|
+
sections.push(`\`\`\`${lang}`);
|
|
407
|
+
sections.push(removedCode);
|
|
251
408
|
sections.push("```\n");
|
|
252
409
|
}
|
|
253
410
|
sections.push("---");
|
|
@@ -257,10 +414,19 @@ function generateReaderMarkdown(diffText, meta = {}) {
|
|
|
257
414
|
// Annotate the CommonJS export names for ESM import in node:
|
|
258
415
|
0 && (module.exports = {
|
|
259
416
|
Priority,
|
|
417
|
+
extractClassName,
|
|
418
|
+
extractJSXComponentName,
|
|
260
419
|
filterDiffLines,
|
|
261
420
|
generateReaderMarkdown,
|
|
421
|
+
hasJSXContent,
|
|
422
|
+
isClassNameOnlyLine,
|
|
423
|
+
isJSXElement,
|
|
424
|
+
isJSXFile,
|
|
262
425
|
normalizeCode,
|
|
426
|
+
parseClassNameChanges,
|
|
263
427
|
parseDiffToLogicalFlow,
|
|
428
|
+
parseJSXToFlowTree,
|
|
264
429
|
parseToFlowTree,
|
|
265
|
-
renderFlowTree
|
|
430
|
+
renderFlowTree,
|
|
431
|
+
renderStyleChanges
|
|
266
432
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -7,6 +7,131 @@ var Priority = /* @__PURE__ */ ((Priority2) => {
|
|
|
7
7
|
Priority2[Priority2["OTHER"] = 5] = "OTHER";
|
|
8
8
|
return Priority2;
|
|
9
9
|
})(Priority || {});
|
|
10
|
+
function isJSXFile(filename) {
|
|
11
|
+
return /\.(jsx|tsx)$/.test(filename);
|
|
12
|
+
}
|
|
13
|
+
function hasJSXContent(lines) {
|
|
14
|
+
return lines.some((l) => /<[A-Z][A-Za-z]*[\s/>]/.test(l) || /return\s*\(/.test(l));
|
|
15
|
+
}
|
|
16
|
+
function isClassNameOnlyLine(line) {
|
|
17
|
+
return /^className=/.test(line.trim());
|
|
18
|
+
}
|
|
19
|
+
function extractClassName(line) {
|
|
20
|
+
const staticMatch = line.match(/className="([^"]*)"/);
|
|
21
|
+
if (staticMatch) return staticMatch[1];
|
|
22
|
+
const ternaryMatch = line.match(/className=\{[^?]+\?\s*"([^"]*)"\s*:\s*"([^"]*)"\}/);
|
|
23
|
+
if (ternaryMatch) return `${ternaryMatch[1]} ${ternaryMatch[2]}`;
|
|
24
|
+
const templateMatch = line.match(/className=\{`([^`]*)`\}/);
|
|
25
|
+
if (templateMatch) {
|
|
26
|
+
const raw = templateMatch[1];
|
|
27
|
+
const literals = raw.replace(/\$\{[^}]*\}/g, " ").trim();
|
|
28
|
+
const exprStrings = [...raw.matchAll(/"([^"]*)"/g)].map((m) => m[1]);
|
|
29
|
+
return [literals, ...exprStrings].filter(Boolean).join(" ");
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function extractComponentFromLine(line) {
|
|
34
|
+
const tagMatch = line.match(/<([A-Za-z][A-Za-z0-9.]*)/);
|
|
35
|
+
if (tagMatch) return tagMatch[1];
|
|
36
|
+
return "unknown";
|
|
37
|
+
}
|
|
38
|
+
function parseClassNameChanges(addedLines, removedLines) {
|
|
39
|
+
const componentMap = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const line of addedLines.filter((l) => /className=/.test(l))) {
|
|
41
|
+
const cls = extractClassName(line);
|
|
42
|
+
const comp = extractComponentFromLine(line);
|
|
43
|
+
if (!cls) continue;
|
|
44
|
+
if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
45
|
+
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).added.add(c));
|
|
46
|
+
}
|
|
47
|
+
for (const line of removedLines.filter((l) => /className=/.test(l))) {
|
|
48
|
+
const cls = extractClassName(line);
|
|
49
|
+
const comp = extractComponentFromLine(line);
|
|
50
|
+
if (!cls) continue;
|
|
51
|
+
if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
|
|
52
|
+
cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).removed.add(c));
|
|
53
|
+
}
|
|
54
|
+
const changes = [];
|
|
55
|
+
for (const [comp, { added, removed }] of componentMap) {
|
|
56
|
+
const pureAdded = [...added].filter((c) => !removed.has(c));
|
|
57
|
+
const pureRemoved = [...removed].filter((c) => !added.has(c));
|
|
58
|
+
if (pureAdded.length === 0 && pureRemoved.length === 0) continue;
|
|
59
|
+
changes.push({ component: comp, added: pureAdded, removed: pureRemoved });
|
|
60
|
+
}
|
|
61
|
+
return changes;
|
|
62
|
+
}
|
|
63
|
+
function renderStyleChanges(changes) {
|
|
64
|
+
const lines = [];
|
|
65
|
+
for (const change of changes) {
|
|
66
|
+
lines.push(`**${change.component}**`);
|
|
67
|
+
if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
|
|
68
|
+
if (change.removed.length > 0) lines.push(` - ${change.removed.join(" ")}`);
|
|
69
|
+
}
|
|
70
|
+
return lines;
|
|
71
|
+
}
|
|
72
|
+
function isJSXElement(line) {
|
|
73
|
+
const t = line.trim();
|
|
74
|
+
return /^<[A-Za-z]/.test(t) || /^<\/[A-Za-z]/.test(t);
|
|
75
|
+
}
|
|
76
|
+
function isJSXClosing(line) {
|
|
77
|
+
return /^<\/[A-Za-z]/.test(line.trim());
|
|
78
|
+
}
|
|
79
|
+
function isJSXSelfClosing(line) {
|
|
80
|
+
return /\/>[\s]*$/.test(line.trim());
|
|
81
|
+
}
|
|
82
|
+
function extractJSXComponentName(line) {
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
const closingMatch = trimmed.match(/^<\/([A-Za-z][A-Za-z0-9.]*)/);
|
|
85
|
+
if (closingMatch) return `/${closingMatch[1]}`;
|
|
86
|
+
const nameMatch = trimmed.match(/^<([A-Za-z][A-Za-z0-9.]*)/);
|
|
87
|
+
if (!nameMatch) return trimmed;
|
|
88
|
+
const name = nameMatch[1];
|
|
89
|
+
const eventProps = [];
|
|
90
|
+
for (const m of trimmed.matchAll(/\b(on[A-Z]\w+)=/g)) {
|
|
91
|
+
eventProps.push(m[1]);
|
|
92
|
+
}
|
|
93
|
+
return eventProps.length > 0 ? `${name}(${eventProps.join(", ")})` : name;
|
|
94
|
+
}
|
|
95
|
+
function shouldIgnoreJSX(line) {
|
|
96
|
+
const t = line.trim();
|
|
97
|
+
return isClassNameOnlyLine(t) || /^style=/.test(t) || /^aria-/.test(t) || /^data-/.test(t) || /^strokeLinecap=/.test(t) || /^strokeLinejoin=/.test(t) || /^strokeWidth=/.test(t) || /^viewBox=/.test(t) || /^fill=/.test(t) || /^stroke=/.test(t) || /^d="/.test(t) || t === "{" || t === "}" || t === "(" || t === ")" || t === "<>" || t === "</>" || /^\{\/\*/.test(t);
|
|
98
|
+
}
|
|
99
|
+
function parseJSXToFlowTree(lines) {
|
|
100
|
+
const roots = [];
|
|
101
|
+
const stack = [];
|
|
102
|
+
for (const line of lines) {
|
|
103
|
+
if (!isJSXElement(line)) continue;
|
|
104
|
+
if (shouldIgnoreJSX(line)) continue;
|
|
105
|
+
const depth = getIndentDepth(line);
|
|
106
|
+
if (isJSXClosing(line)) {
|
|
107
|
+
while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
|
|
108
|
+
stack.pop();
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const name = extractJSXComponentName(line);
|
|
113
|
+
const selfClosing = isJSXSelfClosing(line);
|
|
114
|
+
const node = {
|
|
115
|
+
type: "call",
|
|
116
|
+
name,
|
|
117
|
+
children: [],
|
|
118
|
+
depth,
|
|
119
|
+
priority: 5 /* OTHER */
|
|
120
|
+
};
|
|
121
|
+
while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
|
|
122
|
+
stack.pop();
|
|
123
|
+
}
|
|
124
|
+
if (stack.length === 0) {
|
|
125
|
+
roots.push(node);
|
|
126
|
+
} else {
|
|
127
|
+
stack[stack.length - 1].node.children.push(node);
|
|
128
|
+
}
|
|
129
|
+
if (!selfClosing) {
|
|
130
|
+
stack.push({ node, depth });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return roots;
|
|
134
|
+
}
|
|
10
135
|
function filterDiffLines(diffText) {
|
|
11
136
|
const lines = diffText.split("\n");
|
|
12
137
|
const added = lines.filter((l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+").map((l) => l.substring(1));
|
|
@@ -191,8 +316,20 @@ function parseDiffToLogicalFlow(diffText) {
|
|
|
191
316
|
};
|
|
192
317
|
}
|
|
193
318
|
function generateReaderMarkdown(diffText, meta = {}) {
|
|
194
|
-
const
|
|
319
|
+
const { added, removed } = filterDiffLines(diffText);
|
|
320
|
+
const isJSX = Boolean(
|
|
321
|
+
meta.file && isJSXFile(meta.file) || hasJSXContent(added)
|
|
322
|
+
);
|
|
323
|
+
const addedForFlow = isJSX ? added.filter((l) => !isClassNameOnlyLine(l)) : added;
|
|
324
|
+
const normalizedAdded = normalizeCode(addedForFlow);
|
|
325
|
+
const flowTree = parseToFlowTree(normalizedAdded);
|
|
326
|
+
const rawCode = addedForFlow.join("\n");
|
|
327
|
+
const removedForCode = isJSX ? removed.filter((l) => !isClassNameOnlyLine(l)) : removed;
|
|
328
|
+
const removedCode = removedForCode.join("\n");
|
|
329
|
+
const classNameChanges = isJSX ? parseClassNameChanges(added, removed) : [];
|
|
330
|
+
const jsxTree = isJSX ? parseJSXToFlowTree(added) : [];
|
|
195
331
|
const sections = [];
|
|
332
|
+
const lang = isJSX ? "tsx" : "typescript";
|
|
196
333
|
sections.push("# \u{1F4D6} GitHub Reader View\n");
|
|
197
334
|
sections.push("> Generated by **github-mobile-reader**");
|
|
198
335
|
if (meta.repo) sections.push(`> Repository: ${meta.repo}`);
|
|
@@ -200,22 +337,33 @@ function generateReaderMarkdown(diffText, meta = {}) {
|
|
|
200
337
|
if (meta.commit) sections.push(`> Commit: \`${meta.commit}\``);
|
|
201
338
|
if (meta.file) sections.push(`> File: \`${meta.file}\``);
|
|
202
339
|
sections.push("\n");
|
|
203
|
-
if (
|
|
340
|
+
if (flowTree.length > 0) {
|
|
204
341
|
sections.push("## \u{1F9E0} Logical Flow\n");
|
|
205
342
|
sections.push("```");
|
|
206
|
-
sections.push(...renderFlowTree(
|
|
343
|
+
sections.push(...renderFlowTree(flowTree));
|
|
207
344
|
sections.push("```\n");
|
|
208
345
|
}
|
|
209
|
-
if (
|
|
346
|
+
if (isJSX && jsxTree.length > 0) {
|
|
347
|
+
sections.push("## \u{1F3A8} JSX Structure\n");
|
|
348
|
+
sections.push("```");
|
|
349
|
+
sections.push(...renderFlowTree(jsxTree));
|
|
350
|
+
sections.push("```\n");
|
|
351
|
+
}
|
|
352
|
+
if (isJSX && classNameChanges.length > 0) {
|
|
353
|
+
sections.push("## \u{1F485} Style Changes\n");
|
|
354
|
+
sections.push(...renderStyleChanges(classNameChanges));
|
|
355
|
+
sections.push("");
|
|
356
|
+
}
|
|
357
|
+
if (rawCode.trim()) {
|
|
210
358
|
sections.push("## \u2705 Added Code\n");
|
|
211
|
-
sections.push(
|
|
212
|
-
sections.push(
|
|
359
|
+
sections.push(`\`\`\`${lang}`);
|
|
360
|
+
sections.push(rawCode);
|
|
213
361
|
sections.push("```\n");
|
|
214
362
|
}
|
|
215
|
-
if (
|
|
363
|
+
if (removedCode.trim()) {
|
|
216
364
|
sections.push("## \u274C Removed Code\n");
|
|
217
|
-
sections.push(
|
|
218
|
-
sections.push(
|
|
365
|
+
sections.push(`\`\`\`${lang}`);
|
|
366
|
+
sections.push(removedCode);
|
|
219
367
|
sections.push("```\n");
|
|
220
368
|
}
|
|
221
369
|
sections.push("---");
|
|
@@ -224,10 +372,19 @@ function generateReaderMarkdown(diffText, meta = {}) {
|
|
|
224
372
|
}
|
|
225
373
|
export {
|
|
226
374
|
Priority,
|
|
375
|
+
extractClassName,
|
|
376
|
+
extractJSXComponentName,
|
|
227
377
|
filterDiffLines,
|
|
228
378
|
generateReaderMarkdown,
|
|
379
|
+
hasJSXContent,
|
|
380
|
+
isClassNameOnlyLine,
|
|
381
|
+
isJSXElement,
|
|
382
|
+
isJSXFile,
|
|
229
383
|
normalizeCode,
|
|
384
|
+
parseClassNameChanges,
|
|
230
385
|
parseDiffToLogicalFlow,
|
|
386
|
+
parseJSXToFlowTree,
|
|
231
387
|
parseToFlowTree,
|
|
232
|
-
renderFlowTree
|
|
388
|
+
renderFlowTree,
|
|
389
|
+
renderStyleChanges
|
|
233
390
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "github-mobile-reader",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Transform git diffs into mobile-friendly Markdown — no more horizontal scrolling when reviewing code on your phone.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github",
|
|
@@ -17,10 +17,13 @@
|
|
|
17
17
|
},
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/3rdflr/github-mobile-reader.git"
|
|
20
|
+
"url": "git+https://github.com/3rdflr/github-mobile-reader.git"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"author": "3rdflrhtl@gmail.com",
|
|
24
|
+
"bin": {
|
|
25
|
+
"github-mobile-reader": "dist/cli.js"
|
|
26
|
+
},
|
|
24
27
|
"main": "./dist/index.js",
|
|
25
28
|
"module": "./dist/index.mjs",
|
|
26
29
|
"types": "./dist/index.d.ts",
|
|
@@ -39,7 +42,8 @@
|
|
|
39
42
|
"scripts": {
|
|
40
43
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
41
44
|
"build:action": "tsup src/action.ts --format cjs --outDir dist --no-splitting",
|
|
42
|
-
"build:
|
|
45
|
+
"build:cli": "tsup src/cli.ts --format cjs --outDir dist --no-splitting",
|
|
46
|
+
"build:all": "npm run build && npm run build:action && npm run build:cli",
|
|
43
47
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
44
48
|
"prepublishOnly": "npm run build:all"
|
|
45
49
|
},
|