Skip to content

Commit fee5d61

Browse files
feat(config): add Config class for centralized configuration management
- Introduce Config class to handle tool configuration - Add support for output directory, exclusions, diff format, and dark mode - Refactor CLI and SDK to use new Config class - Update documentation with new configuration options
1 parent ff197a7 commit fee5d61

File tree

6 files changed

+183
-48
lines changed

6 files changed

+183
-48
lines changed

README.md

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A powerful CLI tool that generates beautifully formatted markdown files from git
88

99
## Why?
1010

11-
So GitHub/GitLab/Bitbucket diff views works for you? Good. But sometimes those might be not your remote git provider and/or your diff view from your remote git providers might be not so friendly moreover if you have to review big changes (at least for your eyes). This tool simply offers an alternative that lets you:
11+
So GitHub/GitLab/Bitbucket diff views works for you? Well good for you! But sometimes those remote git providers might be not your remote git provider and/or your diff view from your remote git providers might be not so friendly moreover if you have to review big changes (at least for your eyes). This tool simply offers an alternative that lets you:
1212

1313
- Generate clean, readable diffs when your remote git provider diff views are unavailable or unfriendly
1414
- Work completely offline - only needs internet access when comparing remote branches
@@ -32,7 +32,10 @@ So GitHub/GitLab/Bitbucket diff views works for you? Good. But sometimes those m
3232
- 🎨 Syntax-highlighted diff output
3333
- 📊 Includes file statistics and change summaries
3434
- 🔍 Automatic exclusion of common build artifacts and sensitive files
35-
- 📁 Preserves your repository's directory structure and generates a searchable diff index for easy navigation
35+
- 📁 Output directory structure mirrors your repository layout with a searchable index file containing:
36+
- Total change statistics
37+
- Commit messages between compared refs
38+
- Links to individual file diffs with change summaries
3639
- 💡 Support for comparing specific commits, branches, or tags
3740
- 🚀 Progress indicators for long-running operations
3841

@@ -62,21 +65,114 @@ git-markdown-diff
6265
To generate diffs between specific git references (commits, branches, or tags):
6366

6467
```bash
65-
git-markdown-diff <startRef> <endRef>
68+
git-markdown-diff --start-ref <startRef> --end-ref <endRef>
6669
```
6770

6871
Examples:
6972
```bash
7073
# Compare between two commits
71-
git-markdown-diff abc123 def456
74+
git-markdown-diff -s abc123 -e def456
7275

7376
# Compare between branches
74-
git-markdown-diff main feature/new-feature
77+
git-markdown-diff --start-ref main --end-ref feature/new-feature
7578

7679
# Compare between tags
77-
git-markdown-diff v1.0.0 v1.1.0
80+
git-markdown-diff -s v1.0.0 -e v1.1.0
7881
```
7982

83+
### Configuration Options
84+
85+
```bash
86+
Options:
87+
-s, --start-ref <ref> Starting reference (commit hash, branch name, or tag)
88+
-e, --end-ref <ref> Ending reference (commit hash, branch name, or tag)
89+
-o, --output <dir> Output directory (default: "git-diffs")
90+
--exclude <patterns> Additional file patterns to exclude
91+
-f, --format <format> Diff format: diff, unified, side-by-side (default: "diff")
92+
--light-mode Use light mode theme instead of dark mode
93+
-h, --help Display help
94+
95+
# Examples:
96+
git-markdown-diff -s main -e develop -o custom-diffs # Custom output directory
97+
git-markdown-diff --exclude "*.log" "*.tmp" # Exclude additional files
98+
git-markdown-diff -s HEAD -e HEAD~1 -f side-by-side # Side-by-side diff format
99+
git-markdown-diff -s v1.0 -e v2.0 --light-mode # Light mode theme
100+
```
101+
102+
### Programmatic Usage
103+
104+
```javascript
105+
const GitMarkdownDiff = require('git-markdown-diff');
106+
107+
const differ = new GitMarkdownDiff({
108+
outputDir: 'custom-dir',
109+
exclusions: ['*.log', '*.tmp'],
110+
diffFormat: 'side-by-side',
111+
darkMode: false
112+
});
113+
114+
await differ.run('main', 'feature/branch');
115+
```
116+
117+
#### Use it in your code:
118+
```javascript
119+
// Generate diffs for code review tools
120+
const GitMarkdownDiff = require('git-markdown-diff');
121+
122+
async function generateReviewDiff(prNumber) {
123+
const differ = new GitMarkdownDiff({
124+
outputDir: `pr-${prNumber}-diff`,
125+
diffFormat: 'side-by-side'
126+
});
127+
128+
await differ.run('main', `pr-${prNumber}`);
129+
return `pr-${prNumber}-diff/README.md`;
130+
}
131+
```
132+
133+
#### Git hooks integration:
134+
```javascript
135+
// pre-commit hook (/.git/hooks/pre-commit)
136+
const GitMarkdownDiff = require('git-markdown-diff');
137+
138+
async function preCommitHook() {
139+
const differ = new GitMarkdownDiff({
140+
outputDir: '.git/diff-preview',
141+
diffFormat: 'unified'
142+
});
143+
144+
// Compare staged changes
145+
await differ.run('HEAD', '--staged');
146+
// Open diff preview in default browser
147+
require('open')('.git/diff-preview/README.md');
148+
}
149+
```
150+
151+
#### CI/CD pipeline:
152+
```javascript
153+
// GitHub Actions, Jenkins, etc.
154+
const GitMarkdownDiff = require('git-markdown-diff');
155+
156+
async function generatePRDiff() {
157+
const differ = new GitMarkdownDiff({
158+
outputDir: 'ci-diff-output',
159+
exclusions: ['*.lock', 'dist/*']
160+
});
161+
162+
// Compare PR branch with target branch
163+
await differ.run(process.env.TARGET_BRANCH, process.env.PR_BRANCH);
164+
165+
// Upload diff as artifact or send to review system
166+
await uploadArtifact('ci-diff-output');
167+
}
168+
```
169+
170+
### Diff Formats
171+
172+
- `diff` (default) - Standard git diff format
173+
- `unified` - Unified diff with 3 lines of context
174+
- `side-by-side` - Two-column comparison view
175+
80176
## Output Structure
81177

