commentme 1.0.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/getcoms.js ADDED
@@ -0,0 +1,86 @@
1
+ // import fs from "fs";
2
+
3
+ // export function getAllComments() {
4
+ // if (!fs.existsSync("output.json")) {
5
+ // throw new Error("output.json not found. Run skim first.");
6
+ // }
7
+
8
+ // const raw = fs.readFileSync("output.json", "utf8");
9
+ // const comments = JSON.parse(raw);
10
+
11
+ // console.log(comments);
12
+ // }
13
+ // import { CommentStore } from "./models/CommentStore.js";
14
+ import path from "path";
15
+ import { getCurrentUserId } from "./utils/currentUser.js";
16
+ import { getSession } from "./utils/session.js";
17
+ import { API_BASE_URL } from "./utils/config.js";
18
+
19
+
20
+ export async function getAllComments(filePath = null) {
21
+ const codebase = filePath ? path.basename(filePath) : "default";
22
+
23
+ // Check if user is logged in
24
+ try {
25
+ getCurrentUserId();
26
+ } catch (e) {
27
+ console.error(e.message);
28
+ return;
29
+ }
30
+
31
+ const session = getSession();
32
+ const token = session.token; // Try to get token if saved, though current saveSession only saves userId.
33
+ // If auth middleware requires token, this needs to be updated.
34
+ // However, the user instruction implies we just need to hit the API.
35
+ // We'll pass the token in Authorization header if it exists.
36
+
37
+ try {
38
+ const response = await fetch(`${API_BASE_URL}/comments?codebase=${encodeURIComponent(codebase)}`, {
39
+ method: "GET",
40
+ headers: {
41
+ "Content-Type": "application/json",
42
+ "Authorization": token ? `Bearer ${token}` : "" // Sending token if available
43
+ }
44
+ });
45
+
46
+ const data = await response.json();
47
+
48
+ if (!response.ok) {
49
+ // Gracefully handle empty or error
50
+ if (response.status === 404) {
51
+ console.log("{}");
52
+ return;
53
+ }
54
+ throw new Error(data.message || "Failed to fetch comments");
55
+ }
56
+
57
+ console.log(data); // The API returns the comments object directly
58
+
59
+ } catch (error) {
60
+ if (error.code === 'ECONNREFUSED') {
61
+ console.error("Error: Could not connect to the backend server. Is it running on port 8080?");
62
+ } else {
63
+ console.error("Error:", error.message);
64
+ }
65
+ }
66
+
67
+ /*
68
+ const userId = getCurrentUserId();
69
+ const store = await CommentStore.findOne({ userId });
70
+
71
+ if (!store) {
72
+ console.log("{}");
73
+ return;
74
+ }
75
+
76
+ const codebaseIndex= store.comments.findIndex(c => c.codebase === codebase);
77
+
78
+ if (codebaseIndex === -1 || !store.comments[codebaseIndex].filecomment) {
79
+ console.log("{}");
80
+ return;
81
+ }
82
+ const codebaseEntry = store.comments[codebaseIndex];
83
+
84
+ console.log(Object.fromEntries(codebaseEntry.filecomment));
85
+ */
86
+ }
package/getspeccoms.js ADDED
@@ -0,0 +1,101 @@
1
+ // import fs from "fs";
2
+
3
+ // export function getSpecificComment(key, silent = false) {
4
+ // if (!fs.existsSync("output.json")) {
5
+ // throw new Error("output.json not found. Run skim first.");
6
+ // }
7
+
8
+ // const raw = fs.readFileSync("output.json", "utf8");
9
+ // const comments = JSON.parse(raw);
10
+
11
+ // if (!comments[key]) {
12
+ // throw new Error(`No comment found for key: ${key}`);
13
+ // }
14
+
15
+ // if (!silent) {
16
+ // console.log(`✔ Comment for ${key}:`);
17
+ // console.log(comments[key]);
18
+ // }
19
+
20
+ // return comments[key]; // IMPORTANT for edit flow
21
+ // }
22
+
23
+ // import { CommentStore } from "./models/CommentStore.js";
24
+ import path from "path";
25
+ import { getCurrentUserId } from "./utils/currentUser.js";
26
+ import { getSession } from "./utils/session.js";
27
+ import { API_BASE_URL } from "./utils/config.js";
28
+
29
+ export async function getSpecificComment(key, silent = false, filePath = null) {
30
+ const codebase = filePath ? path.basename(filePath) : "default";
31
+
32
+ // Check authentication
33
+ try {
34
+ getCurrentUserId();
35
+ } catch (e) {
36
+ if (!silent) console.error(e.message);
37
+ throw e;
38
+ }
39
+
40
+ const session = getSession();
41
+ const token = session ? session.token : null;
42
+
43
+ try {
44
+ const response = await fetch(`${API_BASE_URL}/comments/${encodeURIComponent(key)}?codebase=${encodeURIComponent(codebase)}`, {
45
+ method: "GET",
46
+ headers: {
47
+ "Content-Type": "application/json",
48
+ "Authorization": token ? `Bearer ${token}` : ""
49
+ }
50
+ });
51
+
52
+ const data = await response.json();
53
+
54
+ if (!response.ok) {
55
+ // Handle error, throw regular error to match previous behavior for catch blocks up the chain
56
+ throw new Error(data.message || `No comment found for key: ${key}`);
57
+ }
58
+
59
+ const value = data.comment; // Backend returns { key, comment }
60
+
61
+ if (!silent) {
62
+ console.log(`✔ Comment for ${key}:`);
63
+ console.log(value);
64
+ }
65
+
66
+ return value;
67
+
68
+ } catch (error) {
69
+ if (error.code === 'ECONNREFUSED') {
70
+ if (!silent) console.error("Error: Could not connect to the backend server. Is it running on port 8080?");
71
+ throw new Error("Connection failed");
72
+ } else {
73
+ // Re-throw so caller can handle "No comment found" etc.
74
+ throw error;
75
+ }
76
+ }
77
+
78
+ /*
79
+ const userId = getCurrentUserId();
80
+ const store = await CommentStore.findOne({ userId });
81
+
82
+ if (!store) {
83
+ throw new Error(`No comment found for key: ${key}`);
84
+ }
85
+
86
+ const codebaseIndex = store.comments.findIndex(c => c.codebase === codebase);
87
+
88
+ if (codebaseIndex === -1 || !store.comments[codebaseIndex].filecomment.has(key)) {
89
+ throw new Error(`No comment found for key: ${key}`);
90
+ }
91
+ const codebaseEntry = store.comments[codebaseIndex];
92
+ const value = codebaseEntry.filecomment.get(key);
93
+
94
+ if (!silent) {
95
+ console.log(`✔ Comment for ${key}:`);
96
+ console.log(value);
97
+ }
98
+
99
+ return value;
100
+ */
101
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "commentme",
3
+ "version": "1.0.0",
4
+ "description": "A CLI tool to manage, redact and unredact your comments keeping the codebase clutterfree.",
5
+ "type": "module",
6
+ "bin": {
7
+ "commentme": "./bin/commentme.js"
8
+ },
9
+ "main": "bin/commentme.js",
10
+ "files": [
11
+ "bin/",
12
+ "utils/",
13
+ "config/",
14
+ "auth/",
15
+ "*.js"
16
+ ],
17
+ "scripts": {
18
+ "test": "echo \"Error: no test specified\" && exit 1"
19
+ },
20
+ "author": "dotlasher",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "acorn": "^8.15.0",
24
+ "bcryptjs": "^3.0.3",
25
+ "dotenv": "^17.2.3",
26
+ "mongoose": "^9.0.1"
27
+ }
28
+ }
package/skimcoms.js ADDED
@@ -0,0 +1,221 @@
1
+ // import * as acorn from "acorn";
2
+ // import fs from "fs";
3
+
4
+ // export function removeCommentsFromFile(filePath) {
5
+ // const code = fs.readFileSync(filePath, "utf8");
6
+
7
+ // const comments = [];
8
+ // const commentJSON = {};
9
+
10
+ // acorn.parse(code, {
11
+ // ecmaVersion: 2020,
12
+ // locations: true,
13
+ // onComment: (isBlock, text, start, end, startLoc, endLoc) => {
14
+ // comments.push({
15
+ // start,
16
+ // end,
17
+ // text,
18
+ // lineStart: startLoc.line,
19
+ // lineEnd: endLoc.line
20
+ // });
21
+ // }
22
+ // });
23
+
24
+ // for (const c of comments) {
25
+ // const key = `${c.lineStart}-${c.lineEnd}`;
26
+ // commentJSON[key] = c.text.trim();
27
+ // }
28
+
29
+ // comments.sort((a, b) => b.start - a.start);
30
+
31
+ // let cleaned = code;
32
+ // for (const c of comments) {
33
+ // cleaned = cleaned.slice(0, c.start) + cleaned.slice(c.end);
34
+ // }
35
+
36
+ // fs.writeFileSync(filePath, cleaned, "utf8");
37
+ // fs.writeFileSync("output.json", JSON.stringify(commentJSON, null, 2));
38
+
39
+ // return { cleaned, commentJSON };
40
+ // }
41
+ import * as acorn from "acorn";
42
+ import fs from "fs";
43
+ import path from "path";
44
+ // import { CommentStore } from "./models/CommentStore.js";
45
+ import { getSession } from "./utils/session.js";
46
+ import { API_BASE_URL } from "./utils/config.js";
47
+ import { getCurrentUserId } from "./utils/currentUser.js";
48
+ import { getCommentPattern, detectComments, formatReferenceComment } from "./utils/commentPatterns.js";
49
+
50
+ export async function removeCommentsFromFile(filePath, codebase = null) {
51
+ if (!fs.existsSync(filePath)) {
52
+ throw new Error(`File not found: ${filePath}`);
53
+ }
54
+
55
+ // Use filename as codebase if not provided
56
+ if (!codebase) {
57
+ codebase = path.basename(filePath);
58
+ }
59
+
60
+ // Check authentication
61
+ try {
62
+ getCurrentUserId();
63
+ } catch (e) {
64
+ console.error(e.message);
65
+ return;
66
+ }
67
+
68
+ const code = fs.readFileSync(filePath, "utf8");
69
+
70
+ const comments = [];
71
+ const commentMap = {};
72
+ const ext = path.extname(filePath).slice(1).toLowerCase();
73
+
74
+ // Use acorn for JavaScript files (more accurate), regex for others
75
+ if (ext === "js" || ext === "jsx" || ext === "ts" || ext === "tsx") {
76
+ try {
77
+ // Parse comments using acorn with module support
78
+ acorn.parse(code, {
79
+ ecmaVersion: 2020,
80
+ sourceType: "module",
81
+ locations: true,
82
+ onComment: (isBlock, text, start, end, startLoc, endLoc) => {
83
+ // Skip reference comments
84
+ if (text.trim().startsWith("#ref")) {
85
+ return;
86
+ }
87
+ comments.push({
88
+ start,
89
+ end,
90
+ text,
91
+ lineStart: startLoc.line,
92
+ lineEnd: endLoc.line,
93
+ isBlock: isBlock
94
+ });
95
+ }
96
+ });
97
+ } catch (e) {
98
+ // Clear half-populated comments from failed acorn parse
99
+ comments.length = 0;
100
+ // Fallback to regex-based detection if acorn fails (e.g., on JSX or TS syntax)
101
+ const pattern = getCommentPattern(filePath);
102
+ const detectedComments = detectComments(code, pattern);
103
+ comments.push(...detectedComments);
104
+ }
105
+ } else {
106
+ // Use regex-based detection for other file types
107
+ const pattern = getCommentPattern(filePath);
108
+ const detectedComments = detectComments(code, pattern);
109
+ comments.push(...detectedComments);
110
+ }
111
+
112
+ // Build key-value map
113
+ for (const c of comments) {
114
+ const key = `${c.lineStart}-${c.lineEnd}`;
115
+ commentMap[key] = c.text.trim();
116
+ }
117
+
118
+ // Replace comments with reference comments (descending order to preserve positions)
119
+ comments.sort((a, b) => b.start - a.start);
120
+
121
+ let result = code;
122
+ const pattern = getCommentPattern(filePath);
123
+
124
+ for (const c of comments) {
125
+ const key = `${c.lineStart}-${c.lineEnd}`;
126
+ const refComment = formatReferenceComment(key, pattern, c.isBlock);
127
+
128
+ if (c.isInline) {
129
+ // For inline comments: replace comment with reference, preserve code
130
+ // The slice already preserves code before (c.start is at comment start)
131
+ result = result.slice(0, c.start) + refComment + result.slice(c.end);
132
+ } else {
133
+ // Full line comment: replace entire comment
134
+ result = result.slice(0, c.start) + refComment + result.slice(c.end);
135
+ }
136
+ }
137
+
138
+ // Move file write after successful backend storage
139
+ const session = getSession();
140
+ const token = session ? session.token : null;
141
+
142
+ if (!token) {
143
+ throw new Error("Authentication token missing. Please log in again.");
144
+ }
145
+
146
+ try {
147
+ const response = await fetch(`${API_BASE_URL}/comments/upload`, {
148
+ method: "POST",
149
+ headers: {
150
+ "Content-Type": "application/json",
151
+ "Authorization": `Bearer ${token}`
152
+ },
153
+ body: JSON.stringify({ codebase: codebase, comments: commentMap })
154
+ });
155
+
156
+ const data = await response.json();
157
+
158
+ if (!response.ok) {
159
+ throw new Error(data.message || `Failed to upload comments for codebase: ${codebase}`);
160
+ }
161
+
162
+ // ONLY write the file after successful backup to the server
163
+ fs.writeFileSync(filePath, result, "utf8");
164
+ console.log(`✔ File skimmed and comments stored in database (codebase: ${codebase})`);
165
+
166
+ } catch (error) {
167
+ if (error.code === 'ECONNREFUSED') {
168
+ console.error("Error: Could not connect to the backend server. Is it running on Render?");
169
+ } else {
170
+ console.error("Error:", error.message);
171
+ }
172
+ // Return early to prevent returning the "successful" state
173
+ return;
174
+ }
175
+
176
+ /*
177
+ const userId = getCurrentUserId();
178
+
179
+ // 🔥 SAVE/UPDATE COMMENTS TO MONGODB
180
+ let store = await CommentStore.findOne({ userId });
181
+
182
+ if (!store) {
183
+ store = await CommentStore.create({ userId, comments: [] });
184
+ }
185
+
186
+ // Find codebase index
187
+ let codebaseIndex = store.comments.findIndex(c => c.codebase === codebase);
188
+
189
+ if (codebaseIndex === -1) {
190
+ // Create new codebase entry
191
+ store.comments.push({
192
+ codebase,
193
+ filecomment: new Map()
194
+ });
195
+ codebaseIndex = store.comments.length - 1;
196
+ await store.save(); // Save to get the new entry properly initialized
197
+ store = await CommentStore.findById(store._id); // Re-fetch to get proper Mongoose document
198
+ }
199
+
200
+ // Get the codebase entry
201
+ const codebaseEntry = store.comments[codebaseIndex];
202
+
203
+ // Ensure filecomment is a Map
204
+ if (!(codebaseEntry.filecomment instanceof Map)) {
205
+ codebaseEntry.filecomment = new Map(Object.entries(codebaseEntry.filecomment || {}));
206
+ }
207
+
208
+ // Update existing comments or add new ones
209
+ for (const [key, value] of Object.entries(commentMap)) {
210
+ codebaseEntry.filecomment.set(key, value);
211
+ }
212
+
213
+ // Mark as modified and save
214
+ store.markModified(`comments.${codebaseIndex}.filecomment`);
215
+ await store.save();
216
+
217
+ console.log(`✔ File skimmed and comments stored in database (codebase: ${codebase})`);
218
+ */
219
+
220
+ return { cleaned: result, commentMap };
221
+ }
package/unskimcoms.js ADDED
@@ -0,0 +1,147 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ // import { CommentStore } from "./models/CommentStore.js";
4
+ import { getCurrentUserId } from "./utils/currentUser.js";
5
+ import { getSession } from "./utils/session.js";
6
+ import { API_BASE_URL } from "./utils/config.js";
7
+ import { getCommentPattern, formatComment } from "./utils/commentPatterns.js";
8
+
9
+ export async function unskimComments(filePath, codebase = null) {
10
+ if (!fs.existsSync(filePath)) {
11
+ throw new Error(`File not found: ${filePath}`);
12
+ }
13
+
14
+ // Use filename as codebase if not provided
15
+ if (!codebase) {
16
+ codebase = path.basename(filePath);
17
+ }
18
+
19
+ // Check authentication
20
+ try {
21
+ getCurrentUserId();
22
+ } catch (e) {
23
+ console.error(e.message);
24
+ return;
25
+ }
26
+
27
+ const session = getSession();
28
+ const token = session ? session.token : null;
29
+ if (!token) {
30
+ throw new Error("Authentication token missing. Please log in again.");
31
+ }
32
+
33
+ let comments = {};
34
+
35
+ try {
36
+ const response = await fetch(`${API_BASE_URL}/comments?codebase=${encodeURIComponent(codebase)}`, {
37
+ method: "GET",
38
+ headers: {
39
+ "Content-Type": "application/json",
40
+ "Authorization": `Bearer ${token}`
41
+ }
42
+ });
43
+
44
+ const data = await response.json();
45
+
46
+ if (!response.ok) {
47
+ if (response.status === 404) {
48
+ throw new Error(`No comments found for codebase: ${codebase}`);
49
+ }
50
+ throw new Error(data.message || `Failed to fetch comments for codebase: ${codebase}`);
51
+ }
52
+
53
+ comments = data;
54
+
55
+ } catch (error) {
56
+ if (error.code === 'ECONNREFUSED') {
57
+ console.error("Error: Could not connect to the backend server. Is it running on port 8080?");
58
+ return;
59
+ } else {
60
+ throw error;
61
+ }
62
+ }
63
+
64
+
65
+ /*
66
+ const userId = getCurrentUserId();
67
+
68
+ // Fetch comments from DB
69
+ const store = await CommentStore.findOne({ userId });
70
+
71
+ if (!store) {
72
+ throw new Error("No comments found for this user");
73
+ }
74
+
75
+ // Find the specific codebase
76
+ const codebaseEntry = store.comments.find(c => c.codebase === codebase);
77
+
78
+ if (!codebaseEntry) {
79
+ throw new Error(`No comments found for codebase: ${codebase}`);
80
+ }
81
+
82
+ // Ensure filecomment is a Map
83
+ let filecommentMap;
84
+ if (codebaseEntry.filecomment instanceof Map) {
85
+ filecommentMap = codebaseEntry.filecomment;
86
+ } else {
87
+ filecommentMap = new Map(Object.entries(codebaseEntry.filecomment || {}));
88
+ }
89
+
90
+ if (filecommentMap.size === 0) {
91
+ throw new Error(`No comments found for codebase: ${codebase}`);
92
+ }
93
+
94
+ const comments = Object.fromEntries(filecommentMap);
95
+ */
96
+
97
+ // Get comment pattern based on file extension
98
+ const pattern = getCommentPattern(filePath);
99
+
100
+ // Load file
101
+ const code = fs.readFileSync(filePath, "utf8");
102
+ let lines = code.split("\n");
103
+
104
+ // Replace reference comments with actual comments
105
+ for (let i = 0; i < lines.length; i++) {
106
+ const line = lines[i];
107
+
108
+ // Match reference comment pattern (works for different comment styles, including block)
109
+ const refCommentMatch = line.match(/(\/\/|#|--|;|<!--|%|\/\*)\s*#ref\s+(\d+-\d+)\s*(-->|\*\/)?/);
110
+
111
+ if (refCommentMatch) {
112
+ const fullMatch = refCommentMatch[0];
113
+ const prefix = refCommentMatch[1];
114
+ const key = refCommentMatch[2];
115
+ const suffix = refCommentMatch[3] || "";
116
+ const commentText = comments[key];
117
+
118
+ if (commentText) {
119
+ let restoredComment = "";
120
+
121
+ if (prefix === "/*") {
122
+ // Mirror block comment style
123
+ restoredComment = `/* ${commentText} */`;
124
+ } else if (prefix === "<!--") {
125
+ // Mirror HTML comment style
126
+ restoredComment = `<!-- ${commentText} -->`;
127
+ } else {
128
+ // Default to line comment style
129
+ restoredComment = `${prefix} ${commentText}`;
130
+ }
131
+
132
+ // Replace only the reference part, preserving everything else on the line
133
+ lines[i] = line.replace(fullMatch, restoredComment);
134
+ } else {
135
+ // Key not found in DB - remove the reference comment part
136
+ lines[i] = line.replace(fullMatch, "").trimEnd();
137
+ // If line is now just whitespace, clear it to preserve line numbers if possible
138
+ if (lines[i].trim().length === 0) lines[i] = "";
139
+ }
140
+ }
141
+ }
142
+
143
+ // Write restored file
144
+ fs.writeFileSync(filePath, lines.join("\n"), "utf8");
145
+
146
+ console.log(`✔ Comments successfully restored from database (codebase: ${codebase})`);
147
+ }