devsplain 2.2.1 → 2.2.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 +14 -6
- package/bin/setup-hook.js +97 -34
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ const { execSync } = require('child_process');
|
|
|
10
10
|
let rl;
|
|
11
11
|
let askQuestion;
|
|
12
12
|
|
|
13
|
-
//
|
|
13
|
+
// Checks if the Git repository is dirty [ds]
|
|
14
14
|
function isGitDirty() {
|
|
15
15
|
try {
|
|
16
16
|
const gitDir = execSync('git rev-parse --is-inside-work-tree', { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' }).trim();
|
|
@@ -23,7 +23,7 @@ function isGitDirty() {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
//
|
|
26
|
+
// Determines if a line is inside a string literal [ds]
|
|
27
27
|
function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
28
28
|
const isPython = ext.toLowerCase() === '.py';
|
|
29
29
|
const isHTML = ['.html', '.vue', '.svelte'].includes(ext.toLowerCase());
|
|
@@ -236,7 +236,7 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
236
236
|
return inBacktick || inTripleDouble || inTripleSingle || inSingle || inDouble || inCppRawString || inRegex;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
// Analyzes comments
|
|
239
|
+
// Analyzes comments in the given lines of code [ds]
|
|
240
240
|
function analyzeComments(lines, ext = '') {
|
|
241
241
|
const isPython = ext.toLowerCase() === '.py';
|
|
242
242
|
const isHTML = ['.html', '.vue', '.svelte'].includes(ext.toLowerCase());
|
|
@@ -477,7 +477,7 @@ function analyzeComments(lines, ext = '') {
|
|
|
477
477
|
return analysis;
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
-
//
|
|
480
|
+
// Splices comments into the provided data [ds]
|
|
481
481
|
function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
482
482
|
const hasCRLF = data.includes('\r\n');
|
|
483
483
|
const lineEnding = hasCRLF ? '\r\n' : '\n';
|
|
@@ -717,7 +717,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
717
717
|
return annotated.map(line => line.text).join(lineEnding);
|
|
718
718
|
}
|
|
719
719
|
|
|
720
|
-
// Runs the
|
|
720
|
+
// Runs the CLI interface for the application [ds]
|
|
721
721
|
async function runCLI() {
|
|
722
722
|
rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
723
723
|
askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
@@ -744,6 +744,7 @@ Options:
|
|
|
744
744
|
--base-url <url> Override base URL for custom APIs
|
|
745
745
|
--config Force run the configuration setup wizard
|
|
746
746
|
--setup-hook Install Git pre-commit and post-commit hooks in repository
|
|
747
|
+
--remove-hook Remove devsplain-installed Git hooks from repository
|
|
747
748
|
--help, -h Show this help message
|
|
748
749
|
--version, -v Show version information
|
|
749
750
|
`);
|
|
@@ -767,11 +768,18 @@ Options:
|
|
|
767
768
|
|
|
768
769
|
if (args.includes('--setup-hook')) {
|
|
769
770
|
rl.close();
|
|
770
|
-
const installHooks = require('./setup-hook.js');
|
|
771
|
+
const { installHooks } = require('./setup-hook.js');
|
|
771
772
|
await installHooks();
|
|
772
773
|
return;
|
|
773
774
|
}
|
|
774
775
|
|
|
776
|
+
if (args.includes('--remove-hook')) {
|
|
777
|
+
rl.close();
|
|
778
|
+
const { removeHooks } = require('./setup-hook.js');
|
|
779
|
+
await removeHooks();
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
775
783
|
const getArgValue = (flag) => {
|
|
776
784
|
const index = args.indexOf(flag);
|
|
777
785
|
if (index !== -1 && index + 1 < args.length) {
|
package/bin/setup-hook.js
CHANGED
|
@@ -3,7 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const readline = require('readline');
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// Installs Git hooks for automatic comment generation [ds]
|
|
7
7
|
async function installHooks() {
|
|
8
8
|
try {
|
|
9
9
|
const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf8' }).trim();
|
|
@@ -14,7 +14,7 @@ async function installHooks() {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
let modeChoice = '1';
|
|
17
|
-
//
|
|
17
|
+
// Prompt user for commenting mode if running in a TTY [ds]
|
|
18
18
|
if (process.stdout.isTTY) {
|
|
19
19
|
const rl = readline.createInterface({
|
|
20
20
|
input: process.stdin,
|
|
@@ -22,13 +22,13 @@ async function installHooks() {
|
|
|
22
22
|
});
|
|
23
23
|
const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// Display commenting mode options to the user [ds]
|
|
26
26
|
console.log('\nSelect default commenting mode for Git commits:');
|
|
27
27
|
console.log('1. Balanced (mix of JSDoc and sparse inline comments)');
|
|
28
28
|
console.log('2. Light (JSDoc block comments above functions only)');
|
|
29
29
|
console.log('3. Full (aggressive inline commenting)');
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// Loop until a valid mode choice is selected [ds]
|
|
32
32
|
while (true) {
|
|
33
33
|
const answer = (await askQuestion('Select (1-3, default: 1): ')).trim();
|
|
34
34
|
if (answer === '' || ['1', '2', '3'].includes(answer)) {
|
|
@@ -40,7 +40,7 @@ async function installHooks() {
|
|
|
40
40
|
rl.close();
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// Determine mode arguments based on user
|
|
43
|
+
// Determine mode arguments based on user selection [ds]
|
|
44
44
|
let modeArgs = '';
|
|
45
45
|
if (modeChoice === '2') {
|
|
46
46
|
modeArgs = ' --light';
|
|
@@ -48,7 +48,7 @@ async function installHooks() {
|
|
|
48
48
|
modeArgs = ' --full';
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
//
|
|
51
|
+
// Create pre-commit hook script [ds]
|
|
52
52
|
const preCommitHookPath = path.join(hooksDir, 'pre-commit');
|
|
53
53
|
const preCommitContent = `#!/bin/sh
|
|
54
54
|
# devsplain native pre-commit hook
|
|
@@ -57,63 +57,126 @@ if [ -f package.json ] && grep -q '"test"' package.json 2>/dev/null; then
|
|
|
57
57
|
npm test || exit 1
|
|
58
58
|
fi
|
|
59
59
|
`;
|
|
60
|
-
// Write
|
|
60
|
+
// Write pre-commit hook to file [ds]
|
|
61
61
|
fs.writeFileSync(preCommitHookPath, preCommitContent);
|
|
62
62
|
try {
|
|
63
63
|
fs.chmodSync(preCommitHookPath, 0o755);
|
|
64
64
|
} catch (err) {}
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// Path to post-commit script [ds]
|
|
67
67
|
const postCommitScript = path.join(__dirname, 'post-commit.js').replace(/\\/g, '/');
|
|
68
68
|
|
|
69
|
-
//
|
|
69
|
+
// Create post-commit hook script [ds]
|
|
70
70
|
const postCommitHookPath = path.join(hooksDir, 'post-commit');
|
|
71
71
|
const postCommitContent = `#!/bin/sh
|
|
72
72
|
# devsplain native post-commit hook
|
|
73
73
|
echo "Auto-generating comments for files in the last commit..."
|
|
74
74
|
node "${postCommitScript}"${modeArgs} || exit 1
|
|
75
75
|
`;
|
|
76
|
+
// Write post-commit hook to file [ds]
|
|
76
77
|
fs.writeFileSync(postCommitHookPath, postCommitContent);
|
|
77
78
|
try {
|
|
78
79
|
fs.chmodSync(postCommitHookPath, 0o755);
|
|
79
80
|
} catch (err) {}
|
|
80
81
|
|
|
81
|
-
//
|
|
82
|
+
// Log successful installation of post-commit hook [ds]
|
|
82
83
|
console.log(`[devsplain] Git post-commit hook successfully installed at: ${postCommitHookPath}`);
|
|
83
84
|
|
|
84
|
-
//
|
|
85
|
+
// Path to devsplain ignore file [ds]
|
|
85
86
|
const ignorePath = path.join(gitRoot, '.devsplainignore');
|
|
87
|
+
const defaultIgnoreLines = [
|
|
88
|
+
'node_modules/', '.git/', 'dist/', 'build/', 'out/',
|
|
89
|
+
'.next/', '.nuxt/', '.svelte-kit/',
|
|
90
|
+
'venv/', 'env/', '.venv/',
|
|
91
|
+
'.vscode/', '.idea/', 'coverage/',
|
|
92
|
+
'tests/', '__tests__/', 'fixtures/'
|
|
93
|
+
];
|
|
94
|
+
// List of default patterns to ignore [ds]
|
|
95
|
+
|
|
96
|
+
const gitignorePath = path.join(gitRoot, '.gitignore');
|
|
97
|
+
let gitignoreLines = [];
|
|
98
|
+
if (fs.existsSync(gitignorePath)) {
|
|
99
|
+
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
100
|
+
gitignoreLines = gitignoreContent.split(/\r?\n/)
|
|
101
|
+
.map(l => l.trim())
|
|
102
|
+
.filter(l => l && !l.startsWith('#'));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check if .gitignore file exists [ds]
|
|
86
106
|
if (!fs.existsSync(ignorePath)) {
|
|
87
|
-
const
|
|
88
|
-
.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
.
|
|
93
|
-
.
|
|
94
|
-
.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
+
const gitignoreOnly = gitignoreLines.filter(p => !defaultIgnoreLines.includes(p));
|
|
108
|
+
let content = defaultIgnoreLines.join('\n') + '\n';
|
|
109
|
+
if (gitignoreOnly.length > 0) {
|
|
110
|
+
content += '\n# From .gitignore\n' + gitignoreOnly.join('\n') + '\n';
|
|
111
|
+
}
|
|
112
|
+
fs.writeFileSync(ignorePath, content);
|
|
113
|
+
console.log(`[devsplain] Created .devsplainignore at: ${ignorePath}`);
|
|
114
|
+
if (gitignoreOnly.length > 0) {
|
|
115
|
+
console.log(`[devsplain] Merged ${gitignoreOnly.length} pattern(s) from .gitignore into .devsplainignore.`);
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
const existingContent = fs.readFileSync(ignorePath, 'utf8');
|
|
119
|
+
const existingLines = existingContent.split(/\r?\n/).map(l => l.trim()).filter(l => l && !l.startsWith('#'));
|
|
120
|
+
const newPatterns = gitignoreLines.filter(p => !existingLines.includes(p));
|
|
121
|
+
if (newPatterns.length > 0) {
|
|
122
|
+
const appendContent = '\n# From .gitignore\n' + newPatterns.join('\n') + '\n';
|
|
123
|
+
fs.appendFileSync(ignorePath, appendContent);
|
|
124
|
+
console.log(`[devsplain] Merged ${newPatterns.length} new pattern(s) from .gitignore into .devsplainignore.`);
|
|
125
|
+
} else if (gitignoreLines.length > 0) {
|
|
126
|
+
console.log('[devsplain] .devsplainignore is already up-to-date with .gitignore patterns.');
|
|
127
|
+
}
|
|
128
|
+
// Handle any errors during hook installation [ds]
|
|
107
129
|
}
|
|
130
|
+
|
|
108
131
|
} catch (e) {
|
|
109
132
|
console.warn('Warning: Could not set up Git hooks (not inside a git repository or git command missing).');
|
|
110
133
|
console.warn(e.message);
|
|
111
134
|
}
|
|
112
135
|
}
|
|
113
136
|
|
|
114
|
-
//
|
|
137
|
+
// Removes Git hooks installed by devsplain [ds]
|
|
138
|
+
async function removeHooks() {
|
|
139
|
+
try {
|
|
140
|
+
const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf8' }).trim();
|
|
141
|
+
const hooksDir = path.join(gitDir, 'hooks');
|
|
142
|
+
const hookSignatures = {
|
|
143
|
+
'pre-commit': '# devsplain native pre-commit hook',
|
|
144
|
+
'post-commit': '# devsplain native post-commit hook'
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
let removed = 0;
|
|
148
|
+
// Iterate through hook signatures to remove [ds]
|
|
149
|
+
for (const [hookName, signature] of Object.entries(hookSignatures)) {
|
|
150
|
+
const hookPath = path.join(hooksDir, hookName);
|
|
151
|
+
if (fs.existsSync(hookPath)) {
|
|
152
|
+
const content = fs.readFileSync(hookPath, 'utf8');
|
|
153
|
+
if (content.includes(signature)) {
|
|
154
|
+
fs.unlinkSync(hookPath);
|
|
155
|
+
console.log(`[devsplain] Removed ${hookName} hook at: ${hookPath}`);
|
|
156
|
+
removed++;
|
|
157
|
+
} else {
|
|
158
|
+
console.log(`[devsplain] Skipping ${hookName}: not installed by devsplain.`);
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
console.log(`[devsplain] No ${hookName} hook found at: ${hookPath}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Log removal of hooks [ds]
|
|
166
|
+
if (removed > 0) {
|
|
167
|
+
console.log(`[devsplain] Successfully removed ${removed} hook(s).`);
|
|
168
|
+
} else {
|
|
169
|
+
console.log('[devsplain] No devsplain hooks were found to remove.');
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {
|
|
172
|
+
console.warn('Warning: Could not remove Git hooks (not inside a git repository or git command missing).');
|
|
173
|
+
console.warn(e.message);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Run installHooks if this script is executed directly [ds]
|
|
115
178
|
if (require.main === module) {
|
|
116
179
|
installHooks();
|
|
117
180
|
}
|
|
118
|
-
// Export
|
|
119
|
-
module.exports = installHooks;
|
|
181
|
+
// Export installHooks and removeHooks functions [ds]
|
|
182
|
+
module.exports = { installHooks, removeHooks };
|
package/package.json
CHANGED