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`,