WordPress – Register a JS module script AND load inline data

The Problem

Here’s a quick one for you. I’m working on a website where I’m slowly adding a Vue app to replace pages as part of my dream of not using WordPress to show any content.

One annoyance I had with that was trying to register the Vue app in WordPress as a module, while also making use of the wp_add_inline_script function to load data onto the page to preload data for the app.

So I had two pieces, I’ll share some pared down code of handling both situations separately:

1: Loading data into the browser window object to access in the Vue app. it seemed like this was the current recommended way of doing that:

function register_scripts (): void {
  $script_handle = 'my-script-handle';
  $script_src = 'my/script/path.js';
  $js_data_key = 'MY_DATA_KEY';
  
  $some_php_data = [
    'key1' => 'something',
    'key2' => 'something else',
  ];
  $data_embed_string = sprintf('window.%s = %s', $js_data_key, json_encode($some_php_data));

  wp_enqueue_script($script_handle, $script_src);
  wp_add_inline_script($script_handle, $data_embed_string, 'before');
}

2: Registering the JS script as a module so it loads properly. I found code snippets that solved the problem by overriding the HTML tag and adding type=”module” to the relevant script tag:

function register_scripts (): void {
  $script_handle = 'my-script-handle';
  $script_src = 'my/script/path.js';

  wp_enqueue_script($script_handle, $script_src);
  script_type_module($script_handle);
}

function script_type_module(bool|string $scriptHandle = false): void {
  add_filter('script_loader_tag', function ($tag, $handle, $src) use ($scriptHandle) {
    if ($scriptHandle !== $handle) return $tag;
    
    return '<script type="module" src="' . esc_url($src) . '" id="' . $handle . '-js"></script>';
  }, 10, 3);
}

The big glaring problem: Using both of these methods in tandem doesn’t work. Using that script_type_module() causes the embed data not to load. But not registering the script as a module caused the app not to work right (and also mess up scripts in the admin area, so things like hovering over the side menu didn’t work anymore).

Could I have simply loaded the inline data as a separate script and just made sure it loaded sooner? Yes. However, the data was specifically for my Vue app; I wanted to load them together because that felt like the right way to do it and I’m stubborn.

The Solution

Well it turned out to be pretty simple. The reason data wasn’t loading is that the tag from script_loader_tag contained the script tag and the inline script. The code snippets weren’t accounting for that by building the new script tag.

Here’s the code to make them work together:

function register_scripts (): void {
  $script_handle = 'my-script-handle';
  $script_src = 'my/script/path.js';
  $js_data_key = 'MY_DATA_KEY';
  
  $some_php_data = [
    'key1' => 'something',
    'key2' => 'something else',
  ];
  $data_embed_string = sprintf('window.%s = %s', $js_data_key, json_encode($some_php_data));

  wp_enqueue_script($script_handle, $script_src);
  script_type_module($script_handle);
  wp_add_inline_script($script_handle, $data_embed_string, 'before');
}

function script_type_module(bool|string $scriptHandle = false): void {
  add_filter('script_loader_tag', function ($tag, $handle, $src) use ($scriptHandle) {
    if ($scriptHandle !== $handle) return $tag;

    $search = sprintf('id="%s-js"', $this->handle_name_script);
    $replace = $search . ' type="module"';
    return str_replace($search, $replace, $tag);
  }, 10, 3);
}

Instead of replacing the entire tag in script_loader_tag, just do a search for the unique JS handle ID that WordPress generates, and just tack on type="module" after that. Now you’ve got embedded inline data, and the script module. Booyah.

A Little Extra Ending Note

I figured there may be an issue if at any point WordPress started adding the type attribute to the script like (like adding a MIME type or something, as odd as that would be). But I’ve never actually tried to add the same attribute twice on an element. Turns out, the duplicate attributes just get removed and only the first instance is kept. The more you know.

0 0 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x