s($useHandle, $rootSlug . '-' . $handle, $pluginPath . $packageDir . 'languages/frontend/json');
} else {
\wp_enqueue_style($useHandle, $url, $deps, $cachebuster, $media);
}
return $useHandle;
}
return \false;
}
/**
* Enqueue a composer package script from our multi-package repository.
*
* @param string $handle Name of the package.
* @param string[] $deps An array of registered scripts handles this script depends on.
* @param string $src The file to use in dist or dev folder.
* @param boolean $in_footer Whether to enqueue the script before
instead of in the
.
* @return string The used handle
*/
public function enqueueComposerScript($handle, $deps = [], $src = 'index.js', $in_footer = \true)
{
return $this->enqueueComposer($handle, $src, $deps, 'script', $in_footer);
}
/**
* Enqueue a composer package style from our multi-package repository.
*
* @param string $handle Name of the package.
* @param string[] $deps An array of registered scripts handles this script depends on.
* @param string $src The file to use in dist or dev folder.
* @param string $media The media for which this stylesheet has been defined. Accepts media types like 'all', 'print' and 'screen', or media queries like '(orientation: portrait)' and '(max-width: 640px)'.
* @return string The used handle
*/
public function enqueueComposerStyle($handle, $deps = [], $src = 'index.css', $media = 'all')
{
return $this->enqueueComposer($handle, $src, $deps, 'style', null, $media);
}
/**
* Enqueue scripts and styles for admin pages.
*
* @param string $hook_suffix The current admin page
*/
public function admin_enqueue_scripts($hook_suffix)
{
$this->enqueue_scripts_and_styles(Constants::ASSETS_TYPE_ADMIN, $hook_suffix);
}
/**
* Enqueue scripts and styles for frontend pages.
*/
public function wp_enqueue_scripts()
{
$this->enqueue_scripts_and_styles(Constants::ASSETS_TYPE_FRONTEND);
}
/*
* Enqueue blocker and banner in Login screen too, so reCaptcha forms or
* similar scripts can be blocked.
*/
public function login_enqueue_scripts()
{
$this->enqueue_scripts_and_styles(Constants::ASSETS_TYPE_LOGIN);
}
/**
* Enqueue scripts in customize (not preview!)
*/
public function customize_controls_print_scripts()
{
$this->enqueue_scripts_and_styles(Constants::ASSETS_TYPE_CUSTOMIZE);
}
/**
* The function and mechanism of wp_set_script_translations() is great of course. Unfortunately
* popular plugins like WP Rocket and Divi are not compatible with it (especially page builders
* and caching plugins). Why? Shortly explained, the injected inline scripts relies on `wp.i18n`
* which can be deferred or async loaded (the script itself) -> wp is not defined.
*
* In factory i18n.tsx the `window.wpi18nLazy` is automatically detected and the plugin gets localized.
*
* @param string $handle
* @param string $domain
* @param string $path
* @see https://developer.wordpress.org/reference/classes/wp_scripts/print_translations/
* @see https://developer.wordpress.org/reference/functions/wp_set_script_translations/
* @see https://app.clickup.com/t/3mjh0e
*/
public function setLazyScriptTranslations($handle, $domain, $path)
{
\add_filter('load_script_textdomain_relative_path', [$this, 'load_script_textdomain_relative_path']);
\add_filter('load_script_translation_file', [$this, 'load_script_translation_file']);
$json_translations = \load_script_textdomain($handle, $domain, PackageLocalization::getParentLanguageFolder($path));
\remove_filter('load_script_textdomain_relative_path', [$this, 'load_script_textdomain_relative_path']);
\remove_filter('load_script_translation_file', [$this, 'load_script_translation_file']);
if (!empty($json_translations)) {
$output = <<getPluginConstant(Constants::PLUGIN_CONST_ROOT_SLUG) . '-', '/', $file);
}
/**
* Force the basename for the md5 of a loaded JSON translation file.
*
* @param string $src
* @see https://github.com/wp-cli/i18n-command/issues/177#issuecomment-523759266
*/
public function load_script_textdomain_relative_path($src)
{
return \basename($src);
}
/**
* Get the cachebuster entry for a given file. If the $src begins with public/lib/ it
* will use the inc/base/others/cachebuster-lib.php cachebuster instead of inc/base/others/cachebuster.php.
*
* @param string $src The src relative to public/ folder
* @param boolean $isLib If true the cachebuster-lib.php cachebuster is used
* @param string $default
* @return string _VERSION or cachebuster timestamp
*/
public function getCachebusterVersion($src, $isLib = \false, $default = null)
{
$default = $default ?? $this->getPluginConstant(Constants::PLUGIN_CONST_VERSION);
$path = $this->getPluginConstant(Constants::PLUGIN_CONST_INC) . '/base/others/';
$path_lib = $path . 'cachebuster-lib.php';
$path = $path . 'cachebuster.php';
if ($isLib) {
// Library cachebuster
if (\file_exists($path_lib)) {
static $cachebuster_lib = null;
if ($cachebuster_lib === null) {
$cachebuster_lib = (include $path_lib);
}
// Parse module
\preg_match('/^public\\/lib\\/([^\\/]+)/', $src, $matches);
if (\is_array($matches) && isset($matches[1]) && \is_array($cachebuster_lib) && \array_key_exists($matches[1], $cachebuster_lib)) {
// Valid cachebuster
return $cachebuster_lib[$matches[1]];
}
}
} else {
// Main cachebuster
if (\file_exists($path)) {
// Store cachebuster once
static $cachebuster = null;
if ($cachebuster === null) {
$cachebuster = (include $path);
}
// Prepend src/ because the cachebuster task prefixes it
$src = 'src/' . $src;
if (\is_array($cachebuster) && \array_key_exists($src, $cachebuster)) {
// Valid cachebuster
return $cachebuster[$src];
}
}
}
return $default;
}
/**
* Make a localized array anonymous. Some plugins like WP Rocket tries to lazy load also localized scripts
* and this should be avoided in some scenarios like Real Cookie Banners' banner script.
* Use this instead of `wp_localize_script`.
*
* Settings:
*
* ```
* string[] makeBase64Encoded List of keys of the array object which should be converted to base64 at output time (e.g. to avoid ModSecurity issues)
* boolean useCore Use `wp_localize_script` internally instead of custom localize script
* string[] lazyParse A list of pathes of the array which should be lazy parsed. This could be useful to improve performance and parse as needed (e.g. huge arrays).
* ```
*
* @param string $handle Name of the script to attach data to.
* @param string $object_name Name of the variable that will contain the data.
* @param array $l10n Array of data to localize.
* @param array $settings
* @see https://docs.wp-rocket.me/article/1349-delay-javascript-execution#technical
* @see https://developer.wordpress.org/reference/functions/wp_localize_script/
*/
public function anonymous_localize_script($handle, $object_name, $l10n, $settings = [])
{
$settings = \wp_parse_args($settings, ['makeBase64Encoded' => [], 'useCore' => \false, 'lazyParse' => []]);
if ($settings['useCore']) {
return \wp_localize_script($handle, $object_name, $l10n);
}
$makeBase64Encoded = $settings['makeBase64Encoded'];
$lazyParse = $settings['lazyParse'];
// Mark the script tag with some identifier, so our helper script (added below) can read
// the JSON content. See also about: https://stackoverflow.com/q/12090883/5506547
// Do not use a randomized string as it can lead to issues with cached web pages when the
// inline scripts gets somehow into a combined file and the HTML is served not statically.
$uuid = \wp_generate_uuid4();
$uuid = \md5(\sprintf('%s:%s:%s', $handle, $object_name, $this->getPluginConstant(Constants::PLUGIN_CONST_VERSION)));
$base64Marker = 'base64-encoded:';
\add_filter('script_loader_tag', function ($tag, $scriptHandle) use($handle, $uuid, $l10n, $object_name, $makeBase64Encoded, $base64Marker, $lazyParse) {
if ($scriptHandle === $handle) {
if (\count($makeBase64Encoded) > 0) {
\array_walk_recursive($l10n, function (&$val, $key) use($makeBase64Encoded, $base64Marker) {
if (\in_array($key, $makeBase64Encoded, \true) && \is_string($val) && !empty($val)) {
$val = \sprintf('%s%s', $base64Marker, \base64_encode($val));
}
});
}
$foundLazyParse = [];
// Only work with pathes which got converted successfully
foreach ($lazyParse as $arrayPath) {
Utils::arrayModifyByKeyPath($l10n, $arrayPath, function ($value) use($arrayPath, &$foundLazyParse) {
$foundLazyParse[] = $arrayPath;
return \json_encode($value);
});
}
/*
(() => {
var receiver = %5$s;
var createLazyParseProxy = (obj, key) => new Proxy(obj, {
get: (target, property) => {
let value = Reflect.get(target, property);
if (property === key && typeof value === "string") {
value = JSON.parse(value, receiver);
Reflect.set(target, property, value);
}
return value;
}
});
var o = /* document.write * / JSON.parse(document.getElementById("a%1$s1-js-extra").innerHTML, receiver);
%6$s
window.%3$s = o;
const randomId = Math.random().toString(36).substring(2);
window[randomId] = n;
})();
*/
$tag = \sprintf('
', $uuid, \wp_json_encode($l10n), $object_name, \join(' ', [
// TODO: shouldn't this be part of @devowl-wp/cache-invalidate?
// Compatibility with most caching plugins which lazy load JavaScript
'data-skip-lazy-load="js-extra"',
// Compatibility with WP Fastest Cache and "Eliminate render blocking script"
// as WPFC is moving all scripts (even with `type="text/plain"`).
'data-skip-moving="true"',
// Compatibility with LiteSpeed Cache and do not delay this inline script
// See https://github.com/litespeedtech/lscache_wp/blob/6c95240003b89ef1d4ce190f5a96eba83528cd89/src/optimize.cls.php#L903
'data-no-defer',
// Compatibility with NitroPack
'nitro-exclude',
// Compatibility with WP Rocket as the filter `rocket_defer_inline_exclusions` does only check on `