Make WordPress Core

Changeset 61492


Ignore:
Timestamp:
01/16/2026 10:30:09 AM (8 months 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.