@kokiito0926/superhacker 0.0.2 → 0.0.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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/index.js +79 -56
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -11,7 +11,7 @@ $ npm install --global @kokiito0926/superhacker
11
11
 
12
12
  ## 使用方法
13
13
 
14
- ストーリーの一覧を表示します。
14
+ ストーリーの一覧を表示します。
15
15
  --limitのオプションで表示件数を制限することもできます。
16
16
 
17
17
  ```bash
@@ -25,7 +25,7 @@ $ superhacker list --limit 10
25
25
  $ superhacker comment --id 46726480
26
26
  ```
27
27
 
28
- コメントのIDを指定して、すべての返信を再帰的に取得します。
28
+ コメントのIDを指定して、すべての返信を再帰的に取得します。
29
29
  デフォルトでは、ツリー構造で取得されますが、フラットな形式で取得することもできます。
30
30
 
31
31
  ```bash
package/index.js CHANGED
@@ -5,14 +5,23 @@
5
5
  // >> $ node ./index.js comment --id 2921983
6
6
  // >> $ node ./index.js comments --id 8863 --format flat
7
7
 
8
- import { minimist, argv } from "zx";
8
+ import { argv } from "zx";
9
9
 
10
10
  const BASE_URL = "https://hacker-news.firebaseio.com/v0";
11
11
 
12
12
  async function getItem(id) {
13
13
  if (!id) return null;
14
- const res = await fetch(`${BASE_URL}/item/${id}.json`);
15
- return res.json();
14
+ try {
15
+ const res = await fetch(`${BASE_URL}/item/${id}.json`);
16
+ if (!res.ok) {
17
+ console.error(`Error fetching item ${id}: ${res.statusText}`);
18
+ return null;
19
+ }
20
+ return await res.json();
21
+ } catch (error) {
22
+ console.error(`Error fetching item ${id}: ${error.message}`);
23
+ return null;
24
+ }
16
25
  }
17
26
 
18
27
  async function getCommentsRecursive(ids, allComments = []) {
@@ -32,82 +41,96 @@ async function getCommentsRecursive(ids, allComments = []) {
32
41
  }
33
42
 
34
43
  function buildTree(list, parentId) {
35
- return list
36
- .filter((item) => item.parent === parentId)
37
- .map((item) => ({
38
- ...item,
39
- replies: buildTree(list, item.id),
40
- }));
44
+ const map = new Map();
45
+ for (const item of list) {
46
+ map.set(item.id, { ...item, replies: [] });
47
+ }
48
+
49
+ const tree = [];
50
+ for (const item of list) {
51
+ if (item.parent === parentId) {
52
+ tree.push(map.get(item.id));
53
+ } else {
54
+ const parent = map.get(item.parent);
55
+ if (parent) {
56
+ parent.replies.push(map.get(item.id));
57
+ }
58
+ }
59
+ }
60
+ return tree;
41
61
  }
42
62
 
43
63
  const command = argv._[0];
44
64
  if (!command) {
45
- console.error(`Error: Command is required.`);
65
+ console.error(`Error: Command is required (list, comment, comments).`);
46
66
  process.exit(1);
47
67
  }
48
68
  // console.log(command);
49
69
 
50
- const args = minimist(process.argv.slice(2));
51
- const id = args.id;
52
- const limit = args.limit ? parseInt(args.limit) : null;
53
- const flat = args.format ? args.format === "flat" : false;
70
+ const id = argv?.id ? parseInt(argv.id) : null;
71
+ const limit = argv?.limit ? parseInt(argv?.limit) : null;
72
+ const flat = argv?.format ? argv?.format === "flat" : false;
54
73
 
55
74
  if (command === "list") {
56
- try {
57
- const idsResponse = await fetch(`${BASE_URL}/topstories.json`);
58
- let ids = await idsResponse.json();
75
+ const idsResponse = await fetch(`${BASE_URL}/topstories.json`);
76
+ if (!idsResponse.ok) {
77
+ console.error(`Error fetching top stories: ${idsResponse.statusText}`);
78
+ process.exit(1);
79
+ }
80
+ let ids = await idsResponse.json();
59
81
 
60
- if (limit > 0) {
61
- ids = ids.slice(0, limit);
62
- }
82
+ if (limit > 0) {
83
+ ids = ids.slice(0, limit);
84
+ }
63
85
 
64
- const stories = await Promise.all(ids.map((id) => getItem(id)));
65
- const sortedStories = stories.sort((a, b) => b.score - a.score);
86
+ const stories = await Promise.all(ids.map((id) => getItem(id)));
87
+ const sortedStories = stories
88
+ .filter((s) => s !== null)
89
+ .sort((a, b) => (b.score || 0) - (a.score || 0));
66
90
 
67
- console.log(JSON.stringify(sortedStories, null, 2));
68
- } catch (e) {
69
- console.error(`Error fetching list: ${e.message}`);
70
- process.exit(1);
71
- }
72
- } else if (command === "comment") {
91
+ console.log(JSON.stringify(sortedStories, null, 2));
92
+ } else if (command === "comment" || command === "item") {
73
93
  if (!id) {
74
- console.error("Error: ID is required via --id or stdin.");
94
+ console.error("Error: ID is required via --id.");
75
95
  process.exit(1);
76
96
  }
77
97
  const item = await getItem(id);
98
+ if (!item) {
99
+ console.error(`Error: Item ${id} not found.`);
100
+ process.exit(1);
101
+ }
78
102
  console.log(JSON.stringify(item, null, 2));
79
103
  } else if (command === "comments") {
80
104
  if (!id) {
81
- console.error("Error: Story ID is required.");
105
+ console.error("Error: Story/Comment ID is required via --id.");
82
106
  process.exit(1);
83
107
  }
84
108
 
85
- try {
86
- const rootItem = await getItem(id);
87
- if (!rootItem) {
88
- console.log(JSON.stringify([]));
89
- process.exit(0);
90
- }
91
-
92
- const flatComments = rootItem.kids ? await getCommentsRecursive(rootItem.kids) : [];
93
- // console.log(flatComments);
94
-
95
- if (flat) {
96
- console.log(JSON.stringify([rootItem, ...flatComments], null, 2));
97
- } else {
98
- console.log(
99
- JSON.stringify(
100
- {
101
- ...rootItem,
102
- replies: buildTree(flatComments, parseInt(id)),
103
- },
104
- null,
105
- 2,
106
- ),
107
- );
108
- }
109
- } catch (e) {
110
- console.error(`Error fetching comments: ${e.message}`);
109
+ const rootItem = await getItem(id);
110
+ if (!rootItem) {
111
+ console.error(`Error: Root item ${id} not found.`);
111
112
  process.exit(1);
112
113
  }
114
+
115
+ const flatComments = rootItem.kids ? await getCommentsRecursive(rootItem.kids) : [];
116
+ // console.log(flatComments);
117
+
118
+ if (flat) {
119
+ console.log(JSON.stringify([rootItem, ...flatComments], null, 2));
120
+ } else {
121
+ const tree = buildTree(flatComments, id);
122
+ console.log(
123
+ JSON.stringify(
124
+ {
125
+ ...rootItem,
126
+ replies: tree,
127
+ },
128
+ null,
129
+ 2,
130
+ ),
131
+ );
132
+ }
133
+ } else {
134
+ console.error(`Error: Unknown command "${command}". Available commands: list, comment, comments.`);
135
+ process.exit(1);
113
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kokiito0926/superhacker",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "private": false,
5
5
  "description": "Hacker NewsのAPIを利用して、ストーリーやコメントをJSON形式で取得することができるコマンドラインのツールです。",
6
6
  "keywords": [