devsplain 1.5.2 → 1.5.3

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 (2) hide show
  1. package/bin/cli.js +13 -13
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -22,7 +22,7 @@ function isGitDirty() {
22
22
  return false;
23
23
  }
24
24
 
25
- /** Determines if a specific line index falls within a multi-line string or block */
25
+ /** Determines if a specific line index falls within a string literal */
26
26
  function isLineInsideString(lines, targetLineIndex, ext = '') {
27
27
  const isPython = ext.toLowerCase() === '.py';
28
28
  let inBacktick = false;
@@ -31,6 +31,7 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
31
31
  let inSingle = false;
32
32
  let inDouble = false;
33
33
 
34
+ // Iterate through lines prior to the target to track string/block state
34
35
  for (let i = 0; i < targetLineIndex; i++) {
35
36
  const line = lines[i];
36
37
  let j = 0;
@@ -51,6 +52,7 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
51
52
  }
52
53
  }
53
54
  } else {
55
+ // Check for unescaped backtick (JS template strings) or quotes
54
56
  if (!inSingle && !inDouble && line[j] === '`') {
55
57
  let escaped = false;
56
58
  let k = j - 1;
@@ -96,7 +98,7 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
96
98
  return inBacktick || inTripleDouble || inTripleSingle || inSingle || inDouble;
97
99
  }
98
100
 
99
- /** Analyzes code to determine which lines are purely comments */
101
+ /** Performs a lexical analysis to categorize code lines and comment blocks */
100
102
  function analyzeComments(lines, ext = '') {
101
103
  const isPython = ext.toLowerCase() === '.py';
102
104
  const isHTML = ['.html', '.vue', '.svelte'].includes(ext.toLowerCase());
@@ -108,6 +110,7 @@ function analyzeComments(lines, ext = '') {
108
110
  let inDouble = false;
109
111
  let inBlockJS = false;
110
112
  let inBlockHTML = false;
113
+ // Iterate through each line character by character to detect comment boundaries
111
114
  for (let i = 0; i < lines.length; i++) {
112
115
  const line = lines[i];
113
116
  let commentStartIndex = -1;
@@ -247,19 +250,19 @@ function analyzeComments(lines, ext = '') {
247
250
  return analysis;
248
251
  }
249
252
 
250
- /** Splices AI-generated comments into source code or cleans existing ones */
253
+ /** Splices generated comments into the source data or removes existing ones */
251
254
  function spliceComments(data, comments, mode = 'default', ext = '') {
255
+ // Determine platform-specific line endings
252
256
  const hasCRLF = data.includes('\r\n');
253
257
  const lineEnding = hasCRLF ? '\r\n' : '\n';
254
258
  const originalLines = data.split(/\r?\n/);
255
259
  const sortedComments = [...comments].sort((a, b) => b.line - a.line);
256
260
  const validComments = sortedComments.filter(c => c.line >= 1 && c.line <= originalLines.length + 1);
257
261
 
258
- // Map lines to objects to track original positioning after splicing
259
262
  const annotated = originalLines.map((text, index) => ({ text, originalIndex: index }));
260
263
  let analysis = null;
261
264
 
262
- // Logic for removing existing comments
265
+ // 'clean' mode removes all existing comments/documentation
263
266
  if (mode === 'clean') {
264
267
  analysis = analyzeComments(originalLines, ext);
265
268
  const finalDeletions = new Set();
@@ -281,7 +284,6 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
281
284
 
282
285
  const linesToDelete = Array.from(finalDeletions).sort((a, b) => b - a);
283
286
 
284
- // Process deletions in reverse to maintain line integrity
285
287
  for (const lineNum of linesToDelete) {
286
288
  const targetLine = originalLines[lineNum - 1];
287
289
  if (!targetLine) continue;
@@ -309,6 +311,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
309
311
  annotated.splice(lineNum - 1, 1);
310
312
  }
311
313
  } else {
314
+ // 'default'/'light'/'full' mode: Inject AI-generated comments
312
315
  for (const c of validComments) {
313
316
  if (isLineInsideString(originalLines, c.line - 1, ext)) {
314
317
  console.warn(`[devsplain] Skipping comment insertion at line ${c.line} to avoid string literal corruption.`);
@@ -316,7 +319,6 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
316
319
  }
317
320
 
318
321
  const targetLine = originalLines[c.line - 1] || '';
319
- // Determine indentation level for new comment blocks
320
322
  const indentMatch = targetLine.match(/^([ \t]*)/);
321
323
  const indentation = indentMatch ? indentMatch[1] : '';
322
324
 
@@ -334,7 +336,6 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
334
336
  }
335
337
  }
336
338
 
337
- // Verify that the result matches expected output before committing to disk
338
339
  const filtered = annotated.filter(line => line.originalIndex !== -1);
339
340
  const filteredText = filtered.map(line => line.text);
340
341
  const filteredIndices = filtered.map(line => line.originalIndex);
@@ -373,7 +374,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
373
374
  return annotated.map(line => line.text).join(lineEnding);
374
375
  }
375
376
 
376
- /** Main CLI execution loop */
377
+ /** Main entry point for the CLI tool logic */
377
378
  async function runCLI() {
378
379
  rl = readline.createInterface({ input: process.stdin, output: process.stdout });
379
380
  askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
@@ -490,7 +491,7 @@ Options:
490
491
  let successCount = 0;
491
492
  let failCount = 0;
492
493
 
493
- /** Recursively process files or directories */
494
+ /** Recursively traverses the file system to identify and process source files */
494
495
  async function processPath(targetPath) {
495
496
  const stats = fs.statSync(targetPath);
496
497
 
@@ -534,8 +535,8 @@ Options:
534
535
  }
535
536
 
536
537
  console.log(` Analyzing ${filename} in ${mode} mode...`);
537
- // Perform comment generation if not in 'clean' mode
538
538
  try {
539
+ // Logic to either clean existing comments or replace/insert new ones
539
540
  let comments = [];
540
541
  let commentedCode;
541
542
  if (mode !== 'clean') {
@@ -551,7 +552,7 @@ Options:
551
552
  console.log(`---------------------------------------\n`);
552
553
  const answer = await askQuestion("Type 'write' to save to file, or press any key to discard: ");
553
554
  if (answer.toLowerCase() === 'write') {
554
- // Atomic write: write to temp file then rename
555
+ // Use temporary file for atomic write operations
555
556
  const tempPath = targetPath + '.tmp';
556
557
  fs.writeFileSync(tempPath, commentedCode, 'utf8');
557
558
  fs.renameSync(tempPath, targetPath);
@@ -589,7 +590,6 @@ Options:
589
590
  rl.close();
590
591
  }
591
592
 
592
- // Execute main if run directly, otherwise export utility functions
593
593
  if (require.main === module) {
594
594
  runCLI().catch(err => {
595
595
  console.error(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devsplain",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "An agent-agnostic CLI tool that automatically adds JSDoc and inline comments to your code using free LLMs.",
5
5
  "author": "mwahaj36",
6
6
  "license": "MIT",