From 71bb4e9ec07e57992c234b3cd83aa688e200c37b Mon Sep 17 00:00:00 2001 From: David Arenas Date: Wed, 31 Jan 2024 12:21:54 +0100 Subject: [PATCH 1/8] Create `initialVdom` --- packages/interactivity/src/init.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/interactivity/src/init.js b/packages/interactivity/src/init.js index 839302c4f8c6b..fb510a9c00fce 100644 --- a/packages/interactivity/src/init.js +++ b/packages/interactivity/src/init.js @@ -28,6 +28,9 @@ function yieldToMain() { } ); } +// Initial vDOM regions associated with its DOM element. +export const initialVdom = new WeakMap(); + // Initialize the router with the initial DOM. export const init = async () => { const nodes = document.querySelectorAll( @@ -39,6 +42,7 @@ export const init = async () => { await yieldToMain(); const fragment = getRegionRootFragment( node ); const vdom = toVdom( node ); + initialVdom.set( node, vdom ); await yieldToMain(); hydrate( vdom, fragment ); } From abdd0e1811b16563a0eeae00157da526c5d6e0bf Mon Sep 17 00:00:00 2001 From: David Arenas Date: Wed, 31 Jan 2024 12:23:36 +0100 Subject: [PATCH 2/8] Expose private APIs with a simple funciton --- packages/interactivity/src/index.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/interactivity/src/index.js b/packages/interactivity/src/index.js index 5d9165dc9920e..1bc1c88031cbd 100644 --- a/packages/interactivity/src/index.js +++ b/packages/interactivity/src/index.js @@ -2,7 +2,9 @@ * Internal dependencies */ import registerDirectives from './directives'; -import { init } from './init'; +import { init, getRegionRootFragment, initialVdom } from './init'; +import { directivePrefix } from './constants'; +import { toVdom } from './vdom'; export { store } from './store'; export { directive, getContext, getElement, getNamespace } from './hooks'; @@ -15,14 +17,27 @@ export { useCallback, useMemo, } from './utils'; -export { directivePrefix } from './constants'; -export { toVdom } from './vdom'; -export { getRegionRootFragment } from './init'; export { h as createElement, cloneElement, render } from 'preact'; export { useContext, useState, useRef } from 'preact/hooks'; export { deepSignal } from 'deepsignal'; +const requiredConsent = + 'I know using unstable features means my theme or plugin will inevitably break in the next version of WordPress.'; + +export const privateApis = ( lock ) => { + if ( lock === requiredConsent ) { + return { + directivePrefix, + getRegionRootFragment, + initialVdom, + toVdom, + }; + } + + throw new Error( 'TODO: Add a more appropriate message.' ); +}; + document.addEventListener( 'DOMContentLoaded', async () => { registerDirectives(); await init(); From c96b8ee497c34f311b9c7a8da25f2653b6048f4f Mon Sep 17 00:00:00 2001 From: David Arenas Date: Wed, 31 Jan 2024 12:23:50 +0100 Subject: [PATCH 3/8] Fix typescript --- packages/interactivity/src/vdom.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/interactivity/src/vdom.js b/packages/interactivity/src/vdom.js index 4a7cfff9f9d0d..01cf58ba00479 100644 --- a/packages/interactivity/src/vdom.js +++ b/packages/interactivity/src/vdom.js @@ -35,7 +35,12 @@ const nsPathRegExp = /^([\w-_\/]+)::(.+)$/; export const hydratedIslands = new WeakSet(); -// Recursive function that transforms a DOM tree into vDOM. +/** + * Recursive function that transforms a DOM tree into vDOM. + * + * @param {Node} root The root element or node to start traversing on. + * @return {import('preact').VNode[]} The resulting vDOM tree. + */ export function toVdom( root ) { const treeWalker = document.createTreeWalker( root, From c9d1ca7e4094920993a1214f0c342503c3623e2a Mon Sep 17 00:00:00 2001 From: David Arenas Date: Wed, 31 Jan 2024 12:24:39 +0100 Subject: [PATCH 4/8] Use `initialVdom` to cache the first page. --- packages/interactivity-router/src/index.js | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/interactivity-router/src/index.js b/packages/interactivity-router/src/index.js index a985ed3d74b89..6da3130563008 100644 --- a/packages/interactivity-router/src/index.js +++ b/packages/interactivity-router/src/index.js @@ -1,13 +1,12 @@ /** * WordPress dependencies */ -import { - render, - directivePrefix, - toVdom, - getRegionRootFragment, - store, -} from '@wordpress/interactivity'; +import { render, store, privateApis } from '@wordpress/interactivity'; + +const { directivePrefix, getRegionRootFragment, initialVdom, toVdom } = + privateApis( + 'I know using unstable features means my theme or plugin will inevitably break in the next version of WordPress.' + ); // The cache of visited and prefetched pages. const pages = new Map(); @@ -36,12 +35,14 @@ const fetchPage = async ( url, { html } ) => { // Return an object with VDOM trees of those HTML regions marked with a // `router-region` directive. -const regionsToVdom = ( dom ) => { +const regionsToVdom = ( dom, { vdom } = {} ) => { const regions = {}; const attrName = `data-${ directivePrefix }-router-region`; dom.querySelectorAll( `[${ attrName }]` ).forEach( ( region ) => { const id = region.getAttribute( attrName ); - regions[ id ] = toVdom( region ); + regions[ id ] = vdom?.has( region ) + ? vdom.get( region ) + : toVdom( region ); } ); const title = dom.querySelector( 'title' )?.innerText; return { regions, title }; @@ -74,10 +75,10 @@ window.addEventListener( 'popstate', async () => { } } ); -// Cache the current regions. +// Cache the initial page using the intially parsed vDOM. pages.set( getPagePath( window.location ), - Promise.resolve( regionsToVdom( document ) ) + Promise.resolve( regionsToVdom( document, { vdom: initialVdom } ) ) ); // Variable to store the current navigation. From 550835d84cd0fd8c73731857574efa1351de8be9 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Wed, 31 Jan 2024 12:25:51 +0100 Subject: [PATCH 5/8] Fix the Query block when navigating --- packages/block-library/src/query/view.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/block-library/src/query/view.js b/packages/block-library/src/query/view.js index dc82d7968dad4..ee811b4b8e90f 100644 --- a/packages/block-library/src/query/view.js +++ b/packages/block-library/src/query/view.js @@ -23,7 +23,9 @@ store( 'core/query', { *navigate( event ) { const ctx = getContext(); const { ref } = getElement(); - const { queryRef } = ctx; + const queryRef = ref.closest( + '.wp-block-query[data-wp-router-region]' + ); const isDisabled = queryRef?.dataset.wpNavigationDisabled; if ( isValidLink( ref ) && isValidEvent( event ) && ! isDisabled ) { @@ -41,8 +43,10 @@ store( 'core/query', { } }, *prefetch() { - const { queryRef } = getContext(); const { ref } = getElement(); + const queryRef = ref.closest( + '.wp-block-query[data-wp-router-region]' + ); const isDisabled = queryRef?.dataset.wpNavigationDisabled; if ( isValidLink( ref ) && ! isDisabled ) { const { actions } = yield import( @@ -63,10 +67,5 @@ store( 'core/query', { yield actions.prefetch( ref.href ); } }, - setQueryRef() { - const ctx = getContext(); - const { ref } = getElement(); - ctx.queryRef = ref; - }, }, } ); From fa038c1919bbc62dc9c6d55dcea47aac4a46138f Mon Sep 17 00:00:00 2001 From: David Arenas Date: Fri, 2 Feb 2024 16:57:10 +0100 Subject: [PATCH 6/8] Update the required consent text Co-authored-by: Luis Herranz --- packages/interactivity/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interactivity/src/index.js b/packages/interactivity/src/index.js index 1bc1c88031cbd..2d018abca3c99 100644 --- a/packages/interactivity/src/index.js +++ b/packages/interactivity/src/index.js @@ -23,7 +23,7 @@ export { useContext, useState, useRef } from 'preact/hooks'; export { deepSignal } from 'deepsignal'; const requiredConsent = - 'I know using unstable features means my theme or plugin will inevitably break in the next version of WordPress.'; + 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'; export const privateApis = ( lock ) => { if ( lock === requiredConsent ) { From dad62014eb49263cbceaa2b17b378a3a9f8a0221 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Fri, 2 Feb 2024 16:57:31 +0100 Subject: [PATCH 7/8] Change the error text Co-authored-by: Luis Herranz --- packages/interactivity/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interactivity/src/index.js b/packages/interactivity/src/index.js index 2d018abca3c99..477b90db1efc1 100644 --- a/packages/interactivity/src/index.js +++ b/packages/interactivity/src/index.js @@ -35,7 +35,7 @@ export const privateApis = ( lock ) => { }; } - throw new Error( 'TODO: Add a more appropriate message.' ); + throw new Error( 'Forbidden access.' ); }; document.addEventListener( 'DOMContentLoaded', async () => { From f03e339f4c4fbf00291b9eb3a48a44a59f8f4988 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Fri, 2 Feb 2024 16:57:58 +0100 Subject: [PATCH 8/8] Update consent text when calling privateApis Co-authored-by: Luis Herranz --- packages/interactivity-router/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interactivity-router/src/index.js b/packages/interactivity-router/src/index.js index 6da3130563008..46f184b4ec030 100644 --- a/packages/interactivity-router/src/index.js +++ b/packages/interactivity-router/src/index.js @@ -5,7 +5,7 @@ import { render, store, privateApis } from '@wordpress/interactivity'; const { directivePrefix, getRegionRootFragment, initialVdom, toVdom } = privateApis( - 'I know using unstable features means my theme or plugin will inevitably break in the next version of WordPress.' + 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.' ); // The cache of visited and prefetched pages.