From 7425919374e96442b9fc39626e2c2076b0425c87 Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 20 Mar 2026 10:40:55 -0400 Subject: [PATCH 1/7] Only include files for the registered routes. --- Gruntfile.js | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 4aded0a659c00..06d92b2a85f32 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -601,7 +601,7 @@ module.exports = function(grunt) { src: 'vendor/composer/ca-bundle/res/cacert.pem', dest: SOURCE_DIR + 'wp-includes/certificates/ca-bundle.crt' }, - // Gutenberg PHP infrastructure files (routes.php, pages.php, constants.php, pages/, routes/). + // Gutenberg PHP infrastructure files (pages.php, constants.php, pages/). 'gutenberg-php': { options: { process: function( content ) { @@ -616,22 +616,50 @@ module.exports = function(grunt) { expand: true, cwd: 'gutenberg/build', src: [ - 'routes.php', 'pages.php', 'constants.php', 'pages/**/*.php', - 'routes/**/*.php', ], dest: WORKING_DIR + 'wp-includes/build/', } ], }, + /* + * Only copy files relevant to the routes specified in the registry file. + * + * While the registry file does not contain any experimental routes, the `gutenberg/build/routes` directory + * includes the files for all registered routes. Only the files related to the routes specified in the + * registry should be included in the WordPress build. + */ + routes: ( function() { + var registryContent = fs.readFileSync( 'gutenberg/build/routes/registry.php', 'utf8' ); + var routeNames = []; + var namePattern = /'name'\s*=>\s*'([^']+)'/g; + var match; + while ( ( match = namePattern.exec( registryContent ) ) !== null ) { + routeNames.push( match[ 1 ] ); + } + return { + files: [ { + expand: true, + cwd: 'gutenberg/build', + src: [ 'routes.php', 'routes/registry.php' ].concat( + routeNames.flatMap( function( name ) { + return [ + 'routes/' + name + '/**/*.php', + 'routes/' + name + '/**/*.js', + ]; + } ) + ), + dest: WORKING_DIR + 'wp-includes/build/', + } ], + }; + } )(), 'gutenberg-js': { files: [ { expand: true, cwd: 'gutenberg/build', src: [ 'pages/**/*.js', - 'routes/**/*.js', ], dest: WORKING_DIR + 'wp-includes/build/', } ], @@ -2054,6 +2082,7 @@ module.exports = function(grunt) { grunt.registerTask( 'build:gutenberg', [ 'copy:gutenberg-php', + 'copy:routes', 'copy:gutenberg-js', 'gutenberg:copy', 'copy:gutenberg-modules', From f28d4439d2965d9e67170bc3c8aa895a12ac0bb5 Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:06:26 -0400 Subject: [PATCH 2/7] Avoid error due to the IIFE and missing file. --- Gruntfile.js | 60 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 06d92b2a85f32..3a0b6c7d16cd6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -629,31 +629,13 @@ module.exports = function(grunt) { * While the registry file does not contain any experimental routes, the `gutenberg/build/routes` directory * includes the files for all registered routes. Only the files related to the routes specified in the * registry should be included in the WordPress build. + * + * The `src` list is populated at task runtime by `routes:setup`, which reads the registry after + * `gutenberg:download` has run. See the `routes:setup` task registration for implementation details. */ - routes: ( function() { - var registryContent = fs.readFileSync( 'gutenberg/build/routes/registry.php', 'utf8' ); - var routeNames = []; - var namePattern = /'name'\s*=>\s*'([^']+)'/g; - var match; - while ( ( match = namePattern.exec( registryContent ) ) !== null ) { - routeNames.push( match[ 1 ] ); - } - return { - files: [ { - expand: true, - cwd: 'gutenberg/build', - src: [ 'routes.php', 'routes/registry.php' ].concat( - routeNames.flatMap( function( name ) { - return [ - 'routes/' + name + '/**/*.php', - 'routes/' + name + '/**/*.js', - ]; - } ) - ), - dest: WORKING_DIR + 'wp-includes/build/', - } ], - }; - } )(), + routes: { + files: [], + }, 'gutenberg-js': { files: [ { expand: true, @@ -2080,8 +2062,38 @@ module.exports = function(grunt) { } ); } ); + grunt.registerTask( 'routes:setup', 'Reads the routes registry and configures the copy:routes task.', function() { + var registryPath = 'gutenberg/build/routes/registry.php'; + if ( ! fs.existsSync( registryPath ) ) { + grunt.fatal( + 'Route registry not found at ' + registryPath + '. Run `grunt gutenberg:download` first.' + ); + } + var registryContent = fs.readFileSync( registryPath, 'utf8' ); + var routeNames = []; + var namePattern = /'name'\s*=>\s*'([^']+)'/g; + var match; + while ( ( match = namePattern.exec( registryContent ) ) !== null ) { + routeNames.push( match[ 1 ] ); + } + grunt.config( [ 'copy', 'routes', 'files' ], [ { + expand: true, + cwd: 'gutenberg/build', + src: [ 'routes/registry.php' ].concat( + routeNames.flatMap( function( name ) { + return [ + 'routes/' + name + '/**/*.php', + 'routes/' + name + '/**/*.js', + ]; + } ) + ), + dest: WORKING_DIR + 'wp-includes/build/', + } ] ); + } ); + grunt.registerTask( 'build:gutenberg', [ 'copy:gutenberg-php', + 'routes:setup', 'copy:routes', 'copy:gutenberg-js', 'gutenberg:copy', From fadfff852522016fdd74d7a2ff6120f18816032d Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:09:50 -0400 Subject: [PATCH 3/7] Keep settings other than `files` in `copy:routes`. --- Gruntfile.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 3a0b6c7d16cd6..3d874956521b5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -634,7 +634,10 @@ module.exports = function(grunt) { * `gutenberg:download` has run. See the `routes:setup` task registration for implementation details. */ routes: { - files: [], + expand: true, + cwd: 'gutenberg/build', + src: [], + dest: WORKING_DIR + 'wp-includes/build/', }, 'gutenberg-js': { files: [ { @@ -2076,19 +2079,14 @@ module.exports = function(grunt) { while ( ( match = namePattern.exec( registryContent ) ) !== null ) { routeNames.push( match[ 1 ] ); } - grunt.config( [ 'copy', 'routes', 'files' ], [ { - expand: true, - cwd: 'gutenberg/build', - src: [ 'routes/registry.php' ].concat( - routeNames.flatMap( function( name ) { - return [ - 'routes/' + name + '/**/*.php', - 'routes/' + name + '/**/*.js', - ]; - } ) - ), - dest: WORKING_DIR + 'wp-includes/build/', - } ] ); + grunt.config( [ 'copy', 'routes', 'src' ], [ 'routes/registry.php' ].concat( + routeNames.flatMap( function( name ) { + return [ + 'routes/' + name + '/**/*.php', + 'routes/' + name + '/**/*.js', + ]; + } ) + ) ); } ); grunt.registerTask( 'build:gutenberg', [ From a18c96b2650f796640f22eae51c03558976287a5 Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:22:30 -0400 Subject: [PATCH 4/7] Re-add `routes.php` file to the correct copy step. --- Gruntfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Gruntfile.js b/Gruntfile.js index 3d874956521b5..89afbbcbf82f6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -616,6 +616,7 @@ module.exports = function(grunt) { expand: true, cwd: 'gutenberg/build', src: [ + 'routes.php', 'pages.php', 'constants.php', 'pages/**/*.php', From 21ac1488cea3bd65e16a43ce0b22e64552605164 Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:25:41 -0400 Subject: [PATCH 5/7] Trigger a fatal error when the registry is "empty" --- Gruntfile.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Gruntfile.js b/Gruntfile.js index 89afbbcbf82f6..39695dbed0250 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2080,6 +2080,13 @@ module.exports = function(grunt) { while ( ( match = namePattern.exec( registryContent ) ) !== null ) { routeNames.push( match[ 1 ] ); } + + if ( routeNames.length === 0 ) { + grunt.fatal( + 'No route names found in ' + registryPath + '. The format of the file may have changed.' + ); + } + grunt.config( [ 'copy', 'routes', 'src' ], [ 'routes/registry.php' ].concat( routeNames.flatMap( function( name ) { return [ From b5d56d742afc43c2dea8dc57f76cab7b701ab8ac Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:49:04 -0400 Subject: [PATCH 6/7] Add some defensive path validation. Also switches off `var` in favor of `const`/`let`. --- Gruntfile.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 39695dbed0250..805014a050b5a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2067,16 +2067,18 @@ module.exports = function(grunt) { } ); grunt.registerTask( 'routes:setup', 'Reads the routes registry and configures the copy:routes task.', function() { - var registryPath = 'gutenberg/build/routes/registry.php'; - if ( ! fs.existsSync( registryPath ) ) { + const registryPath = 'gutenberg/build/routes/registry.php'; + let registryContent; + try { + registryContent = fs.readFileSync( registryPath, 'utf8' ); + } catch ( e ) { grunt.fatal( 'Route registry not found at ' + registryPath + '. Run `grunt gutenberg:download` first.' ); } - var registryContent = fs.readFileSync( registryPath, 'utf8' ); - var routeNames = []; - var namePattern = /'name'\s*=>\s*'([^']+)'/g; - var match; + const namePattern = /'name'\s*=>\s*'([^']+)'/g; + const routeNames = []; + let match; while ( ( match = namePattern.exec( registryContent ) ) !== null ) { routeNames.push( match[ 1 ] ); } @@ -2087,6 +2089,15 @@ module.exports = function(grunt) { ); } + const validName = /^[A-Za-z0-9_-]+$/; + routeNames.forEach( function( name ) { + if ( ! validName.test( name ) ) { + grunt.fatal( + 'Invalid route name \'' + name + '\' in ' + registryPath + '. Expected only letters, digits, hyphens, and underscores.' + ); + } + } ); + grunt.config( [ 'copy', 'routes', 'src' ], [ 'routes/registry.php' ].concat( routeNames.flatMap( function( name ) { return [ From d903b138ad5c1f9e2049a6013828f89ffa39004c Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:50:17 -0400 Subject: [PATCH 7/7] Update inline comment. --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 5a64dcefaf722..9a35f61e79496 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -603,7 +603,7 @@ module.exports = function(grunt) { src: 'vendor/composer/ca-bundle/res/cacert.pem', dest: SOURCE_DIR + 'wp-includes/certificates/ca-bundle.crt' }, - // Gutenberg PHP infrastructure files (pages.php, constants.php, pages/). + // Gutenberg PHP infrastructure files (routes.php, pages.php, constants.php, pages/). 'gutenberg-php': { options: { process: function( content ) {