82178
The tool creates a `git-diffs` directory with the following structure:

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "git-markdown-diff",
3-
"version": "0.0.39",
3+
"version": "0.0.40",
44
"description": "Git diff tool that outputs markdown formatted diffs",
55
"main": "src/sdk/GitMarkdownDiff.js",
66
"bin": {
@@ -20,6 +20,13 @@
2020
],
2121
"author": "Bayu Prakoso",
2222
"license": "MIT",
23+
"files": [
24+
"src/sdk/**/*",
25+
"src/cli/**/*"
26+
],
27+
"exports": {
28+
".": "./src/sdk/GitMarkdownDiff.js"
29+
},
2330
"dependencies": {
2431
"commander": "^11.0.0",
2532
"ora": "^8.1.1"

src/cli/index.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,25 @@
33
const { program } = require("commander");
44
const GitMarkdownDiff = require("../sdk/GitMarkdownDiff");
55

6-
// CLI setup
76
program
87
.name("git-markdown-diff")
98
.description("Generate markdown-formatted git diffs")
10-
.argument(
11-
"[startRef]",
12-
"Starting reference (commit hash, branch name, or tag)"
13-
)
14-
.argument("[endRef]", "Ending reference (commit hash, branch name, or tag)")
15-
.action(async (startRef, endRef) => {
16-
const differ = new GitMarkdownDiff();
17-
await differ.run(startRef, endRef);
9+
.option("-s, --start-ref <ref>", "Starting reference (commit hash, branch name, or tag)")
10+
.option("-e, --end-ref <ref>", "Ending reference (commit hash, branch name, or tag)")
11+
.option("-o, --output <dir>", "Output directory", "git-diffs")
12+
.option("--exclude <patterns...>", "Additional file patterns to exclude")
13+
.option("-f, --format <format>", "Diff format (diff, unified, side-by-side)", "diff")
14+
.option("--light-mode", "Use light mode theme instead of dark mode")
15+
.action(async (options) => {
16+
const config = {
17+
outputDir: options.output,
18+
exclusions: options.exclude || [],
19+
diffFormat: options.format,
20+
darkMode: !options.lightMode
21+
};
22+
23+
const differ = new GitMarkdownDiff(config);
24+
await differ.run(options.startRef, options.endRef);
1825
});
1926

20-
program.parse();
27+
program.parse();

src/sdk/GitMarkdownDiff.js

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
const gitUtils = require('./utils/gitUtils');
22
const fsUtils = require('./utils/fsUtils');
3-
const EXCLUSIONS = require('./constants/gitExclusions');
3+
const Config = require('./config/Config');
44

55
class GitMarkdownDiff {
6-
constructor(outputDir = "git-diffs") {
7-
this.outputDir = outputDir;
6+
constructor(options = {}) {
7+
this.config = new Config(options);
88
}
99

1010
async run(startRange, endRange) {
@@ -13,21 +13,21 @@ class GitMarkdownDiff {
1313

1414
try {
1515
await gitUtils.validateGit();
16-
fsUtils.cleanupOutputDir(this.outputDir);
16+
fsUtils.cleanupOutputDir(this.config.outputDir);
1717

1818
const range = this.#buildGitRange(startRange, endRange);
1919

2020
spinner.text = "Getting list of changed files...";
21-
const changedFiles = await gitUtils.getChangedFiles(range, EXCLUSIONS);
21+
const changedFiles = await gitUtils.getChangedFiles(range, this.config.getExclusions());
2222
const totalStats = await gitUtils.getTotalStats(range);
2323

2424
const index = await this.#buildIndexContent(startRange, endRange, totalStats, range);
2525
await this.#processFiles(changedFiles, range, spinner, index);
2626

2727
spinner.text = "Writing index file...";
28-
fsUtils.writeIndexFile(this.outputDir, index.join("\n"));
28+
fsUtils.writeIndexFile(this.config.outputDir, index.join("\n"));
2929

30-
spinner.succeed(`Diffs saved to ${this.outputDir}/`);
30+
spinner.succeed(`Diffs saved to ${this.config.outputDir}/`);
3131
} catch (error) {
3232
spinner.fail("Failed to generate diffs");
3333
console.error(error);
@@ -43,7 +43,7 @@ class GitMarkdownDiff {
4343
const fileInfo = await gitUtils.getFileInfo(file, range);
4444
const content = await this.#generateFileContent(file, range, fileInfo);
4545

46-
fsUtils.saveFile(this.outputDir, file, content);
46+
fsUtils.saveFile(this.config.outputDir, file, content);
4747
this.#updateIndex(index, file, fileInfo);
4848
}
4949
}
@@ -71,10 +71,10 @@ class GitMarkdownDiff {
7171
}
7272

7373
async #generateFileContent(file, range, fileInfo) {
74-
const diffOutput = await gitUtils.getFileDiff(file, range);
74+
const diffOutput = await gitUtils.getFileDiff(file, range, this.config.diffFormat);
7575

7676
return [
77-
this.#getCssStyle(),
77+
this.config.getCssStyle(),
7878
`generated at ${new Date().toLocaleString()} (${Intl.DateTimeFormat().resolvedOptions().timeZone})`,
7979
"",
8080
`# Changes in \`${file}\``,
@@ -93,24 +93,6 @@ class GitMarkdownDiff {
9393
].join("\n");
9494
}
9595

96-
#getCssStyle() {
97-
return `<!--
98-
<style>
99-
.markdown-body .highlight pre, .markdown-body pre {
100-
background-color: #0d1117;
101-
}
102-
.markdown-body .diff-deletion {
103-
color: #f85149;
104-
background-color: #3c1618;
105-
}
106-
.markdown-body .diff-addition {
107-
color: #56d364;
108-
background-color: #1b4721;
109-
}
110-
</style>
111-
-->`;
112-
}
113-
11496
#updateIndex(index, file, fileInfo) {
11597
const stats = fileInfo.match(/(\d+) insertion.+(\d+) deletion/)?.[0] || "No changes";
11698
index.push(`- [${file}](./${file}.md) - ${stats}`);

src/sdk/config/Config.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
class Config {
2+
constructor(options = {}) {
3+
this.outputDir = options.outputDir || "git-diffs";
4+
this.exclusions = options.exclusions || [];
5+
this.diffFormat = options.diffFormat || "diff"; // possible values: diff, unified, side-by-side
6+
this.darkMode = options.darkMode ?? true;
7+
}
8+
9+
getExclusions() {
10+
const defaultExclusions = require('../constants/gitExclusions');
11+
return [...defaultExclusions, ...this.exclusions];
12+
}
13+
14+
getCssStyle() {
15+
if (!this.darkMode) return '';
16+
17+
return `<!--
18+
<style>
19+
.markdown-body .highlight pre, .markdown-body pre {
20+
background-color: #0d1117;
21+
}
22+
.markdown-body .diff-deletion {
23+
color: #f85149;
24+
background-color: #3c1618;
25+
}
26+
.markdown-body .diff-addition {
27+
color: #56d364;
28+
background-color: #1b4721;
29+
}
30+
</style>
31+
-->`;
32+
}
33+
}
34+
35+
module.exports = Config;

src/sdk/utils/gitUtils.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,17 @@ const gitUtils = {
3434
return stdout;
3535
},
3636

37-
async getFileDiff(file, range) {
38-
const { stdout } = await execAsync(`git diff ${range} -- "${file}"`, {
39-
maxBuffer: 10 * 1024 * 1024,
37+
async getFileDiff(file, range, format = 'diff') {
38+
const formatFlags = {
39+
'diff': '',
40+
'unified': ' -U3',
41+
'side-by-side': ' --side-by-side --width=180'
42+
};
43+
44+
const flag = formatFlags[format] || '';
45+
const cmd = `git diff${flag} ${range} -- "${file}"`;
46+
const { stdout } = await execAsync(cmd, {
47+
maxBuffer: 10 * 1024 * 1024
4048
});
4149
return stdout;
4250
},

0 commit comments

Comments
 (0)