@programinglive/commiter 1.2.1 โ†’ 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.2.3](https://github.com/programinglive/commiter/compare/v1.2.2...v1.2.3) (2025-11-27)
6
+
7
+
8
+ ### โœจ Features
9
+
10
+ * automate website releases timeline updates from release notes ([5abf788](https://github.com/programinglive/commiter/commit/5abf788afd90dde0b8a52dc3658720044f40b2ee))
11
+
12
+ ### [1.2.2](https://github.com/programinglive/commiter/compare/v1.2.1...v1.2.2) (2025-11-27)
13
+
14
+
15
+ ### ๐Ÿงน Chores
16
+
17
+ * update homepage url ([556b173](https://github.com/programinglive/commiter/commit/556b173db74c99e0d0b20045139f415736389727))
18
+
5
19
  ### [1.2.1](https://github.com/programinglive/commiter/compare/v1.2.0...v1.2.1) (2025-11-26)
6
20
 
7
21
 
@@ -4,6 +4,8 @@ This document summarizes every published version of `@programinglive/commiter`.
4
4
 
5
5
  | Version | Date | Highlights |
6
6
  |---------|------|------------|
7
+ | 1.2.3 | 2025-11-27 | automate website releases timeline updates from release notes (5abf788) |
8
+ | 1.2.2 | 2025-11-27 | update homepage url (556b173) |
7
9
  | 1.2.1 | 2025-11-26 | **release:** improve website version update reliability (18f5ace) |
8
10
  | 1.2.0 | 2025-11-26 | See CHANGELOG for details. |
9
11
  | 1.1.11 | 2025-11-26 | **website:** add open graph social media preview (7a2f911) |
@@ -44,6 +46,20 @@ This document summarizes every published version of `@programinglive/commiter`.
44
46
 
45
47
 
46
48
 
49
+
50
+
51
+ ## 1.2.3 โ€“ โœจ Features
52
+
53
+ Released on **2025-11-27**.
54
+
55
+ - automate website releases timeline updates from release notes (5abf788)
56
+
57
+ ## 1.2.2 โ€“ ๐Ÿงน Chores
58
+
59
+ Released on **2025-11-27**.
60
+
61
+ - update homepage url (556b173)
62
+
47
63
  ## 1.2.1 โ€“ ๐Ÿ› Bug Fixes
48
64
 
49
65
  Released on **2025-11-26**.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@programinglive/commiter",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Commiter keeps repositories release-ready by enforcing conventional commits, generating icon-rich changelog entries, and orchestrating semantic version bumps without manual toil. It bootstraps Husky hooks, commitlint rules, and release scripts that inspect history, detect framework-specific test commands, run them automatically, tag git releases, coordinate npm publishing, surface release metrics, enforce project-specific checks, and give maintainers observability across distributed teams. Plus!",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -14,7 +14,8 @@
14
14
  "release:minor": "node scripts/release.cjs minor",
15
15
  "release:patch": "node scripts/release.cjs patch",
16
16
  "web": "npx serve web",
17
- "update:web": "node scripts/update-web-version.js"
17
+ "update:web": "node scripts/update-web-version.js",
18
+ "update:web:releases": "node scripts/update-web-releases.js"
18
19
  },
19
20
  "keywords": [
20
21
  "commit",
@@ -36,7 +37,7 @@
36
37
  "bugs": {
37
38
  "url": "https://github.com/programinglive/commiter/issues"
38
39
  },
39
- "homepage": "https://github.com/programinglive/commiter#readme",
40
+ "homepage": "https://commiter.programinglive.com",
40
41
  "type": "commonjs",
41
42
  "standard-version": {
42
43
  "releaseCommitMessageFormat": "chore(release): {{currentTag}} ๐Ÿš€",
@@ -188,12 +188,15 @@ function runRelease({
188
188
  }
189
189
  }
190
190
 
191
+ let websiteVersionUpdated = false;
192
+ let websiteTimelineUpdated = false;
193
+
191
194
  // Update website version
192
195
  try {
193
196
  console.log('๐ŸŒ Updating website version...');
194
197
  const updateWebResult = spawnSync(process.execPath, ['scripts/update-web-version.js'], { stdio: 'inherit', cwd });
195
198
  if (updateWebResult.status === 0) {
196
- spawnSync('git', ['add', 'web/index.html'], { stdio: 'inherit', cwd });
199
+ websiteVersionUpdated = true;
197
200
  } else {
198
201
  console.warn('โš ๏ธ Failed to update website version');
199
202
  }
@@ -201,6 +204,23 @@ function runRelease({
201
204
  console.warn(`โš ๏ธ Skipping website version update: ${error.message}`);
202
205
  }
203
206
 
207
+ // Update website releases timeline
208
+ try {
209
+ console.log('๐ŸŒ Updating website releases timeline...');
210
+ const updateTimelineResult = spawnSync(process.execPath, ['scripts/update-web-releases.js'], { stdio: 'inherit', cwd });
211
+ if (updateTimelineResult.status === 0) {
212
+ websiteTimelineUpdated = true;
213
+ } else {
214
+ console.warn('โš ๏ธ Failed to update website releases timeline');
215
+ }
216
+ } catch (error) {
217
+ console.warn(`โš ๏ธ Skipping website releases timeline update: ${error.message}`);
218
+ }
219
+
220
+ if (websiteVersionUpdated || websiteTimelineUpdated) {
221
+ spawnSync('git', ['add', 'web/index.html'], { stdio: 'inherit', cwd });
222
+ }
223
+
204
224
  return releaseResult;
205
225
  }
206
226
  if (require.main === module) {
@@ -0,0 +1,232 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const DEFAULT_MAX_RELEASES = 5;
5
+ const RELEASE_NOTES_PATH = path.join(__dirname, '..', 'docs', 'release-notes', 'RELEASE_NOTES.md');
6
+ const INDEX_PATH = path.join(__dirname, '..', 'web', 'index.html');
7
+ const MARKER_START = '<!-- RELEASES_TIMELINE:START -->';
8
+ const MARKER_END = '<!-- RELEASES_TIMELINE:END -->';
9
+
10
+ function readFileOrThrow(filePath, label) {
11
+ if (!fs.existsSync(filePath)) {
12
+ throw new Error(`${label} not found at ${filePath}`);
13
+ }
14
+ return fs.readFileSync(filePath, 'utf8');
15
+ }
16
+
17
+ function sanitizeText(text = '') {
18
+ return text
19
+ .replace(/\*\*/g, '')
20
+ .replace(/`/g, '')
21
+ .replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1')
22
+ .replace(/\s+/g, ' ')
23
+ .trim();
24
+ }
25
+
26
+ function escapeHtml(text = '') {
27
+ return text
28
+ .replace(/&/g, '&amp;')
29
+ .replace(/</g, '&lt;')
30
+ .replace(/>/g, '&gt;')
31
+ .replace(/"/g, '&quot;')
32
+ .replace(/'/g, '&#39;');
33
+ }
34
+
35
+ function formatDate(dateString) {
36
+ if (!dateString) {
37
+ return 'Date pending';
38
+ }
39
+ const date = new Date(dateString);
40
+ if (Number.isNaN(date.valueOf())) {
41
+ return dateString;
42
+ }
43
+ return date.toLocaleDateString('en-US', {
44
+ year: 'numeric',
45
+ month: 'long',
46
+ day: 'numeric'
47
+ });
48
+ }
49
+
50
+ function extractReleaseSections(content) {
51
+ const normalizedContent = content.replace(/\r\n/g, '\n');
52
+ const tableEntries = extractReleaseTableEntries(normalizedContent);
53
+ const detailMap = extractReleaseDetailsMap(normalizedContent);
54
+
55
+ if (tableEntries.length === 0) {
56
+ return extractSectionEntries(normalizedContent);
57
+ }
58
+
59
+ return tableEntries.map((entry) => {
60
+ const details = detailMap.get(entry.version) || {};
61
+ return {
62
+ version: entry.version,
63
+ releaseType: details.releaseType || '',
64
+ date: entry.date,
65
+ description: details.description || entry.highlight || 'See CHANGELOG for details.'
66
+ };
67
+ });
68
+ }
69
+
70
+ function extractReleaseTableEntries(content) {
71
+ const lines = content.split('\n');
72
+ const headerIndex = lines.findIndex((line) => /^\|\s*Version\s*\|/i.test(line));
73
+ if (headerIndex === -1) {
74
+ return [];
75
+ }
76
+
77
+ const entries = [];
78
+ for (let i = headerIndex + 2; i < lines.length; i++) {
79
+ const line = lines[i];
80
+ if (!line.trim().startsWith('|')) {
81
+ break;
82
+ }
83
+ const cells = line.split('|').slice(1, -1).map((cell) => sanitizeText(cell));
84
+ if (cells.length < 3) {
85
+ continue;
86
+ }
87
+ const [version, date, highlight] = cells;
88
+ if (!version) {
89
+ continue;
90
+ }
91
+ entries.push({ version, date, highlight });
92
+ }
93
+
94
+ return entries;
95
+ }
96
+
97
+ function extractReleaseDetailsMap(content) {
98
+ const map = new Map();
99
+ const sectionRegex = /^##\s+(.+?)\s*$([\s\S]*?)(?=^##\s+|$)/gm;
100
+ let match;
101
+
102
+ while ((match = sectionRegex.exec(content)) !== null) {
103
+ const heading = match[1].trim();
104
+ const body = match[2].trim().replace(/\r/g, '');
105
+ const headingMatch = heading.match(/^(\d+\.\d+\.\d+)(?:\s+[โ€“-]\s+(.+))?/);
106
+ if (!headingMatch) {
107
+ continue;
108
+ }
109
+
110
+ const version = headingMatch[1];
111
+ const releaseType = headingMatch[2] ? headingMatch[2].trim() : '';
112
+ const descriptionMatch = body.match(/^\s*\-\s+(.+)$/m);
113
+ const description = descriptionMatch ? sanitizeText(descriptionMatch[1]) : undefined;
114
+
115
+ map.set(version, {
116
+ releaseType,
117
+ description
118
+ });
119
+ }
120
+
121
+ return map;
122
+ }
123
+
124
+ function extractSectionEntries(content) {
125
+ const sections = [];
126
+ const sectionRegex = /^##\s+(.+?)\s*$([\s\S]*?)(?=^##\s+|$)/gm;
127
+ let match;
128
+
129
+ while ((match = sectionRegex.exec(content)) !== null) {
130
+ const heading = match[1].trim();
131
+ const body = match[2].trim().replace(/\r/g, '');
132
+ const headingMatch = heading.match(/^(\d+\.\d+\.\d+)(?:\s+[โ€“-]\s+(.+))?/);
133
+ if (!headingMatch) {
134
+ continue;
135
+ }
136
+
137
+ const version = headingMatch[1];
138
+ const releaseType = headingMatch[2] ? headingMatch[2].trim() : '';
139
+ const dateMatch = body.match(/Released on \*\*(\d{4}-\d{2}-\d{2})\*\*\.?/);
140
+ const descriptionMatch = body.match(/^\s*\-\s+(.+)$/m);
141
+
142
+ sections.push({
143
+ version,
144
+ releaseType,
145
+ date: dateMatch ? dateMatch[1] : undefined,
146
+ description: descriptionMatch ? sanitizeText(descriptionMatch[1]) : 'See CHANGELOG for details.'
147
+ });
148
+ }
149
+
150
+ return sections;
151
+ }
152
+
153
+ function renderReleaseItem(release, { latest = false, indent = '' } = {}) {
154
+ const badge = latest ? `\n${indent} <div class="release-badge">Latest</div>` : '';
155
+ const typeLine = release.releaseType
156
+ ? `\n${indent} <div class="release-type">${escapeHtml(release.releaseType)}</div>`
157
+ : '';
158
+
159
+ return `${indent}<div class="release-item">${badge}
160
+ ${indent} <div class="release-version">v${escapeHtml(release.version)}</div>
161
+ ${indent} <div class="release-date">${escapeHtml(formatDate(release.date))}</div>${typeLine}
162
+ ${indent} <p class="release-description">${escapeHtml(release.description)}</p>
163
+ ${indent}</div>`;
164
+ }
165
+
166
+ function updateIndexHtml({
167
+ htmlContent,
168
+ releases,
169
+ markerStart = MARKER_START,
170
+ markerEnd = MARKER_END,
171
+ indent = ' '
172
+ }) {
173
+ const startIndex = htmlContent.indexOf(markerStart);
174
+ const endIndex = htmlContent.indexOf(markerEnd);
175
+
176
+ if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
177
+ throw new Error('Release timeline markers not found in web/index.html');
178
+ }
179
+
180
+ const before = htmlContent.slice(0, startIndex + markerStart.length);
181
+ const after = htmlContent.slice(endIndex);
182
+ const timeline = releases
183
+ .map((release, idx) => renderReleaseItem(release, { latest: idx === 0, indent }))
184
+ .join('\n');
185
+
186
+ return `${before}\n${timeline}\n${indent}${after.trimStart()}`;
187
+ }
188
+
189
+ function updateWebReleases({
190
+ releaseNotesPath = RELEASE_NOTES_PATH,
191
+ indexPath = INDEX_PATH,
192
+ maxReleases = DEFAULT_MAX_RELEASES
193
+ } = {}) {
194
+ const notesContent = readFileOrThrow(releaseNotesPath, 'Release notes');
195
+ const releases = extractReleaseSections(notesContent).slice(0, maxReleases);
196
+
197
+ if (releases.length === 0) {
198
+ console.warn('โš ๏ธ No release sections found; skipping website update.');
199
+ return false;
200
+ }
201
+
202
+ const htmlContent = readFileOrThrow(indexPath, 'Landing page');
203
+ const nextContent = updateIndexHtml({ htmlContent, releases });
204
+ fs.writeFileSync(indexPath, nextContent);
205
+ console.log(`โœ… Updated website releases timeline with ${releases.length} entries.`);
206
+ return true;
207
+ }
208
+
209
+ function main() {
210
+ try {
211
+ updateWebReleases();
212
+ } catch (error) {
213
+ console.error(`โŒ Failed to update website releases timeline: ${error.message}`);
214
+ process.exit(1);
215
+ }
216
+ }
217
+
218
+ module.exports = {
219
+ escapeHtml,
220
+ sanitizeText,
221
+ formatDate,
222
+ extractReleaseSections,
223
+ renderReleaseItem,
224
+ updateIndexHtml,
225
+ updateWebReleases,
226
+ MARKER_START,
227
+ MARKER_END
228
+ };
229
+
230
+ if (require.main === module) {
231
+ main();
232
+ }
package/web/index.html CHANGED
@@ -66,7 +66,7 @@
66
66
  <div class="hero-content">
67
67
  <div class="hero-badge">
68
68
  <span class="badge-dot"></span>
69
- <span>v1.2.1 - Latest Release</span>
69
+ <span>v1.2.3 - Latest Release</span>
70
70
  </div>
71
71
  <h1 class="hero-title">
72
72
  Ship Releases with <span class="gradient-text">Confidence</span>
@@ -90,7 +90,7 @@
90
90
  </div>
91
91
  <div class="hero-stats">
92
92
  <div class="stat-item">
93
- <div class="stat-value">1.2.1</div>
93
+ <div class="stat-value">1.2.3</div>
94
94
  <div class="stat-label">Latest Version</div>
95
95
  </div>
96
96
  <div class="stat-item">
@@ -382,39 +382,38 @@
382
382
  <p class="section-subtitle">Latest updates and improvements</p>
383
383
  </div>
384
384
  <div class="releases-timeline">
385
+ <!-- RELEASES_TIMELINE:START -->
385
386
  <div class="release-item">
386
387
  <div class="release-badge">Latest</div>
387
- <div class="release-version">v1.1.9</div>
388
- <div class="release-date">November 24, 2025</div>
389
- <div class="release-type">๐Ÿ› Bug Fixes</div>
390
- <p class="release-description">Convert release scripts to CJS to support ESM projects</p>
388
+ <div class="release-version">v1.2.3</div>
389
+ <div class="release-date">November 27, 2025</div>
390
+ <div class="release-type">โœจ Features</div>
391
+ <p class="release-description">automate website releases timeline updates from release notes (5abf788)</p>
391
392
  </div>
392
393
  <div class="release-item">
393
- <div class="release-version">v1.1.8</div>
394
- <div class="release-date">November 22, 2025</div>
395
- <div class="release-type">๐Ÿ› Bug Fixes</div>
396
- <p class="release-description">Update installation script to copy missing files and update gitignore
397
- </p>
394
+ <div class="release-version">v1.2.2</div>
395
+ <div class="release-date">November 27, 2025</div>
396
+ <div class="release-type">๐Ÿงน Chores</div>
397
+ <p class="release-description">update homepage url (556b173)</p>
398
398
  </div>
399
399
  <div class="release-item">
400
- <div class="release-version">v1.1.7</div>
401
- <div class="release-date">November 11, 2025</div>
402
- <div class="release-type">๐Ÿ“ Documentation</div>
403
- <p class="release-description">Recommend dev workflow MCP server for enhanced workflow automation
404
- </p>
400
+ <div class="release-version">v1.2.1</div>
401
+ <div class="release-date">November 26, 2025</div>
402
+ <div class="release-type">๐Ÿ› Bug Fixes</div>
403
+ <p class="release-description">release: improve website version update reliability (18f5ace)</p>
405
404
  </div>
406
405
  <div class="release-item">
407
- <div class="release-version">v1.1.1</div>
408
- <div class="release-date">November 5, 2025</div>
409
- <div class="release-type">๐Ÿ› Bug Fixes</div>
410
- <p class="release-description">Eliminated fs.F_OK deprecation warning from release runs</p>
406
+ <div class="release-version">v1.2.0</div>
407
+ <div class="release-date">November 26, 2025</div>
408
+ <p class="release-description">See CHANGELOG for details.</p>
411
409
  </div>
412
410
  <div class="release-item">
413
- <div class="release-version">v1.0.12</div>
414
- <div class="release-date">October 29, 2025</div>
415
- <div class="release-type">โœจ Features</div>
416
- <p class="release-description">Autodetects project test command before releasing</p>
411
+ <div class="release-version">v1.1.11</div>
412
+ <div class="release-date">November 26, 2025</div>
413
+ <div class="release-type">๐Ÿ“ Documentation</div>
414
+ <p class="release-description">website: add open graph social media preview (7a2f911)</p>
417
415
  </div>
416
+ <!-- RELEASES_TIMELINE:END -->
418
417
  </div>
419
418
  <div class="releases-cta">
420
419
  <a href="https://github.com/programinglive/commiter/releases" target="_blank" class="btn btn-secondary">