From 08153cdde07e9b2d13fc43d065fed1d73cb6d444 Mon Sep 17 00:00:00 2001 From: James Culveyhouse Date: Wed, 25 Jan 2023 15:39:27 -0600 Subject: [PATCH 1/8] f --- .../fragments/news/src/routes/[...news].tsx | 6 ++ .../app/fragments/todos/src/App.tsx | 16 ++++ productivity-suite/app/legacy-app/index.html | 2 +- .../app/legacy-app/src/worker/script.ts | 3 +- .../src/piercing-fragment-outlet.ts | 23 ++++- .../piercing-library/src/piercing-gateway.ts | 6 ++ .../piercing-library/src/reframed-client.js | 95 +++++++++++-------- .../piercing-library/src/reframed-host.js | 54 +++++++++++ 8 files changed, 161 insertions(+), 44 deletions(-) create mode 100644 productivity-suite/piercing-library/src/reframed-host.js diff --git a/productivity-suite/app/fragments/news/src/routes/[...news].tsx b/productivity-suite/app/fragments/news/src/routes/[...news].tsx index 0ceab49e..6018281f 100644 --- a/productivity-suite/app/fragments/news/src/routes/[...news].tsx +++ b/productivity-suite/app/fragments/news/src/routes/[...news].tsx @@ -17,6 +17,7 @@ export default function News() { let ref: HTMLDivElement; const [page, setPage] = createSignal(1); + const [numbers] = createSignal(new Array(10000)); onMount(() => { getBus(ref).listen<{ page: number }>("news-page", ({ page }) => { @@ -74,6 +75,11 @@ export default function News() { + + {(_, i) => { + return
console.log(i())}>{`number: ` + i()}
; + }} +
); } diff --git a/productivity-suite/app/fragments/todos/src/App.tsx b/productivity-suite/app/fragments/todos/src/App.tsx index a39c450d..e79423b1 100644 --- a/productivity-suite/app/fragments/todos/src/App.tsx +++ b/productivity-suite/app/fragments/todos/src/App.tsx @@ -83,6 +83,21 @@ const App: React.FC<{ } }, [ref.current]); + // const renderBloat = () => { + // return new Array(1000).fill(1).map((_, i) => { + // return ( + //
{ + // console.log(i); + // }} + // data-bloat={new Array(10000).join("x")} + // > + // number {i} + //
+ // ); + // }); + // }; + return (
@@ -119,6 +134,7 @@ const App: React.FC<{ /> )} + {/* {renderBloat()} */}
); diff --git a/productivity-suite/app/legacy-app/index.html b/productivity-suite/app/legacy-app/index.html index 8fdceb80..046873e9 100644 --- a/productivity-suite/app/legacy-app/index.html +++ b/productivity-suite/app/legacy-app/index.html @@ -304,7 +304,7 @@ `; } +function getReframedHostCode() { + return ``; +} + const escapeQuotes = (str: string) => str.replaceAll('"', `"`); const qwikloaderScript = ``; diff --git a/productivity-suite/piercing-library/src/reframed-client.js b/productivity-suite/piercing-library/src/reframed-client.js index 42915b3b..a677e339 100644 --- a/productivity-suite/piercing-library/src/reframed-client.js +++ b/productivity-suite/piercing-library/src/reframed-client.js @@ -1,12 +1,22 @@ const reframedContainer = window.parent.document.querySelector( __FRAGMENT_SELECTOR__ ); + if (!reframedContainer) { console.error("Container element couldn't be found"); } +const fragmentId = reframedContainer.getAttribute("fragment-id"); +window["____XXXXXXXX" + fragmentId + Date.now()] = true; + const reframedDocument = reframedContainer.ownerDocument; const reframedWindow = reframedDocument.defaultView; +const reframedRegistrationSymbol = Symbol.for("reframedRegistration"); +const reframedRegistration = Reflect.get( + reframedWindow, + reframedRegistrationSymbol +); +reframedRegistration(fragmentId, Function); const htmlToReframe = []; const nodesToRemove = []; @@ -52,51 +62,51 @@ document.addEventListener("DOMContentLoaded", () => { originalDocumentBody.innerHTML = ""; }); -const domCreateProperties = [ - "createAttributeNS", - "createCDATASection", - "createComment", - "createDocumentFragment", - "createElement", - "createElementNS", - "createEvent", - "createExpression", - "createNSResolver", - "createNodeIterator", - "createProcessingInstruction", - "createRange", - "createTextNode", - "createTreeWalker", -]; -for (const createProperty of domCreateProperties) { - document[createProperty] = function reframedCreateFn(...args) { - return reframedDocument[createProperty].apply(reframedDocument, args); - }; -} +// const domCreateProperties = [ +// "createAttributeNS", +// "createCDATASection", +// "createComment", +// "createDocumentFragment", +// "createElement", +// "createElementNS", +// "createEvent", +// "createExpression", +// "createNSResolver", +// "createNodeIterator", +// "createProcessingInstruction", +// "createRange", +// "createTextNode", +// "createTreeWalker", +// ]; +// for (const createProperty of domCreateProperties) { +// document[createProperty] = function reframedCreateFn(...args) { +// return reframedDocument[createProperty].apply(reframedDocument, args); +// }; +// } // methods to query for elements that can be retargeted into the reframedContainer -const domQueryProperties = [ - "querySelector", - "querySelectorAll", - "getElementsByClassName", - "getElementsByTagName", - "getElementsByTagNameNS", -]; -for (const queryProperty of domQueryProperties) { - document[queryProperty] = function reframedQueryFn(...args) { - return reframedContainer[queryProperty].apply(reframedContainer, args); - }; -} +// const domQueryProperties = [ +// "querySelector", +// "querySelectorAll", +// "getElementsByClassName", +// "getElementsByTagName", +// "getElementsByTagNameNS", +// ]; +// for (const queryProperty of domQueryProperties) { +// document[queryProperty] = function reframedQueryFn(...args) { +// return reframedContainer[queryProperty].apply(reframedContainer, args); +// }; +// } // scope document.getElementById to execute just on the reframed container -document.getElementById = function reframedGetElementById(id) { - return reframedContainer.querySelector(`#${id}`); -}; +// document.getElementById = function reframedGetElementById(id) { +// return reframedContainer.querySelector(`#${id}`); +// }; // scope document.getElementsByName to execute just on the reframed container -document.getElementsByName = function reframedGetElementsByName(name) { - return reframedContainer.querySelectorAll(`[name="${name}"]`); -}; +// document.getElementsByName = function reframedGetElementsByName(name) { +// return reframedContainer.querySelectorAll(`[name="${name}"]`); +// }; const domListenerProperties = ["addEventListener", "removeEventListener"]; for (const listenerProperty of domListenerProperties) { @@ -111,8 +121,11 @@ for (const listenerProperty of domListenerProperties) { ); }; - window[listenerProperty] = function reframedListenerFn(...args) { - return reframedWindow[listenerProperty].apply(reframedWindow, args); + // window[listenerProperty] = function reframedListenerFn(...args) { + // return reframedWindow[listenerProperty].apply(reframedWindow, args); + // }; + window[listenerProperty] = function reframedListenerFn() { + return reframedWindow[listenerProperty].apply(reframedWindow, arguments); }; } diff --git a/productivity-suite/piercing-library/src/reframed-host.js b/productivity-suite/piercing-library/src/reframed-host.js new file mode 100644 index 00000000..5ef2f57a --- /dev/null +++ b/productivity-suite/piercing-library/src/reframed-host.js @@ -0,0 +1,54 @@ +// Maps that allow us to keep track of registered fragments and their listeners. +// We use FunctionConstructor as a way to identify the origin of a registered listener and map it back to its fragment. +const fragmentRegistrationMap = new Map(); +const fragmentListenerMap = new Map(); + +// monkey patch global addEventListner on window and document so that we can automatically unregister +// any listeners created from within a registered fragment. +const addEventListenerTargets = [window, document]; +// const addEventListenerTargets = [Node.prototype]; +for (const addEventListenerTarget of addEventListenerTargets) { + const originalDocumentAddEventListener = + addEventListenerTarget.addEventListener; + addEventListenerTarget.addEventListener = + function reframedHostAddEventListener(name, listener, options) { + let listenerFnConstructor; + if (listener.handleEvent) { + listenerFnConstructor = listener.handleEvent.constructor; + } else { + listenerFnConstructor = listener.constructor; + } + + const listenerFns = fragmentListenerMap.get(listenerFnConstructor); + + // if this listener comes from a registered fragment, then keep track of it so that we can unregister it + if (listenerFns) { + listenerFns.push({ + target: addEventListenerTarget, + // target: this, + name, + listener, + options, + }); + } + + return originalDocumentAddEventListener.call( + addEventListenerTarget, + // this, + name, + listener, + options + ); + }; +} + +const reframedRegistrationSymbol = Symbol.for("reframedRegistration"); + +Reflect.set( + window, + reframedRegistrationSymbol, + function reframedRegistration(fragmentId, clientFunctionConstructor) { + fragmentRegistrationMap.set(fragmentId, clientFunctionConstructor); + fragmentListenerMap.set(clientFunctionConstructor, []); + } +); From b5d151aec19a875b2abc83cb2a8bf82202ea20f8 Mon Sep 17 00:00:00 2001 From: James Culveyhouse Date: Wed, 25 Jan 2023 22:37:28 -0600 Subject: [PATCH 2/8] removing spellcheck and autocomplete from todos input --- .../app/fragments/todos/src/components/Header.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/productivity-suite/app/fragments/todos/src/components/Header.tsx b/productivity-suite/app/fragments/todos/src/components/Header.tsx index d4238657..57a90825 100644 --- a/productivity-suite/app/fragments/todos/src/components/Header.tsx +++ b/productivity-suite/app/fragments/todos/src/components/Header.tsx @@ -26,6 +26,8 @@ export function Header({ }`} placeholder="What needs to be done?" autoFocus + autoComplete="off" + spellCheck={false} value={newTodoDetails.text} onInput={(event: ChangeEvent) => { const text = event.target.value; From 525b22c948760e6da307f0b959b46d04bc846731 Mon Sep 17 00:00:00 2001 From: James Culveyhouse Date: Thu, 26 Jan 2023 15:47:07 -0600 Subject: [PATCH 3/8] Fix FOUC due to reframed --- .../app/legacy-app/src/worker/script.ts | 3 +- .../piercing-library/src/piercing-gateway.ts | 28 ++++++- .../piercing-library/src/reframed-client.js | 84 +++++++++---------- 3 files changed, 69 insertions(+), 46 deletions(-) diff --git a/productivity-suite/app/legacy-app/src/worker/script.ts b/productivity-suite/app/legacy-app/src/worker/script.ts index 6618bf18..9d15fabe 100644 --- a/productivity-suite/app/legacy-app/src/worker/script.ts +++ b/productivity-suite/app/legacy-app/src/worker/script.ts @@ -11,8 +11,7 @@ const gateway = new PiercingGateway({ return env.APP_BASE_URL; }, isolateFragments(env) { - // return env.ISOLATE_FRAGMENTS || false; - return true; + return env.ISOLATE_FRAGMENTS || false; }, shouldPiercingBeEnabled(request: Request) { const match = request.headers diff --git a/productivity-suite/piercing-library/src/piercing-gateway.ts b/productivity-suite/piercing-library/src/piercing-gateway.ts index 1c320189..a7d53be5 100644 --- a/productivity-suite/piercing-library/src/piercing-gateway.ts +++ b/productivity-suite/piercing-library/src/piercing-gateway.ts @@ -374,12 +374,13 @@ export class PiercingGateway { if (this.config.isolateFragments?.(env)) { // Quotes must be escaped from srcdoc contents template = ` + ${prePiercingStyles} - ${prePiercingStyles}