From a1f57aca4d32c4dc18e3e4ec3e2e2b51900fadd0 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 14 Mar 2024 11:33:32 +0100 Subject: [PATCH 01/14] Strict type checking --- packages/interactivity/tsconfig.json | 11 +++++++++-- tsconfig.base.json | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/interactivity/tsconfig.json b/packages/interactivity/tsconfig.json index 0fb91956773f89..445fce42e73e54 100644 --- a/packages/interactivity/tsconfig.json +++ b/packages/interactivity/tsconfig.json @@ -4,8 +4,15 @@ "compilerOptions": { "rootDir": "src", "declarationDir": "build-types", - "checkJs": false, - "strict": false + + "alwaysStrict": false, + "strictNullChecks": false, + "strictBindCallApply": false, + "strictFunctionTypes": false, + "strictPropertyInitialization": false, + "noImplicitAny": false, + "noImplicitThis": false, + "useUnknownInCatchVariables": false }, "include": [ "src/**/*" ] } diff --git a/tsconfig.base.json b/tsconfig.base.json index 1b30522ee52366..18388b7f71854c 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -7,7 +7,7 @@ "jsx": "preserve", "target": "esnext", "module": "esnext", - "lib": [ "dom", "esnext" ], + "lib": [ "DOM", "DOM.Iterable", "ESNext" ], "declaration": true, "declarationMap": true, "composite": true, From a7e5048f11cc0d9007c706c45e7895786150fe11 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 16:09:26 +0200 Subject: [PATCH 02/14] rename to tsx --- packages/interactivity/src/{directives.js => directives.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/interactivity/src/{directives.js => directives.tsx} (100%) diff --git a/packages/interactivity/src/directives.js b/packages/interactivity/src/directives.tsx similarity index 100% rename from packages/interactivity/src/directives.js rename to packages/interactivity/src/directives.tsx From 879b740209e2bfff436cad15ba4bf732265d5df6 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 17:01:54 +0200 Subject: [PATCH 03/14] isPlainObject --- packages/interactivity/src/directives.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/interactivity/src/directives.tsx b/packages/interactivity/src/directives.tsx index b19067bdc1ad9a..780910ca66cd2c 100644 --- a/packages/interactivity/src/directives.tsx +++ b/packages/interactivity/src/directives.tsx @@ -22,8 +22,8 @@ const contextObjectToProxy = new WeakMap(); const contextProxyToObject = new WeakMap(); const contextObjectToFallback = new WeakMap(); -const isPlainObject = ( item ) => - item && typeof item === 'object' && item.constructor === Object; +const isPlainObject = ( item: unknown ): boolean => + Boolean( item && typeof item === 'object' && item.constructor === Object ); const descriptor = Reflect.getOwnPropertyDescriptor; From 361596bc7472e25e4c327bf18ba350476cccfc38 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 17:05:34 +0200 Subject: [PATCH 04/14] hooks types --- packages/interactivity/src/hooks.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/interactivity/src/hooks.tsx b/packages/interactivity/src/hooks.tsx index 00c3c0d6d1729d..bc38a67d5b85ad 100644 --- a/packages/interactivity/src/hooks.tsx +++ b/packages/interactivity/src/hooks.tsx @@ -1,5 +1,8 @@ /* @jsx createElement */ +// eslint-disable-next-line eslint-comments/disable-enable-pair +/* eslint-disable react-hooks/exhaustive-deps */ + /** * External dependencies */ @@ -17,7 +20,7 @@ import type { VNode, Context, RefObject } from 'preact'; */ import { store, stores, universalUnlock } from './store'; interface DirectiveEntry { - value: string | Object; + value: string | object; namespace: string; suffix: string; } @@ -101,7 +104,7 @@ const immutableError = () => { 'Please use `data-wp-bind` to modify the attributes of an element.' ); }; -const immutableHandlers = { +const immutableHandlers: ProxyHandler< object > = { get( target, key, receiver ) { const value = Reflect.get( target, key, receiver ); return !! value && typeof value === 'object' @@ -111,7 +114,7 @@ const immutableHandlers = { set: immutableError, deleteProperty: immutableError, }; -const deepImmutable = < T extends Object = {} >( target: T ): T => { +const deepImmutable = < T extends object = {} >( target: T ): T => { if ( ! immutableMap.has( target ) ) immutableMap.set( target, new Proxy( target, immutableHandlers ) ); return immutableMap.get( target ); @@ -258,7 +261,7 @@ export const directive = ( }; // Resolve the path to some property of the store object. -const resolve = ( path, namespace ) => { +const resolve = ( path: string, namespace: string ) => { let resolvedStore = stores.get( namespace ); if ( typeof resolvedStore === 'undefined' ) { resolvedStore = store( namespace, undefined, { From 553d5de170197b281d62eb3baba749398b247489 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 17:47:58 +0200 Subject: [PATCH 05/14] types --- packages/interactivity/src/directives.tsx | 66 +++++++++++++---------- packages/interactivity/src/hooks.tsx | 10 ++-- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/packages/interactivity/src/directives.tsx b/packages/interactivity/src/directives.tsx index 780910ca66cd2c..e2a118e163e17a 100644 --- a/packages/interactivity/src/directives.tsx +++ b/packages/interactivity/src/directives.tsx @@ -1,9 +1,12 @@ +// eslint-disable-next-line eslint-comments/disable-enable-pair +/* eslint-disable react-hooks/exhaustive-deps */ + /* @jsx createElement */ /** * External dependencies */ -import { h as createElement } from 'preact'; +import { h as createElement, type RefObject } from 'preact'; import { useContext, useMemo, useRef } from 'preact/hooks'; import { deepSignal, peek } from 'deepsignal'; @@ -36,12 +39,12 @@ const descriptor = Reflect.getOwnPropertyDescriptor; * By default, all plain objects inside the context are wrapped, unless it is * listed in the `ignore` option. * - * @param {Object} current Current context. - * @param {Object} inherited Inherited context, used as fallback. + * @param current Current context. + * @param inherited Inherited context, used as fallback. * - * @return {Object} The wrapped context object. + * @return The wrapped context object. */ -const proxifyContext = ( current, inherited = {} ) => { +const proxifyContext = ( current: object, inherited: object = {} ): object => { // Update the fallback object reference when it changes. contextObjectToFallback.set( current, inherited ); if ( ! contextObjectToProxy.has( current ) ) { @@ -126,10 +129,10 @@ const proxifyContext = ( current, inherited = {} ) => { /** * Recursively update values within a deepSignal object. * - * @param {Object} target A deepSignal instance. - * @param {Object} source Object with properties to update in `target` + * @param target A deepSignal instance. + * @param source Object with properties to update in `target` */ -const updateSignals = ( target, source ) => { +const updateSignals = ( target: object, source: object ) => { for ( const k in source ) { if ( isPlainObject( peek( target, k ) ) && @@ -145,23 +148,23 @@ const updateSignals = ( target, source ) => { /** * Recursively clone the passed object. * - * @param {Object} source Source object. - * @return {Object} Cloned object. + * @param source Source object. + * @return Cloned object. */ -const deepClone = ( source ) => { +function deepClone< T >( source: T ): T { if ( isPlainObject( source ) ) { return Object.fromEntries( - Object.entries( source ).map( ( [ key, value ] ) => [ + Object.entries( source as object ).map( ( [ key, value ] ) => [ key, deepClone( value ), ] ) - ); + ) as T; } if ( Array.isArray( source ) ) { - return source.map( ( i ) => deepClone( i ) ); + return source.map( ( i ) => deepClone( i ) ) as T; } return source; -}; +} const newRule = /(?:([\u0080-\uFFFF\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}\s*)/g; @@ -175,10 +178,10 @@ const empty = ' '; * Made by Cristian Bote (@cristianbote) for Goober. * https://unpkg.com/browse/goober@2.1.13/src/core/astish.js * - * @param {string} val CSS string. - * @return {Object} CSS object. + * @param val CSS string. + * @return CSS object. */ -const cssStringToObject = ( val ) => { +const cssStringToObject = ( val: string ): object => { const tree = [ {} ]; let block, left; @@ -202,11 +205,10 @@ const cssStringToObject = ( val ) => { * Creates a directive that adds an event listener to the global window or * document object. * - * @param {string} type 'window' or 'document' - * @return {void} + * @param type 'window' or 'document' */ const getGlobalEventDirective = - ( type ) => + ( type: 'window' | 'document' ) => ( { directives, evaluate } ) => { directives[ `on-${ type }` ] .filter( ( { suffix } ) => suffix !== 'default' ) @@ -217,7 +219,7 @@ const getGlobalEventDirective = globalVar.addEventListener( entry.suffix, cb ); return () => globalVar.removeEventListener( entry.suffix, cb ); - }, [] ); + } ); } ); }; @@ -314,9 +316,13 @@ export default () => { * need deps because it only needs to do it the first time. */ if ( ! result ) { - element.ref.current.classList.remove( className ); + ( + element.ref as RefObject< HTMLElement > + ).current!.classList.remove( className ); } else { - element.ref.current.classList.add( className ); + ( + element.ref as RefObject< HTMLElement > + ).current!.classList.add( className ); } } ); } ); @@ -345,9 +351,13 @@ export default () => { * because it only needs to do it the first time. */ if ( ! result ) { - element.ref.current.style.removeProperty( styleProp ); + ( + element.ref as RefObject< HTMLElement > + ).current!.style.removeProperty( styleProp ); } else { - element.ref.current.style[ styleProp ] = result; + ( + element.ref as RefObject< HTMLElement > + ).current!.style[ styleProp ] = result; } } ); } ); @@ -367,7 +377,8 @@ export default () => { * first time. After that, Preact will handle the changes. */ useInit( () => { - const el = element.ref.current; + const el = ( element.ref as RefObject< HTMLElement > ) + .current!; /* * We set the value directly to the corresponding HTMLElement instance @@ -476,6 +487,7 @@ export default () => { element, evaluate, } ) => { + // @ts-expect-error: template type is currently missing https://github.com/preactjs/preact/pull/4334 if ( element.type !== 'template' ) return; const { Provider } = inheritedContext; diff --git a/packages/interactivity/src/hooks.tsx b/packages/interactivity/src/hooks.tsx index bc38a67d5b85ad..be7a053ec90081 100644 --- a/packages/interactivity/src/hooks.tsx +++ b/packages/interactivity/src/hooks.tsx @@ -11,6 +11,7 @@ import { options, createContext, cloneElement, + type ComponentChildren, } from 'preact'; import { useRef, useCallback, useContext } from 'preact/hooks'; import type { VNode, Context, RefObject } from 'preact'; @@ -35,11 +36,14 @@ interface DirectiveArgs { /** * Props present in the current element. */ - props: Object; + props: { children?: ComponentChildren }; /** * Virtual node representing the element. */ - element: VNode; + element: VNode< { + class?: string; + style?: string | Record< string, string | number >; + } >; /** * The inherited context. */ @@ -52,7 +56,7 @@ interface DirectiveArgs { } interface DirectiveCallback { - ( args: DirectiveArgs ): VNode | void; + ( args: DirectiveArgs ): VNode | null; } interface DirectiveOptions { From 29e2577963d6fd0ee4a5fdb62f68841acdd2e4b5 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 18:11:32 +0200 Subject: [PATCH 06/14] types --- packages/interactivity/src/directives.tsx | 18 ++++++++++++------ packages/interactivity/src/hooks.tsx | 5 +++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/interactivity/src/directives.tsx b/packages/interactivity/src/directives.tsx index e2a118e163e17a..8e8e783ae125cb 100644 --- a/packages/interactivity/src/directives.tsx +++ b/packages/interactivity/src/directives.tsx @@ -8,7 +8,7 @@ */ import { h as createElement, type RefObject } from 'preact'; import { useContext, useMemo, useRef } from 'preact/hooks'; -import { deepSignal, peek } from 'deepsignal'; +import { deepSignal, peek, type DeepSignal } from 'deepsignal'; /** * Internal dependencies @@ -49,7 +49,7 @@ const proxifyContext = ( current: object, inherited: object = {} ): object => { contextObjectToFallback.set( current, inherited ); if ( ! contextObjectToProxy.has( current ) ) { const proxy = new Proxy( current, { - get: ( target, k ) => { + get: ( target: DeepSignal< any >, k ) => { const fallback = contextObjectToFallback.get( current ); // Always subscribe to prop changes in the current context. const currentProp = target[ k ]; @@ -130,9 +130,12 @@ const proxifyContext = ( current: object, inherited: object = {} ): object => { * Recursively update values within a deepSignal object. * * @param target A deepSignal instance. - * @param source Object with properties to update in `target` + * @param source Object with properties to update in `target`. */ -const updateSignals = ( target: object, source: object ) => { +const updateSignals = ( + target: DeepSignal< any >, + source: DeepSignal< any > +) => { for ( const k in source ) { if ( isPlainObject( peek( target, k ) ) && @@ -181,7 +184,9 @@ const empty = ' '; * @param val CSS string. * @return CSS object. */ -const cssStringToObject = ( val: string ): object => { +const cssStringToObject = ( + val: string +): Record< string, string | number > => { const tree = [ {} ]; let block, left; @@ -449,6 +454,8 @@ export default () => { type: Type, props: { innerHTML, ...rest }, }, + }: { + element: any; } ) => { // Preserve the initial inner HTML. const cached = useMemo( () => innerHTML, [] ); @@ -487,7 +494,6 @@ export default () => { element, evaluate, } ) => { - // @ts-expect-error: template type is currently missing https://github.com/preactjs/preact/pull/4334 if ( element.type !== 'template' ) return; const { Provider } = inheritedContext; diff --git a/packages/interactivity/src/hooks.tsx b/packages/interactivity/src/hooks.tsx index be7a053ec90081..ff8b8180557f93 100644 --- a/packages/interactivity/src/hooks.tsx +++ b/packages/interactivity/src/hooks.tsx @@ -43,6 +43,7 @@ interface DirectiveArgs { element: VNode< { class?: string; style?: string | Record< string, string | number >; + content?: ComponentChildren; } >; /** * The inherited context. @@ -56,7 +57,7 @@ interface DirectiveArgs { } interface DirectiveCallback { - ( args: DirectiveArgs ): VNode | null; + ( args: DirectiveArgs ): VNode | null | void; } interface DirectiveOptions { @@ -71,7 +72,7 @@ interface DirectiveOptions { interface Scope { evaluate: Evaluate; - context: Context< any >; + context?: object; ref: RefObject< HTMLElement >; attributes: createElement.JSX.HTMLAttributes; } From 566b9fb5518c7ea057e4d8c97e920b1aa3b53af6 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 18:13:14 +0200 Subject: [PATCH 07/14] =?UTF-8?q?stricter=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/interactivity/tsconfig.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/interactivity/tsconfig.json b/packages/interactivity/tsconfig.json index 445fce42e73e54..ef79d9873e1989 100644 --- a/packages/interactivity/tsconfig.json +++ b/packages/interactivity/tsconfig.json @@ -5,14 +5,11 @@ "rootDir": "src", "declarationDir": "build-types", - "alwaysStrict": false, "strictNullChecks": false, - "strictBindCallApply": false, - "strictFunctionTypes": false, "strictPropertyInitialization": false, - "noImplicitAny": false, + "noImplicitThis": false, - "useUnknownInCatchVariables": false + "noImplicitAny": false }, "include": [ "src/**/*" ] } From 14383bf7cedf233a2893f82d8427ba17fbafc49a Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 18:15:43 +0200 Subject: [PATCH 08/14] implicit this --- packages/interactivity/src/utils.ts | 2 +- packages/interactivity/tsconfig.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/interactivity/src/utils.ts b/packages/interactivity/src/utils.ts index e9162f0dd7cd79..8b6da05b938447 100644 --- a/packages/interactivity/src/utils.ts +++ b/packages/interactivity/src/utils.ts @@ -63,7 +63,7 @@ const afterNextFrame = ( callback: () => void ) => { */ function createFlusher( compute: () => unknown, notify: () => void ): Flusher { let flush: () => void; - const dispose = effect( function () { + const dispose = effect( function ( this: any ) { flush = this.c.bind( this ); this.x = compute; this.c = notify; diff --git a/packages/interactivity/tsconfig.json b/packages/interactivity/tsconfig.json index ef79d9873e1989..d991e2e56a9084 100644 --- a/packages/interactivity/tsconfig.json +++ b/packages/interactivity/tsconfig.json @@ -8,7 +8,6 @@ "strictNullChecks": false, "strictPropertyInitialization": false, - "noImplicitThis": false, "noImplicitAny": false }, "include": [ "src/**/*" ] From 81fc1f38abdfb607d21f55e89c0ae57d3364eabd Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 19:17:42 +0200 Subject: [PATCH 09/14] strict nulls --- packages/interactivity/src/vdom.ts | 78 +++++++++++++++++++--------- packages/interactivity/tsconfig.json | 1 - 2 files changed, 54 insertions(+), 25 deletions(-) diff --git a/packages/interactivity/src/vdom.ts b/packages/interactivity/src/vdom.ts index 5a997993668094..2ab176b7acf0dc 100644 --- a/packages/interactivity/src/vdom.ts +++ b/packages/interactivity/src/vdom.ts @@ -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. @@ -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; @@ -81,7 +99,7 @@ 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; @@ -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, @@ -127,16 +152,19 @@ 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(); @@ -144,9 +172,11 @@ export function toVdom( root ) { } // 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 ); diff --git a/packages/interactivity/tsconfig.json b/packages/interactivity/tsconfig.json index d991e2e56a9084..bd921d194888ac 100644 --- a/packages/interactivity/tsconfig.json +++ b/packages/interactivity/tsconfig.json @@ -5,7 +5,6 @@ "rootDir": "src", "declarationDir": "build-types", - "strictNullChecks": false, "strictPropertyInitialization": false, "noImplicitAny": false From 7c9afc55a28194be0879bc99f573ff842ad0c86b Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 19:20:38 +0200 Subject: [PATCH 10/14] handle not found text entry --- packages/interactivity/src/directives.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/interactivity/src/directives.tsx b/packages/interactivity/src/directives.tsx index 8e8e783ae125cb..42e07f60e0bf58 100644 --- a/packages/interactivity/src/directives.tsx +++ b/packages/interactivity/src/directives.tsx @@ -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 = From dabad8f4220fb2f181af24053e1a39909bd64e42 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 19:25:57 +0200 Subject: [PATCH 11/14] hooks --- packages/interactivity/src/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/interactivity/src/utils.ts b/packages/interactivity/src/utils.ts index 8b6da05b938447..6919575e97bd3b 100644 --- a/packages/interactivity/src/utils.ts +++ b/packages/interactivity/src/utils.ts @@ -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; @@ -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 () => { @@ -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 ); From 31fcd3da73559b9b43cac1cf04000e485260f7c6 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 19:27:11 +0200 Subject: [PATCH 12/14] store --- packages/interactivity/src/store.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/interactivity/src/store.ts b/packages/interactivity/src/store.ts index 2aad8c23d1db12..ff70b4ab297085 100644 --- a/packages/interactivity/src/store.ts +++ b/packages/interactivity/src/store.ts @@ -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 ) ) { @@ -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 ); } ); } From 44c93217e9506440bfcb535add911f2c75ab87c0 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 19:29:49 +0200 Subject: [PATCH 13/14] Scope --- packages/interactivity/src/hooks.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interactivity/src/hooks.tsx b/packages/interactivity/src/hooks.tsx index ff8b8180557f93..39be45ecd94ada 100644 --- a/packages/interactivity/src/hooks.tsx +++ b/packages/interactivity/src/hooks.tsx @@ -72,7 +72,7 @@ interface DirectiveOptions { interface Scope { evaluate: Evaluate; - context?: object; + context: object; ref: RefObject< HTMLElement >; attributes: createElement.JSX.HTMLAttributes; } From 65491b9fe897d7a37db51470b4dadfcb0e1c5c2a Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Apr 2024 19:31:49 +0200 Subject: [PATCH 14/14] I hate it --- packages/interactivity/src/vdom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interactivity/src/vdom.ts b/packages/interactivity/src/vdom.ts index 2ab176b7acf0dc..692fd0dffea27d 100644 --- a/packages/interactivity/src/vdom.ts +++ b/packages/interactivity/src/vdom.ts @@ -106,7 +106,7 @@ export function toVdom( root: Node ): [ string | VNode | null, Node | null ] { namespaces.push( typeof value === 'string' ? value - : value?.namespace ?? null + : ( value as any )?.namespace ?? null ); } else { directives.push( [ n, ns, value ] );