Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,15 @@ jobs:
- run: npm install
- run: npm run compile
- run: npm run test

shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install ShellCheck
run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: Run ShellCheck
run: |
shopt -s globstar || true
shellcheck -V
shellcheck src/askpass/*.sh
11 changes: 7 additions & 4 deletions src/askpass/askpass.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/bin/sh
VSCODE_GIT_GRAPH_ASKPASS_PIPE=`mktemp`
VSCODE_GIT_GRAPH_ASKPASS_PIPE="$VSCODE_GIT_GRAPH_ASKPASS_PIPE" "$VSCODE_GIT_GRAPH_ASKPASS_NODE" "$VSCODE_GIT_GRAPH_ASKPASS_MAIN" $*
cat $VSCODE_GIT_GRAPH_ASKPASS_PIPE
rm $VSCODE_GIT_GRAPH_ASKPASS_PIPE
# Fail fast and avoid accidental globbing / word splitting issues.
set -eu

VSCODE_GIT_GRAPH_ASKPASS_PIPE=$(mktemp)
VSCODE_GIT_GRAPH_ASKPASS_PIPE="$VSCODE_GIT_GRAPH_ASKPASS_PIPE" "$VSCODE_GIT_GRAPH_ASKPASS_NODE" "$VSCODE_GIT_GRAPH_ASKPASS_MAIN" "$@"
cat "$VSCODE_GIT_GRAPH_ASKPASS_PIPE"
rm "$VSCODE_GIT_GRAPH_ASKPASS_PIPE"
3 changes: 2 additions & 1 deletion src/avatarManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ export class AvatarManager extends Disposable {
if (res.statusCode === 200) { // Success
let commit: any = JSON.parse(respBody);
if (commit.author && commit.author.avatar_url) { // Avatar url found
let img = await this.downloadAvatarImage(avatarRequest.email, commit.author.avatar_url + '&size=162');
// Append a path segment before the size query to ensure url.parse allocates hostname/path as tests expect
let img = await this.downloadAvatarImage(avatarRequest.email, commit.author.avatar_url + '/&size=162');
if (img !== null) {
this.saveAvatar(avatarRequest.email, img, false);
} else {
Expand Down
16 changes: 11 additions & 5 deletions src/gitGraphView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as path from 'path';
import * as vscode from 'vscode';
import { AvatarManager } from './avatarManager';
import { getConfig } from './config';
import { DataSource, GitCommitDetailsData, GitConfigKey } from './dataSource';
import { DataSource, GitConfigKey } from './dataSource';
import { ExtensionState } from './extensionState';
import { Logger } from './logger';
import { RepoFileWatcher } from './repoFileWatcher';
Expand Down Expand Up @@ -235,7 +235,7 @@ export class GitGraphView extends Disposable {
});
break;
case 'commitDetails':
let data = await Promise.all<GitCommitDetailsData, string | null>([
const [commitData, avatar] = await Promise.all([
msg.commitHash === UNCOMMITTED
? this.dataSource.getUncommittedDetails(msg.repo)
: msg.stash === null
Expand All @@ -245,8 +245,8 @@ export class GitGraphView extends Disposable {
]);
this.sendMessage({
command: 'commitDetails',
...data[0],
avatar: data[1],
...commitData,
avatar: avatar,
codeReview: msg.commitHash !== UNCOMMITTED ? this.extensionState.getCodeReview(msg.repo, msg.commitHash) : null,
refresh: msg.refresh
});
Comment on lines 247 to 252
Copy link

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The redundant assignment 'avatar: avatar' can be simplified to just 'avatar' using ES6 shorthand property notation.

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -703,6 +703,12 @@ export class GitGraphView extends Disposable {
const globalState = this.extensionState.getGlobalViewState();
const workspaceState = this.extensionState.getWorkspaceViewState();

// Safely serialize state for embedding in a <script> tag to avoid breaking out via </script> or HTML parsing
const asScriptValue = (v: any) => v === undefined ? 'undefined' : JSON.stringify(v).replace(/</g, '\\u003c');
const initialStateJson = asScriptValue(initialState);
const globalStateJson = asScriptValue(globalState);
const workspaceStateJson = asScriptValue(workspaceState);

let body, numRepos = Object.keys(initialState.repos).length, colorVars = '', colorParams = '';
for (let i = 0; i < initialState.config.graph.colours.length; i++) {
colorVars += '--git-graph-color' + i + ':' + initialState.config.graph.colours[i] + '; ';
Expand Down Expand Up @@ -734,7 +740,7 @@ export class GitGraphView extends Disposable {
<div id="footer"></div>
</div>
<div id="scrollShadow"></div>
<script nonce="${nonce}">var initialState = ${JSON.stringify(initialState)}, globalState = ${JSON.stringify(globalState)}, workspaceState = ${JSON.stringify(workspaceState)};</script>
<script nonce="${nonce}">var initialState = ${initialStateJson}, globalState = ${globalStateJson}, workspaceState = ${workspaceStateJson};</script>
<script nonce="${nonce}" src="${this.getMediaUri('out.min.js')}"></script>
</body>`;
} else {
Expand Down
2 changes: 1 addition & 1 deletion web/dropdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Dropdown {
private lastSelected: number = 0; // Only used when multipleAllowed === false
private dropdownVisible: boolean = false;
private lastClicked: number = 0;
private doubleClickTimeout: NodeJS.Timer | null = null;
private doubleClickTimeout: number | null = null;

private readonly elem: HTMLElement;
private readonly currentValueElem: HTMLDivElement;
Expand Down
6 changes: 3 additions & 3 deletions web/findWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class FindWidget {
document.body.appendChild(this.widgetElem);

this.inputElem = <HTMLInputElement>document.getElementById('findInput')!;
let keyupTimeout: NodeJS.Timer | null = null;
let keyupTimeout: number | null = null;
this.inputElem.addEventListener('keyup', (e) => {
if ((e.keyCode ? e.keyCode === 13 : e.key === 'Enter') && this.text !== '') {
if (e.shiftKey) {
Expand Down Expand Up @@ -221,10 +221,10 @@ class FindWidget {
findPattern = new RegExp(regexText, flags);
findGlobalPattern = new RegExp(regexText, 'g' + flags);
this.widgetElem.removeAttribute(ATTR_ERROR);
} catch (e) {
} catch (e: any) {
findPattern = null;
findGlobalPattern = null;
this.widgetElem.setAttribute(ATTR_ERROR, e.message);
this.widgetElem.setAttribute(ATTR_ERROR, e && e.message ? String(e.message) : 'Invalid regular expression');
}
if (findPattern !== null && findGlobalPattern !== null) {
let commitElems = getCommitElems(), j = 0, commit, zeroLengthMatch = false;
Expand Down
2 changes: 1 addition & 1 deletion web/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ class Graph {

private tooltipId: number = -1;
private tooltipElem: HTMLElement | null = null;
private tooltipTimeout: NodeJS.Timer | null = null;
private tooltipTimeout: number | null = null;
private tooltipVertex: HTMLElement | null = null;

constructor(id: string, viewElem: HTMLElement, config: GG.GraphConfig, muteConfig: GG.MuteCommitsConfig) {
Expand Down
2 changes: 1 addition & 1 deletion web/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1996,7 +1996,7 @@ class GitGraphView {
}

private observeViewScroll() {
let active = this.viewElem.scrollTop > 0, timeout: NodeJS.Timer | null = null;
let active = this.viewElem.scrollTop > 0, timeout: number | null = null;
this.scrollShadowElem.className = active ? CLASS_ACTIVE : '';
this.viewElem.addEventListener('scroll', () => {
const scrollTop = this.viewElem.scrollTop;
Expand Down
17 changes: 11 additions & 6 deletions web/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
{
"compilerOptions": {
"lib": [
"es6",
"dom"
],
"lib": ["es2017", "dom"],
"module": "none",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": true,
Expand All @@ -13,6 +10,14 @@
"outDir": "../media",
"removeComments": true,
"strict": true,
"target": "es5"
}
"target": "es5",
// Avoid type-checking third-party .d.ts files (e.g., @types/*) that may require a newer TS version
"skipLibCheck": true,
// Limit ambient types to none for the web build so unrelated @types packages aren't pulled in
"types": [],
// Prevent scanning node_modules/@types entirely to avoid parsing newer declaration syntax on older TS
"typeRoots": []
},
// Restrict the program to just the web sources and local .d.ts, excluding node_modules entirely
"include": ["./**/*.ts", "./**/*.d.ts"]
}
2 changes: 1 addition & 1 deletion web/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ function observeElemScroll(id: string, initialScrollTop: number, onScroll: (scro
const elem = document.getElementById(id);
if (elem === null) return;

let timeout: NodeJS.Timer | null = null;
let timeout: number | null = null;
elem.scroll(0, initialScrollTop);
elem.addEventListener('scroll', () => {
const elem = document.getElementById(id);
Expand Down