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/dist/action.js CHANGED
@@ -28,6 +28,131 @@ var path = __toESM(require("path"));
28
28
  var import_child_process = require("child_process");
29
29
 
30
30
  // src/parser.ts
31
+ function isJSXFile(filename) {
32
+ return /\.(jsx|tsx)$/.test(filename);
33
+ }
34
+ function hasJSXContent(lines) {
35
+ return lines.some((l) => /<[A-Z][A-Za-z]*[\s/>]/.test(l) || /return\s*\(/.test(l));
36
+ }
37
+ function isClassNameOnlyLine(line) {
38
+ return /^className=/.test(line.trim());
39
+ }
40
+ function extractClassName(line) {
41
+ const staticMatch = line.match(/className="([^"]*)"/);
42
+ if (staticMatch) return staticMatch[1];
43
+ const ternaryMatch = line.match(/className=\{[^?]+\?\s*"([^"]*)"\s*:\s*"([^"]*)"\}/);
44
+ if (ternaryMatch) return `${ternaryMatch[1]} ${ternaryMatch[2]}`;
45
+ const templateMatch = line.match(/className=\{`([^`]*)`\}/);
46
+ if (templateMatch) {
47
+ const raw = templateMatch[1];
48
+ const literals = raw.replace(/\$\{[^}]*\}/g, " ").trim();
49
+ const exprStrings = [...raw.matchAll(/"([^"]*)"/g)].map((m) => m[1]);
50
+ return [literals, ...exprStrings].filter(Boolean).join(" ");
51
+ }
52
+ return null;
53
+ }
54
+ function extractComponentFromLine(line) {
55
+ const tagMatch = line.match(/<([A-Za-z][A-Za-z0-9.]*)/);
56
+ if (tagMatch) return tagMatch[1];
57
+ return "unknown";
58
+ }
59
+ function parseClassNameChanges(addedLines, removedLines) {
60
+ const componentMap = /* @__PURE__ */ new Map();
61
+ for (const line of addedLines.filter((l) => /className=/.test(l))) {
62
+ const cls = extractClassName(line);
63
+ const comp = extractComponentFromLine(line);
64
+ if (!cls) continue;
65
+ if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
66
+ cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).added.add(c));
67
+ }
68
+ for (const line of removedLines.filter((l) => /className=/.test(l))) {
69
+ const cls = extractClassName(line);
70
+ const comp = extractComponentFromLine(line);
71
+ if (!cls) continue;
72
+ if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
73
+ cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).removed.add(c));
74
+ }
75
+ const changes = [];
76
+ for (const [comp, { added, removed }] of componentMap) {
77
+ const pureAdded = [...added].filter((c) => !removed.has(c));
78
+ const pureRemoved = [...removed].filter((c) => !added.has(c));
79
+ if (pureAdded.length === 0 && pureRemoved.length === 0) continue;
80
+ changes.push({ component: comp, added: pureAdded, removed: pureRemoved });
81
+ }
82
+ return changes;
83
+ }
84
+ function renderStyleChanges(changes) {
85
+ const lines = [];
86
+ for (const change of changes) {
87
+ lines.push(`**${change.component}**`);
88
+ if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
89
+ if (change.removed.length > 0) lines.push(` - ${change.removed.join(" ")}`);
90
+ }
91
+ return lines;
92
+ }
93
+ function isJSXElement(line) {
94
+ const t = line.trim();
95
+ return /^<[A-Za-z]/.test(t) || /^<\/[A-Za-z]/.test(t);
96
+ }
97
+ function isJSXClosing(line) {
98
+ return /^<\/[A-Za-z]/.test(line.trim());
99
+ }
100
+ function isJSXSelfClosing(line) {
101
+ return /\/>[\s]*$/.test(line.trim());
102
+ }
103
+ function extractJSXComponentName(line) {
104
+ const trimmed = line.trim();
105
+ const closingMatch = trimmed.match(/^<\/([A-Za-z][A-Za-z0-9.]*)/);
106
+ if (closingMatch) return `/${closingMatch[1]}`;
107
+ const nameMatch = trimmed.match(/^<([A-Za-z][A-Za-z0-9.]*)/);
108
+ if (!nameMatch) return trimmed;
109
+ const name = nameMatch[1];
110
+ const eventProps = [];
111
+ for (const m of trimmed.matchAll(/\b(on[A-Z]\w+)=/g)) {
112
+ eventProps.push(m[1]);
113
+ }
114
+ return eventProps.length > 0 ? `${name}(${eventProps.join(", ")})` : name;
115
+ }
116
+ function shouldIgnoreJSX(line) {
117
+ const t = line.trim();
118
+ 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);
119
+ }
120
+ function parseJSXToFlowTree(lines) {
121
+ const roots = [];
122
+ const stack = [];
123
+ for (const line of lines) {
124
+ if (!isJSXElement(line)) continue;
125
+ if (shouldIgnoreJSX(line)) continue;
126
+ const depth = getIndentDepth(line);
127
+ if (isJSXClosing(line)) {
128
+ while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
129
+ stack.pop();
130
+ }
131
+ continue;
132
+ }
133
+ const name = extractJSXComponentName(line);
134
+ const selfClosing = isJSXSelfClosing(line);
135
+ const node = {
136
+ type: "call",
137
+ name,
138
+ children: [],
139
+ depth,
140
+ priority: 5 /* OTHER */
141
+ };
142
+ while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
143
+ stack.pop();
144
+ }
145
+ if (stack.length === 0) {
146
+ roots.push(node);
147
+ } else {
148
+ stack[stack.length - 1].node.children.push(node);
149
+ }
150
+ if (!selfClosing) {
151
+ stack.push({ node, depth });
152
+ }
153
+ }
154
+ return roots;
155
+ }
31
156
  function filterDiffLines(diffText) {
32
157
  const lines = diffText.split("\n");
33
158
  const added = lines.filter((l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+").map((l) => l.substring(1));
@@ -37,10 +162,10 @@ function filterDiffLines(diffText) {
37
162
  function normalizeCode(lines) {
38
163
  return lines.map((line) => {
39
164
  let normalized = line;
40
- normalized = normalized.replace(/;$/, "");
41
165
  normalized = normalized.replace(/\/\/.*$/, "");
42
166
  normalized = normalized.replace(/\/\*.*?\*\//, "");
43
167
  normalized = normalized.trim();
168
+ normalized = normalized.replace(/;$/, "");
44
169
  return normalized;
45
170
  }).filter((line) => line.length > 0);
46
171
  }
@@ -77,7 +202,13 @@ function isLoop(line) {
77
202
  return /^(for|while)\s*\(/.test(line.trim());
78
203
  }
79
204
  function isFunctionDeclaration(line) {
80
- return /^(function|const|let|var)\s+\w+\s*=?\s*(async\s*)?\(/.test(line.trim()) || /^(async\s+)?function\s+\w+/.test(line.trim());
205
+ const t = line.trim();
206
+ return (
207
+ // function foo() / async function foo()
208
+ /^(async\s+)?function\s+\w+/.test(t) || // const foo = () => / const foo = async () => / const foo = async (x: T) =>
209
+ /^(const|let|var)\s+\w+\s*=\s*(async\s*)?\(/.test(t) || // const foo = function / const foo = async function
210
+ /^(const|let|var)\s+\w+\s*=\s*(async\s+)?function/.test(t)
211
+ );
81
212
  }
82
213
  function shouldIgnore(line) {
83
214
  const ignorePatterns = [
@@ -136,6 +267,19 @@ function parseToFlowTree(lines) {
136
267
  prevLine = line;
137
268
  continue;
138
269
  }
270
+ if (isFunctionDeclaration(line)) {
271
+ const funcMatch = line.match(/(?:function|const|let|var)\s+(\w+)/);
272
+ roots.push({
273
+ type: "function",
274
+ name: funcMatch ? `${funcMatch[1]}()` : "function()",
275
+ children: [],
276
+ depth: relativeDepth,
277
+ priority: 4 /* FUNCTION */
278
+ });
279
+ currentChain = null;
280
+ prevLine = line;
281
+ continue;
282
+ }
139
283
  const root = extractRoot(line);
140
284
  if (root) {
141
285
  currentChain = {
@@ -166,16 +310,6 @@ function parseToFlowTree(lines) {
166
310
  priority: 3 /* LOOP */
167
311
  });
168
312
  currentChain = null;
169
- } else if (isFunctionDeclaration(line)) {
170
- const funcMatch = line.match(/(?:function|const|let|var)\s+(\w+)/);
171
- roots.push({
172
- type: "function",
173
- name: funcMatch ? `${funcMatch[1]}()` : "function()",
174
- children: [],
175
- depth: relativeDepth,
176
- priority: 4 /* FUNCTION */
177
- });
178
- currentChain = null;
179
313
  }
180
314
  prevLine = line;
181
315
  }
@@ -192,19 +326,21 @@ function renderFlowTree(nodes, indent = 0) {
192
326
  }
193
327
  return lines;
194
328
  }
195
- function parseDiffToLogicalFlow(diffText) {
329
+ function generateReaderMarkdown(diffText, meta = {}) {
196
330
  const { added, removed } = filterDiffLines(diffText);
197
- const normalizedAdded = normalizeCode(added);
331
+ const isJSX = Boolean(
332
+ meta.file && isJSXFile(meta.file) || hasJSXContent(added)
333
+ );
334
+ const addedForFlow = isJSX ? added.filter((l) => !isClassNameOnlyLine(l)) : added;
335
+ const normalizedAdded = normalizeCode(addedForFlow);
198
336
  const flowTree = parseToFlowTree(normalizedAdded);
199
- return {
200
- root: flowTree,
201
- rawCode: added.join("\n"),
202
- removedCode: removed.join("\n")
203
- };
204
- }
205
- function generateReaderMarkdown(diffText, meta = {}) {
206
- const result = parseDiffToLogicalFlow(diffText);
337
+ const rawCode = addedForFlow.join("\n");
338
+ const removedForCode = isJSX ? removed.filter((l) => !isClassNameOnlyLine(l)) : removed;
339
+ const removedCode = removedForCode.join("\n");
340
+ const classNameChanges = isJSX ? parseClassNameChanges(added, removed) : [];
341
+ const jsxTree = isJSX ? parseJSXToFlowTree(added) : [];
207
342
  const sections = [];
343
+ const lang = isJSX ? "tsx" : "typescript";
208
344
  sections.push("# \u{1F4D6} GitHub Reader View\n");
209
345
  sections.push("> Generated by **github-mobile-reader**");
210
346
  if (meta.repo) sections.push(`> Repository: ${meta.repo}`);
@@ -212,22 +348,33 @@ function generateReaderMarkdown(diffText, meta = {}) {
212
348
  if (meta.commit) sections.push(`> Commit: \`${meta.commit}\``);
