github-mobile-reader 0.1.0

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/index.js ADDED
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Priority: () => Priority,
24
+ filterDiffLines: () => filterDiffLines,
25
+ generateReaderMarkdown: () => generateReaderMarkdown,
26
+ normalizeCode: () => normalizeCode,
27
+ parseDiffToLogicalFlow: () => parseDiffToLogicalFlow,
28
+ parseToFlowTree: () => parseToFlowTree,
29
+ renderFlowTree: () => renderFlowTree
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/parser.ts
34
+ var Priority = /* @__PURE__ */ ((Priority2) => {
35
+ Priority2[Priority2["CHAINING"] = 1] = "CHAINING";
36
+ Priority2[Priority2["CONDITIONAL"] = 2] = "CONDITIONAL";
37
+ Priority2[Priority2["LOOP"] = 3] = "LOOP";
38
+ Priority2[Priority2["FUNCTION"] = 4] = "FUNCTION";
39
+ Priority2[Priority2["OTHER"] = 5] = "OTHER";
40
+ return Priority2;
41
+ })(Priority || {});
42
+ function filterDiffLines(diffText) {
43
+ const lines = diffText.split("\n");
44
+ const added = lines.filter((l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+").map((l) => l.substring(1));
45
+ const removed = lines.filter((l) => l.startsWith("-") && !l.startsWith("---") && l.trim() !== "-").map((l) => l.substring(1));
46
+ return { added, removed };
47
+ }
48
+ function normalizeCode(lines) {
49
+ return lines.map((line) => {
50
+ let normalized = line;
51
+ normalized = normalized.replace(/;$/, "");
52
+ normalized = normalized.replace(/\/\/.*$/, "");
53
+ normalized = normalized.replace(/\/\*.*?\*\//, "");
54
+ normalized = normalized.trim();
55
+ return normalized;
56
+ }).filter((line) => line.length > 0);
57
+ }
58
+ function getIndentDepth(line) {
59
+ const match = line.match(/^(\s*)/);
60
+ if (!match) return 0;
61
+ return Math.floor(match[1].length / 2);
62
+ }
63
+ function isChaining(line, prevLine) {
64
+ if (!prevLine) return false;
65
+ if (!line.trim().startsWith(".")) return false;
66
+ if (!prevLine.match(/[)\}]$/)) return false;
67
+ return true;
68
+ }
69
+ function extractChainMethod(line) {
70
+ const match = line.match(/\.(\w+)\(/);
71
+ if (match) return `${match[1]}()`;
72
+ return line.trim();
73
+ }
74
+ function simplifyCallback(methodCall) {
75
+ const arrowMatch = methodCall.match(/(\w+)\((\w+)\s*=>\s*(\w+)\.(\w+)\)/);
76
+ if (arrowMatch) {
77
+ const [, method, param, , prop] = arrowMatch;
78
+ return `${method}(${param} \u2192 ${prop})`;
79
+ }
80
+ const callbackMatch = methodCall.match(/(\w+)\([^)]+\)/);
81
+ if (callbackMatch) return `${callbackMatch[1]}(callback)`;
82
+ return methodCall;
83
+ }
84
+ function isConditional(line) {
85
+ return /^(if|else|switch)\s*[\(\{]/.test(line.trim());
86
+ }
87
+ function isLoop(line) {
88
+ return /^(for|while)\s*\(/.test(line.trim());
89
+ }
90
+ function isFunctionDeclaration(line) {
91
+ return /^(function|const|let|var)\s+\w+\s*=?\s*(async\s*)?\(/.test(line.trim()) || /^(async\s+)?function\s+\w+/.test(line.trim());
92
+ }
93
+ function shouldIgnore(line) {
94
+ const ignorePatterns = [
95
+ /^import\s+/,
96
+ /^export\s+/,
97
+ /^type\s+/,
98
+ /^interface\s+/,
99
+ /^console\./,
100
+ /^return$/,
101
+ /^throw\s+/
102
+ ];
103
+ return ignorePatterns.some((p) => p.test(line.trim()));
104
+ }
105
+ function extractRoot(line) {
106
+ const assignMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*(\w+)/);
107
+ if (assignMatch) return assignMatch[2];
108
+ const callMatch = line.match(/^(\w+)\(/);
109
+ if (callMatch) return `${callMatch[1]}()`;
110
+ const methodMatch = line.match(/^(\w+)\./);
111
+ if (methodMatch) return methodMatch[1];
112
+ return null;
113
+ }
114
+ function parseToFlowTree(lines) {
115
+ const roots = [];
116
+ let currentChain = null;
117
+ let prevLine = null;
118
+ let baseDepth = -1;
119
+ for (let i = 0; i < lines.length; i++) {
120
+ const line = lines[i];
121
+ if (shouldIgnore(line)) {
122
+ prevLine = line;
123
+ continue;
124
+ }
125
+ const depth = getIndentDepth(lines[i]);
126
+ if (baseDepth === -1) baseDepth = depth;
127
+ const relativeDepth = depth - baseDepth;
128
+ if (isChaining(line, prevLine)) {
129
+ const method = extractChainMethod(line);
130
+ const simplified = simplifyCallback(method);
131
+ if (currentChain) {
132
+ const chainNode = {
133
+ type: "chain",
134
+ name: simplified,
135
+ children: [],
136
+ depth: relativeDepth,
137
+ priority: 1 /* CHAINING */
138
+ };
139
+ let parent = currentChain;
140
+ while (parent.children.length > 0 && parent.children[parent.children.length - 1].depth >= relativeDepth) {
141
+ const last = parent.children[parent.children.length - 1];
142
+ if (last.children.length > 0) parent = last;
143
+ else break;
144
+ }
145
+ parent.children.push(chainNode);
146
+ }
147
+ prevLine = line;
148
+ continue;
149
+ }
150
+ const root = extractRoot(line);
151
+ if (root) {
152
+ currentChain = {
153
+ type: "root",
154
+ name: root,
155
+ children: [],
156
+ depth: relativeDepth,
157
+ priority: 1 /* CHAINING */
158
+ };
159
+ roots.push(currentChain);
160
+ } else if (isConditional(line)) {
161
+ const condMatch = line.match(/(if|else|switch)\s*\(([^)]+)\)/);
162
+ const condName = condMatch ? `${condMatch[1]} (${condMatch[2]})` : line.trim();
163
+ roots.push({
164
+ type: "condition",
165
+ name: condName,
166
+ children: [],
167
+ depth: relativeDepth,
168
+ priority: 2 /* CONDITIONAL */
169
+ });
170
+ currentChain = null;
171
+ } else if (isLoop(line)) {
172
+ roots.push({
173
+ type: "loop",
174
+ name: "loop",
175
+ children: [],
176
+ depth: relativeDepth,
177
+ priority: 3 /* LOOP */
178
+ });
179
+ currentChain = null;
180
+ } else if (isFunctionDeclaration(line)) {
181
+ const funcMatch = line.match(/(?:function|const|let|var)\s+(\w+)/);
182
+ roots.push({
183
+ type: "function",
184
+ name: funcMatch ? `${funcMatch[1]}()` : "function()",
185
+ children: [],
186
+ depth: relativeDepth,
187
+ priority: 4 /* FUNCTION */
188
+ });
189
+ currentChain = null;
190
+ }
191
+ prevLine = line;
192
+ }
193
+ return roots;
194
+ }
195
+ function renderFlowTree(nodes, indent = 0) {
196
+ const lines = [];
197
+ const prefix = indent === 0 ? "" : " ".repeat((indent - 1) * 4) + " \u2514\u2500 ";
198
+ for (const node of nodes) {
199
+ lines.push(prefix + node.name);
200
+ if (node.children.length > 0) {
201
+ lines.push(...renderFlowTree(node.children, indent + 1));
202
+ }
203
+ }
204
+ return lines;
205
+ }
206
+ function parseDiffToLogicalFlow(diffText) {
207
+ const { added, removed } = filterDiffLines(diffText);
208
+ const normalizedAdded = normalizeCode(added);
209
+ const flowTree = parseToFlowTree(normalizedAdded);
210
+ return {
211
+ root: flowTree,
212
+ rawCode: added.join("\n"),
213
+ removedCode: removed.join("\n")
214
+ };
215
+ }
216
+ function generateReaderMarkdown(diffText, meta = {}) {
217
+ const result = parseDiffToLogicalFlow(diffText);
218
+ const sections = [];
219
+ sections.push("# \u{1F4D6} GitHub Reader View\n");
220
+ sections.push("> Generated by **github-mobile-reader**");
221
+ if (meta.repo) sections.push(`> Repository: ${meta.repo}`);
222
+ if (meta.pr) sections.push(`> Pull Request: #${meta.pr}`);
223
+ if (meta.commit) sections.push(`> Commit: \`${meta.commit}\``);
224
+ if (meta.file) sections.push(`> File: \`${meta.file}\``);
225
+ sections.push("\n");
226
+ if (result.root.length > 0) {
227
+ sections.push("## \u{1F9E0} Logical Flow\n");
228
+ sections.push("```");
229
+ sections.push(...renderFlowTree(result.root));
230
+ sections.push("```\n");
231
+ }
232
+ if (result.rawCode.trim()) {
233
+ sections.push("## \u2705 Added Code\n");
234
+ sections.push("```typescript");
235
+ sections.push(result.rawCode);
236
+ sections.push("```\n");
237
+ }
238
+ if (result.removedCode.trim()) {
239
+ sections.push("## \u274C Removed Code\n");
240
+ sections.push("```typescript");
241
+ sections.push(result.removedCode);
242
+ sections.push("```\n");
243
+ }
244
+ sections.push("---");
245
+ sections.push("\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/your-org/github-mobile-reader). Do not edit manually.");
246
+ return sections.join("\n");
247
+ }
248
+ // Annotate the CommonJS export names for ESM import in node:
249
+ 0 && (module.exports = {
250
+ Priority,
251
+ filterDiffLines,
252
+ generateReaderMarkdown,
253
+ normalizeCode,
254
+ parseDiffToLogicalFlow,
255
+ parseToFlowTree,
256
+ renderFlowTree
257
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,224 @@
1
+ // src/parser.ts
2
+ var Priority = /* @__PURE__ */ ((Priority2) => {
3
+ Priority2[Priority2["CHAINING"] = 1] = "CHAINING";
4
+ Priority2[Priority2["CONDITIONAL"] = 2] = "CONDITIONAL";
5
+ Priority2[Priority2["LOOP"] = 3] = "LOOP";
6
+ Priority2[Priority2["FUNCTION"] = 4] = "FUNCTION";
7
+ Priority2[Priority2["OTHER"] = 5] = "OTHER";
8
+ return Priority2;
9
+ })(Priority || {});
10
+ function filterDiffLines(diffText) {
11
+ const lines = diffText.split("\n");
12
+ const added = lines.filter((l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+").map((l) => l.substring(1));
13
+ const removed = lines.filter((l) => l.startsWith("-") && !l.startsWith("---") && l.trim() !== "-").map((l) => l.substring(1));
14
+ return { added, removed };
15
+ }
16
+ function normalizeCode(lines) {
17
+ return lines.map((line) => {
18
+ let normalized = line;
19
+ normalized = normalized.replace(/;$/, "");
20
+ normalized = normalized.replace(/\/\/.*$/, "");
21
+ normalized = normalized.replace(/\/\*.*?\*\//, "");
22
+ normalized = normalized.trim();
23
+ return normalized;
24
+ }).filter((line) => line.length > 0);
25
+ }
26
+ function getIndentDepth(line) {
27
+ const match = line.match(/^(\s*)/);
28
+ if (!match) return 0;
29
+ return Math.floor(match[1].length / 2);
30
+ }
31
+ function isChaining(line, prevLine) {
32
+ if (!prevLine) return false;
33
+ if (!line.trim().startsWith(".")) return false;
34
+ if (!prevLine.match(/[)\}]$/)) return false;
35
+ return true;
36
+ }
37
+ function extractChainMethod(line) {
38
+ const match = line.match(/\.(\w+)\(/);
39
+ if (match) return `${match[1]}()`;
40
+ return line.trim();
41
+ }
42
+ function simplifyCallback(methodCall) {
43
+ const arrowMatch = methodCall.match(/(\w+)\((\w+)\s*=>\s*(\w+)\.(\w+)\)/);
44
+ if (arrowMatch) {
45
+ const [, method, param, , prop] = arrowMatch;
46
+ return `${method}(${param} \u2192 ${prop})`;
47
+ }
48
+ const callbackMatch = methodCall.match(/(\w+)\([^)]+\)/);
49
+ if (callbackMatch) return `${callbackMatch[1]}(callback)`;
50
+ return methodCall;
51
+ }
52
+ function isConditional(line) {
53
+ return /^(if|else|switch)\s*[\(\{]/.test(line.trim());
54
+ }
55
+ function isLoop(line) {
56
+ return /^(for|while)\s*\(/.test(line.trim());
57
+ }
58
+ function isFunctionDeclaration(line) {
59
+ return /^(function|const|let|var)\s+\w+\s*=?\s*(async\s*)?\(/.test(line.trim()) || /^(async\s+)?function\s+\w+/.test(line.trim());
60
+ }
61
+ function shouldIgnore(line) {
62
+ const ignorePatterns = [
63
+ /^import\s+/,
64
+ /^export\s+/,
65
+ /^type\s+/,
66
+ /^interface\s+/,
67
+ /^console\./,
68
+ /^return$/,
69
+ /^throw\s+/
70
+ ];
71
+ return ignorePatterns.some((p) => p.test(line.trim()));
72
+ }
73
+ function extractRoot(line) {
74
+ const assignMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*(\w+)/);
75
+ if (assignMatch) return assignMatch[2];
76
+ const callMatch = line.match(/^(\w+)\(/);
77
+ if (callMatch) return `${callMatch[1]}()`;
78
+ const methodMatch = line.match(/^(\w+)\./);
79
+ if (methodMatch) return methodMatch[1];
80
+ return null;
81
+ }
82
+ function parseToFlowTree(lines) {
83
+ const roots = [];
84
+ let currentChain = null;
85
+ let prevLine = null;
86
+ let baseDepth = -1;
87
+ for (let i = 0; i < lines.length; i++) {
88
+ const line = lines[i];
89
+ if (shouldIgnore(line)) {
90
+ prevLine = line;
91
+ continue;
92
+ }
93
+ const depth = getIndentDepth(lines[i]);
94
+ if (baseDepth === -1) baseDepth = depth;
95
+ const relativeDepth = depth - baseDepth;
96
+ if (isChaining(line, prevLine)) {
97
+ const method = extractChainMethod(line);
98
+ const simplified = simplifyCallback(method);
99
+ if (currentChain) {
100
+ const chainNode = {
101
+ type: "chain",
102
+ name: simplified,
103
+ children: [],
104
+ depth: relativeDepth,
105
+ priority: 1 /* CHAINING */
106
+ };
107
+ let parent = currentChain;
108
+ while (parent.children.length > 0 && parent.children[parent.children.length - 1].depth >= relativeDepth) {
109
+ const last = parent.children[parent.children.length - 1];
110
+ if (last.children.length > 0) parent = last;
111
+ else break;
112
+ }
113
+ parent.children.push(chainNode);
114
+ }
115
+ prevLine = line;
116
+ continue;
117
+ }
118
+ const root = extractRoot(line);
119
+ if (root) {
120
+ currentChain = {
121
+ type: "root",
122
+ name: root,
123
+ children: [],
124
+ depth: relativeDepth,
125
+ priority: 1 /* CHAINING */
126
+ };
127
+ roots.push(currentChain);
128
+ } else if (isConditional(line)) {
129
+ const condMatch = line.match(/(if|else|switch)\s*\(([^)]+)\)/);
130
+ const condName = condMatch ? `${condMatch[1]} (${condMatch[2]})` : line.trim();
131
+ roots.push({
132
+ type: "condition",
133
+ name: condName,
134
+ children: [],
135
+ depth: relativeDepth,
136
+ priority: 2 /* CONDITIONAL */
137
+ });
138
+ currentChain = null;
139
+ } else if (isLoop(line)) {
140
+ roots.push({
141
+ type: "loop",
142
+ name: "loop",
143
+ children: [],
144
+ depth: relativeDepth,
145
+ priority: 3 /* LOOP */
146
+ });
147
+ currentChain = null;
148
+ } else if (isFunctionDeclaration(line)) {
149
+ const funcMatch = line.match(/(?:function|const|let|var)\s+(\w+)/);
150
+ roots.push({
151
+ type: "function",
152
+ name: funcMatch ? `${funcMatch[1]}()` : "function()",
153
+ children: [],
154
+ depth: relativeDepth,
155
+ priority: 4 /* FUNCTION */
156
+ });
157
+ currentChain = null;
158
+ }
159
+ prevLine = line;
160
+ }
161
+ return roots;
162
+ }
163
+ function renderFlowTree(nodes, indent = 0) {
164
+ const lines = [];
165
+ const prefix = indent === 0 ? "" : " ".repeat((indent - 1) * 4) + " \u2514\u2500 ";
166
+ for (const node of nodes) {
167
+ lines.push(prefix + node.name);
168
+ if (node.children.length > 0) {
169
+ lines.push(...renderFlowTree(node.children, indent + 1));
170
+ }
171
+ }
172
+ return lines;
173
+ }
174
+ function parseDiffToLogicalFlow(diffText) {
175
+ const { added, removed } = filterDiffLines(diffText);
176
+ const normalizedAdded = normalizeCode(added);
177
+ const flowTree = parseToFlowTree(normalizedAdded);
178
+ return {
179
+ root: flowTree,
180
+ rawCode: added.join("\n"),
181
+ removedCode: removed.join("\n")
182
+ };
183
+ }
184
+ function generateReaderMarkdown(diffText, meta = {}) {
185
+ const result = parseDiffToLogicalFlow(diffText);
186
+ const sections = [];
187
+ sections.push("# \u{1F4D6} GitHub Reader View\n");
188
+ sections.push("> Generated by **github-mobile-reader**");
189
+ if (meta.repo) sections.push(`> Repository: ${meta.repo}`);
190
+ if (meta.pr) sections.push(`> Pull Request: #${meta.pr}`);
191
+ if (meta.commit) sections.push(`> Commit: \`${meta.commit}\``);
192
+ if (meta.file) sections.push(`> File: \`${meta.file}\``);
193
+ sections.push("\n");
194
+ if (result.root.length > 0) {
195
+ sections.push("## \u{1F9E0} Logical Flow\n");
196
+ sections.push("```");
197
+ sections.push(...renderFlowTree(result.root));
198
+ sections.push("```\n");
199
+ }
200
+ if (result.rawCode.trim()) {
201
+ sections.push("## \u2705 Added Code\n");
202
+ sections.push("```typescript");
203
+ sections.push(result.rawCode);
204
+ sections.push("```\n");
205
+ }
206
+ if (result.removedCode.trim()) {
207
+ sections.push("## \u274C Removed Code\n");
208
+ sections.push("```typescript");
209
+ sections.push(result.removedCode);
210
+ sections.push("```\n");
211
+ }
212
+ sections.push("---");
213
+ sections.push("\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/your-org/github-mobile-reader). Do not edit manually.");
214
+ return sections.join("\n");
215
+ }
216
+ export {
217
+ Priority,
218
+ filterDiffLines,
219
+ generateReaderMarkdown,
220
+ normalizeCode,
221
+ parseDiffToLogicalFlow,
222
+ parseToFlowTree,
223
+ renderFlowTree
224
+ };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "github-mobile-reader",
3
+ "version": "0.1.0",
4
+ "description": "Transform git diffs into mobile-friendly Markdown — no more horizontal scrolling when reviewing code on your phone.",
5
+ "keywords": [
6
+ "github",
7
+ "mobile",
8
+ "code-review",
9
+ "diff",
10
+ "markdown",
11
+ "logical-flow",
12
+ "github-actions"
13
+ ],
14
+ "homepage": "https://github.com/3rdflr/github-mobile-reader#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/3rdflr/github-mobile-reader/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/3rdflr/github-mobile-reader.git"
21
+ },
22
+ "license": "MIT",
23
+ "author": "3rdflrhtl@gmail.com",
24
+ "main": "./dist/index.js",
25
+ "module": "./dist/index.mjs",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.mjs",
31
+ "require": "./dist/index.js"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "action.yml",
37
+ "README.md"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
41
+ "build:action": "tsup src/action.ts --format cjs --outDir dist --no-splitting",
42
+ "build:all": "npm run build && npm run build:action",
43
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
44
+ "prepublishOnly": "npm run build:all"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^20.0.0",
48
+ "tsup": "^8.0.0",
49
+ "typescript": "^5.3.0"
50
+ },
51
+ "engines": {
52
+ "node": ">=18.0.0"
53
+ }
54
+ }