github-mobile-reader 0.1.2 → 0.1.4
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 +74 -65
- package/README.md +73 -66
- package/dist/action.js +72 -188
- package/dist/cli.js +74 -189
- package/dist/index.d.mts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +85 -35
- package/dist/index.mjs +83 -35
- package/package.json +1 -1
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,18 +66,21 @@ 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 = [];
|
|
76
82
|
for (const [comp, { added, removed }] of componentMap) {
|
|
83
|
+
if (comp === "unknown") continue;
|
|
77
84
|
const pureAdded = [...added].filter((c) => !removed.has(c));
|
|
78
85
|
const pureRemoved = [...removed].filter((c) => !added.has(c));
|
|
79
86
|
if (pureAdded.length === 0 && pureRemoved.length === 0) continue;
|
|
@@ -86,7 +93,8 @@ function renderStyleChanges(changes) {
|
|
|
86
93
|
for (const change of changes) {
|
|
87
94
|
lines.push(`**${change.component}**`);
|
|
88
95
|
if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
|
|
89
|
-
if (change.removed.length > 0)
|
|
96
|
+
if (change.removed.length > 0)
|
|
97
|
+
lines.push(` - ${change.removed.join(" ")}`);
|
|
90
98
|
}
|
|
91
99
|
return lines;
|
|
92
100
|
}
|
|
@@ -155,192 +163,76 @@ function parseJSXToFlowTree(lines) {
|
|
|
155
163
|
}
|
|
156
164
|
function filterDiffLines(diffText) {
|
|
157
165
|
const lines = diffText.split("\n");
|
|
158
|
-
const added = lines.filter(
|
|
159
|
-
|
|
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));
|
|
160
172
|
return { added, removed };
|
|
161
173
|
}
|
|
162
|
-
function normalizeCode(lines) {
|
|
163
|
-
return lines.map((line) => {
|
|
164
|
-
let normalized = line;
|
|
165
|
-
normalized = normalized.replace(/\/\/.*$/, "");
|
|
166
|
-
normalized = normalized.replace(/\/\*.*?\*\//, "");
|
|
167
|
-
normalized = normalized.trim();
|
|
168
|
-
normalized = normalized.replace(/;$/, "");
|
|
169
|
-
return normalized;
|
|
170
|
-
}).filter((line) => line.length > 0);
|
|
171
|
-
}
|
|
172
174
|
function getIndentDepth(line) {
|
|
173
175
|
const match = line.match(/^(\s*)/);
|
|
174
176
|
if (!match) return 0;
|
|
175
177
|
return Math.floor(match[1].length / 2);
|
|
176
178
|
}
|
|
177
|
-
function
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
function simplifyCallback(methodCall) {
|
|
189
|
-
const arrowMatch = methodCall.match(/(\w+)\((\w+)\s*=>\s*(\w+)\.(\w+)\)/);
|
|
190
|
-
if (arrowMatch) {
|
|
191
|
-
const [, method, param, , prop] = arrowMatch;
|
|
192
|
-
return `${method}(${param} \u2192 ${prop})`;
|
|
193
|
-
}
|
|
194
|
-
const callbackMatch = methodCall.match(/(\w+)\([^)]+\)/);
|
|
195
|
-
if (callbackMatch) return `${callbackMatch[1]}(callback)`;
|
|
196
|
-
return methodCall;
|
|
197
|
-
}
|
|
198
|
-
function isConditional(line) {
|
|
199
|
-
return /^(if|else|switch)\s*[\(\{]/.test(line.trim());
|
|
200
|
-
}
|
|
201
|
-
function isLoop(line) {
|
|
202
|
-
return /^(for|while)\s*\(/.test(line.trim());
|
|
203
|
-
}
|
|
204
|
-
function isFunctionDeclaration(line) {
|
|
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
|
-
);
|
|
212
|
-
}
|
|
213
|
-
function shouldIgnore(line) {
|
|
214
|
-
const ignorePatterns = [
|
|
215
|
-
/^import\s+/,
|
|
216
|
-
/^export\s+/,
|
|
217
|
-
/^type\s+/,
|
|
218
|
-
/^interface\s+/,
|
|
219
|
-
/^console\./,
|
|
220
|
-
/^return$/,
|
|
221
|
-
/^throw\s+/
|
|
222
|
-
];
|
|
223
|
-
return ignorePatterns.some((p) => p.test(line.trim()));
|
|
224
|
-
}
|
|
225
|
-
function extractRoot(line) {
|
|
226
|
-
const assignMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*(\w+)/);
|
|
227
|
-
if (assignMatch) return assignMatch[2];
|
|
228
|
-
const callMatch = line.match(/^(\w+)\(/);
|
|
229
|
-
if (callMatch) return `${callMatch[1]}()`;
|
|
230
|
-
const methodMatch = line.match(/^(\w+)\./);
|
|
231
|
-
if (methodMatch) return methodMatch[1];
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
function parseToFlowTree(lines) {
|
|
235
|
-
const roots = [];
|
|
236
|
-
let currentChain = null;
|
|
237
|
-
let prevLine = null;
|
|
238
|
-
let baseDepth = -1;
|
|
239
|
-
for (let i = 0; i < lines.length; i++) {
|
|
240
|
-
const line = lines[i];
|
|
241
|
-
if (shouldIgnore(line)) {
|
|
242
|
-
prevLine = line;
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
const depth = getIndentDepth(lines[i]);
|
|
246
|
-
if (baseDepth === -1) baseDepth = depth;
|
|
247
|
-
const relativeDepth = depth - baseDepth;
|
|
248
|
-
if (isChaining(line, prevLine)) {
|
|
249
|
-
const method = extractChainMethod(line);
|
|
250
|
-
const simplified = simplifyCallback(method);
|
|
251
|
-
if (currentChain) {
|
|
252
|
-
const chainNode = {
|
|
253
|
-
type: "chain",
|
|
254
|
-
name: simplified,
|
|
255
|
-
children: [],
|
|
256
|
-
depth: relativeDepth,
|
|
257
|
-
priority: 1 /* CHAINING */
|
|
258
|
-
};
|
|
259
|
-
let parent = currentChain;
|
|
260
|
-
while (parent.children.length > 0 && parent.children[parent.children.length - 1].depth >= relativeDepth) {
|
|
261
|
-
const last = parent.children[parent.children.length - 1];
|
|
262
|
-
if (last.children.length > 0) parent = last;
|
|
263
|
-
else break;
|
|
264
|
-
}
|
|
265
|
-
parent.children.push(chainNode);
|
|
179
|
+
function extractChangedSymbols(addedLines, removedLines) {
|
|
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*[<(]/;
|
|
181
|
+
const COMPONENT_RE = /^(?:export\s+)?(?:default\s+)?(?:function|const)\s+([A-Z][a-z][A-Za-z0-9]*)/;
|
|
182
|
+
const extract = (lines) => {
|
|
183
|
+
const names = /* @__PURE__ */ new Set();
|
|
184
|
+
for (const line of lines) {
|
|
185
|
+
const cm = line.match(COMPONENT_RE) || line.match(FUNC_RE);
|
|
186
|
+
if (cm) {
|
|
187
|
+
const name = cm[1] || cm[2] || cm[3];
|
|
188
|
+
if (name) names.add(name);
|
|
266
189
|
}
|
|
267
|
-
prevLine = line;
|
|
268
|
-
continue;
|
|
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
190
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
children: [],
|
|
300
|
-
depth: relativeDepth,
|
|
301
|
-
priority: 2 /* CONDITIONAL */
|
|
302
|
-
});
|
|
303
|
-
currentChain = null;
|
|
304
|
-
} else if (isLoop(line)) {
|
|
305
|
-
roots.push({
|
|
306
|
-
type: "loop",
|
|
307
|
-
name: "loop",
|
|
308
|
-
children: [],
|
|
309
|
-
depth: relativeDepth,
|
|
310
|
-
priority: 3 /* LOOP */
|
|
311
|
-
});
|
|
312
|
-
currentChain = null;
|
|
191
|
+
return names;
|
|
192
|
+
};
|
|
193
|
+
const addedNames = extract(addedLines);
|
|
194
|
+
const removedNames = extract(removedLines);
|
|
195
|
+
const results = [];
|
|
196
|
+
const seen = /* @__PURE__ */ new Set();
|
|
197
|
+
for (const name of addedNames) {
|
|
198
|
+
seen.add(name);
|
|
199
|
+
results.push({
|
|
200
|
+
name,
|
|
201
|
+
status: removedNames.has(name) ? "modified" : "added"
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
for (const name of removedNames) {
|
|
205
|
+
if (!seen.has(name)) {
|
|
206
|
+
results.push({ name, status: "removed" });
|
|
313
207
|
}
|
|
314
|
-
prevLine = line;
|
|
315
208
|
}
|
|
316
|
-
return
|
|
209
|
+
return results;
|
|
317
210
|
}
|
|
318
|
-
function
|
|
211
|
+
function renderJSXTreeCompact(nodes, maxDepth = 3) {
|
|
319
212
|
const lines = [];
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
213
|
+
function walk(node, depth) {
|
|
214
|
+
if (depth > maxDepth) return;
|
|
215
|
+
const indent = " ".repeat(depth);
|
|
216
|
+
const hasChildren = node.children.length > 0;
|
|
217
|
+
lines.push(`${indent}${node.name}${hasChildren ? "" : ""}`);
|
|
218
|
+
for (const child of node.children) {
|
|
219
|
+
walk(child, depth + 1);
|
|
325
220
|
}
|
|
326
221
|
}
|
|
327
|
-
|
|
222
|
+
for (const root of nodes) {
|
|
223
|
+
walk(root, 0);
|
|
224
|
+
}
|
|
225
|
+
return lines.join("\n");
|
|
328
226
|
}
|
|
329
227
|
function generateReaderMarkdown(diffText, meta = {}) {
|
|
330
228
|
const { added, removed } = filterDiffLines(diffText);
|
|
331
229
|
const isJSX = Boolean(
|
|
332
230
|
meta.file && isJSXFile(meta.file) || hasJSXContent(added)
|
|
333
231
|
);
|
|
334
|
-
const
|
|
335
|
-
const normalizedAdded = normalizeCode(addedForFlow);
|
|
336
|
-
const flowTree = parseToFlowTree(normalizedAdded);
|
|
337
|
-
const rawCode = addedForFlow.join("\n");
|
|
338
|
-
const removedForCode = isJSX ? removed.filter((l) => !isClassNameOnlyLine(l)) : removed;
|
|
339
|
-
const removedCode = removedForCode.join("\n");
|
|
232
|
+
const changedSymbols = extractChangedSymbols(added, removed);
|
|
340
233
|
const classNameChanges = isJSX ? parseClassNameChanges(added, removed) : [];
|
|
341
234
|
const jsxTree = isJSX ? parseJSXToFlowTree(added) : [];
|
|
342
235
|
const sections = [];
|
|
343
|
-
const lang = isJSX ? "tsx" : "typescript";
|
|
344
236
|
sections.push("# \u{1F4D6} GitHub Reader View\n");
|
|
345
237
|
sections.push("> Generated by **github-mobile-reader**");
|
|
346
238
|
if (meta.repo) sections.push(`> Repository: ${meta.repo}`);
|
|
@@ -348,37 +240,29 @@ function generateReaderMarkdown(diffText, meta = {}) {
|
|
|
348
240
|
if (meta.commit) sections.push(`> Commit: \`${meta.commit}\``);
|
|
349
241
|
if (meta.file) sections.push(`> File: \`${meta.file}\``);
|
|
350
242
|
sections.push("\n");
|
|
351
|
-
if (
|
|
352
|
-
sections.push("
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
243
|
+
if (changedSymbols.length > 0) {
|
|
244
|
+
sections.push("### \uBCC0\uACBD\uB41C \uD568\uC218 / \uCEF4\uD3EC\uB10C\uD2B8\n");
|
|
245
|
+
const STATUS_ICON = { added: "\u2705", removed: "\u274C", modified: "\u270F\uFE0F" };
|
|
246
|
+
for (const { name, status } of changedSymbols) {
|
|
247
|
+
sections.push(`- ${STATUS_ICON[status]} \`${name}()\` \u2014 ${status}`);
|
|
248
|
+
}
|
|
249
|
+
sections.push("");
|
|
356
250
|
}
|
|
357
251
|
if (isJSX && jsxTree.length > 0) {
|
|
358
|
-
sections.push("
|
|
252
|
+
sections.push("### \u{1F3A8} JSX Structure\n");
|
|
359
253
|
sections.push("```");
|
|
360
|
-
sections.push(
|
|
254
|
+
sections.push(renderJSXTreeCompact(jsxTree));
|
|
361
255
|
sections.push("```\n");
|
|
362
256
|
}
|
|
363
257
|
if (isJSX && classNameChanges.length > 0) {
|
|
364
|
-
sections.push("
|
|
258
|
+
sections.push("### \u{1F485} Style Changes\n");
|
|
365
259
|
sections.push(...renderStyleChanges(classNameChanges));
|
|
366
260
|
sections.push("");
|
|
367
261
|
}
|
|
368
|
-
if (rawCode.trim()) {
|
|
369
|
-
sections.push("## \u2705 Added Code\n");
|
|
370
|
-
sections.push(`\`\`\`${lang}`);
|
|
371
|
-
sections.push(rawCode);
|
|
372
|
-
sections.push("```\n");
|
|
373
|
-
}
|
|
374
|
-
if (removedCode.trim()) {
|
|
375
|
-
sections.push("## \u274C Removed Code\n");
|
|
376
|
-
sections.push(`\`\`\`${lang}`);
|
|
377
|
-
sections.push(removedCode);
|
|
378
|
-
sections.push("```\n");
|
|
379
|
-
}
|
|
380
262
|
sections.push("---");
|
|
381
|
-
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
|
+
);
|
|
382
266
|
return sections.join("\n");
|
|
383
267
|
}
|
|
384
268
|
|