diff --git a/README.md b/README.md
index 093703c..0303b37 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@ A library for managing asset registration and enqueuing in WordPress.
* [Conditional enqueuing](#conditional-enqueuing)
* [Firing a callback after enqueuing occurs](#firing-a-callback-after-enqueuing-occurs)
* [Output JS data](#output-js-data)
+ * [Using a callable to provide localization data](#using-a-callable-to-provide-localization-data)
* [Output content before/after a JS asset is output](#output-content-beforeafter-a-js-asset-is-output)
* [Style meta data](#style-meta-data)
@@ -500,6 +501,28 @@ Note the `my-second-script-mod` handle is overriding a specific nested
key, `boomshakalaka.project.secondScriptData.animal`, in the `boomshakalaka.project.secondScriptData` object while
preserving the other keys.
+#### Using a callable to provide localization data
+
+If you need to provide localization data dynamically, you can use a callable to do so. The callable will be called
+when the asset is enqueued and the return value will be used. The callable will be passed the asset as the first
+argument and should return an array.
+
+```php
+Asset::add( 'my-script', 'js/some-asset.js' )
+ ->add_localize_script(
+ 'boomshakalaka.project.myScriptData',
+ function( Asset $asset ) {
+ return [
+ 'animal' => 'cat',
+ 'color' => 'orange',
+ ];
+ }
+ )
+ ->register();
+```
+
+Any valid callable can be used, including Closures, like in the example above.
+
### Output content before/after a JS asset is output
There may be times when you wish to output markup or text immediately before or immediately after outputting the JS
diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php
index d93b479..2797f81 100644
--- a/src/Assets/Asset.php
+++ b/src/Assets/Asset.php
@@ -414,11 +414,13 @@ public function call_after_enqueue( $callable ) {
* @since 1.0.0
*
* @param string $object_name JS object name.
- * @param array $data Data assigned to the JS object.
+ * @param array|callable $data Data assigned to the JS object. If a callable is passed, it will be called
+ * when the asset is enqueued and the return value will be used. The callable
+ * will be passed the asset as the first argument and should return an array.
*
* @return static
*/
- public function add_localize_script( string $object_name, array $data ) {
+ public function add_localize_script( string $object_name, $data ) {
if ( str_contains( $object_name, '.' ) ) {
$this->custom_localize_script_objects[] = [ $object_name, $data ];
} else {
diff --git a/src/Assets/Assets.php b/src/Assets/Assets.php
index afdf42e..6fddbcf 100755
--- a/src/Assets/Assets.php
+++ b/src/Assets/Assets.php
@@ -357,7 +357,7 @@ public function filter_add_localization_data( $tag, $handle ) {
// If we have a Callable as the Localize data we execute it.
if ( is_callable( $localize ) ) {
- $localize = call_user_func( $localize, $asset );
+ $localize = $localize( $asset );
}
wp_localize_script( $asset->get_slug(), $key, $localize );
@@ -378,6 +378,11 @@ public function filter_add_localization_data( $tag, $handle ) {
* Print the dot.notation namespaced objects for the asset.
*/
foreach ( $custom_localize_scripts as [$object_name, $data] ) {
+ // If we have a Callable as the Localize data we execute it.
+ if ( is_callable( $data ) ) {
+ $data = $data( $asset );
+ }
+
$localized_key = "{$asset->get_slug()}::{$object_name}";
if ( in_array( $localized_key, $this->localized, true ) ) {
diff --git a/tests/wpunit/AssetsTest.php b/tests/wpunit/AssetsTest.php
index 3345662..b94c677 100644
--- a/tests/wpunit/AssetsTest.php
+++ b/tests/wpunit/AssetsTest.php
@@ -215,4 +215,55 @@ public function should_allow_localizing_data_in_normal_and_namespaced_form_for_s
$apply_filters
);
}
+
+ /**
+ * It should allow localizing data using a Closure
+ *
+ * @test
+ */
+ public function should_allow_localizing_data_using_a_closure(): void {
+ $resolved_one = false;
+ $resolved_two = false;
+ $data_callback_one = function () use ( &$resolved_one ) {
+ $resolved_one = true;
+
+ return [
+ 'animal' => 'cat',
+ 'color' => 'orange',
+ ];
+ };
+ $data_callback_two = function () use ( &$resolved_two ) {
+ $resolved_two = true;
+
+ return [
+ 'animal' => 'dog',
+ 'color' => 'green',
+ ];
+ };
+
+ Asset::add( 'my-script-with-closure-data', 'test-script.js' )
+ ->add_localize_script( 'scriptWithClosureData', $data_callback_one )
+ ->add_localize_script( 'acme.project.closureData', $data_callback_two )
+ ->register();
+
+ $this->assertFalse( $resolved_one, 'The first callback should not have been resolved yet.' );
+ $this->assertFalse( $resolved_two, 'The second callback should not have been resolved yet.' );
+
+ $apply_filters = apply_filters( 'script_loader_tag', '', 'my-script-with-closure-data' );
+ $this->assertEquals( <<< SCRIPT
+
+
+SCRIPT,
+ $apply_filters
+ );
+
+ $this->assertTrue( $resolved_one );
+ $this->assertTrue( $resolved_two );
+ }
}