Skip to content
This repository has been archived by the owner on Jul 28, 2023. It is now read-only.

Commit

Permalink
Merge pull request #142 from WordPress/define-prefixes-as-constants
Browse files Browse the repository at this point in the history
Create constants file for `wp-` prefixes
  • Loading branch information
DAreRodz authored Feb 2, 2023
2 parents bb6abaa + 4990097 commit e640394
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 37 deletions.
10 changes: 6 additions & 4 deletions src/runtime/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ import { deepSignal } from 'deepsignal';
import { component } from './hooks';

export default () => {
const WpContext = ({ children, data, context: { Provider } }) => {
// <wp-context>
const Context = ({ children, data, context: { Provider } }) => {
const signals = useMemo(() => deepSignal(JSON.parse(data)), [data]);
return <Provider value={signals}>{children}</Provider>;
};
component('wp-context', WpContext);
component('context', Context);

const WpShow = ({ children, when, evaluate, context }) => {
// <wp-show>
const Show = ({ children, when, evaluate, context }) => {
const contextValue = useContext(context);
if (evaluate(when, { context: contextValue })) {
return children;
} else {
return <template>{children}</template>;
}
};
component('wp-show', WpShow);
component('show', Show);
};
3 changes: 3 additions & 0 deletions src/runtime/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const cstMetaTagItemprop = 'wp-client-side-transitions';
export const componentPrefix = 'wp-';
export const directivePrefix = 'wp-';
34 changes: 19 additions & 15 deletions src/runtime/hooks.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { h, options, createContext } from 'preact';
import { useRef } from 'preact/hooks';
import { store } from './wpx';
import { componentPrefix } from './constants';

// Main context.
const context = createContext({});

// WordPress Directives.
const directives = {};
const directiveMap = {};
export const directive = (name, cb) => {
directives[name] = cb;
directiveMap[name] = cb;
};

// WordPress Components.
const components = {};
const componentMap = {};
export const component = (name, Comp) => {
components[name] = Comp;
componentMap[name] = Comp;
};

// Resolve the path to some property of the wpx object.
Expand All @@ -39,15 +40,15 @@ const getEvaluate =
};

// Directive wrapper.
const WpDirective = ({ type, wp, props: originalProps }) => {
const Directive = ({ type, directives, props: originalProps }) => {
const ref = useRef(null);
const element = h(type, { ...originalProps, ref, _wrapped: true });
const props = { ...originalProps, children: element };
const evaluate = getEvaluate({ ref: ref.current });
const directiveArgs = { directives: wp, props, element, context, evaluate };
const directiveArgs = { directives, props, element, context, evaluate };

for (const d in wp) {
const wrapper = directives[d]?.(directiveArgs);
for (const d in directives) {
const wrapper = directiveMap[d]?.(directiveArgs);
if (wrapper !== undefined) props.children = wrapper;
}

Expand All @@ -58,20 +59,23 @@ const WpDirective = ({ type, wp, props: originalProps }) => {
const old = options.vnode;
options.vnode = (vnode) => {
const type = vnode.type;
const wp = vnode.props.wp;
const { directives } = vnode.props;

if (typeof type === 'string' && type.startsWith('wp-')) {
if (
typeof type === 'string' &&
type.slice(0, componentPrefix.length) === componentPrefix
) {
vnode.props.children = h(
components[type],
componentMap[type.slice(componentPrefix.length)],
{ ...vnode.props, context, evaluate: getEvaluate() },
vnode.props.children
);
} else if (wp) {
} else if (directives) {
const props = vnode.props;
delete props.wp;
delete props.directives;
if (!props._wrapped) {
vnode.props = { type: vnode.type, wp, props };
vnode.type = WpDirective;
vnode.props = { type: vnode.type, directives, props };
vnode.type = Directive;
} else {
delete props._wrapped;
}
Expand Down
19 changes: 11 additions & 8 deletions src/runtime/router.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { hydrate, render } from 'preact';
import { toVdom, hydratedIslands } from './vdom';
import { createRootFragment } from './utils';
import { cstMetaTagItemprop, directivePrefix } from './constants';

// The root to render the vdom (document.body).
let rootFragment;
Expand All @@ -19,7 +20,7 @@ const cleanUrl = (url) => {
// Helper to check if a page has client-side transitions activated.
export const hasClientSideTransitions = (dom) =>
dom
.querySelector("meta[itemprop='wp-client-side-transitions']")
.querySelector(`meta[itemprop='${cstMetaTagItemprop}']`)
?.getAttribute('content') === 'active';

// Fetch styles of a new page.
Expand Down Expand Up @@ -107,12 +108,14 @@ export const init = async () => {
const head = await fetchHead(document.head);
pages.set(cleanUrl(window.location), Promise.resolve({ body, head }));
} else {
document.querySelectorAll('[wp-island]').forEach((node) => {
if (!hydratedIslands.has(node)) {
const fragment = createRootFragment(node.parentNode, node);
const vdom = toVdom(node);
hydrate(vdom, fragment);
}
});
document
.querySelectorAll(`[${directivePrefix}island]`)
.forEach((node) => {
if (!hydratedIslands.has(node)) {
const fragment = createRootFragment(node.parentNode, node);
const vdom = toVdom(node);
hydrate(vdom, fragment);
}
});
}
};
25 changes: 15 additions & 10 deletions src/runtime/vdom.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { h } from 'preact';
import { directivePrefix as p } from './constants';

const ignoreAttr = `${p}ignore`;
const islandAttr = `${p}island`;
const directiveParser = new RegExp(`${p}([^:]+):?(.*)$`);

export const hydratedIslands = new WeakSet();

// Recursive function that transfoms a DOM tree into vDOM.
export function toVdom(node) {
const props = {};
const { attributes, childNodes } = node;
const wpDirectives = {};
let hasWpDirectives = false;
const directives = {};
let hasDirectives = false;
let ignore = false;
let island = false;

Expand All @@ -19,20 +24,20 @@ export function toVdom(node) {

for (let i = 0; i < attributes.length; i++) {
const n = attributes[i].name;
if (n[0] === 'w' && n[1] === 'p' && n[2] === '-' && n[3]) {
if (n === 'wp-ignore') {
if (n[p.length] && n.slice(0, p.length) === p) {
if (n === ignoreAttr) {
ignore = true;
} else if (n === 'wp-island') {
} else if (n === islandAttr) {
island = true;
} else {
hasWpDirectives = true;
hasDirectives = true;
let val = attributes[i].value;
try {
val = JSON.parse(val);
} catch (e) {}
const [, prefix, suffix] = /wp-([^:]+):?(.*)$/.exec(n);
wpDirectives[prefix] = wpDirectives[prefix] || {};
wpDirectives[prefix][suffix || 'default'] = val;
const [, prefix, suffix] = directiveParser.exec(n);
directives[prefix] = directives[prefix] || {};
directives[prefix][suffix || 'default'] = val;
}
} else if (n === 'ref') {
continue;
Expand All @@ -47,7 +52,7 @@ export function toVdom(node) {
});
if (island) hydratedIslands.add(node);

if (hasWpDirectives) props.wp = wpDirectives;
if (hasDirectives) props.directives = directives;

const children = [];
for (let i = 0; i < childNodes.length; i++) {
Expand Down

0 comments on commit e640394

Please sign in to comment.