diff --git a/docs/changelog/assets/css/app.css b/docs/changelog/assets/css/app.css index 31de425f3..a6607e7d0 100644 --- a/docs/changelog/assets/css/app.css +++ b/docs/changelog/assets/css/app.css @@ -1,9 +1,9 @@ -/* +/* * CSS Variables for consistent theming - * + * * Usage in CSS: background-color: var(--color-webex-blue); * Usage in JS: element.style.backgroundColor = 'var(--color-success)'; - * + * * Available variables: * --color-success: Success state (green) * --color-success-hover: Success hover state diff --git a/docs/changelog/assets/js/app.js b/docs/changelog/assets/js/app.js index 6368c66d1..fa8439b31 100644 --- a/docs/changelog/assets/js/app.js +++ b/docs/changelog/assets/js/app.js @@ -5,17 +5,9 @@ let comparisonListenersInitialized = false; const githubBaseUrl = 'https://github.com/webex/widgets/'; import { comparisonState, - extractPackagesFromVersion, - findLatestPackageVersion, - getEffectiveVersion, - getPackageVersion, - determinePackageStatus, - createPackageComparisonRow, - calculateComparisonStats, - buildPackagesList, - comparePackages, - fetchAndCompareVersions, generatePackageComparisonData, + sortStableVersions, + collectCommitsFromStable, } from './comparison-view.js'; // DOM elements @@ -133,17 +125,18 @@ Handlebars.registerHelper('convertDate', function (timestamp) { return `${new Date(timestamp).toDateString()} ${new Date(timestamp).toTimeString()}`; }); +Handlebars.registerHelper('math', function (index, offset) { + return index + offset; +}); + // Util Methods const populateFormFieldsFromURL = async () => { const queryParams = new URLSearchParams(window.location.search); - // Skip single-view URL handling if comparison parameters are present if ( - queryParams.has('compare') || - queryParams.has('compareStableA') || - (queryParams.has('versionA') && queryParams.has('versionB')) - ) { - return; // Comparison mode will handle these parameters + queryParams.has('compareStableA')) + { + return; } const searchParams = { @@ -680,20 +673,6 @@ window.onhashchange = () => { populateVersions(); -let comparisonMode = false; -/* ============================================ - UI HELPER FUNCTIONS - ============================================ */ - -/** - * Show loading state for comparison - */ -const showComparisonLoading = () => { - if (!comparisonResults) return; - comparisonResults.innerHTML = '

Loading comparison...

'; - comparisonResults.classList.remove('hide'); -}; - /** * Show error state for comparison * @param {Error} error - The error object @@ -714,145 +693,6 @@ const showComparisonError = (error) => { DATA LAYER FUNCTIONS ============================================ */ -/** - * UI LAYER: Handle version comparison UI updates - * @param {string} versionA - Base version - * @param {string} versionB - Target version - */ -const performVersionComparison = async (versionA, versionB) => { - // Show loading state - showComparisonLoading(); - - try { - // Fetch and compare data (pure data logic) - const result = await fetchAndCompareVersions(versionA, versionB, versionPaths); - - // Display results (UI logic) - displayComparison(result.versionA, result.versionB, result.comparisonData); - } catch (error) { - // Handle error display (UI logic) - showComparisonError(error); - } -}; - -/** - * Display comparison results - * @param {string} versionA - Base version - * @param {string} versionB - Target version - * @param {Object} comparisonData - Comparison results - */ -const displayComparison = (versionA, versionB, comparisonData) => { - if (!comparisonResults) { - console.error('comparison-results element not found!'); - return; - } - - if (!comparisonTemplateElement) { - console.error('comparison-template element not found!'); - return; - } - - const comparisonTemplate = Handlebars.compile(comparisonTemplateElement.innerHTML); - - const templateData = { - versionA, - versionB, - ...comparisonData, - }; - - console.log('Template data:', templateData); - - try { - const html = comparisonTemplate(templateData); - console.log('Generated HTML length:', html.length); - - comparisonResults.innerHTML = html; - comparisonResults.classList.remove('hide'); - - // Update URL with comparison parameters for permalinks - updateComparisonURL(versionA, versionB); - - // Show the copy link button and helper text - if (copyComparisonLinkBtn) { - copyComparisonLinkBtn.classList.remove('hide'); - console.log('Copy link button shown'); - } else { - console.warn('Copy link button not found in DOM'); - } - if (comparisonHelper) { - comparisonHelper.classList.remove('hide'); - } - - // Scroll to results smoothly - setTimeout(() => { - comparisonResults.scrollIntoView({behavior: 'smooth', block: 'start'}); - }, 100); - - console.log('Comparison displayed successfully'); - } catch (error) { - console.error('Error rendering template:', error); - comparisonResults.innerHTML = `
Error rendering comparison: ${error.message}
`; - } -}; - -/** - * Update URL with comparison parameters for sharing/bookmarking - * @param {string} versionA - Base version - * @param {string} versionB - Target version - */ -const updateComparisonURL = (versionA, versionB) => { - const url = new URL(window.location); - - // Clear any single-view parameters - url.searchParams.delete('stable_version'); - url.searchParams.delete('package'); - url.searchParams.delete('version'); - url.searchParams.delete('commitMessage'); - url.searchParams.delete('commitHash'); - // Clear enhanced (package-level) comparison params so full comparison link is not stale - url.searchParams.delete('compareStableA'); - url.searchParams.delete('compareStableB'); - url.searchParams.delete('comparePackage'); - url.searchParams.delete('compareVersionA'); - url.searchParams.delete('compareVersionB'); - - - // Set comparison parameters - url.searchParams.set('compare', `${versionA}vs${versionB}`); - - // Update URL without reloading the page - window.history.pushState({}, '', url); -}; - -/** - * Parse and handle comparison URL parameters - * Supports formats: ?compare=3.9.0vs3.10.0 or ?versionA=3.9.0&versionB=3.10.0 - */ -const handleComparisonURLParams = async () => { - const urlParams = new URLSearchParams(window.location.search); - - let versionA = null; - let versionB = null; - - // Check for ?compare=AvB format - const compareParam = urlParams.get('compare'); - if (compareParam && compareParam.includes('vs')) { - const versions = compareParam.split('vs'); - versionA = versions[0]?.trim(); - versionB = versions[1]?.trim(); - } - - // Also support ?versionA=X&versionB=Y format - if (!versionA) versionA = urlParams.get('versionA'); - if (!versionB) versionB = urlParams.get('versionB'); - - // If comparison parameters are found, switch to comparison mode - if (versionA && versionB && versionA !== versionB) { - return {versionA, versionB, shouldCompare: true}; - } - - return {shouldCompare: false}; -}; /** * Switch to comparison mode programmatically @@ -860,9 +700,6 @@ const handleComparisonURLParams = async () => { * @param {string} versionB - Target version (optional) */ const switchToComparisonMode = (versionA = null, versionB = null) => { - // Update mode - comparisonMode = true; - // Update button states if (comparisonViewBtn && singleViewBtn) { comparisonViewBtn.classList.add('active', 'btn-primary'); @@ -920,9 +757,8 @@ const getUnionPackages = (changelogA, changelogB) => { * @param {Object} changelogA - Changelog A * @param {Object} changelogB - Changelog B */ -const compareAndRenderPackageVersions = (packageName, versionASpecific, versionBSpecific, changelogA, changelogB) => { +const compareAndRenderPackageVersions = (packageName, versionASpecific, versionBSpecific, changelogA, changelogB, totalCommits = 0) => { try { - // Generate comparison data (pure data logic from comparison-view.js) const comparisonData = generatePackageComparisonData( packageName, versionASpecific, @@ -931,9 +767,8 @@ const compareAndRenderPackageVersions = (packageName, versionASpecific, versionB changelogB ); - console.log('comparisonData', comparisonData); + comparisonData.totalCommits = totalCommits; - // Validate DOM elements if (!comparisonResults) { console.error('comparison-results element not found'); return; @@ -944,12 +779,12 @@ const compareAndRenderPackageVersions = (packageName, versionASpecific, versionB return; } - // Render template const template = Handlebars.compile(comparisonTemplateElement.innerHTML); const html = template(comparisonData); - // Update DOM - comparisonResults.innerHTML = html; + // NOTE: renderCommitHistory must be called first — it resets this container via `=`. + // This `+=` appends the package comparison table below the commit history. + comparisonResults.innerHTML += html; comparisonResults.classList.remove('hide'); // Update URL for sharing @@ -996,7 +831,7 @@ const populateUnionPackages = (changelogA, changelogB) => { return; } - let optionsHtml = ''; + let optionsHtml = ''; allPackages.forEach((pkg) => { optionsHtml += ``; }); @@ -1117,18 +952,6 @@ const handleEnhancedComparisonURL = async () => { return {shouldCompare: false}; }; - -/** - * Populate version dropdowns for comparison mode - */ -const populateComparisonVersions = () => { - if (versionSelectDropdown && versionSelectDropdown.innerHTML) { - const options = versionSelectDropdown.innerHTML; - if (versionASelect) versionASelect.innerHTML = options; - if (versionBSelect) versionBSelect.innerHTML = options; - } -}; - /** * Reset comparison form selections */ @@ -1162,9 +985,6 @@ const clearComparisonForm = () => { const clearComparisonURLParams = () => { const url = new URL(window.location); [ - 'compare', - 'versionA', - 'versionB', 'compareStableA', 'compareStableB', 'comparePackage', @@ -1195,8 +1015,7 @@ const updateCompareButtonState = () => { compareButton.disabled = false; } } else { - // No package selected - enable for full version comparison - compareButton.disabled = false; + compareButton.disabled = true; } }; @@ -1216,7 +1035,6 @@ const updatePrereleaseLabels = () => { * Handle stable version changes - fetch changelogs and populate packages */ const handleStableVersionChange = async () => { - console.log('🟢 handleStableVersionChange FIRED'); const stableA = versionASelect.value; const stableB = versionBSelect.value; @@ -1244,7 +1062,6 @@ const handleStableVersionChange = async () => { * Handle package selection - populate pre-release versions */ const handlePackageChange = () => { - console.log('🟢 handlePackageChange FIRED'); const selectedPackage = comparisonPackageSelect.value; if (versionAPrereleaseSelect) versionAPrereleaseSelect.value = ''; @@ -1279,8 +1096,6 @@ const handlePackageChange = () => { * Switch to single view mode */ const switchToSingleViewMode = () => { - console.log('🔵 Switching to SINGLE VIEW mode'); - comparisonMode = false; // Update button styles singleViewBtn.classList.add('active', 'btn-primary'); @@ -1294,25 +1109,6 @@ const switchToSingleViewMode = () => { clearComparisonURLParams(); }; -/** - * Switch to comparison view mode - */ -const switchToComparisonViewMode = () => { - console.log('🔵 Switching to COMPARISON VIEW mode'); - comparisonMode = true; - - // Update button styles - comparisonViewBtn.classList.add('active', 'btn-primary'); - comparisonViewBtn.classList.remove('btn-default'); - singleViewBtn.classList.remove('active', 'btn-primary'); - singleViewBtn.classList.add('btn-default'); - - // Toggle visibility (centralized view state) - updateUIVisibility('comparison'); - - populateComparisonVersions(); -}; - /** * Validate comparison form inputs */ @@ -1327,13 +1123,132 @@ const validateComparisonInputs = (stableA, stableB, selectedPackage, versionASpe return false; } + // Base stable version must be older than or equal to target + const allStables = sortStableVersions(Object.keys(versionPaths)); + const idxA = allStables.indexOf(stableA); + const idxB = allStables.indexOf(stableB); + + if (idxA > idxB) { + alert('Base version must be older than or equal to target version'); + return false; + } + + // If same stable and both pre-releases selected, base must be older + if (stableA === stableB && versionASpecific && versionBSpecific) { + const numA = parseInt(versionASpecific.match(/\.(\d+)$/)?.[1] || '0', 10); + const numB = parseInt(versionBSpecific.match(/\.(\d+)$/)?.[1] || '0', 10); + if (numA > numB) { + alert('Base pre-release version must be older than target pre-release version'); + return false; + } + } + return true; }; +/* ============================================ + CROSS-STABLE COMMIT AGGREGATION (app-level) + These functions interact with versionPaths and + the DOM, so they live here instead of comparison-view.js + ============================================ */ + +/** + * Return the ordered list of stable versions between stableA and stableB (inclusive). + */ +const getStableVersionsBetween = (stableA, stableB) => { + const allStables = sortStableVersions(Object.keys(versionPaths)); + const idxA = allStables.indexOf(stableA); + const idxB = allStables.indexOf(stableB); + if (idxA === -1 || idxB === -1) return []; + const lo = Math.min(idxA, idxB); + const hi = Math.max(idxA, idxB); + return allStables.slice(lo, hi + 1); +}; + +/** + * Walk every stable between stableA..stableB, + * fetch each log file, and collect deduplicated commits. + */ +const collectCommitsAcrossStables = async (packageName, stableA, stableB, versionA, versionB) => { + const stables = getStableVersionsBetween(stableA, stableB); + if (stables.length === 0) return []; + + const allCommits = []; + for (let i = 0; i < stables.length; i++) { + const stable = stables[i]; + const path = versionPaths[stable]; + if (!path) continue; + + let changelog; + if (stable === stableA && comparisonState.cachedChangelogA ) { + changelog = comparisonState.cachedChangelogA; + } else if (stable === stableB && comparisonState.cachedChangelogB) { + changelog = comparisonState.cachedChangelogB; + } else { + try { + const res = await fetch(path); + if (!res.ok) { + throw new Error(`Failed to fetch changelog for stable ${stable}`); + } + changelog = await res.json(); + } catch (error) { + + continue; + } + } + + const packageData = changelog[packageName]; + if (!packageData) continue; + + let position; + if (stables.length === 1) position = 'only'; + else if (i === 0) position = 'start'; + else if (i === stables.length - 1) position = 'end'; + else position = 'middle'; + + const commits = collectCommitsFromStable(packageData, stable, versionA, versionB, position); + allCommits.push(...commits); + } + + // Final dedup by hash + const seen = new Map(); + allCommits.forEach((c) => { + if (!seen.has(c.hash)) seen.set(c.hash, c); + }); + return Array.from(seen.values()); +}; + +/** + * Render the commit history using the Handlebars template in index.html + */ +const renderCommitHistory = (packageName, versionA, versionB, commits) => { + const templateEl = document.getElementById('commit-history-template'); + if (!templateEl) { + console.error('commit-history-template not found in DOM'); + return; + } + + const template = Handlebars.compile(templateEl.innerHTML); + const html = template({ + packageName, + versionA, + versionB, + commits, + totalCommits: commits.length, + githubBaseUrl, + }); + + comparisonResults.innerHTML = html; + comparisonResults.classList.remove('hide'); + + if (copyComparisonLinkBtn) copyComparisonLinkBtn.classList.remove('hide'); + if (comparisonHelper) comparisonHelper.classList.remove('hide'); +}; + /** * Handle comparison form submission */ -const handleComparisonSubmit = (event) => { +const handleComparisonSubmit = async (event) => { event.preventDefault(); const stableA = versionASelect.value; @@ -1347,21 +1262,38 @@ const handleComparisonSubmit = (event) => { } if (selectedPackage && (versionASpecific || versionBSpecific)) { - // Package-level comparison const finalVersionA = versionASpecific || stableA; const finalVersionB = versionBSpecific || stableB; - console.log('Comparing:', finalVersionA, 'vs', finalVersionB); - compareAndRenderPackageVersions( - selectedPackage, - finalVersionA, - finalVersionB, - comparisonState.cachedChangelogA, - comparisonState.cachedChangelogB - ); + try { + const commits = await collectCommitsAcrossStables( + selectedPackage, + stableA, + stableB, + finalVersionA, + finalVersionB + ); + + // Order matters: renderCommitHistory resets the container; compareAndRender appends to it. + renderCommitHistory(selectedPackage, finalVersionA, finalVersionB, commits); + + // Package-level comparison table (appended below) + compareAndRenderPackageVersions( + selectedPackage, + finalVersionA, + finalVersionB, + comparisonState.cachedChangelogA, + comparisonState.cachedChangelogB, + commits.length + ); + + updateEnhancedComparisonURL(stableA, stableB, selectedPackage, finalVersionA, finalVersionB); + } catch (error) { + showComparisonError(error); + } } else { - // Full version comparison - performVersionComparison(stableA, stableB); + alert('Please select a package and at least one pre-release version to compare.'); + return; } if (compareButton) compareButton.disabled = false; @@ -1380,15 +1312,13 @@ const handleClearClick = () => { */ const setupComparisonEventListeners = () => { if (comparisonListenersInitialized) { - console.log('🔴 Comparison listeners already initialized,skipping......'); return; } - console.log('🟢 Setting up comparison event listeners first time......'); comparisonListenersInitialized = true; // Mode toggle buttons if (singleViewBtn) singleViewBtn.addEventListener('click', switchToSingleViewMode); - if (comparisonViewBtn) comparisonViewBtn.addEventListener('click', switchToComparisonViewMode); + if (comparisonViewBtn) comparisonViewBtn.addEventListener('click', () => switchToComparisonMode()); // Version and package selectors if (versionASelect) versionASelect.addEventListener('change', handleStableVersionChange); @@ -1432,24 +1362,33 @@ const loadEnhancedComparisonFromURL = async (enhancedParams) => { versionAPrereleaseSelect.value = enhancedParams.versionA; versionBPrereleaseSelect.value = enhancedParams.versionB; - compareAndRenderPackageVersions( - enhancedParams.packageName, - enhancedParams.versionA, - enhancedParams.versionB, - comparisonState.cachedChangelogA, - comparisonState.cachedChangelogB - ); -}; - -/** - * Handle standard comparison URL parameters on page load - */ -const loadStandardComparisonFromURL = async (urlParams) => { - switchToComparisonMode(urlParams.versionA, urlParams.versionB); + try { + const commits = await collectCommitsAcrossStables( + enhancedParams.packageName, + enhancedParams.stableA, + enhancedParams.stableB, + enhancedParams.versionA, + enhancedParams.versionB + ); - await new Promise((resolve) => setTimeout(resolve, 300)); + renderCommitHistory( + enhancedParams.packageName, + enhancedParams.versionA, + enhancedParams.versionB, + commits + ); - performVersionComparison(urlParams.versionA, urlParams.versionB); + compareAndRenderPackageVersions( + enhancedParams.packageName, + enhancedParams.versionA, + enhancedParams.versionB, + comparisonState.cachedChangelogA, + comparisonState.cachedChangelogB, + commits.length + ); + } catch (error) { + console.error('Error loading commit history from URL:', error); + } }; /** @@ -1465,12 +1404,6 @@ const initializeComparisonMode = async () => { await loadEnhancedComparisonFromURL(enhancedParams); return; } - - // Check for standard comparison URL - const urlParams = await handleComparisonURLParams(); - if (urlParams.shouldCompare) { - await loadStandardComparisonFromURL(urlParams); - } }; /** diff --git a/docs/changelog/assets/js/comparison-view.js b/docs/changelog/assets/js/comparison-view.js index 7352606aa..80e5363f4 100644 --- a/docs/changelog/assets/js/comparison-view.js +++ b/docs/changelog/assets/js/comparison-view.js @@ -19,53 +19,6 @@ const comparisonState = { this.currentStableB = stableB; }, }; -const extractPackagesFromVersion = (changelog, specificVersions = null) => { - const packageMap = {}; - - for (const packageName of Object.keys(changelog)) { - const packageVersions = changelog[packageName]; - console.log('packageVersions', packageVersions); - - // Safety check: ensure packageVersions is an object - if (!packageVersions || typeof packageVersions !== 'object') continue; - - const versionKeys = Object.keys(packageVersions); - console.log('versionKeys', versionKeys); - - if (versionKeys.length === 0) continue; - - let selectedVersion = null; - - // Check if user specified a specific version for this package - if (specificVersions && specificVersions[packageName]) { - const requestedVersion = specificVersions[packageName]; - - if (packageVersions[requestedVersion]) { - selectedVersion = requestedVersion; - } - } - - // If no specific version requested or not found, use earliest (first) version - if (!selectedVersion) { - let earliestVersion = versionKeys[0]; - let earliestDate = packageVersions[earliestVersion]?.published_date || Infinity; - - for (const version of versionKeys) { - const publishedDate = packageVersions[version]?.published_date || Infinity; - if (publishedDate < earliestDate) { - earliestDate = publishedDate; - earliestVersion = version; - } - } - - selectedVersion = earliestVersion; - } - - packageMap[packageName] = selectedVersion; - } - - return packageMap; -}; const findLatestPackageVersion = (changelog, packageName) => { if (!changelog[packageName]) return null; @@ -202,145 +155,148 @@ const buildPackagesList = ( return packagesArray; }; -const comparePackages = (packagesA, packagesB, changelogA, changelogB, stableVersionA, stableVersionB) => { - // Get ALL package names from both changelogs (entire changelog, not just specific versions) - const allPackageNames = new Set([ - ...Object.keys(changelogA), //ALL packages in changelog A - ...Object.keys(changelogB), //ALL packages in changelog B - ]); - - const packages = []; - let changedCount = 0; - let unchangedCount = 0; - let onlyInACount = 0; - let onlyInBCount = 0; - - // Helper function to find stable version first, then highest pre-release version - const findStableVersion = (changelog, packageName, stableVersion) => { - if (!changelog[packageName]) return null; - - const versions = Object.keys(changelog[packageName]); - if (versions.length === 0) return null; - - // Escape dots in version string for regex (3.4.0 -> 3\.4\.0) - const escapedVersion = stableVersion.replace(/\./g, '\\.'); - - // Priority 1: Find exact stable version (e.g., "3.4.0" only, no suffixes) - const exactStablePattern = new RegExp(`^${escapedVersion}$`); - const exactStableVersion = versions.find((ver) => exactStablePattern.test(ver)); - - if (exactStableVersion) { - return exactStableVersion; - } - - // Priority 2: Find oldest pre-release version (any tag: next, alpha, beta, rc, etc.) - // Pattern: 3.4.0-{tag}.{number} -> captures tag and number - const prereleasePattern = new RegExp(`^${escapedVersion}-([a-z]+)\\.(\\d+)$`, 'i'); - - const prereleaseVersions = versions - .filter((ver) => prereleasePattern.test(ver)) - .sort((a, b) => { - const matchA = a.match(prereleasePattern); - const matchB = b.match(prereleasePattern); - if (!matchA || !matchB) return 0; - const numA = parseInt(matchA[2], 10); - const numB = parseInt(matchB[2], 10); - return numA - numB; // Sort ascending (lowest first) - }); - //console.log('prereleaseVersions', prereleaseVersions); - //console.log('versions', versions); - // Return highest pre-release version, or fallback to first available - return prereleaseVersions[0] || versions[0]; - }; +/* ============================================ + COMMIT HISTORY — CROSS-STABLE COLLECTION + Walk every stable version between stableA and stableB, + open each log file, and collect commits per the rules below. + ============================================ */ + +const sortStableVersions = (versions) => + [...versions].sort((a, b) => { + const p = (v) => v.split('.').map(Number); + const [aMaj, aMin, aPatch] = p(a); + const [bMaj, bMin, bPatch] = p(b); + return aMaj !== bMaj ? aMaj - bMaj : aMin !== bMin ? aMin - bMin : aPatch - bPatch; + }); - allPackageNames.forEach((packageName) => { - // Use release version per stable train (exact stable or highest prerelease), not chronologically earliest - const versionA = findStableVersion(changelogA, packageName, stableVersionA); - const versionB = findStableVersion(changelogB, packageName, stableVersionB); - - let status, changeClass; //Declare variables for status label and CSS class - - if (versionA && versionB) { - //checks if package is in both changelogs - if (versionA === versionB) { - //if versionA is the same as versionB, then it is unchanged - status = 'Unchanged'; - changeClass = 'unchanged'; - unchangedCount++; - } else { - status = 'Version Changed'; - changeClass = 'version-changed'; - changedCount++; - } - } else if (versionA && !versionB) { - status = 'Removed'; - changeClass = 'only-in-a'; - onlyInACount++; - } else if (!versionA && versionB) { - status = 'Added'; - changeClass = 'only-in-b'; - onlyInBCount++; - } +const isPreRelease = (version, stableVersion) => { + const escaped = stableVersion.replace(/\./g, '\\.'); + return new RegExp(`^${escaped}-`).test(version); +}; - packages.push({ - packageName, - versionA: versionA || 'N/A', - versionB: versionB || 'N/A', - status, - changeClass, - }); - }); +const isExactStable = (version) => /^\d+\.\d+\.\d+$/.test(version); - // Sort packages alphabetically - packages.sort((a, b) => a.packageName.localeCompare(b.packageName)); +const getPreReleaseNum = (version) => { + const match = version.match(/\.(\d+)$/); + return match ? parseInt(match[1], 10) : 0; +}; - return { - packages, - totalPackages: allPackageNames.size, - changedCount, - unchangedCount, - onlyInACount, - onlyInBCount, - }; +const getPreReleaseTag = (version, stableVersion) => { + return version.slice(stableVersion.length + 1).replace(/\.\d+$/, ''); }; -//Data Fetching -const fetchAndCompareVersions = async (versionA, versionB, versionPaths) => { - const [changelogA, changelogB] = await Promise.all([ - fetch(versionPaths[versionA]).then((res) => { - if (!res.ok) throw new Error(`Failed to fetch ${versionA}`); - return res.json(); - }), - fetch(versionPaths[versionB]).then((res) => { - if (!res.ok) throw new Error(`Failed to fetch ${versionB}`); - return res.json(); - }), - ]); - - // Extract packages from both versions - const packagesA = extractPackagesFromVersion(changelogA); - const packagesB = extractPackagesFromVersion(changelogB); - - // Compare packages - const comparisonData = comparePackages(packagesA, packagesB, changelogA, changelogB, versionA, versionB); +/** + * Collect commits from one stable version's package data. + * + * Rules: + * 'start' → from versionA (inclusive) through ALL remaining pre-releases + * 'middle' → skip exact stable entry; ALL pre-releases of this stable + * 'end' → ALL pre-releases from next.1 up to versionB (inclusive) + * 'only' → stableA === stableB; from versionA to versionB within same file + */ +const collectCommitsFromStable = (packageData, stableVersion, versionA, versionB, position) => { + if (!packageData) return []; + const all = Object.keys(packageData); + let versionsToUse = []; + + // Determine the target tag from the user-selected versions + const targetTag = !isExactStable(versionA) + ? getPreReleaseTag(versionA, stableVersion) + : !isExactStable(versionB) + ? getPreReleaseTag(versionB, stableVersion) + : null; + + if (position === 'start') { + if (versionA === stableVersion) { + versionsToUse = all.filter((v)=>isPreRelease(v,stableVersion)); + } else { + const tagA = getPreReleaseTag(versionA, stableVersion); + const numA = getPreReleaseNum(versionA); + versionsToUse = all.filter((v) => { + if (!isPreRelease(v, stableVersion)) return false; + const tag = getPreReleaseTag(v, stableVersion); + if (tag !== tagA) return false; + const num = getPreReleaseNum(v); + return num >= numA; + }); + } + } else if (position === 'middle') { + versionsToUse = + isExactStable(versionA) && isExactStable(versionB) + ? [] + : all.filter((v) => { + if (!isPreRelease(v, stableVersion)) return false; + if (!targetTag) return true; + return getPreReleaseTag(v, stableVersion) === targetTag; + }); + } else if (position === 'end') { + if (versionB === stableVersion) { + versionsToUse = [stableVersion]; + } else { + const tagB = getPreReleaseTag(versionB, stableVersion); + const numB = getPreReleaseNum(versionB); + versionsToUse = all.filter((v) => { + if (!isPreRelease(v, stableVersion)) return false; + const tag = getPreReleaseTag(v, stableVersion); + if (tag !== tagB) return false; + const num = getPreReleaseNum(v); + return num <= numB; + }); + } + } else { + // 'only' — stableA === stableB + if (versionA === stableVersion && versionB === stableVersion) { + versionsToUse = [stableVersion]; + } else if (versionA === stableVersion) { + const tagB = getPreReleaseTag(versionB, stableVersion); + const numB = getPreReleaseNum(versionB); + versionsToUse = all.filter((v) => { + if (v === stableVersion) return true; + if (!isPreRelease(v, stableVersion)) return false; + const tag = getPreReleaseTag(v, stableVersion); + if (tag !== tagB) return false; + const num = getPreReleaseNum(v); + return num <= numB; + }); + } else { + const tagA = getPreReleaseTag(versionA, stableVersion); + const numA = getPreReleaseNum(versionA); + const tagB = getPreReleaseTag(versionB, stableVersion); + const numB = getPreReleaseNum(versionB); + versionsToUse = all.filter((v) => { + if (!isPreRelease(v, stableVersion)) return false; + const tag = getPreReleaseTag(v, stableVersion); + const num = getPreReleaseNum(v); + const afterStart = tag === tagA ? num >= numA : true; + const beforeEnd = tag === tagB ? num <= numB : true; + return afterStart && beforeEnd; + }); + } + } - return { - versionA, - versionB, - comparisonData, - }; + const seen = new Map(); + versionsToUse.forEach((ver) => { + Object.entries(packageData[ver]?.commits || {}).forEach(([hash, message]) => { + if (!seen.has(hash)) { + seen.set(hash, { + hash, + shortHash: hash.substring(0, 7), + message, + version: ver, + stableGroup: stableVersion, + }); + } + }); + }); + return Array.from(seen.values()); }; + const generatePackageComparisonData = (packageName, versionASpecific, versionBSpecific, changelogA, changelogB) => { const effectiveVersionA = getEffectiveVersion(changelogA, packageName, versionASpecific); const effectiveVersionB = getEffectiveVersion(changelogB, packageName, versionBSpecific); - console.log('effectiveVersionA', effectiveVersionA); - console.log('effectiveVersionB', effectiveVersionB); // Get package data from changelogs const pkgDataA = changelogA[packageName]?.[effectiveVersionA]; const pkgDataB = changelogB[packageName]?.[effectiveVersionB]; - console.log('pkgDataA', pkgDataA); - console.log('pkgDataB', pkgDataB); - // Validate versions exist if (!pkgDataA && !pkgDataB) { throw new Error(`Could not find version data for ${packageName}`); @@ -373,15 +329,7 @@ const generatePackageComparisonData = (packageName, versionASpecific, versionBSp //Export All the functions export { comparisonState, - extractPackagesFromVersion, - findLatestPackageVersion, - getEffectiveVersion, - getPackageVersion, - determinePackageStatus, - createPackageComparisonRow, - calculateComparisonStats, - buildPackagesList, - comparePackages, - fetchAndCompareVersions, generatePackageComparisonData, + sortStableVersions, + collectCommitsFromStable, }; diff --git a/docs/changelog/index.html b/docs/changelog/index.html index 18a7eb548..e6e9e5f9f 100644 --- a/docs/changelog/index.html +++ b/docs/changelog/index.html @@ -154,9 +154,9 @@ @@ -180,7 +180,7 @@
-
@@ -193,7 +193,7 @@ Search Examples: