Skip to content

Commit

Permalink
Adds fast case for pursx with no attributes
Browse files Browse the repository at this point in the history
or elements
  • Loading branch information
mikesol committed Aug 16, 2024
1 parent 98c6753 commit a75b2ac
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 4 deletions.
7 changes: 7 additions & 0 deletions deku-core/src/Deku/Core.purs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module Deku.Core
, RemoveElement
, RemoveText
, SetCb
, SetInnerHtml
, SetProp
, SetText
, Tag(..)
Expand Down Expand Up @@ -188,6 +189,10 @@ type RemoveText = EffectFn1 DekuText Unit
type AttachElement =
EffectFn2 DekuChild Anchor Unit

-- | An optimization to attach innerhtml
type SetInnerHtml =
EffectFn2 String Anchor Unit

type AttachText =
EffectFn2 DekuText Anchor Unit

Expand Down Expand Up @@ -278,6 +283,8 @@ newtype DOMInterpret = DOMInterpret
, initializeDynRendering :: STFn1 Ancestry Global Unit
-- fixed
, initializeFixedRendering :: STFn1 Ancestry Global Unit
-- pursx optimization
, setInnerHTML :: SetInnerHtml
}

derive instance Newtype DOMInterpret _
Expand Down
1 change: 1 addition & 0 deletions deku-core/src/Deku/FullDOMInterpret.purs
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ fullDOMInterpret = Core.DOMInterpret
--
, initializeDynRendering: mkSTFn1 \_ -> pure unit
, initializeFixedRendering: mkSTFn1 \_ -> pure unit
, setInnerHTML: I.setInnerHtmlEffect
}
1 change: 1 addition & 0 deletions deku-core/src/Deku/HydratingDOMInterpret.purs
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,5 @@ hydratingDOMInterpret
, initializePortalRendering: mkSTFn1 \_ -> pure unit
, initializeDynRendering: mkSTFn1 \_ -> pure unit
, initializeFixedRendering: mkSTFn1 \_ -> pure unit
, setInnerHTML: mkEffectFn2 \_ _ -> pure unit
}
3 changes: 3 additions & 0 deletions deku-core/src/Deku/Interpret.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const setInnerHtml = (html, element) => {
element.innerHTML = html;
}
16 changes: 16 additions & 0 deletions deku-core/src/Deku/Interpret.purs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import Data.List (List(..), (:))
import Data.Maybe (Maybe(..), fromJust, fromMaybe, isJust)
import Data.Nullable (toMaybe)
import Data.Tuple (Tuple(..))
import Deku.Core (SetInnerHtml)
import Deku.Core as Core
import Deku.Internal.Entities (DekuChild(..), DekuElement, DekuParent(..), fromDekuElement, fromDekuText, toDekuElement, toDekuText)
import Deku.Internal.Region (Anchor(..))
import Deku.UnsafeDOM (addEventListener, after, createDocumentFragment, createElement, createElementNS, createText, eventListener, popCb, prepend, pushCb, removeEventListener, setTextContent)
import Effect (Effect, whileE)
import Effect.Exception (error, throwException)
import Effect.Ref as Ref
import Effect.Uncurried (EffectFn2, EffectFn3, mkEffectFn1, mkEffectFn2, mkEffectFn3, runEffectFn1, runEffectFn2, runEffectFn3, runEffectFn4)
import Partial.Unsafe (unsafePartial)
Expand Down Expand Up @@ -268,3 +270,17 @@ attachNodeEffect = mkEffectFn2 \nodes anchor -> do

Text txt -> do
runEffectFn2 after nodes (fromDekuText @Node txt)

foreign import setInnerHtml :: EffectFn2 String DekuElement Unit

setInnerHtmlEffect :: SetInnerHtml
setInnerHtmlEffect = mkEffectFn2 \html anchor -> do
case anchor of
ParentStart (DekuParent parent) -> do
runEffectFn2 setInnerHtml html parent

Element _ -> do
throwException $ error "setInnerHtmlEffect: Cannot set innerHTML on an Element"

Text _ -> do
throwException $ error "setInnerHtmlEffect: Cannot set innerHTML on an Element"
29 changes: 29 additions & 0 deletions deku-core/src/Deku/Pursx.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,32 @@ export const splitOnDelimiter = (delimiter) => (str) =>
.map((part) =>
part.replace(new RegExp(`^${delimiter}|${delimiter}$`, "g"), "")
);

export const removeOuterTags = (html) =>
html.replace(/^[^>]*>([\s\S]*?)<[^<]*$/, '$1');

export const getOuterTagInfo = (html) => {
const result = {
tagName: '',
attributes: {}
};

// Regular expression to match the outermost tag and capture the tag name and attributes
const tagMatch = html.match(/^<(\w+)([^>]*)>/);

if (tagMatch) {
result.tagName = tagMatch[1];

// Extracting attributes
const attrString = tagMatch[2];
const attrRegex = /(\w+)=["']([^"']*)["']/g;
let match;

while ((match = attrRegex.exec(attrString)) !== null) {
result.attributes[match[1]] = match[2];
}
}

return result;
}

36 changes: 32 additions & 4 deletions deku-core/src/Deku/Pursx.purs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module Deku.Pursx
import Prelude

import Control.Monad.ST (ST, run)
import Control.Monad.ST.Class (liftST)
import Control.Monad.ST.Ref as STRef
import Data.Either (Either(..), isLeft, isRight)
import Data.Exists (Exists, mkExists, runExists)
Expand All @@ -26,10 +27,14 @@ import Data.List (List(..), (:))
import Data.Map (Map)
import Data.Map as Map
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Newtype (un)
import Data.Symbol (class IsSymbol, reflectSymbol)
import Data.Tuple (uncurry)
import Deku.Core (Attribute, Nut, attributeAtYourOwnRisk, elementify, text_)
import Data.Tuple.Nested ((/\))
import Deku.Core (Attribute, DOMInterpret(..), Nut(..), PSR(..), attributeAtYourOwnRisk, elementify, text_)
import Deku.Internal.Region (StaticRegion(..))
import Deku.PursxParser (AttributeVerb, ElementVerb, PursxState, PxElt'P)
import Effect.Uncurried (mkEffectFn2, runEffectFn2)
import FRP.Poll (Poll)
import Foreign.Object (Object, toUnfoldable)
import Foreign.Object as Object
Expand Down Expand Up @@ -117,6 +122,25 @@ onOpenTag info stack name attributes = do
(nut : stackValue)
stack

foreign import getOuterTagInfo :: String -> {
tagName:: String,
attributes:: Object String
}

optimizedPursx
:: String -> Nut
optimizedPursx html = do
let outerInfo = getOuterTagInfo html
let innerStuff = removeOuterTags html
elementify Nothing outerInfo.tagName
( map (\(k /\ v) -> (pure $ attributeAtYourOwnRisk k v)) $ toUnfoldable
outerInfo.attributes
)
[ Nut $ mkEffectFn2 \psr di -> do
anchor <- liftST $ (un StaticRegion (un PSR psr).region).end
runEffectFn2 (un DOMInterpret di).setInnerHTML innerStuff anchor
]

onText
:: forall r
. PursxInfoMap
Expand Down Expand Up @@ -148,8 +172,10 @@ onCloseTag _ stack _ = do
f : g : rest -> do
void $ STRef.write ((\i -> g ([ f [] ] <> i)) : rest) stack

foreign import removeOuterTags :: String -> String

purs :: PursxInfo -> Nut
purs (PursxInfo html i) = run do
purs (PursxInfo html i) = if Map.isEmpty i then optimizedPursx html else run do
r <- STRef.new Nil
parser <- createParser (onOpenTag i r) (onText i r) (onCloseTag i r)
write parser html
Expand Down Expand Up @@ -240,7 +266,8 @@ pursx'
:: forall @verb (@html :: Symbol) r0 rl0 h t r rl
. IsSymbol html
=> IsSymbol verb
=> Parse html (PxElt'P verb) (PursxState () L.Nil) (R.Success h t) (PursxState r0 L.Nil)
=> Parse html (PxElt'P verb) (PursxState () L.Nil) (R.Success h t)
(PursxState r0 L.Nil)
=> RL.RowToList r0 rl0
=> RL.RowToList r rl
=> PursxSubstitutions rl0 r
Expand All @@ -253,7 +280,8 @@ pursx' = lenientPursx' (reflectSymbol (Proxy :: _ verb))
pursx
:: forall (@html :: Symbol) r0 rl0 h t r rl
. IsSymbol html
=> Parse html (PxElt'P "~") (PursxState () L.Nil) (R.Success h t) (PursxState r0 L.Nil)
=> Parse html (PxElt'P "~") (PursxState () L.Nil) (R.Success h t)
(PursxState r0 L.Nil)
=> RL.RowToList r0 rl0
=> RL.RowToList r rl
=> PursxSubstitutions rl0 r
Expand Down
3 changes: 3 additions & 0 deletions deku-core/src/Deku/SSRDOMInterpret.purs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ ssrDOMInterpret
dynCache
, initializeFixedRendering: mkSTFn1 \a -> void $ STRef.modify (Set.insert a)
fixedCache
, setInnerHTML: I.setInnerHtmlEffect

}

noOpDomInterpret
Expand Down Expand Up @@ -203,4 +205,5 @@ noOpDomInterpret =
, initializePortalRendering: mkSTFn1 \_ -> pure unit
, initializeDynRendering: mkSTFn1 \_ -> pure unit
, initializeFixedRendering: mkSTFn1 \_ -> pure unit
, setInnerHTML: mkEffectFn2 \_ _ -> pure unit
}
7 changes: 7 additions & 0 deletions deku/test/Test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,13 @@ pursXWiresUp = Deku.do
, D.span [ DA.id_ "span0" ] [ text message ]
]

optimizedPursx :: Nut
optimizedPursx = Deku.do
D.div [ DA.id_ "div0" ]
[ pursx @"<div><h1 id=\"px\">hi there</h1><span id=\"check-me\">i am a span</span></div>" { }
, D.span [ DA.id_ "span0" ] [ text_ "oh hi" ]
]

pursXWiresUp2 :: Nut
pursXWiresUp2 = Deku.do
setMessage /\ message <- useState'
Expand Down
9 changes: 9 additions & 0 deletions index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,14 @@ describe("deku", () => {
})
);


doTest("optimized pursx renders", (f) =>
f(tests.optimizedPursx, () => {
const $ = require("jquery");
expect($("#check-me").text()).toBe("i am a span");
})
);

doTest("pursx adds listeners", (f) =>
f(tests.pursXWiresUp, () => {
const $ = require("jquery");
Expand All @@ -383,6 +391,7 @@ describe("deku", () => {
expect($("#span0").text()).toBe("goodbye");
})
);

doTest("pursx adds listeners 2", (f) =>
f(tests.pursXWiresUp2, () => {
const $ = require("jquery");
Expand Down

0 comments on commit a75b2ac

Please sign in to comment.