: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* where they are defined, or added to the main context when they don't exist.
* By default, all plain objects inside the context are wrapped, unless it is
* listed in the `ignore` option.
* @param current Current context.
* @param inherited Inherited context, used as fallback.
* @return The wrapped context object.
const proxifyContext = (current, inherited = {}) => {
// Update the fallback object reference when it changes.
contextObjectToFallback.set(current, inherited);
if (!contextObjectToProxy.has(current)) {
const proxy = new Proxy(current, {
const fallback = contextObjectToFallback.get(current);
// Always subscribe to prop changes in the current context.
const currentProp = target[k];
// Return the inherited prop when missing in target.
if (!(k in target) && k in fallback) {
// Proxify plain objects that were not directly assigned.
if (k in target && !contextAssignedObjects.get(target)?.has(k) && isPlainObject(deepsignal_module_p(target, k))) {
return proxifyContext(currentProp, fallback[k]);
// Return the stored proxy for `currentProp` when it exists.
if (contextObjectToProxy.has(currentProp)) {
return contextObjectToProxy.get(currentProp);
* For other cases, return the value from target, also
* subscribing to changes in the parent context when the current
return k in target ? currentProp : fallback[k];
set: (target, k, value) => {
const fallback = contextObjectToFallback.get(current);
const obj = k in target || !(k in fallback) ? target : fallback;
* Assigned object values should not be proxified so they point
* to the original object and don't inherit unexpected
if (value && typeof value === 'object') {
if (!contextAssignedObjects.has(obj)) {
contextAssignedObjects.set(obj, new Set());
contextAssignedObjects.get(obj).add(k);
* When the value is a proxy, it's because it comes from the
* context, so the inner value is assigned instead.
if (contextProxyToObject.has(value)) {
const innerValue = contextProxyToObject.get(value);
ownKeys: target => [...new Set([...Object.keys(contextObjectToFallback.get(current)), ...Object.keys(target)])],
getOwnPropertyDescriptor: (target, k) => descriptor(target, k) || descriptor(contextObjectToFallback.get(current), k)
contextObjectToProxy.set(current, proxy);
contextProxyToObject.set(proxy, current);
return contextObjectToProxy.get(current);
* Recursively update values within a deepSignal object.
* @param target A deepSignal instance.
* @param source Object with properties to update in `target`.
const updateSignals = (target, source) => {
for (const k in source) {
if (isPlainObject(deepsignal_module_p(target, k)) && isPlainObject(deepsignal_module_p(source, k))) {
updateSignals(target[`$${k}`].peek(), source[k]);
* Recursively clone the passed object.
* @param source Source object.
function deepClone(source) {
if (isPlainObject(source)) {
return Object.fromEntries(Object.entries(source).map(([key, value]) => [key, deepClone(value)]));
if (Array.isArray(source)) {
return source.map(i => deepClone(i));
const newRule = /(?:([\u0080-\uFFFF\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}\s*)/g;
const ruleClean = /\/\*[^]*?\*\/| +/g;
const ruleNewline = /\n+/g;
* Convert a css style string into a object.
* Made by Cristian Bote (@cristianbote) for Goober.
* https://unpkg.com/browse/goober@2.1.13/src/core/astish.js
const cssStringToObject = val => {
while (block = newRule.exec(val.replace(ruleClean, ''))) {
left = block[3].replace(ruleNewline, empty).trim();
tree.unshift(tree[0][left] = tree[0][left] || {});
tree[0][block[1]] = block[2].replace(ruleNewline, empty).trim();
* Creates a directive that adds an event listener to the global window or
* @param type 'window' or 'document'
const getGlobalEventDirective = type => {
directives[`on-${type}`].filter(({
}) => suffix !== 'default').forEach(entry => {
const eventName = entry.suffix.split('--', 1)[0];
const cb = event => evaluate(entry, event);
const globalVar = type === 'window' ? window : document;
globalVar.addEventListener(eventName, cb);
return () => globalVar.removeEventListener(eventName, cb);
* Creates a directive that adds an async event listener to the global window or
* @param type 'window' or 'document'
const getGlobalAsyncEventDirective = type => {
directives[`on-async-${type}`].filter(({
}) => suffix !== 'default').forEach(entry => {
const eventName = entry.suffix.split('--', 1)[0];
const cb = async event => {
const globalVar = type === 'window' ? window : document;
globalVar.addEventListener(eventName, cb, {
return () => globalVar.removeEventListener(eventName, cb);
/* harmony default export */ const directives = (() => {
context: inheritedContext
const inheritedValue = hooks_module_P(inheritedContext);
const currentValue = hooks_module_F(deepsignal_module_g({}));
const defaultEntry = context.find(({
}) => suffix === 'default');
// No change should be made if `defaultEntry` does not exist.
const contextStack = hooks_module_q(() => {
// Check that the value is a JSON object. Send a console warning if not.
if (!isPlainObject(value)) {
warn(`The value of data-wp-context in "${namespace}" store must be a valid stringified JSON object.`);
updateSignals(currentValue.current, {
[namespace]: deepClone(value)
return proxifyContext(currentValue.current, inheritedValue);
}, [defaultEntry, inheritedValue]);
useWatch(() => evaluate(entry));
// TODO: Replace with useEffect to prevent unneeded scopes.
useInit(() => evaluate(entry));
const events = new Map();
}) => suffix !== 'default').forEach(entry => {
const event = entry.suffix.split('--')[0];
if (!events.has(event)) {
events.set(event, new Set());
events.get(event).add(entry);
events.forEach((entries, eventType) => {
const existingHandler = element.props[`on${eventType}`];
element.props[`on${eventType}`] = event => {
entries.forEach(entry => {
// data-wp-on-async--[event]
const events = new Map();
}) => suffix !== 'default').forEach(entry => {
const event = entry.suffix.split('--')[0];
if (!events.has(event)) {
events.set(event, new Set());
events.get(event).add(entry);
events.forEach((entries, eventType) => {
const existingHandler = element.props[`on${eventType}`];
element.props[`on${eventType}`] = event => {
entries.forEach(async entry => {
// data-wp-on-window--[event]
directive('on-window', getGlobalEventDirective('window'));
// data-wp-on-document--[event]
directive('on-document', getGlobalEventDirective('document'));
// data-wp-on-async-window--[event]
directive('on-async-window', getGlobalAsyncEventDirective('window'));
// data-wp-on-async-document--[event]
directive('on-async-document', getGlobalAsyncEventDirective('document'));
// data-wp-class--[classname]
}) => suffix !== 'default').forEach(entry => {
const className = entry.suffix;
const result = evaluate(entry);
const currentClass = element.props.class || '';
const classFinder = new RegExp(`(^|\\s)${className}(\\s|$)`, 'g');
element.props.class = currentClass.replace(classFinder, ' ').trim();
} else if (!classFinder.test(currentClass)) {
element.props.class = currentClass ? `${currentClass} ${className}` : className;
* This seems necessary because Preact doesn't change the class
* names on the hydration, so we have to do it manually. It doesn't
* need deps because it only needs to do it the first time.
element.ref.current.classList.remove(className);
element.ref.current.classList.add(className);
// data-wp-style--[style-prop]
}) => suffix !== 'default').forEach(entry => {
const styleProp = entry.suffix;
const result = evaluate(entry);
element.props.style = element.props.style || {};
if (typeof element.props.style === 'string') {
element.props.style = cssStringToObject(element.props.style);
delete element.props.style[styleProp];
element.props.style[styleProp] = result;
* This seems necessary because Preact doesn't change the styles on
* the hydration, so we have to do it manually. It doesn't need deps
* because it only needs to do it the first time.
element.ref.current.style.removeProperty(styleProp);
element.ref.current.style[styleProp] = result;
// data-wp-bind--[attribute]
}) => suffix !== 'default').forEach(entry => {
const attribute = entry.suffix;
const result = evaluate(entry);
element.props[attribute] = result;
* This is necessary because Preact doesn't change the attributes on the
* hydration, so we have to do it manually. It only needs to do it the
* first time. After that, Preact will handle the changes.
const el = element.ref.current;
* We set the value directly to the corresponding HTMLElement instance
* property excluding the following special cases. We follow Preact's
* logic: https://github.com/preactjs/preact/blob/ea49f7a0f9d1ff2c98c0bdd66aa0cbc583055246/src/diff/props.js#L110-L129
if (attribute === 'style') {
if (typeof result === 'string') {
el.style.cssText = result;
} else if (attribute !== 'width' && attribute !== 'height' && attribute !== 'href' && attribute !== 'list' && attribute !== 'form' &&
* The value for `tabindex` follows the parsing rules for an
* integer. If that fails, or if the attribute isn't present, then
* the browsers should "follow platform conventions to determine if
* the element should be considered as a focusable area",
* practically meaning that most elements get a default of `-1` (not
* focusable), but several also get a default of `0` (focusable in
* order after all elements with a positive `tabindex` value).
* @see https://html.spec.whatwg.org/#tabindex-value
attribute !== 'tabIndex' && attribute !== 'download' && attribute !== 'rowSpan' && attribute !== 'colSpan' && attribute !== 'role' && attribute in el) {
el[attribute] = result === null || result === undefined ? '' : result;
* aria- and data- attributes have no boolean representation.
* A `false` value is different from the attribute not being
* present, so we can't remove it.
* We follow Preact's logic: https://github.com/preactjs/preact/blob/ea49f7a0f9d1ff2c98c0bdd66aa0cbc583055246/src/diff/props.js#L131C24-L136
if (result !== null && result !== undefined && (result !== false || attribute[4] === '-')) {
el.setAttribute(attribute, result);
el.removeAttribute(attribute);
// Preserve the initial inner HTML.
const cached = hooks_module_q(() => innerHTML, []);
dangerouslySetInnerHTML: {