Skip to content

Commit 1c9b955

Browse files
authored
Merge pull request #99 from rtfpessoa/misc-improvements
Add proper exit code on error, other fixes and bumps
2 parents 7725898 + bc7e951 commit 1c9b955

File tree

7 files changed

+994
-926
lines changed

7 files changed

+994
-926
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Diff to Html generates pretty HTML diffs from unified and git diff output in you
2323
- [Distributions](#distributions)
2424
- [Setup](#setup)
2525
- [Usage](#usage)
26+
- [Exit Status Codes](#exit-status-codes)
2627
- [Custom HTML wrapper template](#custom-html-wrapper-template)
2728
- [Examples](#examples)
2829
- [Contribute](#contribute)
@@ -89,6 +90,13 @@ Usage: diff2html [ flags and/or options ] -- [git diff passthrough flags and opt
8990
| -v | --version | Show version number | | |
9091
| -h | --help | Show help | | |
9192

93+
### Exit Status Codes
94+
95+
- :tada: 0: Success
96+
- :dizzy_face: 1: Generic Error
97+
- :cold_sweat: 3: Input diff is empty
98+
- :cop: 4: Value of `--hwt | --htmlWrapperTemplate` is not a valid file
99+
92100
### Custom HTML wrapper template
93101

94102
The template is a very based on a simple replace of several placeholders as coded

package.json

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
"git",
88
"diff",
99
"pretty",
10+
"parser",
11+
"diffparser",
12+
"gen",
13+
"generator",
1014
"side",
1115
"line",
1216
"side-by-side",
@@ -52,7 +56,7 @@
5256
"test:watch": "jest --watch",
5357
"test:debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --watch",
5458
"coverage:open": "yarn run test:coverage && open ./coverage/index.html",
55-
"coverage:push": "cat ./coverage/lcov.info | codacy-coverage",
59+
"coverage:push": "curl -Ls https://coverage.codacy.com/get.sh | bash",
5660
"validate": "yarn run format:check && yarn run lint:check && yarn run build && yarn run test:coverage",
5761
"fix": "yarn run format:fix && yarn run lint:fix",
5862
"preversion": "yarn run validate",
@@ -81,42 +85,38 @@
8185
]
8286
},
8387
"dependencies": {
84-
"clipboardy": "^2.2.0",
88+
"clipboardy": "^2.3.0",
8589
"diff2html": "3.1.6",
90+
"node-fetch": "^2.6.0",
8691
"open": "^7.0.3",
87-
"request": "^2.88.2",
88-
"yargs": "^15.3.0"
92+
"yargs": "^15.3.1"
8993
},
9094
"devDependencies": {
9195
"@types/hogan.js": "^3.0.0",
92-
"@types/jest": "25.1.4",
93-
"@types/node": "13.9.1",
96+
"@types/jest": "25.2.1",
97+
"@types/node": "13.11.1",
98+
"@types/node-fetch": "^2.5.6",
9499
"@types/request": "2.48.4",
95-
"@typescript-eslint/eslint-plugin": "2.23.0",
96-
"@typescript-eslint/parser": "2.23.0",
97-
"codacy-coverage": "3.4.0",
100+
"@typescript-eslint/eslint-plugin": "2.28.0",
101+
"@typescript-eslint/parser": "2.28.0",
98102
"eslint": "6.8.0",
99-
"eslint-config-prettier": "6.10.0",
100-
"eslint-plugin-import": "2.20.1",
103+
"eslint-config-prettier": "6.10.1",
104+
"eslint-plugin-import": "2.20.2",
101105
"eslint-plugin-jest": "23.8.2",
102-
"eslint-plugin-json": "2.1.0",
103-
"eslint-plugin-node": "11.0.0",
106+
"eslint-plugin-json": "2.1.1",
107+
"eslint-plugin-node": "11.1.0",
104108
"eslint-plugin-optimize-regex": "1.1.7",
105109
"eslint-plugin-promise": "4.2.1",
106110
"eslint-plugin-sonarjs": "0.5.0",
107-
"husky": "4.2.3",
111+
"husky": "4.2.5",
108112
"is-ci-cli": "2.0.0",
109-
"jest": "25.1.0",
110-
"lint-staged": "10.0.8",
113+
"jest": "25.3.0",
114+
"lint-staged": "10.1.4",
111115
"markdown-toc": "^1.2.0",
112-
"prettier": "1.19.1",
113-
"ts-jest": "25.2.1",
116+
"prettier": "2.0.4",
117+
"ts-jest": "25.4.0",
114118
"typescript": "3.8.3"
115119
},
116-
"resolutions": {
117-
"acorn": ">=7.1.1",
118-
"autolinker": ">=3.0.0"
119-
},
120120
"license": "MIT",
121121
"files": [
122122
"bin",

src/cli.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,26 +73,28 @@ export async function getInput(inputType: InputType, inputArgs: string[], ignore
7373
case 'stdin':
7474
return utils.readStdin();
7575

76-
default:
76+
case 'command':
7777
return runGitDiff(inputArgs, ignore);
7878
}
7979
}
8080

8181
export function getOutput(options: Diff2HtmlConfig, config: Configuration, input: string): string {
8282
if (config.htmlWrapperTemplate && !fs.existsSync(config.htmlWrapperTemplate)) {
83+
process.exitCode = 4;
8384
throw new Error(`Template ('${config.htmlWrapperTemplate}') not found!`);
8485
}
8586

8687
const diffJson = parse(input, options);
8788

88-
if (config.formatType === 'html') {
89-
const htmlContent = html(diffJson, { ...options });
90-
return prepareHTML(htmlContent, config);
91-
} else if (config.formatType === 'json') {
92-
return JSON.stringify(diffJson);
89+
switch (config.formatType) {
90+
case 'html': {
91+
const htmlContent = html(diffJson, { ...options });
92+
return prepareHTML(htmlContent, config);
93+
}
94+
case 'json': {
95+
return JSON.stringify(diffJson);
96+
}
9397
}
94-
95-
throw new Error(`Wrong output format '${config.formatType}'!`);
9698
}
9799

98100
export function preview(content: string, format: string): void {
@@ -102,8 +104,30 @@ export function preview(content: string, format: string): void {
102104
open(filePath, { wait: false });
103105
}
104106

107+
type CreateDiffResponse = { id: string };
108+
109+
type ApiError = { error: string };
110+
111+
function isCreateDiffResponse(obj: unknown): obj is CreateDiffResponse {
112+
return (obj as CreateDiffResponse).id !== undefined;
113+
}
114+
115+
function isApiError(obj: unknown): obj is ApiError {
116+
return (obj as ApiError).error !== undefined;
117+
}
118+
105119
export async function postToDiffy(diff: string, diffyOutput: DiffyType): Promise<string> {
106-
const response = await http.put<{ id: string }>('https://diffy.org/api/diff/', { diff: diff });
120+
const response = await http.put('https://diffy.org/api/diff/', { diff: diff });
121+
122+
if (!isCreateDiffResponse(response)) {
123+
if (isApiError(response)) {
124+
throw new Error(response.error);
125+
} else {
126+
throw new Error(
127+
`Could not find 'id' of created diff in the response json.\nBody:\n\n${JSON.stringify(response, null, 2)}`,
128+
);
129+
}
130+
}
107131

108132
const url = `https://diffy.org/diff/${response.id}`;
109133

src/http-utils.ts

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,23 @@
1-
import request from 'request';
1+
import fetch from 'node-fetch';
22

3-
export function put<T extends object>(url: string, payload: object): Promise<T> {
4-
return new Promise<T>((resolve, reject): void => {
5-
request({
6-
url: url,
7-
method: 'PUT',
8-
headers: {},
9-
body: payload,
10-
json: true,
11-
})
12-
.on('response', response => {
13-
response.on('data', body => {
14-
try {
15-
const jsonObj = JSON.parse(body.toString('utf8'));
16-
if (jsonObj as T) {
17-
return resolve(jsonObj);
18-
} else if (jsonObj.error !== undefined && typeof jsonObj.error === 'string') {
19-
return reject(new Error(jsonObj.error));
20-
} else {
21-
return reject(
22-
new Error(
23-
`Failed to read response.
24-
Body:
25-
${body.toString('utf8')}`,
26-
),
27-
);
28-
}
29-
} catch (err) {
30-
return reject(err);
31-
}
32-
});
33-
})
34-
.on('error', err => reject(err));
3+
export function put(url: string, payload: object): Promise<unknown> {
4+
return fetch(url, {
5+
method: 'PUT',
6+
headers: {
7+
Accept: 'application/json',
8+
'Content-Type': 'application/json',
9+
},
10+
body: JSON.stringify(payload),
11+
}).then(async response => {
12+
const bodyText = await response.text();
13+
try {
14+
return JSON.parse(bodyText);
15+
} catch (error) {
16+
if (error instanceof SyntaxError) {
17+
throw new Error(`Failed to parse response json.\nBody:\n\n${bodyText}`);
18+
} else {
19+
throw error;
20+
}
21+
}
3522
});
3623
}

src/main.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export async function main(): Promise<void> {
1212
const input = await cli.getInput(configuration.inputSource, argv.extraArguments, configuration.ignore);
1313

1414
if (!input) {
15+
process.exitCode = 3;
1516
log.error('The input is empty. Try again.');
1617
yargs.help();
1718
return;
@@ -26,12 +27,17 @@ export async function main(): Promise<void> {
2627

2728
if (configuration.outputDestinationFile) utils.writeFile(configuration.outputDestinationFile, output);
2829

29-
if (configuration.outputDestinationType === 'preview') {
30-
cli.preview(output, configuration.formatType);
31-
} else if (configuration.outputDestinationType === 'stdout') {
32-
log.print(output);
30+
switch (configuration.outputDestinationType) {
31+
case 'preview':
32+
return cli.preview(output, configuration.formatType);
33+
34+
case 'stdout':
35+
return log.print(output);
3336
}
3437
} catch (error) {
38+
if (process.exitCode === undefined || process.exitCode === 0) {
39+
process.exitCode = 1;
40+
}
3541
log.error(error);
3642
}
3743
}

template.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
});
2626
</script>
2727
</head>
28-
<body style="text-align: center; font-family: 'Source Sans Pro',sans-serif;">
28+
<body style="text-align: center; font-family: 'Source Sans Pro', sans-serif;">
2929
<h1>Diff to HTML by <a href="https://github.com/rtfpessoa">rtfpessoa</a></h1>
3030

3131
<div id="diff">

0 commit comments

Comments
 (0)