devsplain 1.5.1 → 1.5.2
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 +15 -7
- package/bin/setup-hook.js +14 -12
- 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
|
|
25
|
+
/** Determines if a specific line index falls within a multi-line string or block */
|
|
26
26
|
function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
27
27
|
const isPython = ext.toLowerCase() === '.py';
|
|
28
28
|
let inBacktick = false;
|
|
@@ -96,7 +96,7 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
96
96
|
return inBacktick || inTripleDouble || inTripleSingle || inSingle || inDouble;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
/** Analyzes code to
|
|
99
|
+
/** Analyzes code to determine which lines are purely comments */
|
|
100
100
|
function analyzeComments(lines, ext = '') {
|
|
101
101
|
const isPython = ext.toLowerCase() === '.py';
|
|
102
102
|
const isHTML = ['.html', '.vue', '.svelte'].includes(ext.toLowerCase());
|
|
@@ -247,7 +247,7 @@ function analyzeComments(lines, ext = '') {
|
|
|
247
247
|
return analysis;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
/**
|
|
250
|
+
/** Splices AI-generated comments into source code or cleans existing ones */
|
|
251
251
|
function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
252
252
|
const hasCRLF = data.includes('\r\n');
|
|
253
253
|
const lineEnding = hasCRLF ? '\r\n' : '\n';
|
|
@@ -255,9 +255,11 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
255
255
|
const sortedComments = [...comments].sort((a, b) => b.line - a.line);
|
|
256
256
|
const validComments = sortedComments.filter(c => c.line >= 1 && c.line <= originalLines.length + 1);
|
|
257
257
|
|
|
258
|
+
// Map lines to objects to track original positioning after splicing
|
|
258
259
|
const annotated = originalLines.map((text, index) => ({ text, originalIndex: index }));
|
|
259
260
|
let analysis = null;
|
|
260
261
|
|
|
262
|
+
// Logic for removing existing comments
|
|
261
263
|
if (mode === 'clean') {
|
|
262
264
|
analysis = analyzeComments(originalLines, ext);
|
|
263
265
|
const finalDeletions = new Set();
|
|
@@ -279,6 +281,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
279
281
|
|
|
280
282
|
const linesToDelete = Array.from(finalDeletions).sort((a, b) => b - a);
|
|
281
283
|
|
|
284
|
+
// Process deletions in reverse to maintain line integrity
|
|
282
285
|
for (const lineNum of linesToDelete) {
|
|
283
286
|
const targetLine = originalLines[lineNum - 1];
|
|
284
287
|
if (!targetLine) continue;
|
|
@@ -313,6 +316,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
313
316
|
}
|
|
314
317
|
|
|
315
318
|
const targetLine = originalLines[c.line - 1] || '';
|
|
319
|
+
// Determine indentation level for new comment blocks
|
|
316
320
|
const indentMatch = targetLine.match(/^([ \t]*)/);
|
|
317
321
|
const indentation = indentMatch ? indentMatch[1] : '';
|
|
318
322
|
|
|
@@ -330,6 +334,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
330
334
|
}
|
|
331
335
|
}
|
|
332
336
|
|
|
337
|
+
// Verify that the result matches expected output before committing to disk
|
|
333
338
|
const filtered = annotated.filter(line => line.originalIndex !== -1);
|
|
334
339
|
const filteredText = filtered.map(line => line.text);
|
|
335
340
|
const filteredIndices = filtered.map(line => line.originalIndex);
|
|
@@ -368,7 +373,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
368
373
|
return annotated.map(line => line.text).join(lineEnding);
|
|
369
374
|
}
|
|
370
375
|
|
|
371
|
-
/** Main CLI
|
|
376
|
+
/** Main CLI execution loop */
|
|
372
377
|
async function runCLI() {
|
|
373
378
|
rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
374
379
|
askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
@@ -416,7 +421,8 @@ Options:
|
|
|
416
421
|
|
|
417
422
|
if (args.includes('--setup-hook')) {
|
|
418
423
|
rl.close();
|
|
419
|
-
require('./setup-hook.js');
|
|
424
|
+
const installHooks = require('./setup-hook.js');
|
|
425
|
+
await installHooks();
|
|
420
426
|
return;
|
|
421
427
|
}
|
|
422
428
|
|
|
@@ -484,7 +490,7 @@ Options:
|
|
|
484
490
|
let successCount = 0;
|
|
485
491
|
let failCount = 0;
|
|
486
492
|
|
|
487
|
-
/** Recursively
|
|
493
|
+
/** Recursively process files or directories */
|
|
488
494
|
async function processPath(targetPath) {
|
|
489
495
|
const stats = fs.statSync(targetPath);
|
|
490
496
|
|
|
@@ -528,6 +534,7 @@ Options:
|
|
|
528
534
|
}
|
|
529
535
|
|
|
530
536
|
console.log(` Analyzing ${filename} in ${mode} mode...`);
|
|
537
|
+
// Perform comment generation if not in 'clean' mode
|
|
531
538
|
try {
|
|
532
539
|
let comments = [];
|
|
533
540
|
let commentedCode;
|
|
@@ -544,6 +551,7 @@ Options:
|
|
|
544
551
|
console.log(`---------------------------------------\n`);
|
|
545
552
|
const answer = await askQuestion("Type 'write' to save to file, or press any key to discard: ");
|
|
546
553
|
if (answer.toLowerCase() === 'write') {
|
|
554
|
+
// Atomic write: write to temp file then rename
|
|
547
555
|
const tempPath = targetPath + '.tmp';
|
|
548
556
|
fs.writeFileSync(tempPath, commentedCode, 'utf8');
|
|
549
557
|
fs.renameSync(tempPath, targetPath);
|
|
@@ -581,7 +589,7 @@ Options:
|
|
|
581
589
|
rl.close();
|
|
582
590
|
}
|
|
583
591
|
|
|
584
|
-
//
|
|
592
|
+
// Execute main if run directly, otherwise export utility functions
|
|
585
593
|
if (require.main === module) {
|
|
586
594
|
runCLI().catch(err => {
|
|
587
595
|
console.error(err);
|
package/bin/setup-hook.js
CHANGED
|
@@ -4,12 +4,12 @@ const { execSync } = require('child_process');
|
|
|
4
4
|
const readline = require('readline');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Initializes and installs Git pre-commit and post-commit hooks.
|
|
8
|
+
* Prompts the user for documentation mode configuration.
|
|
9
9
|
*/
|
|
10
10
|
async function installHooks() {
|
|
11
11
|
try {
|
|
12
|
-
// Resolve the
|
|
12
|
+
// Resolve the root .git directory path
|
|
13
13
|
const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf8' }).trim();
|
|
14
14
|
const hooksDir = path.join(gitDir, 'hooks');
|
|
15
15
|
if (!fs.existsSync(hooksDir)) {
|
|
@@ -17,13 +17,13 @@ async function installHooks() {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
let modeChoice = '1';
|
|
20
|
-
//
|
|
20
|
+
// Interactive mode requires a TTY terminal
|
|
21
21
|
if (process.stdout.isTTY) {
|
|
22
22
|
const rl = readline.createInterface({
|
|
23
23
|
input: process.stdin,
|
|
24
24
|
output: process.stdout
|
|
25
25
|
});
|
|
26
|
-
//
|
|
26
|
+
// Promisify the readline interface for async/await flow
|
|
27
27
|
const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
28
28
|
|
|
29
29
|
console.log('\nSelect default commenting mode for Git commits:');
|
|
@@ -35,7 +35,7 @@ async function installHooks() {
|
|
|
35
35
|
rl.close();
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// Determine documentation style flags based on user input
|
|
39
39
|
let modeArgs = '';
|
|
40
40
|
if (modeChoice === '2') {
|
|
41
41
|
modeArgs = ' --light';
|
|
@@ -43,7 +43,7 @@ async function installHooks() {
|
|
|
43
43
|
modeArgs = ' --full';
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
//
|
|
46
|
+
// Write the pre-commit script to the git hooks directory
|
|
47
47
|
const preCommitHookPath = path.join(hooksDir, 'pre-commit');
|
|
48
48
|
const preCommitContent = `#!/bin/sh
|
|
49
49
|
# devsplain native pre-commit hook
|
|
@@ -51,12 +51,12 @@ echo "Running pre-commit tests..."
|
|
|
51
51
|
npm test || exit 1
|
|
52
52
|
`;
|
|
53
53
|
fs.writeFileSync(preCommitHookPath, preCommitContent);
|
|
54
|
-
// Ensure the shell script is executable by the system
|
|
55
54
|
try {
|
|
55
|
+
// Ensure the hook file is executable
|
|
56
56
|
fs.chmodSync(preCommitHookPath, 0o755);
|
|
57
57
|
} catch (err) {}
|
|
58
58
|
|
|
59
|
-
//
|
|
59
|
+
// Write the post-commit script that triggers documentation generation
|
|
60
60
|
const postCommitHookPath = path.join(hooksDir, 'post-commit');
|
|
61
61
|
const postCommitContent = `#!/bin/sh
|
|
62
62
|
# devsplain native post-commit hook
|
|
@@ -64,7 +64,6 @@ echo "Auto-generating comments for files in the last commit..."
|
|
|
64
64
|
node bin/post-commit.js${modeArgs} || exit 1
|
|
65
65
|
`;
|
|
66
66
|
fs.writeFileSync(postCommitHookPath, postCommitContent);
|
|
67
|
-
// Ensure the post-commit shell script is executable
|
|
68
67
|
try {
|
|
69
68
|
fs.chmodSync(postCommitHookPath, 0o755);
|
|
70
69
|
} catch (err) {}
|
|
@@ -75,5 +74,8 @@ node bin/post-commit.js${modeArgs} || exit 1
|
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
// Execute the
|
|
79
|
-
|
|
77
|
+
// Execute the function automatically if the file is run directly
|
|
78
|
+
if (require.main === module) {
|
|
79
|
+
installHooks();
|
|
80
|
+
}
|
|
81
|
+
module.exports = installHooks;
|
package/package.json
CHANGED