10up / 10up-toolkit

Official 10up asset building toolkit.
111 stars 20 forks source link

Enable CSS Hot Reloading without explicit JS Entry points. #168

Open nicholasio opened 2 years ago

nicholasio commented 2 years ago

Is your enhancement related to a problem? Please describe.

Enable CSS Hot Reloading without explicit JS Entry points.

This needs a PoC but the overall idea would be to generate a dev-only custom JS entry point and automatically enqueue it via the generated PHP code so that end users don't have to manually create js entry points for every CSS entry point.

Designs

No response

Describe alternatives you've considered

No response

Code of Conduct

nicholasio commented 1 year ago

todo: look into making this work for css for blocks as well.

fabiankaegy commented 1 year ago

Just in case it helps here is where / how we are generating the entrypoints of the CSS of blocks:

https://github.com/10up/10up-toolkit/blob/develop/packages/toolkit/config/webpack/entry.js#L43-L84

nicholasio commented 11 months ago

Upon further investigation (and after trying to come up with PoC for the original proposal) I realized we likely don't need this. For every css-only entry point 10up-toolkit is already generating a corresponding JS file that if enqueued would enable HMR for css.

So this can be solved at the wp-scaffold land. For front-end css the current approach to import css inside js entrypoints still works.

For blocks all we need to do is enqueue a the js version for every css entry point.

For instance, let's say we have the following block.json

{
  "editorScript": "file:./index.js",
  "editorStyle": "file:./editor.css",
  "style": "file:./style.css",
  "viewScript": "file:./view.js",
  "script": "file:./script.js",
  "version": "ddb93b251ea43170aa6fbfdff63ed3d0d2598792d0d65ee83373d9b740602f5a"
}

Toolkit currently generates the following files:

image

The style.js and editor.js files if loaded in WordPress will enable HMR for editor styles and front-end styles. We could modify how we're registering the blocks and manually enqueue those files if they exist and if SCRIPT_DEBUG is true.

Alternatively, we could look if there's a way to use https://github.com/WordPress/wordpress-develop/blob/6.3/src/wp-includes/blocks.php#L393C30-L393C49 to ship an filter with toolkit hmr php file to handle this automagically.

nicholasio commented 11 months ago

quick PoC, dropping this in 10up-theme enables HMR for editor styles in the example block (without linaria).

    add_filter( 'block_type_metadata_settings', function( $settings, $metadata ) { 
        if ( isset( $metadata['editorStyle'] ) && str_starts_with( $metadata['editorStyle'], 'file:' ) ) {
            $editorStylePath = $metadata['editorStyle'];
            $file = $metadata['file'];

            $editorScriptFileName = basename( $metadata['editorScript'] );
            $editorStyleFileName = str_replace( '.css', '.js', basename( $editorStylePath ) );
            $dir = dirname( $file );

            $js_hmr_file = $dir. '/' . str_replace( '.css', '.js', $editorStyleFileName );
            if ( file_exists( $js_hmr_file ) && $editorScriptFileName !== $editorStyleFileName ) {
                wp_register_script(
                    'css-hmr-editor-test-example',
                    TENUP_THEME_DIST_URL . 'blocks/example/editor.js',
                    []
                );
                $settings['editor_script_handles'][] = 'css-hmr-editor-test-example';
            }

        }

        return $settings;
    }, 10, 2);

We can probably make this more generic and ship this alongside toolkit's fast-refresh.php file

fabiankaegy commented 11 months ago

@nicholasio I tested this and generalized the PHP a bit. But sadly it was still not hot reloading the CSS for me 🤔

add_filter(
'block_type_metadata_settings',
    static function( $settings, $metadata ) {
        if ( isset( $metadata['editorStyle'] ) && str_starts_with( $metadata['editorStyle'], 'file:' ) ) {
            $editor_style_path = $metadata['editorStyle'];
            $file              = $metadata['file'];
            $editor_script_file_name = basename( $metadata['editorScript'] );
            $editor_style_file_name  = str_replace( '.css', '.js', basename( $editor_style_path ) );
            $dir                     = dirname( $file );
            $js_hmr_file = $dir . '/' . str_replace( '.css', '.js', $editor_style_file_name );
            $script_index = count( $settings['editor_script_handles'] ) ?? 0;
            $script_handle = generate_block_asset_handle( $metadata['name'], 'editorScript', $script_index );
            if ( file_exists( $js_hmr_file ) && $editor_script_file_name !== $editor_style_file_name ) {
                $script_asset = include str_replace( '.js', '.asset.php', $js_hmr_file );
                $script_url = get_block_asset_url( $js_hmr_file );

                wp_register_script(
                    $script_handle,
                    $script_url,
                    $script_asset['dependencies'],
                    $script_asset['version'],
                    true
                );
                $settings['editor_script_handles'][] = $script_handle;
            }
        }

        return $settings;
    },
    10,
    2
);

function get_block_asset_url( $path ) {
    static $template_paths_norm = array();

    $template = get_template();
    if ( ! isset( $template_paths_norm[ $template ] ) ) {
        $template_paths_norm[ $template ] = wp_normalize_path( get_template_directory() );
    }

    if ( str_starts_with( $path, trailingslashit( $template_paths_norm[ $template ] ) ) ) {
        return get_theme_file_uri( str_replace( $template_paths_norm[ $template ], '', $path ) );
    }
}