| /* This file is a part of @mdn/browser-compat-data |
| * See LICENSE file for more information. */ |
| |
| import * as fs from 'node:fs'; |
| |
| import chalk from 'chalk-template'; |
| |
| import stringify from '../lib/stringify-and-order-properties.js'; |
| |
| import { newBrowserEntry, updateBrowserEntry } from './utils.js'; |
| |
| // |
| // Content of the two release note files |
| // |
| let releaseNotesText; |
| let archivedReleaseNotesText; |
| |
| /** |
| * initReleaseNoteFiles - Fetch both release notes file and store them |
| * @returns Logs (error messages) |
| */ |
| const initReleaseNoteFiles = async () => { |
| let result = ''; |
| |
| // Fetch the regular page1 |
| const releaseNote = await fetch( |
| 'https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel', |
| ); |
| |
| // Check that it exists, if not exit. |
| if (releaseNote.status != 200) { |
| // File not found -> log a warning |
| result = chalk`{red \nRelease note files not found for Edge (${releaseNote.status}).`; |
| } else { |
| releaseNotesText = await releaseNote.text(); |
| } |
| |
| // Fetch the archived page |
| const archivedReleaseNotes = await fetch( |
| 'https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-archive-stable-channel', |
| ); |
| if (archivedReleaseNotes.status != 200) { |
| // File not found -> log a warning |
| result += chalk`{red \nArchive release note files not found for Edge (${archivedReleaseNotes.status}).`; |
| } else { |
| archivedReleaseNotesText = await archivedReleaseNotes.text(); |
| } |
| |
| return result; |
| }; |
| |
| /** |
| * updateReleaseNotesIfArchived - Return the new release notes URL to use |
| * @param originalURL The URL that is currently used |
| * @returns The new URL to use (eventually the same) |
| */ |
| const updateReleaseNotesIfArchived = (originalURL) => { |
| const id = originalURL.split('#')[1]; |
| |
| // Test if the original URL still works |
| // If the files doesn't exist or the id not found in the archive |
| // Keep the original file |
| if ( |
| !id || |
| !releaseNotesText || |
| releaseNotesText.indexOf(`<h2 id="${id}">`) != -1 || |
| !archivedReleaseNotesText || |
| archivedReleaseNotesText.indexOf(`<h2 id="${id}">`) == -1 |
| ) { |
| return originalURL; // We keep the original URL |
| } |
| // Return the archived entry. |
| return `https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-archive-stable-channel#${id}`; |
| }; |
| |
| /** |
| * getFutureReleaseDate - Read the future release date |
| * @param release The release number of the version |
| * @param releaseScheduleURL The url of the MD file having it |
| * @returns The date in the YYYY-MM-DD format |
| * Throws a string in case of an error |
| */ |
| const getFutureReleaseDate = async (release, releaseScheduleURL) => { |
| // Fetch the MD of the release schedule |
| const scheduleMD = await fetch(releaseScheduleURL); |
| const text = await scheduleMD.text(); |
| if (!text) { |
| throw chalk`{red \nRelease file not found.}`; |
| } |
| // Find the line |
| //const regexp = new RegExp(`| ${release} |\\w*|\\w*| ?Week of (\\w*) ?|\\w*|`, 'i'); |
| const regexp = new RegExp( |
| `\\| ${release} \\|.*\\|.*\\| ?Week of (.*) ?\\|.*\\|`, |
| 'i', |
| ); |
| const result = text.match(regexp); |
| if (!result) { |
| throw chalk`{yellow \nRelease date not found for Edge ${release}.}`; |
| } |
| const releaseDateText = result[1]; |
| |
| // Get the date from the file |
| const months = [ |
| 'Jan', |
| 'Feb', |
| 'Mar', |
| 'Apr', |
| 'May', |
| 'Jun', |
| 'Jul', |
| 'Aug', |
| 'Sep', |
| 'Oct', |
| 'Nov', |
| 'Dec', |
| ]; |
| const year = releaseDateText.substring(7, 11); |
| const month = months.indexOf(releaseDateText.substring(3, 6)) + 1; |
| const day = releaseDateText.substring(0, 2); |
| |
| const releaseDate = new Date(`${year}-${month}-${day}Z`); |
| |
| return releaseDate.toISOString().substring(0, 10); // Remove the time part |
| }; |
| |
| /** |
| * getReleaseNotesURL - Guess the URL of the release notes |
| * @param status The status of the release |
| * @param fullRelease The release of the release |
| * @param date The date of the release |
| * @returns The URL of the release notes or the empty string if not found |
| * Throws a string in case of error |
| */ |
| const getReleaseNotesURL = async (status, fullRelease, date) => { |
| // If the status isn't stable, do not give back any release notes. |
| if (status !== 'Stable') { |
| return ''; |
| } |
| |
| // Calculate the URL |
| const releaseStr = fullRelease.replace(/\./g, ''); |
| const month = [ |
| 'january', |
| 'february', |
| 'march', |
| 'april', |
| 'may', |
| 'june', |
| 'july', |
| 'august', |
| 'september', |
| 'october', |
| 'november', |
| 'december', |
| ]; |
| const dateObj = new Date(date); |
| const dateStr = `${ |
| month[dateObj.getMonth()] |
| }-${dateObj.getDate()}-${dateObj.getFullYear()}`; |
| |
| // Check if the id exists |
| if ( |
| releaseNotesText.indexOf(`<h2 id="version-${releaseStr}-${dateStr}">`) == -1 |
| ) { |
| // Section not found -> log a warning |
| throw chalk`{red \nSection not found in official release notes for Edge ${fullRelease}}`; |
| } |
| |
| return `https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-${releaseStr}-${dateStr}`; |
| }; |
| |
| /** |
| * updateEdgeReleases - Update the json file listing the browser releases of an Edge browser |
| * @param options The list of options for Edge. |
| * @returns The log of what has been generated (empty if nothing) |
| */ |
| export const updateEdgeReleases = async (options) => { |
| // |
| // Read the release note files |
| // |
| let result = await initReleaseNoteFiles(); |
| |
| // |
| // Get the JSON with the versions from edge releases |
| // |
| const buffer = await fetch(options.edgeupdatesURL); |
| const edgeVersions = JSON.parse(await buffer.text()); |
| |
| // |
| // Get the edge.json from the local BCD |
| // |
| const file = fs.readFileSync(`${options.bcdFile}`); |
| const edgeBCD = JSON.parse(file.toString()); |
| |
| // |
| // Update the three channels |
| // |
| const channels = new Map([ |
| ['current', options.releaseBranch], |
| ['beta', options.betaBranch], |
| ['nightly', options.nightlyBranch], |
| ]); |
| |
| // Information store to be used outside the for loop |
| const data = {}; |
| |
| for (const [key, value] of channels) { |
| // |
| // Extract the useful data |
| // |
| |
| // Find the right entry |
| const entry = edgeVersions.find((e) => e['Product'] === value); |
| |
| // Get the last version |
| const version = parseFloat(entry['Releases'][0]['ProductVersion']); |
| |
| // We need to find the first release with this version. |
| // As, they are from the newer to the older in the JSON object, |
| // we are looking for the first that is not the latest version |
| // and get the one immediately above (the first release of that |
| // version) |
| // |
| // The full version number is needed for the release note URL |
| // as well as the date |
| let id = 1; |
| while ( |
| id < entry['Releases'].length && |
| parseFloat(entry['Releases'][id]['ProductVersion']) === version |
| ) { |
| id++; |
| } |
| id--; |
| |
| data[value] = {}; |
| data[value].version = parseFloat(entry['Releases'][id]['ProductVersion']); |
| data[value].fullVersion = entry['Releases'][id]['ProductVersion']; |
| |
| // |
| // Update the JSON in memory |
| // |
| |
| // We skip beta and nightly versions if they are of the same version as the released one |
| if ( |
| key === 'current' || |
| (key !== 'current' && |
| data[value].version !== data[channels.get('current')].version) |
| ) { |
| // NOTE: the published date is the one of the actual release |
| // (being the Stable, Beta, or Nightly), |
| // and not the one of the future release like we would like |
| // So we only get it if we are on the 'current' channel. |
| |
| // Get published date |
| if (key === 'current') { |
| data[value].versionDate = entry['Releases'][id][ |
| 'PublishedTime' |
| ].substring(0, 10); // Remove the time part; |
| } else { |
| try { |
| data[value].versionDate = await getFutureReleaseDate( |
| data[value].version, |
| options.releaseScheduleURL, |
| ); |
| } catch (str) { |
| result += str; |
| } |
| } |
| |
| // Get the release notes |
| let releaseNotesURL = ''; |
| try { |
| releaseNotesURL = await getReleaseNotesURL( |
| value, |
| data[value].fullVersion, |
| data[value].versionDate, |
| ); |
| } catch (s) { |
| result += s; |
| } |
| |
| if ( |
| edgeBCD.browsers[options.bcdBrowserName].releases[data[value].version] |
| ) { |
| // The entry already exists |
| result += updateBrowserEntry( |
| edgeBCD, |
| options.bcdBrowserName, |
| data[value].version, |
| data[value]?.versionDate, |
| key, |
| releaseNotesURL, |
| '', |
| ); |
| } else { |
| // New entry |
| result += newBrowserEntry( |
| edgeBCD, |
| options.bcdBrowserName, |
| data[value].version, |
| key, |
| options.browserEngine, |
| data[value]?.versionDate, |
| releaseNotesURL, |
| data[value].version, |
| ); |
| } |
| } |
| } |
| |
| // |
| // Check that all older releases are 'retired' |
| // |
| for ( |
| let i = options.firstRelease; |
| i < data[options.releaseBranch].version; |
| i++ |
| ) { |
| if (!options.skippedReleases.includes(i)) { |
| if (edgeBCD.browsers[options.bcdBrowserName].releases[i.toString()]) { |
| result += updateBrowserEntry( |
| edgeBCD, |
| options.bcdBrowserName, |
| i.toString(), |
| edgeBCD.browsers[options.bcdBrowserName].releases[i.toString()] |
| .release_date, |
| 'retired', |
| updateReleaseNotesIfArchived( |
| edgeBCD.browsers[options.bcdBrowserName].releases[i.toString()] |
| .release_notes, |
| ), |
| '', |
| ); |
| } else { |
| // There is a retired version missing. Edgeupdates doesn't list them. |
| // There is an oddity: the version is not skipped but not in edgeupdates |
| result += chalk`{yellow \nEdge ${i} not found in Edgeupdates! Add it manually or add an exception.}`; |
| } |
| } |
| } |
| |
| // |
| // Add a planned version entry |
| // |
| const planned = (data[options.nightlyBranch].version + 1).toString(); |
| let releaseDate; |
| try { |
| releaseDate = await getFutureReleaseDate( |
| planned, |
| options.releaseScheduleURL, |
| ); |
| } catch (s) { |
| result += s; |
| } |
| |
| if (edgeBCD.browsers[options.bcdBrowserName].releases[planned]) { |
| result += updateBrowserEntry( |
| edgeBCD, |
| options.bcdBrowserName, |
| planned, |
| releaseDate, |
| 'planned', |
| '', |
| planned, |
| ); |
| } else { |
| // New entry |
| result += newBrowserEntry( |
| edgeBCD, |
| options.bcdBrowserName, |
| planned.toString(), |
| 'planned', |
| options.browserEngine, |
| releaseDate, |
| '', |
| planned.toString(), |
| ); |
| } |
| |
| // |
| // Write the update browser's json to file |
| // |
| fs.writeFileSync(`./${options.bcdFile}`, stringify(edgeBCD) + '\n'); |
| |
| // Returns the log |
| if (result) { |
| result = `### Updates for ${options.browserName}${result}`; |
| } |
| return result; |
| }; |