Skip to content

Commit 8d2eff7

Browse files
authored
Set up typescript-eslint (#198)
* eslint-plugin-react-hooks * fix most strict/stylistic errors * enable type-checked linting; 86 problems * number in template literal is A-OK (70) * down to 7 lint errors * object-shorthand * down to 6 * 5 eslint errors; just before/after undefined issue * no errors! * target es2019 for webpack * delete extra package files
1 parent 55b54d9 commit 8d2eff7

25 files changed

+219
-127
lines changed

ts/CodeDiffContainer.tsx

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ export function CodeDiffContainer(props: {filePair: FilePair; diffOptions: Parti
9494
Accept: 'application/json',
9595
'Content-Type': 'application/json',
9696
},
97-
body: JSON.stringify({options: encodeDiffOptions(diffOptions ?? {})}),
97+
body: JSON.stringify({options: encodeDiffOptions(diffOptions)}),
9898
});
99-
return response.json();
99+
return response.json() as Promise<DiffRange[]>;
100100
};
101101

102102
const {a, b} = filePair;
@@ -109,7 +109,7 @@ export function CodeDiffContainer(props: {filePair: FilePair; diffOptions: Parti
109109
getDiff(),
110110
]);
111111
setContents({before, after, diffOps});
112-
})().catch(e => {
112+
})().catch((e: unknown) => {
113113
alert('Unable to get diff!');
114114
console.error(e);
115115
});
@@ -150,6 +150,10 @@ function extractFilename(path: string) {
150150
const HIGHLIGHT_BLACKLIST = ['TODO', 'README', 'NOTES'];
151151
declare const GIT_CONFIG: GitConfig;
152152

153+
function lengthOrZero(data: unknown[] | string | null | undefined) {
154+
return data?.length ?? 0;
155+
}
156+
153157
function FileDiff(props: FileDiffProps) {
154158
const {pathBefore, pathAfter, contentsBefore, contentsAfter, diffOps} = props;
155159
// build the diff view and add it to the current DOM
@@ -165,35 +169,27 @@ function FileDiff(props: FileDiffProps) {
165169
const path = pathBefore || pathAfter;
166170
let language = guessLanguageUsingFileName(path);
167171

168-
const lengthOrZero = function (data: unknown[] | string | null | undefined) {
169-
return data ? data.length : 0;
170-
};
171172
const lastOp = diffOps[diffOps.length - 1];
172173
const numLines = Math.max(lastOp.before[1], lastOp.after[1]);
173174

174175
if (
175176
!language &&
176-
HIGHLIGHT_BLACKLIST.indexOf(extractFilename(path)) === -1 &&
177+
!HIGHLIGHT_BLACKLIST.includes(extractFilename(path)) &&
177178
numLines < GIT_CONFIG.webdiff.maxLinesForSyntax
178179
) {
179180
let byLength = [contentsBefore, contentsAfter];
180181
if (contentsAfter && lengthOrZero(contentsAfter) > lengthOrZero(contentsBefore)) {
181-
byLength = [byLength![1], byLength![0]];
182+
byLength = [byLength[1], byLength[0]];
182183
}
183-
language = guessLanguageUsingContents(byLength[0]!) ?? null;
184+
language = byLength[0] ? guessLanguageUsingContents(byLength[0]) ?? null : null;
184185
}
185186
if (language) {
186187
opts.language = language;
187188
}
188189

189190
return (
190191
<div className="diff">
191-
<CodeDiff
192-
beforeText={contentsBefore!}
193-
afterText={contentsAfter!}
194-
ops={diffOps}
195-
params={opts}
196-
/>
192+
<CodeDiff beforeText={contentsBefore} afterText={contentsAfter} ops={diffOps} params={opts} />
197193
</div>
198194
);
199195
}

ts/DiffOptions.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ export function DiffOptionsControl(props: Props) {
5757
const {options, setOptions} = props;
5858
const [isPopupVisible, setIsPopupVisible] = React.useState(false);
5959

60-
const togglePopup = () => setIsPopupVisible(oldVal => !oldVal);
60+
const togglePopup = () => {
61+
setIsPopupVisible(oldVal => !oldVal);
62+
};
6163
const toggleIgnoreAllSpace = () => {
6264
setOptions({...options, ignoreAllSpace: !options.ignoreAllSpace});
6365
};

ts/DiffView.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export function DiffView(props: Props) {
2828
...newFilePair,
2929
idx: thinFilePair.idx,
3030
});
31-
})();
31+
})().catch((e: unknown) => {
32+
console.error(e);
33+
});
3234
}, [thinFilePair, setFilePair]);
3335

