Skip to content

Commit

Permalink
Merge branch 'add/strict-null-interactivity-types' into add/interacti…
Browse files Browse the repository at this point in the history
…vity-type-checking
  • Loading branch information
sirreal committed Apr 10, 2024
2 parents 29e65b8 + b71e897 commit 9472ea4
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 33 deletions.
5 changes: 5 additions & 0 deletions packages/interactivity/src/directives.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,11 @@ export default () => {
// data-wp-text
directive( 'text', ( { directives: { text }, element, evaluate } ) => {
const entry = text.find( ( { suffix } ) => suffix === 'default' );
if ( ! entry ) {
element.props.children = null;
return;
}

try {
const result = evaluate( entry );
element.props.children =
Expand Down
2 changes: 1 addition & 1 deletion packages/interactivity/src/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ interface DirectiveOptions {

interface Scope {
evaluate: Evaluate;
context?: object;
context: object;
ref: RefObject< HTMLElement >;
attributes: createElement.JSX.HTMLAttributes;
}
Expand Down
6 changes: 3 additions & 3 deletions packages/interactivity/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from './hooks';

const isObject = ( item: unknown ): item is Record< string, unknown > =>
item && typeof item === 'object' && item.constructor === Object;
Boolean( item && typeof item === 'object' && item.constructor === Object );

const deepMerge = ( target: any, source: any ) => {
if ( isObject( target ) && isObject( source ) ) {
Expand Down Expand Up @@ -332,12 +332,12 @@ export const populateInitialData = ( data?: {
config?: Record< string, unknown >;
} ) => {
if ( isObject( data?.state ) ) {
Object.entries( data.state ).forEach( ( [ namespace, state ] ) => {
Object.entries( data!.state ).forEach( ( [ namespace, state ] ) => {
store( namespace, { state }, { lock: universalUnlock } );
} );
}
if ( isObject( data?.config ) ) {
Object.entries( data.config ).forEach( ( [ namespace, config ] ) => {
Object.entries( data!.config ).forEach( ( [ namespace, config ] ) => {
storeConfigs.set( namespace, config );
} );
}
Expand Down
6 changes: 3 additions & 3 deletions packages/interactivity/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const afterNextFrame = ( callback: () => void ) => {
* @return The Flusher object with `flush` and `dispose` properties.
*/
function createFlusher( compute: () => unknown, notify: () => void ): Flusher {
let flush: () => void;
let flush: () => void = () => undefined;
const dispose = effect( function ( this: any ) {
flush = this.c.bind( this );
this.x = compute;
Expand All @@ -82,7 +82,7 @@ function createFlusher( compute: () => unknown, notify: () => void ): Flusher {
*/
export function useSignalEffect( callback: () => unknown ) {
_useEffect( () => {
let eff = null;
let eff: Flusher | null = null;
let isExecuting = false;

const notify = async () => {
Expand Down Expand Up @@ -271,7 +271,7 @@ export const createRootFragment = (
parent: Element,
replaceNode: Node | Node[]
) => {
replaceNode = [].concat( replaceNode );
replaceNode = ( [] as Node[] ).concat( replaceNode );
const sibling = replaceNode[ replaceNode.length - 1 ].nextSibling;
function insert( child: any, root: any ) {
parent.insertBefore( child, root || sibling );
Expand Down
80 changes: 55 additions & 25 deletions packages/interactivity/src/vdom.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
/**
* External dependencies
*/
import { h } from 'preact';
import { h, type VNode, type JSX } from 'preact';
/**
* Internal dependencies
*/
import { directivePrefix as p } from './constants';

type TreeWalkerReturn = string | Node | VNode< any > | null;

const ignoreAttr = `data-${ p }-ignore`;
const islandAttr = `data-${ p }-interactive`;
const fullPrefix = `data-${ p }-`;
const namespaces = [];
const namespaces: Array< string > = [];
const currentNamespace = () => namespaces[ namespaces.length - 1 ] ?? null;

// Regular expression for directive parsing.
Expand Down Expand Up @@ -38,33 +40,49 @@ export const hydratedIslands = new WeakSet();
/**
* 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.
* @param root The root element or node to start traversing on.
* @return The resulting vDOM tree.
*/
export function toVdom( root ) {
export function toVdom( root: Node ): [ string | VNode | null, Node | null ] {
const treeWalker = document.createTreeWalker(
root,
205 // ELEMENT + TEXT + COMMENT + CDATA_SECTION + PROCESSING_INSTRUCTION
);

function walk( node ) {
const { attributes, nodeType, localName } = node;
function walk( node: Node ): [ string | VNode | null, Node | null ] {
const { nodeType } = node;

// TEXT_NODE (3)
if ( nodeType === 3 ) {
return [ ( node as Text ).data, null ];
}

if ( nodeType === 3 ) return [ node.data ];
// CDATA_SECTION_NODE (4)
if ( nodeType === 4 ) {
const next = treeWalker.nextSibling();
node.replaceWith( new window.Text( node.nodeValue ) );
( node as CDATASection ).replaceWith(
new window.Text( ( node as CDATASection ).nodeValue ?? '' )
);
return [ node.nodeValue, next ];
}

// COMMENT_NODE (8) || PROCESSING_INSTRUCTION_NODE (7)
if ( nodeType === 8 || nodeType === 7 ) {
const next = treeWalker.nextSibling();
node.remove();
( node as Comment | ProcessingInstruction ).remove();
return [ null, next ];
}

const elementNode = node as HTMLElement;

const attributes = elementNode.attributes;
const localName = elementNode.localName as keyof JSX.IntrinsicElements;

const props: Record< string, any > = {};
const children = [];
const directives = [];
const children: Array< TreeWalkerReturn > = [];
const directives: Array<
[ name: string, namespace: string | null, value: unknown ]
> = [];
let ignore = false;
let island = false;

Expand All @@ -81,14 +99,14 @@ export function toVdom( root ) {
.exec( attributes[ i ].value )
?.slice( 1 ) ?? [ null, attributes[ i ].value ];
try {
value = JSON.parse( value );
value = JSON.parse( value as string );
} catch ( e ) {}
if ( n === islandAttr ) {
island = true;
namespaces.push(
typeof value === 'string'
? value
: value?.namespace ?? null
: ( value as any )?.namespace ?? null
);
} else {
directives.push( [ n, ns, value ] );
Expand All @@ -100,22 +118,29 @@ export function toVdom( root ) {
props[ n ] = attributes[ i ].value;
}

if ( ignore && ! island )
if ( ignore && ! island ) {
return [
h( localName, {
h< any, any >( localName, {
...props,
innerHTML: node.innerHTML,
innerHTML: elementNode.innerHTML,
__directives: { ignore: true },
} ),
null,
];
if ( island ) hydratedIslands.add( node );
}

if ( island ) {
hydratedIslands.add( elementNode );
}

if ( directives.length ) {
props.__directives = directives.reduce(
( obj, [ name, ns, value ] ) => {
const [ , prefix, suffix = 'default' ] =
directiveParser.exec( name );
directiveParser.exec( name )!;

if ( ! obj[ prefix ] ) obj[ prefix ] = [];

obj[ prefix ].push( {
namespace: ns ?? currentNamespace(),
value,
Expand All @@ -127,26 +152,31 @@ export function toVdom( root ) {
);
}

// @ts-expect-error Fixed in upcoming preact release https://github.com/preactjs/preact/pull/4334
if ( localName === 'template' ) {
props.content = [ ...node.content.childNodes ].map( ( childNode ) =>
toVdom( childNode )
);
props.content = [
...( elementNode as HTMLTemplateElement ).content.childNodes,
].map( ( childNode ) => toVdom( childNode ) );
} else {
let child = treeWalker.firstChild();
if ( child ) {
while ( child ) {
const [ vnode, nextChild ] = walk( child );
if ( vnode ) children.push( vnode );
if ( vnode ) {
children.push( vnode );
}
child = nextChild || treeWalker.nextSibling();
}
treeWalker.parentNode();
}
}

// Restore previous namespace.
if ( island ) namespaces.pop();
if ( island ) {
namespaces.pop();
}

return [ h( localName, props, children ) ];
return [ h( localName, props, children ), null ];
}

return walk( treeWalker.currentNode );
Expand Down
1 change: 0 additions & 1 deletion packages/interactivity/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"rootDir": "src",
"declarationDir": "build-types",

"strictNullChecks": false,
"strictPropertyInitialization": false,

"noImplicitAny": false
Expand Down

0 comments on commit 9472ea4

Please sign in to comment.