diff --git a/bin/create-draft-release.sh b/bin/create-draft-release.sh index 24d4499f6f..7e63d1c457 100755 --- a/bin/create-draft-release.sh +++ b/bin/create-draft-release.sh @@ -15,6 +15,12 @@ # RELEASE_DATE=$(date +%Y-%m-%d) ./create-draft-release.sh # RELEASE_DATE=2026-06-30 npm run create-draft-release # npm run create-draft-release +# +# Any plugin slugs given are forwarded to `npm run prepare-release-notes`, which then reads +# each plugin's changelog straight from its readme.txt rather than looking for a release +# milestone. Use this for a release that does not use milestones: +# +# RELEASE_DATE=2026-06-30 npm run create-draft-release -- auto-sizes speculation-rules set -e @@ -60,13 +66,28 @@ if github_token="$(gh auth token 2> /dev/null)" && [ -n "$github_token" ]; then fi echo "Generating release notes for $tag..." >&2 -npm run prepare-release-notes --silent -- "${notes_args[@]}" > "$notes_file" +npm run prepare-release-notes --silent -- "${notes_args[@]}" "$@" > "$notes_file" if [ ! -s "$notes_file" ]; then echo "Error: No release notes were generated; aborting." >&2 exit 1 fi +# Guard against notes that cover only some of what is being released. Without plugin slugs +# the selection comes from release milestones, and a plugin missing its milestone is simply +# skipped, which would otherwise yield a release whose notes quietly omit it. +if [ $# -gt 0 ]; then + for plugin_slug in "$@"; do + if ! grep -qF "## \`$plugin_slug\`" "$notes_file"; then + echo "Error: No release notes were generated for \"$plugin_slug\"; aborting." >&2 + exit 1 + fi + done +else + echo "Note: No plugin slugs were given, so the release notes cover only plugins with an open, dated release milestone. Check that every plugin being released is present below." >&2 + grep -oE '^## `[^`]+` .*' "$notes_file" | sed 's/^/ /' >&2 +fi + echo "Creating draft release \"$tag\" targeting \"$target\"..." >&2 gh release create "$tag" \ --draft \ diff --git a/bin/generate-pending-release-diffs.sh b/bin/generate-pending-release-diffs.sh index d66555ba5a..af081dd5ed 100755 --- a/bin/generate-pending-release-diffs.sh +++ b/bin/generate-pending-release-diffs.sh @@ -42,10 +42,17 @@ for plugin_slug in $( if [ $# -gt 0 ]; then echo "$@"; else jq '.plugins[]' -r p remote_stable_tag=$( grep "Stable tag:" "$stable_dir/$plugin_slug/readme.txt" | awk '{print $3}' ) local_stable_tag=$( grep "Stable tag:" "build/$plugin_slug/readme.txt" | awk '{print $3}' ) - # Exclude minified assets: they are build artifacts derived from their sources, so - # diffing them just adds noise. Excluding them here also protects the stable copies - # from --delete, so svn status/diff below will not report them. - rsync -avz --delete --exclude=".svn" --exclude="*.min.js" --exclude="*.min.css" "build/$plugin_slug/" "$stable_dir/$plugin_slug/" >&2 + # Copy everything, including generated assets. They are excluded from the *diff* further + # below rather than from the copy, so that "svn status" reports the true set of files + # being added, removed, and modified. Excluding them here instead would protect them + # from --delete and make them invisible to svn entirely: a stray minified asset dropped + # into an already-tracked directory would then be reported nowhere at all. + # + # Compare by checksum (-c) and stamp copied files with a fresh mtime (--no-times). + # Both are required: "Tested up to: 7.0" -> "7.1" is a same-length edit, and rsync's + # default quick check (size+mtime) as well as SVN's own stat cache would each conclude + # the file is unchanged, so the plugin gets wrongly reported as having nothing to release. + rsync -avzc --no-times --delete --exclude=".svn" "build/$plugin_slug/" "$stable_dir/$plugin_slug/" >&2 cd "$stable_dir/$plugin_slug/" @@ -67,13 +74,49 @@ for plugin_slug in $( if [ $# -gt 0 ]; then echo "$@"; else jq '.plugins[]' -r p echo "\`svn status\`:" echo '```' - svn status + # SVN reports an unversioned directory without listing what is inside it, so a batch + # of added files would otherwise surface only as their parent directory. Expand those + # so every file being added is named. + svn status | while IFS= read -r status_line; do + echo "$status_line" + if [ "${status_line:0:1}" = "?" ]; then + status_path="${status_line:8}" + if [ -d "$status_path" ]; then + find "$status_path" -type f | sort | sed 's|^|? |' + fi + fi + done echo '```' echo echo '
svn diff' echo echo '```diff' - svn diff + # Keep generated files in the diff so it is clear they changed, but replace their + # contents with a placeholder. They are minified or bundled build output, so their + # diffs are unreadable and enormous: the two web-vitals bundles alone were 35% of + # the entire report, for what amounts to a library version bump. The sibling + # *.asset.php is left intact, since its 'version' is the readable signal of that. + svn diff | awk ' + /^Index: / { + path = substr( $0, 8 ) + suppress = ( path ~ /\.min\.(js|css)$/ || path ~ /(^|\/)build\// ) + if ( path ~ /\.asset\.php$/ ) { + suppress = 0 + } + placed = 0 + print + next + } + suppress && /^(=+|--- |\+\+\+ )/ { print; next } + suppress { + if ( ! placed ) { + print "(Built file content suppressed.)" + placed = 1 + } + next + } + { print } + ' echo '```' echo '
' fi diff --git a/bin/plugin/cli.js b/bin/plugin/cli.js index 9a72c8e09e..3e26a0ced7 100755 --- a/bin/plugin/cli.js +++ b/bin/plugin/cli.js @@ -85,19 +85,22 @@ withOptions( program.command( 'verify-version-consistency' ), versionsOptions ) .description( 'Verifies consistency of versions in plugins' ) .action( catchException( versionsHandler ) ); -withOptions( program.command( 'bump-plugin-versions' ), bumpVersionsOptions ) +withOptions( + program.command( 'bump-plugin-versions [plugins...]' ), + bumpVersionsOptions +) .alias( 'bump-versions' ) .description( - 'Bumps plugin versions based on open, dated release milestones (titled "$plugin_slug $version" without "n.e.x.t")' + 'Bumps plugin versions based on open, dated release milestones (titled "$plugin_slug $version" without "n.e.x.t"); pass --increment or --set-version with a list of plugin slugs to bump without a milestone' ) .action( catchException( bumpVersionsHandler ) ); withOptions( - program.command( 'prepare-release-notes' ), + program.command( 'prepare-release-notes [plugins...]' ), prepareReleaseNotesOptions ) .description( - 'Prints the combined per-plugin changelogs (release notes) for plugins with an open, dated release milestone; progress goes to STDERR so STDOUT can be piped' + 'Prints the combined per-plugin changelogs (release notes) for plugins with an open, dated release milestone, or for a list of plugin slugs given without a milestone; progress goes to STDERR so STDOUT can be piped' ) .action( catchException( prepareReleaseNotesHandler ) ); diff --git a/bin/plugin/commands/bump-versions.js b/bin/plugin/commands/bump-versions.js index 05ab108d91..295ec7dec3 100644 --- a/bin/plugin/commands/bump-versions.js +++ b/bin/plugin/commands/bump-versions.js @@ -18,9 +18,15 @@ const { plugins } = require( '../../../plugins.json' ); /** * @typedef WPBumpVersionsCommandOptions * - * @property {string=} plugin Optional plugin slug to limit bumping to a single plugin. - * @property {string=} token Optional personal GitHub access token. - * @property {boolean=} dryRun Whether to only report what would change without writing. + * @property {string=} plugin Optional plugin slug to limit bumping to a single plugin. + * @property {string=} increment Optional increment level ("major", "minor", "patch", or + * "prerelease") to derive the target version from the current + * one instead of from a release milestone. + * @property {string=} setVersion Optional explicit target version, bypassing both release + * milestones and --increment. + * @property {boolean=} all Whether to target every plugin; requires --increment. + * @property {string=} token Optional personal GitHub access token. + * @property {boolean=} dryRun Whether to only report what would change without writing. */ /** @@ -35,12 +41,31 @@ const { plugins } = require( '../../../plugins.json' ); const VERSION_PATTERN = '\\d+\\.\\d+\\.\\d+(?:-[\\w.]+)?'; +const INCREMENT_LEVELS = [ 'major', 'minor', 'patch', 'prerelease' ]; + exports.options = [ { argname: '-p, --plugin ', description: 'The plugin slug to bump; if omitted, all plugins with a matching milestone are bumped', }, + { + argname: '-i, --increment ', + description: `Derive the target version from the current one rather than from a release milestone: ${ INCREMENT_LEVELS.join( + ', ' + ) }`, + }, + { + argname: '-s, --set-version ', + description: + 'Set an explicit target version, bypassing both release milestones and --increment', + }, + { + argname: '-a, --all', + description: + 'Target every plugin; only valid together with --increment', + defaults: false, + }, { argname: '-t, --token ', description: 'GitHub token', @@ -64,27 +89,65 @@ exports.options = [ * - the "Stable tag" in readme.txt, and * - a new (empty) changelog entry is inserted so the readme is ready for `npm run readme`. * + * Alternatively, passing --increment or --set-version skips the milestone lookup entirely + * and derives the target version from each plugin's current "Stable tag". In that mode the + * plugins to bump are named explicitly (as positional arguments), because the appropriate + * increment level differs per plugin and must not be guessed. + * + * @param {string[]} pluginSlugs Plugin slugs given as positional arguments; + * commander passes an empty array when none. * @param {WPBumpVersionsCommandOptions} opt */ -exports.handler = async ( opt ) => { - if ( opt.plugin && ! plugins.includes( opt.plugin ) ) { +exports.handler = async ( pluginSlugs, opt ) => { + const requestedSlugs = [ ...new Set( pluginSlugs ) ]; + if ( opt.plugin && ! requestedSlugs.includes( opt.plugin ) ) { + requestedSlugs.push( opt.plugin ); + } + + for ( const slug of requestedSlugs ) { + if ( ! plugins.includes( slug ) ) { + throw new Error( + `The plugin "${ slug }" is not a valid plugin managed as part of this project.` + ); + } + } + + if ( opt.increment && opt.setVersion ) { + throw new Error( + 'The --increment and --set-version options are mutually exclusive.' + ); + } + + if ( opt.increment || opt.setVersion ) { + await bumpWithoutMilestone( requestedSlugs, opt ); + return; + } + + if ( opt.all ) { throw new Error( - `The plugin "${ opt.plugin }" is not a valid plugin managed as part of this project.` + 'The --all option requires --increment, since milestones already determine which plugins to bump.' ); } + if ( requestedSlugs.length > 1 ) { + throw new Error( + `Milestone-based bumping targets at most one plugin, but ${ requestedSlugs.length } were given. Pass --increment or --set-version to bump several plugins at once.` + ); + } + + const milestonePlugin = requestedSlugs[ 0 ]; const milestones = await getReleaseMilestones( opt.token ); let selected = milestones; - if ( opt.plugin ) { - selected = milestones.filter( ( m ) => m.slug === opt.plugin ); + if ( milestonePlugin ) { + selected = milestones.filter( ( m ) => m.slug === milestonePlugin ); } if ( selected.length === 0 ) { log( formats.warning( - opt.plugin - ? `⚠ No release milestone (open, dated, without "n.e.x.t") found for ${ opt.plugin }.` + milestonePlugin + ? `⚠ No release milestone (open, dated, without "n.e.x.t") found for ${ milestonePlugin }.` : '⚠ No release milestones (open, dated, without "n.e.x.t") found.' ) ); @@ -155,6 +218,188 @@ exports.handler = async ( opt ) => { } }; +/** + * Bumps the given plugins without consulting release milestones, deriving each target + * version from the plugin's current "Stable tag" via --increment, or using --set-version + * verbatim. + * + * Every target version is resolved before anything is written, so a validation failure + * (most importantly the prerelease guard) aborts the whole run rather than leaving some + * plugins bumped and others not. + * + * @param {string[]} requestedSlugs Explicitly named plugin slugs. + * @param {WPBumpVersionsCommandOptions} opt + */ +async function bumpWithoutMilestone( requestedSlugs, opt ) { + if ( opt.increment && ! INCREMENT_LEVELS.includes( opt.increment ) ) { + throw new Error( + `Unknown --increment level "${ + opt.increment + }"; expected one of: ${ INCREMENT_LEVELS.join( ', ' ) }.` + ); + } + + if ( + opt.setVersion && + ! new RegExp( `^${ VERSION_PATTERN }$` ).test( opt.setVersion ) + ) { + throw new Error( + `"${ opt.setVersion }" is not a valid version (expected e.g. "1.2.3" or "1.0.0-beta4").` + ); + } + + let targetSlugs = requestedSlugs; + if ( opt.all ) { + if ( requestedSlugs.length > 0 ) { + throw new Error( + 'The --all option cannot be combined with an explicit list of plugins.' + ); + } + targetSlugs = plugins; + } + + if ( targetSlugs.length === 0 ) { + throw new Error( + 'No plugins were named. List the plugin slugs to bump (e.g. `bump-versions --increment minor auto-sizes webp-uploads`), or pass --all for every plugin.' + ); + } + + if ( opt.setVersion && targetSlugs.length > 1 ) { + throw new Error( + `The --set-version option applies to a single plugin, but ${ targetSlugs.length } were given.` + ); + } + + const pluginRoot = path.resolve( __dirname, '../../../' ); + + // Resolve first, write second. + const targets = [ ...targetSlugs ].sort().map( ( slug ) => { + const pluginDirectory = path.resolve( pluginRoot, 'plugins', slug ); + const currentVersion = readCurrentVersion( pluginDirectory, slug ); + const version = + opt.setVersion ?? + incrementVersion( currentVersion, opt.increment ?? '', slug ); + return { slug, pluginDirectory, currentVersion, version }; + } ); + + for ( const { + slug, + pluginDirectory, + currentVersion, + version, + } of targets ) { + try { + const changes = await bumpPlugin( + pluginDirectory, + version, + opt.dryRun + ); + const prefix = opt.dryRun ? '🔍 [dry-run] ' : '💃 '; + log( + formats.success( + `${ prefix }${ slug }: ${ currentVersion } → ${ version }:` + ) + ); + for ( const change of changes ) { + log( ` ${ change }` ); + } + } catch ( error ) { + const message = + error instanceof Error ? error.message : 'Unknown error'; + log( formats.error( `❌ ${ slug }: ${ message }` ) ); + process.exitCode = 1; + } + } +} + +/** + * Reads a plugin's current version from the "Stable tag" in its readme.txt. + * + * @param {string} pluginDirectory Absolute path to the plugin directory. + * @param {string} slug Plugin slug, for error messages. + * @return {string} The current version. + */ +function readCurrentVersion( pluginDirectory, slug ) { + const readmeFile = path.resolve( pluginDirectory, 'readme.txt' ); + if ( ! fs.existsSync( readmeFile ) ) { + throw new Error( `Unable to locate readme.txt for ${ slug }.` ); + } + const matches = fs + .readFileSync( readmeFile, 'utf-8' ) + .match( new RegExp( `^Stable tag:\\s*(${ VERSION_PATTERN })$`, 'm' ) ); + if ( ! matches ) { + throw new Error( `Unable to locate "Stable tag" in ${ readmeFile }.` ); + } + return matches[ 1 ]; +} + +/** + * Increments a version by the given level. + * + * The "prerelease" level increments the trailing number of the prerelease component, so + * "1.0.0-beta5" becomes "1.0.0-beta6". This is deliberately hand-rolled rather than + * delegated to semver: `semver.inc( '1.0.0-beta5', 'prerelease' )` yields "1.0.0-beta5.0", + * because semver treats "beta5" as a single alphanumeric identifier. + * + * Incrementing a version that has a prerelease component by major/minor/patch is refused, + * since that would silently graduate a beta to a stable release. + * + * @param {string} currentVersion Current version. + * @param {string} level Increment level. + * @param {string} slug Plugin slug, for error messages. + * @return {string} The incremented version. + */ +function incrementVersion( currentVersion, level, slug ) { + const matches = currentVersion.match( + /^(\d+)\.(\d+)\.(\d+)(?:-([\w.]+))?$/ + ); + if ( ! matches ) { + throw new Error( + `Unable to parse the current version "${ currentVersion }" of ${ slug }.` + ); + } + const [ , major, minor, patch, prerelease ] = matches; + + if ( 'prerelease' === level ) { + if ( ! prerelease ) { + throw new Error( + `${ slug } is at ${ currentVersion }, which has no prerelease component to increment. Use --increment patch/minor/major, or --set-version.` + ); + } + const prereleaseMatches = prerelease.match( /^(.*?)(\d+)$/ ); + if ( ! prereleaseMatches ) { + throw new Error( + `Unable to increment the prerelease component of ${ slug }'s version "${ currentVersion }" because it does not end in a number. Use --set-version.` + ); + } + const [ , label, number ] = prereleaseMatches; + return `${ major }.${ minor }.${ patch }-${ label }${ + Number( number ) + 1 + }`; + } + + if ( prerelease ) { + throw new Error( + `${ slug } is at ${ currentVersion }, so --increment ${ level } would graduate it to a stable release. Use --increment prerelease for the next prerelease, or --set-version to set it explicitly.` + ); + } + + switch ( level ) { + case 'major': + return `${ Number( major ) + 1 }.0.0`; + case 'minor': + return `${ major }.${ Number( minor ) + 1 }.0`; + case 'patch': + return `${ major }.${ minor }.${ Number( patch ) + 1 }`; + default: + throw new Error( + `Unknown --increment level "${ level }"; expected one of: ${ INCREMENT_LEVELS.join( + ', ' + ) }.` + ); + } +} + /** * Fetches the release milestones: open milestones with a due date whose title is * " " (i.e. without "n.e.x.t"). diff --git a/bin/plugin/commands/prepare-release-notes.js b/bin/plugin/commands/prepare-release-notes.js index e1d9daa8ef..11a82c892a 100644 --- a/bin/plugin/commands/prepare-release-notes.js +++ b/bin/plugin/commands/prepare-release-notes.js @@ -39,74 +39,111 @@ exports.options = [ * Progress and warnings are written to STDERR so that STDOUT (the Markdown release * notes) can be piped to a file or the clipboard. * + * Naming plugin slugs explicitly skips the milestone lookup entirely, for releases that do + * not use milestones. The notes come from the same place either way, since the milestone + * only ever decided which plugins to include. + * + * There is deliberately no option to select every plugin. A plugin that is not being + * released still has a changelog entry for its current stable tag, so including it would + * silently repeat an already-published entry in the new release notes. + * + * @param {string[]} pluginSlugs Plugin slugs given as positional arguments. * @param {WPPrepareReleaseNotesCommandOptions} opt */ -exports.handler = async ( opt ) => { - if ( opt.plugin && ! plugins.includes( opt.plugin ) ) { - throw new Error( - `The plugin "${ opt.plugin }" is not a valid plugin managed as part of this project.` - ); +exports.handler = async ( pluginSlugs, opt ) => { + const requestedSlugs = [ ...new Set( pluginSlugs ) ]; + if ( opt.plugin && ! requestedSlugs.includes( opt.plugin ) ) { + requestedSlugs.push( opt.plugin ); + } + + for ( const slug of requestedSlugs ) { + if ( ! plugins.includes( slug ) ) { + throw new Error( + `The plugin "${ slug }" is not a valid plugin managed as part of this project.` + ); + } + } + + // With slugs named, the milestone lookup is skipped and their readmes are read directly. + if ( requestedSlugs.length > 0 ) { + writeSections( [ ...requestedSlugs ].sort(), true ); + return; } const milestones = await getReleaseMilestones( opt.token ); - const selected = ( - opt.plugin - ? milestones.filter( - ( milestone ) => milestone.slug === opt.plugin - ) - : milestones - ).sort( ( a, b ) => a.slug.localeCompare( b.slug ) ); + const selected = milestones + .map( ( milestone ) => milestone.slug ) + .sort( ( a, b ) => a.localeCompare( b ) ); if ( selected.length === 0 ) { process.stderr.write( formats.warning( - opt.plugin - ? `⚠ Skipping ${ opt.plugin }: no open release milestone with a due date (and without "n.e.x.t") was found.\n` - : '⚠ No plugins have an open release milestone with a due date (and without "n.e.x.t"); nothing to prepare.\n' + '⚠ No plugins have an open release milestone with a due date (and without "n.e.x.t"); nothing to prepare. Name the plugin slugs explicitly to prepare notes without a milestone.\n' ) ); return; } + writeSections( selected, false ); +}; + +/** + * Writes the release notes for the given plugins to STDOUT. + * + * When the plugins were named explicitly, any failure is fatal and nothing is written: the + * caller asked for those specific plugins, so quietly emitting notes for a subset would + * produce a release whose notes silently omit part of what is being released. Milestone + * selection keeps the original per-plugin tolerance. + * + * @param {string[]} slugs Plugin slugs, already sorted. + * @param {boolean} isExplicit Whether the plugins were named explicitly. + */ +function writeSections( slugs, isExplicit ) { const pluginRoot = path.resolve( __dirname, '../../../' ); const sections = []; + const failures = []; - for ( const milestone of selected ) { + for ( const slug of slugs ) { try { const readmeFilePath = path.resolve( pluginRoot, 'plugins', - milestone.slug, + slug, 'readme.txt' ); const { version, changelog } = getReadmeChangelogEntry( readmeFilePath ); - sections.push( - `## \`${ milestone.slug }\` ${ version }\n\n${ changelog }` - ); + sections.push( `## \`${ slug }\` ${ version }\n\n${ changelog }` ); process.stderr.write( formats.success( - `✔ Prepared release notes for ${ milestone.slug } ${ version }.\n` + `✔ Prepared release notes for ${ slug } ${ version }.\n` ) ); } catch ( error ) { const message = error instanceof Error ? error.message : 'Unknown error'; + failures.push( `${ slug }: ${ message }` ); process.stderr.write( - formats.error( - `${ milestone.slug } failed to prepare: ${ message }\n` - ) + formats.error( `${ slug } failed to prepare: ${ message }\n` ) ); } } + if ( isExplicit && failures.length > 0 ) { + throw new Error( + `Release notes could not be prepared for every plugin named, so none were written:\n ${ failures.join( + '\n ' + ) }` + ); + } + if ( sections.length === 0 ) { return; } log( sections.join( '\n\n' ) ); -}; +} /** * Reads the changelog entry for a plugin's stable tag from its `readme.txt`. diff --git a/plugins/auto-sizes/auto-sizes.php b/plugins/auto-sizes/auto-sizes.php index 1342544dbc..b4c34ab7e3 100644 --- a/plugins/auto-sizes/auto-sizes.php +++ b/plugins/auto-sizes/auto-sizes.php @@ -5,7 +5,7 @@ * Description: Improves responsive images with better sizes calculations and auto-sizes for lazy-loaded images. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.7.0 + * Version: 1.8.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -27,7 +27,7 @@ return; } -define( 'IMAGE_AUTO_SIZES_VERSION', '1.7.0' ); +define( 'IMAGE_AUTO_SIZES_VERSION', '1.8.0' ); require_once __DIR__ . '/includes/improve-calculate-sizes.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/auto-sizes/readme.txt b/plugins/auto-sizes/readme.txt index 259c8b84d3..6000a114af 100644 --- a/plugins/auto-sizes/readme.txt +++ b/plugins/auto-sizes/readme.txt @@ -1,8 +1,8 @@ === Enhanced Responsive Images === Contributors: wordpressdotorg -Tested up to: 7.0 -Stable tag: 1.7.0 +Tested up to: 7.1 +Stable tag: 1.8.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, images, auto-sizes @@ -52,6 +52,15 @@ Contributions are always welcome! Learn more about how to get involved in the [C == Changelog == += 1.8.0 = + +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + = 1.7.0 = **Enhancements** diff --git a/plugins/dominant-color-images/load.php b/plugins/dominant-color-images/load.php index 5640401957..f064954de8 100644 --- a/plugins/dominant-color-images/load.php +++ b/plugins/dominant-color-images/load.php @@ -5,7 +5,7 @@ * Description: Displays placeholders based on an image's dominant color while the image is loading. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.2.1 + * Version: 1.3.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -28,7 +28,7 @@ return; } -define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.2.1' ); +define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.3.0' ); require_once __DIR__ . '/helper.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/dominant-color-images/readme.txt b/plugins/dominant-color-images/readme.txt index 1c1e9c2a50..97e97f51bf 100644 --- a/plugins/dominant-color-images/readme.txt +++ b/plugins/dominant-color-images/readme.txt @@ -1,8 +1,8 @@ === Image Placeholders === Contributors: wordpressdotorg -Tested up to: 7.0 -Stable tag: 1.2.1 +Tested up to: 7.1 +Stable tag: 1.3.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, images, dominant color @@ -47,6 +47,15 @@ Contributions are always welcome! Learn more about how to get involved in the [C == Changelog == += 1.3.0 = + +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + = 1.2.1 = **Bug Fixes** diff --git a/plugins/embed-optimizer/load.php b/plugins/embed-optimizer/load.php index ebfbf11b0f..b40583f878 100644 --- a/plugins/embed-optimizer/load.php +++ b/plugins/embed-optimizer/load.php @@ -5,7 +5,7 @@ * Description: Optimizes the performance of embeds through lazy-loading, adding dns-prefetch links, and reserving space to reduce layout shifts. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.0.0-beta5 + * Version: 1.0.0-beta6 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -73,7 +73,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'embed_optimizer_pending_plugin', - '1.0.0-beta5', + '1.0.0-beta6', static function ( string $version ): void { if ( defined( 'EMBED_OPTIMIZER_VERSION' ) ) { return; diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index 47de6656a9..d8f846010a 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -1,8 +1,8 @@ === Embed Optimizer === Contributors: wordpressdotorg -Tested up to: 7.0 -Stable tag: 1.0.0-beta5 +Tested up to: 7.1 +Stable tag: 1.0.0-beta6 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, embeds, optimization-detective @@ -67,6 +67,15 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 1.0.0-beta6 = + +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + = 1.0.0-beta5 = **Bug Fixes** diff --git a/plugins/image-prioritizer/load.php b/plugins/image-prioritizer/load.php index 90b991a018..2e6b1a2865 100644 --- a/plugins/image-prioritizer/load.php +++ b/plugins/image-prioritizer/load.php @@ -6,7 +6,7 @@ * Requires at least: 6.9 * Requires PHP: 7.4 * Requires Plugins: optimization-detective - * Version: 1.0.0-beta3 + * Version: 1.0.0-beta4 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -74,7 +74,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'image_prioritizer_pending_plugin', - '1.0.0-beta3', + '1.0.0-beta4', static function ( string $version ): void { if ( defined( 'IMAGE_PRIORITIZER_VERSION' ) ) { return; diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index f50adea92f..e8f397ec60 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -1,8 +1,8 @@ === Image Prioritizer === Contributors: wordpressdotorg -Tested up to: 7.0 -Stable tag: 1.0.0-beta3 +Tested up to: 7.1 +Stable tag: 1.0.0-beta4 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, optimization, image, optimization-detective @@ -72,6 +72,19 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 1.0.0-beta4 = + +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + +**Bug Fixes** + +* Fix TypeScript 6 type errors in the video lazy-loading script. ([2434](https://github.com/WordPress/performance/pull/2434)) + = 1.0.0-beta3 = **Enhancements** diff --git a/plugins/optimization-detective/load.php b/plugins/optimization-detective/load.php index 652a037318..0b072e7111 100644 --- a/plugins/optimization-detective/load.php +++ b/plugins/optimization-detective/load.php @@ -5,7 +5,7 @@ * Description: Provides a framework for leveraging real user metrics to detect optimizations for improving page performance. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.0.0-beta6 + * Version: 1.0.0-beta7 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -73,7 +73,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'optimization_detective_pending_plugin', - '1.0.0-beta6', + '1.0.0-beta7', static function ( string $version ): void { if ( defined( 'OPTIMIZATION_DETECTIVE_VERSION' ) ) { return; diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 31df6fbdd1..8c8fd5213d 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -1,8 +1,8 @@ === Optimization Detective === Contributors: wordpressdotorg -Tested up to: 7.0 -Stable tag: 1.0.0-beta6 +Tested up to: 7.1 +Stable tag: 1.0.0-beta7 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, optimization, rum @@ -55,6 +55,19 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 1.0.0-beta7 = + +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) +* Bump the bundled web-vitals library from 5.1.0 to 6.1.0, including its 6.0.0 major release. ([2606](https://github.com/WordPress/performance/pull/2606), [2629](https://github.com/WordPress/performance/pull/2629)) +* Ship the source maps for the bundled web-vitals library, which it began publishing in 6.0.0. +* Move URL Metrics storage HMAC validation into the REST endpoint callback so it runs once all parameters have been validated and sanitized, returning a `rest_invalid_param` error. ([2436](https://github.com/WordPress/performance/pull/2436)) +* Remove the deprecated constants along with the `deprecated.php` file that contained them. + = 1.0.0-beta6 = **Security** diff --git a/plugins/performance-lab/readme.txt b/plugins/performance-lab/readme.txt index 718c97313f..076c600d99 100644 --- a/plugins/performance-lab/readme.txt +++ b/plugins/performance-lab/readme.txt @@ -1,7 +1,7 @@ === Performance Lab === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 4.2.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/speculation-rules/load.php b/plugins/speculation-rules/load.php index 0910a2a1d2..20f4ff6f85 100644 --- a/plugins/speculation-rules/load.php +++ b/plugins/speculation-rules/load.php @@ -5,7 +5,7 @@ * Description: Enables browsers to speculatively prerender or prefetch pages to achieve near-instant loads based on user interaction. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.6.0 + * Version: 1.7.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -67,7 +67,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'plsr_pending_plugin_info', - '1.6.0', + '1.7.0', static function ( string $version ): void { // Define the constant. diff --git a/plugins/speculation-rules/readme.txt b/plugins/speculation-rules/readme.txt index 5216f41bbb..f2e25889f7 100644 --- a/plugins/speculation-rules/readme.txt +++ b/plugins/speculation-rules/readme.txt @@ -1,8 +1,8 @@ === Speculative Loading === Contributors: wordpressdotorg -Tested up to: 7.0 -Stable tag: 1.6.0 +Tested up to: 7.1 +Stable tag: 1.7.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, javascript, speculation rules, prerender, prefetch @@ -122,6 +122,16 @@ Contributions are always welcome! Learn more about how to get involved in the [C == Changelog == += 1.7.0 = + +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) +* Harden the JSON encoding of the inline speculation rules script and add a `sourceURL` to the authentication admin notice script. ([2169](https://github.com/WordPress/performance/pull/2169)) + = 1.6.0 = **Enhancements** diff --git a/plugins/view-transitions/readme.txt b/plugins/view-transitions/readme.txt index 429f93962f..6cb568bb16 100644 --- a/plugins/view-transitions/readme.txt +++ b/plugins/view-transitions/readme.txt @@ -1,7 +1,7 @@ === View Transitions === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 1.2.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/web-worker-offloading/load.php b/plugins/web-worker-offloading/load.php index 60979fedec..9848aea319 100644 --- a/plugins/web-worker-offloading/load.php +++ b/plugins/web-worker-offloading/load.php @@ -5,7 +5,7 @@ * Description: Offloads select JavaScript execution to a Web Worker to reduce work on the main thread and improve the Interaction to Next Paint (INP) metric. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 0.2.1 + * Version: 0.3.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -45,7 +45,7 @@ ); } -define( 'WEB_WORKER_OFFLOADING_VERSION', '0.2.1' ); +define( 'WEB_WORKER_OFFLOADING_VERSION', '0.3.0' ); require_once __DIR__ . '/helper.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/web-worker-offloading/readme.txt b/plugins/web-worker-offloading/readme.txt index 3cbe343e1b..6a2f0b4a05 100644 --- a/plugins/web-worker-offloading/readme.txt +++ b/plugins/web-worker-offloading/readme.txt @@ -1,8 +1,8 @@ === Web Worker Offloading === Contributors: wordpressdotorg -Tested up to: 7.0 -Stable tag: 0.2.1 +Tested up to: 7.1 +Stable tag: 0.3.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, JavaScript, web worker, partytown, analytics @@ -94,6 +94,15 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 0.3.0 = + +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + = 0.2.1 = * Intend to sunset. ([2404](https://github.com/WordPress/performance/pull/2404)) diff --git a/plugins/webp-uploads/readme.txt b/plugins/webp-uploads/readme.txt index 59b7ddc992..fb1f1218fc 100644 --- a/plugins/webp-uploads/readme.txt +++ b/plugins/webp-uploads/readme.txt @@ -1,7 +1,7 @@ === Modern Image Formats === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 2.7.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/tools/webpack/utils.js b/tools/webpack/utils.js index e20c6f7f77..92fc0778eb 100644 --- a/tools/webpack/utils.js +++ b/tools/webpack/utils.js @@ -81,6 +81,43 @@ const generateBuildManifest = ( slug, from ) => { fs.writeFileSync( manifestPath, JSON.stringify( manifest, null, 2 ) ); }; +/** + * Creates a transformer that repoints a bundle's sourceMappingURL at a given file name. + * + * A bundle vendored out of node_modules keeps the sourceMappingURL its publisher emitted, + * which names the map as it is called inside that package. Where the bundle is renamed on + * the way in, that reference stops matching the map shipped beside it, so it has to be + * rewritten to the name the map is given here. + * + * @param {string} mapFileName File name the source map is shipped under. + * + * @return {function(Buffer): string} Transformer. + */ +const sourceMappingUrlTransformer = ( mapFileName ) => ( content ) => + content + .toString() + .replace( + /\/\/# sourceMappingURL=\S+/, + `//# sourceMappingURL=${ mapFileName }` + ); + +/** + * Creates a transformer that repoints a source map's "file" field at a given file name. + * + * The field names the generated file a map belongs to, as that file is called inside the + * package the map was published in. Where the bundle is renamed on the way in, the field + * has to be repointed at the new name to stay consistent with it. + * + * @param {string} bundleFileName File name the bundle is shipped under. + * + * @return {function(Buffer): string} Transformer. + */ +const sourceMapFileTransformer = ( bundleFileName ) => ( content ) => { + const map = JSON.parse( content.toString() ); + map.file = bundleFileName; + return JSON.stringify( map ); +}; + /** * Transformer to get version from package.json and return it as a PHP file. * @@ -293,6 +330,8 @@ module.exports = { getPluginVersion, generateBuildManifest, assetDataTransformer, + sourceMappingUrlTransformer, + sourceMapFileTransformer, cssMinifyTransformer, getZipContentFingerprint, getUntrackedIncludedFiles, diff --git a/webpack.config.js b/webpack.config.js index a21df0cedd..1d9f0a41a9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,6 +12,8 @@ const { plugins: standalonePlugins } = require( './plugins.json' ); const { createPluginZip, assetDataTransformer, + sourceMappingUrlTransformer, + sourceMapFileTransformer, cssMinifyTransformer, deleteFileOrDirectory, generateBuildManifest, @@ -194,11 +196,40 @@ const optimizationDetective = ( env ) => { from: `${ source }/dist/web-vitals.js`, to: `${ destination }/build/web-vitals.js`, info: { minimized: true }, + transform: { + transformer: + sourceMappingUrlTransformer( + 'web-vitals.js.map' + ), + cache: false, + }, + }, + { + from: `${ source }/dist/web-vitals.js.map`, + to: `${ destination }/build/web-vitals.js.map`, + info: { minimized: true }, }, { from: `${ source }/dist/web-vitals.attribution.js`, to: `${ destination }/build/web-vitals-attribution.js`, info: { minimized: true }, + transform: { + transformer: sourceMappingUrlTransformer( + 'web-vitals-attribution.js.map' + ), + cache: false, + }, + }, + { + from: `${ source }/dist/web-vitals.attribution.js.map`, + to: `${ destination }/build/web-vitals-attribution.js.map`, + info: { minimized: true }, + transform: { + transformer: sourceMapFileTransformer( + 'web-vitals-attribution.js' + ), + cache: false, + }, }, { from: `${ source }/package.json`,