213
349
  if (meta.file) sections.push(`> File: \`${meta.file}\``);
214
350
  sections.push("\n");
215
- if (result.root.length > 0) {
351
+ if (flowTree.length > 0) {
216
352
  sections.push("## \u{1F9E0} Logical Flow\n");
217
353
  sections.push("```");
218
- sections.push(...renderFlowTree(result.root));
354
+ sections.push(...renderFlowTree(flowTree));
219
355
  sections.push("```\n");
220
356
  }
221
- if (result.rawCode.trim()) {
357
+ if (isJSX && jsxTree.length > 0) {
358
+ sections.push("## \u{1F3A8} JSX Structure\n");
359
+ sections.push("```");
360
+ sections.push(...renderFlowTree(jsxTree));
361
+ sections.push("```\n");
362
+ }
363
+ if (isJSX && classNameChanges.length > 0) {
364
+ sections.push("## \u{1F485} Style Changes\n");
365
+ sections.push(...renderStyleChanges(classNameChanges));
366
+ sections.push("");
367
+ }
368
+ if (rawCode.trim()) {
222
369
  sections.push("## \u2705 Added Code\n");
223
- sections.push("```typescript");
224
- sections.push(result.rawCode);
370
+ sections.push(`\`\`\`${lang}`);
371
+ sections.push(rawCode);
225
372
  sections.push("```\n");
226
373
  }
227
- if (result.removedCode.trim()) {
374
+ if (removedCode.trim()) {
228
375
  sections.push("## \u274C Removed Code\n");
229
- sections.push("```typescript");
230
- sections.push(result.removedCode);
376
+ sections.push(`\`\`\`${lang}`);
377
+ sections.push(removedCode);
231
378
  sections.push("```\n");
232
379
  }
233
380
  sections.push("---");