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.
- package/bin/cli.js +13 -13
- 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
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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