3436
if (!filePair) {

ts/FileDropdown.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ export function FileDropdown(props: Props) {
1717
return <i>none</i>;
1818
} else {
1919
return (
20-
<a href="#" onClick={() => fileChangeHandler(idx)}>
20+
<a
21+
href="#"
22+
onClick={() => {
23+
fileChangeHandler(idx);
24+
}}>
2125
{filePairDisplayName(filePairs[idx])}
2226
</a>
2327
);
@@ -37,7 +41,11 @@ export function FileDropdown(props: Props) {
3741
<div className="file-dropdown">
3842
Prev (k): {prevLink}
3943
<br />
40-
<select value={selectedIndex} onChange={e => fileChangeHandler(Number(e.target.value))}>
44+
<select
45+
value={selectedIndex}
46+
onChange={e => {
47+
fileChangeHandler(Number(e.target.value));
48+
}}>
4149
{options}
4250
</select>
4351
<br />

ts/FileList.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ export function FileList(props: Props) {
1919
const displayName = filePairDisplayName(filePair);
2020
const content =
2121
idx !== selectedIndex ? (
22-
<a onClick={() => fileChangeHandler(idx)} href="#">
22+
<a
23+
onClick={() => {
24+
fileChangeHandler(idx);
25+
}}
26+
href="#">
2327
{displayName}
2428
</a>
2529
) : (

ts/ImageBlinker.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,12 @@ export function ImageBlinker(props: ImageDiffProps) {
4242

4343
React.useEffect(() => {
4444
if (autoBlink) {
45-
const interval = setInterval(() => setIdx(idx => 1 - idx), 500 /* ms */);
46-
return () => clearInterval(interval);
45+
const interval = setInterval(() => {
46+
setIdx(idx => 1 - idx);
47+
}, 500 /* ms */);
48+
return () => {
49+
clearInterval(interval);
50+
};
4751
}
4852
}, [autoBlink, setIdx]);
4953

ts/ImageDiff.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ export function ImageDiff(props: Props) {
3333
setShrinkToFit(e.target.checked);
3434
};
3535

36-
let mode = props.imageDiffMode;
36+
const {changePDiffMode, pdiffMode} = props;
37+
let {imageDiffMode} = props;
3738
const pair = props.filePair;
3839
if (isOneSided(pair)) {
39-
mode = 'side-by-side'; // Only one that makes sense for one-sided diffs.
40+
imageDiffMode = 'side-by-side'; // Only one that makes sense for one-sided diffs.
4041
}
4142

4243
const [, forceUpdate] = React.useState(0);
@@ -49,17 +50,17 @@ export function ImageDiff(props: Props) {
4950
const {diffData} = fp;
5051
// XXX are there other fields?
5152
fp.diffData = {
52-
...(diffData || {}),
53+
...diffData,
5354
diffBounds: bbox,
5455
};
5556
console.log('forcing update');
5657
forceUpdate(n => n + 1); // tell react about this change
57-
})().catch(error => {
58+
})().catch((error: unknown) => {
5859
console.error(error);
5960
});
6061
};
6162

62-
if (props.pdiffMode === 'bbox' && !pair.diffData) {
63+
if (pdiffMode === 'bbox' && !pair.diffData) {
6364
// XXX this might shoot off unnecessary XHRs--use a Promise!
6465
computePerceptualDiffBox(pair);
6566
}
@@ -79,11 +80,11 @@ export function ImageDiff(props: Props) {
7980
blink: ImageBlinker,
8081
'onion-skin': ImageOnionSkin,
8182
swipe: ImageSwipe,
82-
}[mode];
83+
}[imageDiffMode];
8384
const image = React.createElement(component, {
8485
filePair: pair,
8586
shrinkToFit,
86-
pdiffMode: props.pdiffMode,
87+
pdiffMode,
8788
});
8889
const diffBoxEnabled = isSameSizeImagePair(pair);
8990
const boxClasses = diffBoxEnabled ? '' : 'diff-box-disabled';
@@ -114,36 +115,42 @@ export function ImageDiff(props: Props) {
114115
type="radio"
115116
name="pdiff-mode"
116117
id="pdiff-off"
117-
checked={props.pdiffMode === 'off'}
118+
checked={pdiffMode === 'off'}
118119
disabled={!diffBoxEnabled}
119-
onChange={() => props.changePDiffMode('off')}
120+
onChange={() => {
121+
changePDiffMode('off');
122+
}}
120123
/>
121124
<label htmlFor="pdiff-off"> None</label>
122125
&nbsp;
123126
<input
124127
type="radio"
125128
name="pdiff-mode"
126129
id="pdiff-bbox"
127-
checked={props.pdiffMode === 'bbox'}
130+
checked={pdiffMode === 'bbox'}
128131
disabled={!diffBoxEnabled}
129-
onChange={() => props.changePDiffMode('bbox')}
132+
onChange={() => {
133+
changePDiffMode('bbox');
134+
}}
130135
/>
131136
<label htmlFor="pdiff-bbox"> Box</label>
132137
&nbsp;
133138
<input
134139
type="radio"
135140
name="pdiff-mode"
136141
id="pdiff-pixels"
137-
checked={props.pdiffMode === 'pixels'}
142+
checked={pdiffMode === 'pixels'}
138143
disabled={!diffBoxEnabled}
139-
onChange={() => props.changePDiffMode('pixels')}
144+
onChange={() => {
145+
changePDiffMode('pixels');
146+
}}
140147
/>
141148
<label htmlFor="pdiff-pixels"> Differing Pixels</label>
142149
</span>
143150
{imageMagickCallout}
144151
</span>
145152
</div>
146-
<div className={'image-diff ' + mode}>
153+
<div className={'image-diff ' + imageDiffMode}>
147154
<NoChanges filePair={props.filePair} />
148155
{image}
149156
</div>

ts/ImageDiffModeSelector.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ export function ImageDiffModeSelector(props: Props) {
2020
}
2121

2222
// Returns the text, optionally wrapped in a link and/or <b> tag.
23-
const linkOrB = (isLink: boolean, isB: boolean, val: ImageDiffMode, text: string) => {
24-
const inner = isB ? <b>{text}</b> : text;
23+
const linkOrB = (isLink: boolean, isBold: boolean, val: ImageDiffMode, text: string) => {
24+
const inner = isBold ? <b>{text}</b> : text;
2525
if (isLink) {
2626
return (
27-
<a href="#" onClick={() => props.changeImageDiffModeHandler(val)}>
27+
<a
28+
href="#"
29+
onClick={() => {
30+
props.changeImageDiffModeHandler(val);
31+
}}>
2832
{inner}
2933
</a>
3034
);

ts/ImageSwipe.tsx

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ export function ImageOnionSkin(props: ImageDiffProps) {
1616
// Two images on top of one another with a slider to move the divider from left
1717
// to right.
1818
export function ImageSwipe(props: ImageSwipeProps) {
19-
const mode = props.mode || 'swipe';
19+
const mode = props.mode ?? 'swipe';
2020
const [rangePosition, setRangePosition] = React.useState<number | null>(null);
2121
const sliderRef = React.createRef<HTMLInputElement>();
2222
const onSlide = () => {
23-
setRangePosition(Number(sliderRef.current!.value));
23+
if (sliderRef.current) {
24+
setRangePosition(Number(sliderRef.current.value));
25+
}
2426
};
2527

2628
const pair = props.filePair;
@@ -40,33 +42,34 @@ export function ImageSwipe(props: ImageSwipeProps) {
4042
containerWidth = Math.max(imA.width, imB.width);
4143
}
4244
const diffBoxDiv = makePerceptualBoxDiv(props.pdiffMode, pair, scaleDown);
43-
const urlA = '/a/image/' + pair.a;
44-
const urlB = '/b/image/' + pair.b;
45-
const styleA: React.CSSProperties = {
46-
backgroundImage: 'url(' + urlA + ')',
47-
backgroundSize: imA.width + 'px ' + imA.height + 'px',
48-
width: imA.width + 'px',
49-
height: imA.height + 'px',
45+
const urlA = `/a/image/${pair.a}`;
46+
const urlB = `/b/image/${pair.b}`;
47+
const styleA: React.CSSProperties & {backgroundSize: string; backgroundImage: string} = {
48+
backgroundImage: `url(${urlA})`,
49+
backgroundSize: `${imA.width}px ${imA.height}px`,
50+
width: `${imA.width}px`,
51+
height: `${imA.height}px`,
5052
};
51-
const styleB: React.CSSProperties = {
52-
backgroundImage: 'url(' + urlB + ')',
53-
backgroundSize: imB.width + 'px ' + imB.height + 'px',
54-
width: imB.width + 'px',
55-
height: imB.height + 'px',
53+
const styleB: React.CSSProperties & {backgroundSize: string; backgroundImage: string} = {
54+
backgroundImage: `url(${urlB})`,
55+
backgroundSize: `${imB.width}px ${imB.height}px`,
56+
width: `${imB.width}px`,
57+
height: `${imB.height}px`,
5658
};
5759
const styleContainer: React.CSSProperties = {
58-
width: containerWidth + 'px',
59-
height: Math.max(imA.height, imB.height) + 'px',
60+
width: containerWidth,
61+
height: Math.max(imA.height, imB.height),
6062
};
6163
if (mode === 'swipe') {
6264
_.extend(styleA, {
63-
width: Math.floor(frac * imA.width) + 'px',
65+
width: Math.floor(frac * imA.width),
6466
});
67+
const bgTop = -Math.floor(frac * imB.width);
6568
_.extend(styleB, {
66-
left: Math.floor(frac * imB.width) + 'px',
69+
left: Math.floor(frac * imB.width),
6770
width: null,
68-
right: containerWidth - imB.width + 'px',
69-
backgroundPosition: -Math.floor(frac * imB.width) + 'px top',
71+
right: containerWidth - imB.width,
72+
backgroundPosition: `${bgTop} px top`,
7073
});
7174
} else {
7275
_.extend(styleB, {opacity: frac});

ts/Root.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@ export function Root(props: Props) {
2323
const [diffOptions, setDiffOptions] = React.useState<Partial<DiffOptions>>({});
2424

2525
const history = useHistory();
26-
const selectIndex = (idx: number) => {
27-
history.push(`/${idx}`);
28-
};
26+
const selectIndex = React.useCallback(
27+
(idx: number) => {
28+
history.push(`/${idx}`);
29+
},
30+
[history],
31+
);
2932

3033
const idx = Number(props.match.params.index ?? initialIdx);
3134
const filePair = pairs[idx];
@@ -57,14 +60,14 @@ export function Root(props: Props) {
5760
return () => {
5861
document.removeEventListener('keydown', handleKeydown);
5962
};
60-
}, [idx, pairs, selectIndex, setImageDiffMode, setPDiffMode]);
63+
}, [idx, pdiffMode, selectIndex, setImageDiffMode, setPDiffMode]);
6164

6265
return (
6366
<div>
6467
<DiffOptionsControl options={diffOptions} setOptions={setDiffOptions} />
6568
<FileSelector selectedFileIndex={idx} filePairs={pairs} fileChangeHandler={selectIndex} />
6669
<DiffView
67-
key={'diff-' + idx}
70+
key={`diff-${idx}`}
6871
thinFilePair={filePair}
6972
imageDiffMode={imageDiffMode}
7073
pdiffMode={pdiffMode}

0 commit comments

Comments
 (0)