@programinglive/commiter 1.2.6 → 1.2.12

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.
@@ -1,232 +0,0 @@
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
- }
@@ -1,37 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { execSync } = require('child_process');
4
-
5
- // Get current version from latest git tag
6
- let version;
7
- try {
8
- // Get the latest tag
9
- const tag = execSync('git describe --tags --abbrev=0', { encoding: 'utf8' }).trim();
10
- // Remove 'v' prefix if present
11
- version = tag.startsWith('v') ? tag.substring(1) : tag;
12
- } catch (error) {
13
- // Fallback to package.json if no tags exist
14
- console.warn('⚠️ No git tags found, falling back to package.json version');
15
- const packageJson = require('../package.json');
16
- version = packageJson.version;
17
- }
18
-
19
- const indexPath = path.join(__dirname, '../web/index.html');
20
- let content = fs.readFileSync(indexPath, 'utf8');
21
-
22
- // Update version in the badge
23
- // Matches: <span>v1.1.9 - Latest Release</span>
24
- content = content.replace(
25
- /<span>v\d+\.\d+\.\d+ - Latest Release<\/span>/,
26
- `<span>v${version} - Latest Release</span>`
27
- );
28
-
29
- // Update version in the stats
30
- // Matches: <div class="stat-value">1.1.9</div>
31
- content = content.replace(
32
- /<div class="stat-value">\d+\.\d+\.\d+<\/div>(\s*<div class="stat-label">Latest Version<\/div>)/,
33
- `<div class="stat-value">${version}</div>$1`
34
- );
35
-
36
- fs.writeFileSync(indexPath, content);
37
- console.log(`✅ Updated website to version ${version} (from git tag)`);