Make WordPress Core

Changeset 61492


Ignore:
Timestamp:
01/16/2026 10:30:09 AM (13 days ago)
Author:
youknowriad
Message:

Build: Improve Gutenberg integration workflow.

This changeset improves the Gutenberg build integration to simplify the developer workflow and reinstore a flow similar to how package dependencies worked before the Gutenberg checkout-and-build approach was introduced.

Key improvements:

  • Automatic rebuild on ref change: Adds a new gutenberg:sync script that stores a hash of the built ref in .gutenberg-hash and only rebuilds when the ref changes.
  • Full integration on npm install: Running npm install now produces a fully working development environment with Gutenberg assets in src/.
  • Clean Gutenberg checkout: Restores Gutenberg's package.json after the build completes.
  • Stops copying .js.map files to wp-includes/js/dist since they reference non-existent paths.
  • Remove package.json files from the build folder.
  • Avoid closures and use prefixed functions.
  • Updates build checks to use jquery.js instead of edit-post.js as the build indicator.

Props youknowriad, ellatrix, mcsf, dmsnell, ntsekouras, jorgefilipecosta, tobiasbg, peterwilsoncc.
Fixes #64393.

Location:
trunk
Files:
1 added
14 edited

Legend:

Unmodified
Added
Removed
  • trunk/.gitignore

    r61439 r61492  
    4444/src/wp-includes/theme.json
    4545/packagehash.txt
     46/.gutenberg-hash
    4647/artifacts
    4748/setup.log
  • trunk/Gruntfile.js

    r61475 r61492  
    14591459    } );
    14601460
    1461     grunt.registerTask( 'gutenberg-integrate', 'Complete Gutenberg integration workflow.', [
    1462         'gutenberg-build',
    1463         'gutenberg-copy'
    1464     ] );
     1461    grunt.registerTask( 'gutenberg-sync', 'Syncs Gutenberg checkout and build if ref has changed.', function() {
     1462        const done = this.async();
     1463        grunt.util.spawn( {
     1464            cmd: 'node',
     1465            args: [ 'tools/gutenberg/sync-gutenberg.js' ],
     1466            opts: { stdio: 'inherit' }
     1467        }, function( error ) {
     1468            done( ! error );
     1469        } );
     1470    } );
    14651471
    14661472    grunt.registerTask( 'copy-vendor-scripts', 'Copies vendor scripts from node_modules to wp-includes/js/dist/vendor/.', function() {
     
    18971903                'build:js',
    18981904                'build:css',
    1899                 'gutenberg-integrate',
     1905                'gutenberg-sync',
     1906                'gutenberg-copy',
    19001907                'copy-vendor-scripts',
    19011908                'build:certificates'
     
    19071914                'build:js',
    19081915                'build:css',
    1909                 'gutenberg-integrate',
     1916                'gutenberg-sync',
     1917                'gutenberg-copy',
    19101918                'copy-vendor-scripts',
    19111919                'replace:source-maps',
  • trunk/package.json

    r61487 r61492  
    88    },
    99    "gutenberg": {
    10         "ref": "892bfad51d2261f44f3a21f934b1c72bd29a2449"
     10        "ref": "7bf80ea84eb8b62eceb1bb3fe82e42163673ca79"
    1111    },
    1212    "engines": {
     
    100100    },
    101101    "scripts": {
    102         "postinstall": "npm run gutenberg:checkout",
     102        "postinstall": "npm run gutenberg:sync && npm run gutenberg:copy -- --dev",
    103103        "build": "grunt build",
    104104        "build:dev": "grunt build --dev",
     
    127127        "gutenberg:build": "node tools/gutenberg/build-gutenberg.js",
    128128        "gutenberg:copy": "node tools/gutenberg/copy-gutenberg-build.js",
    129         "gutenberg:integrate": "npm run gutenberg:checkout && npm run gutenberg:build && npm run gutenberg:copy",
     129        "gutenberg:sync": "node tools/gutenberg/sync-gutenberg.js",
    130130        "vendor:copy": "node tools/vendors/copy-vendors.js",
    131131        "sync-gutenberg-packages": "grunt sync-gutenberg-packages",
  • trunk/src/index.php

    r56241 r61492  
    1616 * Note: WPINC is not defined yet, it is defined later in wp-settings.php.
    1717 */
    18 if ( file_exists( ABSPATH . 'wp-includes/js/dist/edit-post.js' ) ) {
     18if ( file_exists( ABSPATH . 'wp-includes/js/jquery/jquery.js' ) && is_dir( ABSPATH . 'wp-includes/build' ) ) {
    1919    require_once ABSPATH . '_index.php';
    2020    return;
  • trunk/src/wp-admin/font-library.php

    r61438 r61492  
    2020
    2121// Check if Gutenberg build files are available
    22 if ( ! function_exists( 'font_library_wp_admin_render_page' ) ) {
     22if ( ! function_exists( 'wp_font_library_wp_admin_render_page' ) ) {
    2323    wp_die(
    2424        '<h1>' . __( 'Font Library is not available.' ) . '</h1>' .
    25         '<p>' . __( 'The Font Library requires Gutenberg integration. Please run <code>npm run gutenberg:integrate</code> to build the necessary files.' ) . '</p>',
     25        '<p>' . __( 'The Font Library requires Gutenberg build files. Please run <code>npm install</code> to build the necessary files.' ) . '</p>',
    2626        503
    2727    );
     
    3434
    3535// Render the Font Library page
    36 font_library_wp_admin_render_page();
     36wp_font_library_wp_admin_render_page();
    3737
    3838require_once ABSPATH . 'wp-admin/admin-footer.php';
  • trunk/src/wp-admin/index.php

    r52352 r61492  
    77 */
    88
    9 if ( file_exists( __DIR__ . '/../wp-includes/js/dist/edit-post.js' ) ) {
     9if ( file_exists( __DIR__ . '/../wp-includes/js/jquery/jquery.js' ) && is_dir( __DIR__ . '/../wp-includes/build' ) ) {
    1010    require_once __DIR__ . '/_index.php';
    1111    return;
  • trunk/src/wp-includes/blocks.php

    r61431 r61492  
    24222422    $parser_class = apply_filters( 'block_parser_class', 'WP_Block_Parser' );
    24232423
     2424    if ( ! class_exists( $parser_class ) ) {
     2425        return array();
     2426    }
     2427
    24242428    $parser = new $parser_class();
    24252429    return $parser->parse( $content );
  • trunk/src/wp-includes/blocks/index.php

    r59159 r61492  
    1414
    1515// Include files required for core blocks registration.
    16 require BLOCKS_PATH . 'legacy-widget.php';
    17 require BLOCKS_PATH . 'widget-group.php';
    18 require BLOCKS_PATH . 'require-dynamic-blocks.php';
     16if ( file_exists( BLOCKS_PATH . 'legacy-widget.php' ) ) {
     17    require BLOCKS_PATH . 'legacy-widget.php';
     18}
     19if ( file_exists( BLOCKS_PATH . 'widget-group.php' ) ) {
     20    require BLOCKS_PATH . 'widget-group.php';
     21}
     22if ( file_exists( BLOCKS_PATH . 'require-dynamic-blocks.php' ) ) {
     23    require BLOCKS_PATH . 'require-dynamic-blocks.php';
     24}
    1925
    2026/**
     
    4450    static $core_blocks_meta;
    4551    if ( ! $core_blocks_meta ) {
     52        if ( ! file_exists( BLOCKS_PATH . 'blocks-json.php' ) ) {
     53            return;
     54        }
    4655        $core_blocks_meta = require BLOCKS_PATH . 'blocks-json.php';
    4756    }
     
    151160 */
    152161function register_core_block_types_from_metadata() {
     162    if ( ! file_exists( BLOCKS_PATH . 'require-static-blocks.php' ) ) {
     163        return;
     164    }
    153165    $block_folders = require BLOCKS_PATH . 'require-static-blocks.php';
    154166    foreach ( $block_folders as $block_folder ) {
     
    170182 */
    171183function wp_register_core_block_metadata_collection() {
     184    if ( ! file_exists( BLOCKS_PATH . 'blocks-json.php' ) ) {
     185        return;
     186    }
    172187    wp_register_block_metadata_collection(
    173188        BLOCKS_PATH,
  • trunk/src/wp-includes/formatting.php

    r61436 r61492  
    52285228 */
    52295229function wp_pre_kses_block_attributes( $content, $allowed_html, $allowed_protocols ) {
     5230    // If the block parser isn't available, skip block attribute filtering.
     5231    if ( ! class_exists( 'WP_Block_Parser' ) ) {
     5232        return $content;
     5233    }
     5234
    52305235    /*
    52315236     * `filter_block_content` is expected to call `wp_kses`. Temporarily remove
  • trunk/src/wp-includes/script-loader.php

    r61491 r61492  
    282282     *     'api-fetch.js' => array(...
    283283     */
    284     $assets = include ABSPATH . WPINC . "/assets/script-loader-packages{$suffix}.php";
     284    $assets_file = ABSPATH . WPINC . "/assets/script-loader-packages{$suffix}.php";
     285    $assets      = file_exists( $assets_file ) ? include $assets_file : array();
    285286
    286287    foreach ( $assets as $file_name => $package_data ) {
  • trunk/src/wp-includes/script-modules.php

    r61438 r61492  
    154154     *     'block-library/navigation/view.min.js' => …
    155155     */
    156     $assets = include ABSPATH . WPINC . "/assets/script-modules-packages{$suffix}.php";
     156    $assets_file = ABSPATH . WPINC . "/assets/script-modules-packages{$suffix}.php";
     157    $assets      = file_exists( $assets_file ) ? include $assets_file : array();
    157158
    158159    foreach ( $assets as $file_name => $script_module_data ) {
  • trunk/src/wp-settings.php

    r61491 r61492  
    243243require ABSPATH . WPINC . '/deprecated.php';
    244244require ABSPATH . WPINC . '/script-loader.php';
    245 require ABSPATH . WPINC . '/build/routes.php';
    246 require ABSPATH . WPINC . '/build/pages.php';
     245if ( file_exists( ABSPATH . WPINC . '/build/routes.php' ) ) {
     246    require ABSPATH . WPINC . '/build/routes.php';
     247}
     248if ( file_exists( ABSPATH . WPINC . '/build/pages.php' ) ) {
     249    require ABSPATH . WPINC . '/build/pages.php';
     250}
    247251require ABSPATH . WPINC . '/taxonomy.php';
    248252require ABSPATH . WPINC . '/class-wp-taxonomy.php';
     
    374378require ABSPATH . WPINC . '/class-wp-block-list.php';
    375379require ABSPATH . WPINC . '/class-wp-block-metadata-registry.php';
    376 require ABSPATH . WPINC . '/class-wp-block-parser-block.php';
    377 require ABSPATH . WPINC . '/class-wp-block-parser-frame.php';
    378 require ABSPATH . WPINC . '/class-wp-block-parser.php';
     380if ( file_exists( ABSPATH . WPINC . '/class-wp-block-parser-block.php' ) ) {
     381    require ABSPATH . WPINC . '/class-wp-block-parser-block.php';
     382}
     383if ( file_exists( ABSPATH . WPINC . '/class-wp-block-parser-frame.php' ) ) {
     384    require ABSPATH . WPINC . '/class-wp-block-parser-frame.php';
     385}
     386if ( file_exists( ABSPATH . WPINC . '/class-wp-block-parser.php' ) ) {
     387    require ABSPATH . WPINC . '/class-wp-block-parser.php';
     388}
    379389require ABSPATH . WPINC . '/class-wp-classic-to-block-menu-converter.php';
    380390require ABSPATH . WPINC . '/class-wp-navigation-fallback.php';
  • trunk/tools/gutenberg/build-gutenberg.js

    r61450 r61492  
    157157    } catch ( error ) {
    158158        console.error( '❌ Build failed:', error.message );
    159         process.exit( 1 );
     159        throw error;
     160    } finally {
     161        // Restore Gutenberg's package.json regardless of success or failure
     162        await restorePackageJson();
     163    }
     164}
     165
     166/**
     167 * Restore Gutenberg's package.json to its original state.
     168 */
     169async function restorePackageJson() {
     170    console.log( '\n🔄 Restoring Gutenberg package.json...' );
     171    try {
     172        await exec( 'git', [ 'checkout', '--', 'package.json' ], {
     173            cwd: gutenbergDir,
     174        } );
     175        console.log( '✅ package.json restored' );
     176    } catch ( error ) {
     177        console.warn( '⚠️  Could not restore package.json:', error.message );
    160178    }
    161179}
  • trunk/tools/gutenberg/copy-gutenberg-build.js

    r61438 r61492  
    4949        destination: 'js/dist',
    5050        copyDirectories: true, // Copy subdirectories
    51         patterns: [ '*.js', '*.js.map' ],
     51        patterns: [ '*.js' ],
    5252        // Rename vendors/ to vendor/ when copying
    5353        directoryRenames: {
     
    917917                        const vendorFiles = fs.readdirSync( src );
    918918                        let copiedCount = 0;
     919                        fs.mkdirSync( dest, { recursive: true } );
    919920                        for ( const file of vendorFiles ) {
    920                             if ( file.startsWith( 'react-jsx-runtime' ) ) {
     921                            if (
     922                                file.startsWith( 'react-jsx-runtime' ) &&
     923                                file.endsWith( '.js' )
     924                            ) {
    921925                                const srcFile = path.join( src, file );
    922926                                const destFile = path.join( dest, file );
    923                                 fs.mkdirSync( dest, { recursive: true } );
    924 
    925                                 if (
    926                                     file.endsWith( '.js' ) &&
    927                                     ! file.endsWith( '.js.map' )
    928                                 ) {
    929                                     let content = fs.readFileSync(
    930                                         srcFile,
    931                                         'utf8'
    932                                     );
    933                                     content = removeSourceMaps( content );
    934                                     fs.writeFileSync( destFile, content );
    935                                 } else {
    936                                     fs.copyFileSync( srcFile, destFile );
    937                                 }
     927
     928                                let content = fs.readFileSync(
     929                                    srcFile,
     930                                    'utf8'
     931                                );
     932                                content = removeSourceMaps( content );
     933                                fs.writeFileSync( destFile, content );
    938934                                copiedCount++;
    939935                            }
     
    956952                    for ( const file of packageFiles ) {
    957953                        if (
    958                             /^index\.(js|js\.map|min\.js|min\.js\.map|min\.asset\.php)$/.test(
    959                                 file
    960                             )
     954                            /^index\.(js|min\.js|min\.asset\.php)$/.test( file )
    961955                        ) {
    962956                            const srcFile = path.join( src, file );
     
    973967
    974968                            // Apply source map removal for .js files
    975                             if (
    976                                 file.endsWith( '.js' ) &&
    977                                 ! file.endsWith( '.js.map' )
    978                             ) {
     969                            if ( file.endsWith( '.js' ) ) {
    979970                                let content = fs.readFileSync(
    980971                                    srcFile,
     
    984975                                fs.writeFileSync( destPath, content );
    985976                            } else {
    986                                 // Copy other files as-is
     977                                // Copy other files as-is (.min.asset.php)
    987978                                fs.copyFileSync( srcFile, destPath );
    988979                            }
     
    992983            } else if (
    993984                entry.isFile() &&
    994                 /\.(js|js\.map)$/.test( entry.name )
     985                entry.name.endsWith( '.js' )
    995986            ) {
    996987                // Copy root-level JS files
     
    998989                fs.mkdirSync( path.dirname( dest ), { recursive: true } );
    999990
    1000                 if (
    1001                     entry.name.endsWith( '.js' ) &&
    1002                     ! entry.name.endsWith( '.js.map' )
    1003                 ) {
    1004                     let content = fs.readFileSync( src, 'utf8' );
    1005                     content = removeSourceMaps( content );
    1006                     fs.writeFileSync( dest, content );
    1007                 } else {
    1008                     fs.copyFileSync( src, dest );
    1009                 }
     991                let content = fs.readFileSync( src, 'utf8' );
     992                content = removeSourceMaps( content );
     993                fs.writeFileSync( dest, content );
    1010994            }
    1011995        }
Note: See TracChangeset for help on using the changeset viewer.