Skip to content

Commit 15ce3f0

Browse files
authored
Version 4.0.1 (#92)
# Version 4.0.1 Upgraded Bootstrap framework integration with Bootstrap Icons. ## Enhancements and Updates - Upgraded Bootstrap framework integration with Bootstrap Icons. ## General Code Improvements - Applied `fs.strict` check to HTML files (via Vite upgrade). - Refactored conditional rendering logic across components for improved readability and maintainability. - Improved error handling and type annotations. ## Licence and Documentation - Added JSDoc comments for better documentation. ## General Technical Changes - Improved builder scripts (`GeneratePrDescription.ts`, `GenerateReleaseNotes.ts`). - Improved code readability and maintainability. - Enhanced type safety in utility functions. - Resolved project errors and warnings. ## Dependencies - Fixed security vulnerabilities in dependencies. - **Vite** upgraded from 6.3.5 → 6.3.6 → 6.4.1 (security fixes and improvements). - Added `bootstrap-icons` for icon library support. - Added `react-google-recaptcha-v3` for reCAPTCHA integration. - Added `@types/react-google-recaptcha-v3` for type definitions. ## Changes by @tahoni @dependabot
2 parents 353a75e + d75100d commit 15ce3f0

File tree

102 files changed

+2019
-282
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+2019
-282
lines changed

.idea/scopes/src.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515

1616
## Description
1717

18-
This repository contains the source code for the Hartbeespoortdam Practical Shooting Club (HPSC) website.
18+
This repository contains the source code for the Hartbeespoortdam Practical
19+
Shooting Club (HPSC) website.
1920

2021
## Summary
2122

2223
The [HPSC website](https://hpsc.co.za) uses modern web technologies
23-
to provide an informative and user-friendly platform for members and visitors.<br/>
24+
to provide an informative and user-friendly platform for members and
25+
visitors.<br/>
2426
The primary technologies used in this project include TypeScript, SCSS, and MDX.
2527

2628
## Repository
@@ -29,14 +31,16 @@ The repository for this project is located at
2931
[GitHub](https://github.com/tahoni/hpsc-web-vite).
3032

3133
Feature requests, suggestions for improvements and bugs can be
32-
logged using the project's [Issues](https://github.com/tahoni/hpsc-web-vite/issues) page.
34+
logged using the
35+
project's [Issues](https://github.com/tahoni/hpsc-web-vite/issues) page.
3336

3437
An overview of the project can be found at
3538
[https://tahoni.info/projects/hpsc-web-vite](https://www.tahoni.info/projects/hpsc-web-vite).
3639

3740
## Technology
3841

39-
This is a React project bootstrapped using Vite with the TypeScript React template.
42+
This is a React project bootstrapped using Vite with the TypeScript React
43+
template.
4044

4145
It is written in TypeScript and uses both JSX and MDX components.
4246

@@ -53,36 +57,42 @@ Clone the project from [GitHub](https://github.com/tahoni/hpsc-web-vite).
5357

5458
Install the project using `npm install`.<br/>
5559

56-
Run it locally with `npm run dev`.<br/>
57-
It will load at http://localhost:5173/.<br/>
60+
Run it locally with `npm run dev`.<br/>
61+
It will load at http://localhost:5173/.<br/>
5862
This page will reload if you make edits.
5963

60-
Build it for production with `npm run build` to the `dist` directory.<br/>
64+
Build it for production with `npm run build` to the `dist` directory.<br/>
6165
The production build can be verified by running `npm run preview`.
6266

6367
### Generate a Pull Request Description
6468

65-
To generate a Markdown PR description comparing the current branch to main:
69+
To generate a Markdown PR description comparing the current branch to the main
70+
branch:
6671

6772
- Run: `npm run pr:desc`
68-
- To compare to a different base (e.g., develop): `npm run pr:desc -- --base=develop`
73+
- To compare to a different base (e.g. develop):
74+
`npm run pr:desc -- --base=develop`
6975

70-
The script prints Markdown to stdout; you can copy it into your PR description or redirect it to a file:
76+
The script prints Markdown to stdout; you can copy it into your PR description
77+
or redirect it to a file:
7178

7279
- `npm run pr:desc > target/pr-description.md`
7380

7481
### Generate Release Notes (this branch → main)
7582

76-
To generate structured release notes comparing the current branch to the main branch:
83+
To generate structured release notes comparing the current branch to the main
84+
branch:
7785

7886
- Run: `npm run release:notes`
79-
- To compare to a different base (e.g., develop): `npm run release:notes -- --base=develop`
87+
- To compare to a different base (e.g. develop):
88+
`npm run release:notes -- --base=develop`
8089

81-
The script prints Markdown to stdout and also writes a file under `target/` named like:
90+
The script prints Markdown to stdout and also writes a file under `target/`
91+
named like:
8292

8393
- `target/release-notes-<branch>-to-<base>.md`
8494

85-
You can paste the output into PRs or include in RELEASE_NOTES.md as needed.
95+
You can paste the output into PRs or include it in RELEASE_NOTES.md as needed.
8696

8797
## Architecture
8898

builders/GeneratePrDescription.ts

Lines changed: 85 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,140 @@
11
import { execSync } from "child_process";
22

3+
/**
4+
* Executes a shell command and returns the output as a trimmed string.
5+
* Returns an empty string if the command fails.
6+
*/
37
function run(cmd: string): string {
48
try {
5-
return execSync(cmd, { stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
9+
const output = execSync(cmd, { stdio: ["ignore", "pipe", "pipe"] });
10+
return output.toString().trim();
611
} catch {
712
return "";
813
}
914
}
1015

16+
/**
17+
* Detects the base branch for the PR, with fallback logic.
18+
*/
1119
function detectBaseBranch(argBase?: string): string {
12-
const base = argBase || process.env.PR_BASE || "main";
20+
const base = argBase ?? process.env.PR_BASE ?? "main";
21+
1322
// Prefer remote if available
1423
const hasOriginMain = run(`git rev-parse --verify --quiet origin/${base}`);
1524
if (hasOriginMain) return `origin/${base}`;
25+
1626
const hasLocalBase = run(`git rev-parse --verify --quiet ${base}`);
1727
if (hasLocalBase) return base;
18-
// Fallback to default branch from remote
28+
29+
// Fallback to the default branch from remote
1930
const upstream = run("git remote show origin");
2031
const match = upstream.match(/HEAD branch: (.+)/);
21-
if (match) {
32+
if (match?.[1]) {
2233
const head = match[1];
2334
const hasOriginHead = run(`git rev-parse --verify --quiet origin/${head}`);
2435
if (hasOriginHead) return `origin/${head}`;
2536
}
37+
2638
return base; // last resort
2739
}
2840

41+
/**
42+
* Gets the current Git branch name.
43+
*/
2944
function getCurrentBranch(): string {
3045
const branch = run("git rev-parse --abbrev-ref HEAD");
3146
return branch || "(detached)";
3247
}
3348

34-
function getMergeBase(base: string, head: string): string | null {
49+
/**
50+
* Gets the merge base between two Git references.
51+
*/
52+
function getMergeBase(base: string, head: string): string {
3553
const mb = run(`git merge-base ${base} ${head}`);
36-
return mb || null;
54+
return mb || base;
3755
}
3856

57+
/**
58+
* Formats a Date object as a local datetime string with timezone.
59+
*/
3960
function formatDateTime(d: Date): string {
40-
// Use the user's local time; include offset
41-
const pad = (n: number) => n.toString().padStart(2, "0");
61+
const pad = (n: number): string => n.toString().padStart(2, "0");
62+
4263
const yyyy = d.getFullYear();
4364
const mm = pad(d.getMonth() + 1);
4465
const dd = pad(d.getDate());
4566
const hh = pad(d.getHours());
4667
const mi = pad(d.getMinutes());
4768
const ss = pad(d.getSeconds());
69+
4870
const tz = -d.getTimezoneOffset();
4971
const sign = tz >= 0 ? "+" : "-";
5072
const tzh = pad(Math.floor(Math.abs(tz) / 60));
5173
const tzm = pad(Math.abs(tz) % 60);
74+
5275
return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss} ${sign}${tzh}:${tzm}`;
5376
}
5477

78+
/**
79+
* Creates a Markdown section with a title and body.
80+
*/
81+
function section(title: string, body: string): string {
82+
return `## ${title}\n${body}\n`;
83+
}
84+
85+
/**
86+
* Parses a file change line from git diff output.
87+
*/
88+
function parseFileChangeLine(line: string): string {
89+
const parts = line.split(/\t/);
90+
const status = parts[0];
91+
92+
if (!status) {
93+
return "";
94+
}
95+
96+
if (status.startsWith("R")) {
97+
// Rename: "R100\told\tnew"
98+
const from = parts[1];
99+
const to = parts[2];
100+
if (from && to) {
101+
return `- ${status}: ${from}${to}`;
102+
}
103+
}
104+
105+
// Add, Modify, Delete: "M\tpath"
106+
const file = parts[1];
107+
if (file) {
108+
return `- ${status}: ${file}`;
109+
}
110+
111+
return "";
112+
}
113+
114+
/**
115+
* Generates a PR description based on Git history.
116+
*/
55117
function generate(): string {
56118
const args = process.argv.slice(2);
57119
const baseArg = args.find((a) => a.startsWith("--base="))?.split("=")[1];
58-
const section = (title: string, body: string) => `## ${title}\n${body}\n`;
59120

60121
const headBranch = getCurrentBranch();
61122
const baseRef = detectBaseBranch(baseArg);
62123

63124
// Ensure we have up-to-date remote info (best-effort; ignore errors)
64125
run("git fetch --all --prune");
65126

66-
const mergeBase = getMergeBase(baseRef, "HEAD") || baseRef;
127+
const mergeBase = getMergeBase(baseRef, "HEAD");
67128

68129
const shortstat = run(`git diff --shortstat ${mergeBase}..HEAD`);
69130
const filesChangedList = run(`git diff --name-status ${mergeBase}..HEAD`);
70131
const commits = run(
71-
`git log --no-merges --pretty=format:"- %h %s (%an, %ad)" --date=short ${mergeBase}..HEAD`
132+
`git log --no-merges --pretty=format:"- %h %s (%an, %ad)" --date=short ${mergeBase}..HEAD`,
72133
);
73134

74135
// Try to propose a title from the most recent commit subject
75136
const latestSubject = run(`git log -1 --pretty=%s`);
76-
const defaultTitle = latestSubject ? latestSubject : `Update: ${headBranch}`;
137+
const defaultTitle = latestSubject || `Update: ${headBranch}`;
77138

78139
const now = new Date();
79140
const nowStr = formatDateTime(now);
@@ -92,19 +153,8 @@ function generate(): string {
92153
? filesChangedList
93154
.split(/\r?\n/)
94155
.filter(Boolean)
95-
.map((line) => {
96-
// Example formats: "M\tpath", "A\tpath", "R100\told\tnew"
97-
const parts = line.split(/\t/);
98-
if (parts[0].startsWith("R")) {
99-
const status = parts[0];
100-
const from = parts[1];
101-
const to = parts[2];
102-
return `- ${status}: ${from}${to}`;
103-
}
104-
const status = parts[0];
105-
const file = parts[1];
106-
return `- ${status}: ${file}`;
107-
})
156+
.map(parseFileChangeLine)
157+
.filter(Boolean)
108158
.join("\n")
109159
: "(No files changed)";
110160

@@ -116,9 +166,10 @@ function generate(): string {
116166
"- [ ] No sensitive secrets or keys committed",
117167
].join("\n");
118168

119-
const metadata = [`Generated: ${nowStr}`, `Generator: builders/GeneratePrDescription.ts`].join(
120-
"\n"
121-
);
169+
const metadata = [
170+
`Generated: ${nowStr}`,
171+
`Generator: builders/GeneratePrDescription.ts`,
172+
].join("\n");
122173

123174
const header = `# ${defaultTitle}`;
124175

@@ -134,9 +185,13 @@ function generate(): string {
134185
return `${header}\n\n${body}`;
135186
}
136187

137-
if (import.meta.url === `file://${process.argv[1]}` || process.argv[1].endsWith("GeneratePrDescription.ts")) {
188+
// Execute when run directly
189+
if (
190+
import.meta.url === `file://${process.argv[1]}` ||
191+
process.argv[1]?.endsWith("GeneratePrDescription.ts")
192+
) {
138193
const output = generate();
139194
console.log(output);
140195
}
141196

142-
export {};
197+
export {};

0 commit comments

Comments
 (0)