Comparing version 1.4.1 to 1.4.2
# Changelog | ||
## 1.4.2- 2021-05-27 | ||
- Use another parser | ||
- Use cache for setReactivity | ||
- Add minor tweaks | ||
## 1.4.1- 2021-05-21 | ||
@@ -4,0 +10,0 @@ |
@@ -33,8 +33,8 @@ declare global { | ||
}; | ||
interface hydroObject extends Record<keyof any, any> { | ||
interface hydroObject extends Record<PropertyKey, any> { | ||
isProxy: boolean; | ||
asyncUpdate: boolean; | ||
observe: (key: keyof any, fn: Function) => any; | ||
observe: (key: PropertyKey, fn: Function) => any; | ||
getObservers: () => Map<string, Set<Function>>; | ||
unobserve: (key?: keyof any, handler?: Function) => undefined; | ||
unobserve: (key?: PropertyKey, handler?: Function) => undefined; | ||
} | ||
@@ -54,4 +54,4 @@ interface EventObject { | ||
declare function h(name: string | ((...args: any[]) => ReturnType<typeof h>), props: Record<keyof any, any> | null, ...children: Array<any>): ReturnType<typeof html>; | ||
declare function setReactivity(DOM: Node, eventFunctions?: eventFunctions): void; | ||
declare function compare(elem: Element, where: Element, onlyTextChildren?: boolean): boolean; | ||
declare function setReactivity(DOM: ReturnType<typeof html>, eventFunctions?: eventFunctions): void; | ||
declare function compare(elem: Element | DocumentFragment, where: Element | DocumentFragment | Text, onlyTextChildren?: boolean): boolean; | ||
declare function render(elem: ReturnType<typeof html> | reactiveObject<any>, where?: ReturnType<typeof html> | string, shouldSchedule?: boolean): ChildNode["remove"]; | ||
@@ -58,0 +58,0 @@ declare function reactive<T>(initial: T): reactiveObject<T>; |
@@ -10,7 +10,2 @@ // Safari Polyfills | ||
// Safari Polyfills END | ||
// Parser to create HTML elements from strings | ||
const parser = ((range = document.createRange()) => { | ||
range.selectNodeContents(range.createContextualFragment(`<${"template" /* template */}>`).lastChild); | ||
return range.createContextualFragment.bind(range); | ||
})(); | ||
const allNodeChanges = new WeakMap(); // Maps a Node against a change. This is necessary for nodes that have multiple changes for one text / attribute. | ||
@@ -24,2 +19,3 @@ const elemEventFunctions = new WeakMap(); // Stores event functions in order to compare Elements against each other. | ||
const fragmentToElements = new WeakMap(); // Used to retreive Elements from DocumentFragment after it has been rendered – for diffing | ||
const setReactivityElements = new WeakSet(); | ||
const _boundFunctions = Symbol("boundFunctions"); // Cache for bound functions in Proxy, so that we create the bound version of each function only once | ||
@@ -178,4 +174,7 @@ let globalSchedule = true; // Decides whether to schedule rendering and updating (async) | ||
DOMString = DOMString.replace(HTML_FIND_INVALID, `<$1$2${"-dummy" /* dummy */}$3`); | ||
const DOM = parser(DOMString); | ||
// Delay Elemen iteration and manipulation after the elements have been added to the DOM. | ||
// Parser | ||
const template = document.createElement("template" /* template */); | ||
template.innerHTML = DOMString; | ||
const DOM = template.content; | ||
// Delay Element iteration and manipulation after the elements have been added to the DOM. | ||
if (viewElements) { | ||
@@ -190,6 +189,6 @@ onRender(fillDOM, DOM.firstChild, DOM.firstChild, insertNodes, eventFunctions); | ||
return DOM; | ||
// Return Text Node | ||
// Return empty Text Node | ||
if (!DOM.firstChild) | ||
return document.createTextNode(""); | ||
// Return Element | ||
// Return Element | Text | ||
return DOM.firstChild; | ||
@@ -214,5 +213,7 @@ } | ||
// Insert HTML Elements, which were stored in insertNodes | ||
elem | ||
.querySelectorAll("template[id^=lbInsertNodes]") | ||
.forEach((template) => template.replaceWith(insertNodes.shift())); | ||
if (!isTextNode(elem)) { | ||
elem | ||
.querySelectorAll("template[id^=lbInsertNodes]") | ||
.forEach((template) => template.replaceWith(insertNodes.shift())); | ||
} | ||
if (shouldSetReactivity) | ||
@@ -234,3 +235,8 @@ setReactivity(elem, eventFunctions); | ||
elem.append(...flatChildren); | ||
onRender(setReactivity, elem, elem); | ||
if (viewElements) { | ||
onRender(setReactivity, elem, elem); | ||
} | ||
else { | ||
setReactivity(elem); | ||
} | ||
return elem; | ||
@@ -241,7 +247,10 @@ } | ||
// Set events and reactive behaviour(checks for {{ key }} where key is on hydro) | ||
if (isTextNode(DOM)) { | ||
if (isTextNode(DOM) && !setReactivityElements.has(DOM)) { | ||
setReactivityElements.add(DOM); | ||
setReactivitySingle(DOM); | ||
return; | ||
} | ||
const root = [...DOM.querySelectorAll("*")].reverse(); | ||
const root = [ | ||
...DOM.querySelectorAll("*"), | ||
].reverse(); | ||
if (!isDocumentFragment(DOM)) { | ||
@@ -251,2 +260,6 @@ root.push(DOM); | ||
root.forEach((elem) => { | ||
if (setReactivityElements.has(elem)) { | ||
return; // continue | ||
} | ||
setReactivityElements.add(elem); | ||
// Check Text Nodes | ||
@@ -543,2 +556,4 @@ // This is somehow faster than createNodeIterator and createTreeWalker | ||
function compare(elem, where, onlyTextChildren) { | ||
if (isDocumentFragment(elem) || isDocumentFragment(where)) | ||
return false; | ||
return (elem.isEqualNode(where) && compareEvents(elem, where, onlyTextChildren)); | ||
@@ -567,3 +582,3 @@ } | ||
if (typeof where === "string" /* string */) { | ||
const resolveStringToElement = document.querySelector(where); | ||
const resolveStringToElement = $(where); | ||
if (resolveStringToElement) { | ||
@@ -645,4 +660,11 @@ where = resolveStringToElement; | ||
function treeDiff(elem, where) { | ||
const elemElements = document.createNodeIterator(elem, NodeFilter.SHOW_ELEMENT); | ||
const whereElements = document.createNodeIterator(where, NodeFilter.SHOW_ELEMENT); | ||
let elemElements = [...elem.querySelectorAll("*")]; | ||
if (!isDocumentFragment(elem)) | ||
elemElements.unshift(elem); | ||
let whereElements = []; | ||
if (!isTextNode(where)) { | ||
whereElements = [...where.querySelectorAll("*")]; | ||
if (!isDocumentFragment(where)) | ||
whereElements.unshift(where); | ||
} | ||
let template; | ||
@@ -666,8 +688,7 @@ if (insertBeforeDiffing) { | ||
// Create Mapping for easier diffing, eg: "div" -> [...Element] | ||
let wElem; | ||
const tag2Elements = new Map(); | ||
while ((wElem = whereElements.nextNode())) { | ||
whereElements.forEach((wElem) => { | ||
/* c8 ignore next 2 */ | ||
if (insertBeforeDiffing && wElem === template) | ||
continue; | ||
return; | ||
if (tag2Elements.has(wElem.localName)) { | ||
@@ -679,6 +700,5 @@ tag2Elements.get(wElem.localName).push(wElem); | ||
} | ||
} | ||
}); | ||
// Re-use any where Element if possible, then remove elem Element | ||
let subElem; | ||
while ((subElem = elemElements.nextNode())) { | ||
elemElements.forEach((subElem) => { | ||
const sameElements = tag2Elements.get(subElem.localName); | ||
@@ -696,3 +716,3 @@ if (sameElements) { | ||
} | ||
} | ||
}); | ||
if (insertBeforeDiffing) { | ||
@@ -699,0 +719,0 @@ const newElems = isDocumentFragment(elem) |
{ | ||
"name": "hydro-js", | ||
"version": "1.4.1", | ||
"version": "1.4.2", | ||
"description": "A lightweight reactive library", | ||
@@ -10,3 +10,2 @@ "type": "module", | ||
"exports": { | ||
"require": "./dist/library.cjs.js", | ||
"import": "./dist/library.js", | ||
@@ -16,3 +15,3 @@ "default": "./dist/library.js" | ||
"scripts": { | ||
"build": "tsc && node utils/esm2c.js", | ||
"build": "tsc", | ||
"dev": "concurrently \"serve dist\" \"tsc -w\"", | ||
@@ -34,8 +33,8 @@ "test": "tsc && wtr src/test.html --node-resolve --coverage --playwright --browsers chromium firefox webkit", | ||
"@esm-bundle/chai": "^4.3.4", | ||
"@types/concurrently": "^6.0.1", | ||
"@web/test-runner": "^0.13.4", | ||
"@types/concurrently": "^6.2.0", | ||
"@web/test-runner": "^0.13.5", | ||
"@web/test-runner-playwright": "^0.8.6", | ||
"concurrently": "^6.1.0", | ||
"concurrently": "^6.2.0", | ||
"serve": "^11.3.2", | ||
"typescript": "^4.2.4" | ||
"typescript": "^4.3.2" | ||
}, | ||
@@ -42,0 +41,0 @@ "repository": { |
@@ -35,8 +35,8 @@ declare const window: any; | ||
interface hydroObject extends Record<keyof any, any> { | ||
interface hydroObject extends Record<PropertyKey, any> { | ||
isProxy: boolean; | ||
asyncUpdate: boolean; | ||
observe: (key: keyof any, fn: Function) => any; | ||
observe: (key: PropertyKey, fn: Function) => any; | ||
getObservers: () => Map<string, Set<Function>>; | ||
unobserve: (key?: keyof any, handler?: Function) => undefined; | ||
unobserve: (key?: PropertyKey, handler?: Function) => undefined; | ||
} | ||
@@ -91,9 +91,2 @@ type nodeChange = Array<[number, number, string | undefined]>; | ||
// Parser to create HTML elements from strings | ||
const parser = ((range = document.createRange()) => { | ||
range.selectNodeContents( | ||
range.createContextualFragment(`<${Placeholder.template}>`).lastChild! | ||
); | ||
return range.createContextualFragment.bind(range); | ||
})(); | ||
const allNodeChanges = new WeakMap<Text | Element, nodeChange>(); // Maps a Node against a change. This is necessary for nodes that have multiple changes for one text / attribute. | ||
@@ -107,2 +100,3 @@ const elemEventFunctions = new WeakMap<Element, Array<EventListener>>(); // Stores event functions in order to compare Elements against each other. | ||
const fragmentToElements = new WeakMap<DocumentFragment, Array<ChildNode>>(); // Used to retreive Elements from DocumentFragment after it has been rendered – for diffing | ||
const setReactivityElements = new WeakSet<ReturnType<typeof html>>(); | ||
const _boundFunctions = Symbol("boundFunctions"); // Cache for bound functions in Proxy, so that we create the bound version of each function only once | ||
@@ -286,5 +280,9 @@ | ||
); | ||
const DOM = parser(DOMString); | ||
// Delay Elemen iteration and manipulation after the elements have been added to the DOM. | ||
// Parser | ||
const template = document.createElement(Placeholder.template); | ||
template.innerHTML = DOMString; | ||
const DOM = template.content; | ||
// Delay Element iteration and manipulation after the elements have been added to the DOM. | ||
if (viewElements) { | ||
@@ -305,10 +303,10 @@ onRender( | ||
// Return Text Node | ||
// Return empty Text Node | ||
if (!DOM.firstChild) return document.createTextNode(""); | ||
// Return Element | ||
return DOM.firstChild as Element; | ||
// Return Element | Text | ||
return DOM.firstChild as Element | Text; | ||
} | ||
function fillDOM( | ||
elem: DocumentFragment | Element, | ||
elem: ReturnType<typeof html>, | ||
insertNodes: Node[], | ||
@@ -335,5 +333,7 @@ eventFunctions: eventFunctions | ||
// Insert HTML Elements, which were stored in insertNodes | ||
elem | ||
.querySelectorAll("template[id^=lbInsertNodes]") | ||
.forEach((template) => template.replaceWith(insertNodes.shift()!)); | ||
if (!isTextNode(elem)) { | ||
elem | ||
.querySelectorAll("template[id^=lbInsertNodes]") | ||
.forEach((template) => template.replaceWith(insertNodes.shift()!)); | ||
} | ||
@@ -362,14 +362,23 @@ if (shouldSetReactivity) setReactivity(elem, eventFunctions); | ||
elem.append(...flatChildren); | ||
onRender(setReactivity, elem, elem); | ||
if (viewElements) { | ||
onRender(setReactivity, elem, elem); | ||
} else { | ||
setReactivity(elem); | ||
} | ||
return elem; | ||
} | ||
/* c8 ignore end */ | ||
function setReactivity(DOM: Node, eventFunctions?: eventFunctions) { | ||
function setReactivity( | ||
DOM: ReturnType<typeof html>, | ||
eventFunctions?: eventFunctions | ||
) { | ||
// Set events and reactive behaviour(checks for {{ key }} where key is on hydro) | ||
if (isTextNode(DOM)) { | ||
if (isTextNode(DOM) && !setReactivityElements.has(DOM)) { | ||
setReactivityElements.add(DOM); | ||
setReactivitySingle(DOM); | ||
return; | ||
} | ||
const root = [...(DOM as Element).querySelectorAll("*")].reverse(); | ||
const root = [ | ||
...(DOM as DocumentFragment | Element).querySelectorAll("*"), | ||
].reverse(); | ||
if (!isDocumentFragment(DOM)) { | ||
@@ -380,2 +389,6 @@ root.push(DOM as Element); | ||
root.forEach((elem) => { | ||
if (setReactivityElements.has(elem)) { | ||
return; // continue | ||
} | ||
setReactivityElements.add(elem); | ||
// Check Text Nodes | ||
@@ -651,3 +664,3 @@ // This is somehow faster than createNodeIterator and createTreeWalker | ||
// Helper function to return a Hydro Obj with a aalue from a chain of properties on hydro | ||
function resolveObject(propertyArray: Array<keyof any>): [any, hydroObject] { | ||
function resolveObject(propertyArray: Array<PropertyKey>): [any, hydroObject] { | ||
let value: any, prev: hydroObject; | ||
@@ -744,6 +757,7 @@ value = prev = hydro; | ||
function compare( | ||
elem: Element, | ||
where: Element, | ||
elem: Element | DocumentFragment, | ||
where: Element | DocumentFragment | Text, | ||
onlyTextChildren?: boolean | ||
): boolean { | ||
if (isDocumentFragment(elem) || isDocumentFragment(where)) return false; | ||
return ( | ||
@@ -781,3 +795,3 @@ elem.isEqualNode(where) && compareEvents(elem, where, onlyTextChildren) | ||
if (typeof where === Placeholder.string) { | ||
const resolveStringToElement = document.querySelector(where as string); | ||
const resolveStringToElement = $(where as string); | ||
if (resolveStringToElement) { | ||
@@ -797,4 +811,7 @@ where = resolveStringToElement; | ||
runLifecyle(where as Element, onCleanupMap); | ||
} else if (!compare(elem, where as Element)) { | ||
treeDiff(elem, where as Element); | ||
} else if (!compare(elem, where as Element | DocumentFragment | Text)) { | ||
treeDiff( | ||
elem as Element | DocumentFragment, | ||
where as Element | DocumentFragment | Text | ||
); | ||
} | ||
@@ -880,12 +897,15 @@ } | ||
} | ||
function treeDiff(elem: Element | DocumentFragment, where: Element) { | ||
const elemElements = document.createNodeIterator( | ||
elem, | ||
NodeFilter.SHOW_ELEMENT | ||
); | ||
const whereElements = document.createNodeIterator( | ||
where, | ||
NodeFilter.SHOW_ELEMENT | ||
); | ||
function treeDiff( | ||
elem: Element | DocumentFragment, | ||
where: Element | DocumentFragment | Text | ||
) { | ||
let elemElements = [...elem.querySelectorAll("*")]; | ||
if (!isDocumentFragment(elem)) elemElements.unshift(elem); | ||
let whereElements: typeof elemElements = []; | ||
if (!isTextNode(where)) { | ||
whereElements = [...where.querySelectorAll("*")]; | ||
if (!isDocumentFragment(where)) whereElements.unshift(where); | ||
} | ||
let template: HTMLTemplateElement; | ||
@@ -908,7 +928,6 @@ if (insertBeforeDiffing) { | ||
// Create Mapping for easier diffing, eg: "div" -> [...Element] | ||
let wElem: Element; | ||
const tag2Elements = new Map<string, Array<Element>>(); | ||
while ((wElem = whereElements.nextNode() as Element)) { | ||
whereElements.forEach((wElem) => { | ||
/* c8 ignore next 2 */ | ||
if (insertBeforeDiffing && wElem === template!) continue; | ||
if (insertBeforeDiffing && wElem === template!) return; | ||
@@ -920,7 +939,6 @@ if (tag2Elements.has(wElem.localName)) { | ||
} | ||
} | ||
}); | ||
// Re-use any where Element if possible, then remove elem Element | ||
let subElem: Element; | ||
while ((subElem = elemElements.nextNode() as Element)) { | ||
elemElements.forEach((subElem) => { | ||
const sameElements = tag2Elements!.get(subElem.localName); | ||
@@ -940,3 +958,3 @@ | ||
} | ||
} | ||
}); | ||
@@ -1050,3 +1068,3 @@ if (insertBeforeDiffing) { | ||
} | ||
function chainKeys(initial: Function | any, keys: Array<keyof any>): any { | ||
function chainKeys(initial: Function | any, keys: Array<PropertyKey>): any { | ||
return new Proxy(initial, { | ||
@@ -1157,6 +1175,6 @@ get(target, subKey, _receiver) { | ||
const trackProxies = new Set<hydroObject>(); | ||
const trackMap = new WeakMap<hydroObject, Set<keyof any>>(); | ||
const trackMap = new WeakMap<hydroObject, Set<PropertyKey>>(); | ||
const unobserveMap = new WeakMap< | ||
Function, | ||
Array<{ proxy: hydroObject; key: keyof any }> | ||
Array<{ proxy: hydroObject; key: PropertyKey }> | ||
>(); | ||
@@ -1168,3 +1186,3 @@ function watchEffect(fn: Function) { | ||
const reRun = (newVal: keyof any) => { | ||
const reRun = (newVal: PropertyKey) => { | ||
if (newVal !== null) fn(); | ||
@@ -1431,6 +1449,6 @@ }; | ||
Reflect.defineProperty(proxy, handlers, { | ||
value: new Map<keyof any, Set<Function>>(), | ||
value: new Map<PropertyKey, Set<Function>>(), | ||
}); | ||
Reflect.defineProperty(proxy, Placeholder.observe, { | ||
value: (key: keyof any, handler: Function) => { | ||
value: (key: PropertyKey, handler: Function) => { | ||
const map = Reflect.get(proxy, handlers); | ||
@@ -1451,3 +1469,3 @@ | ||
Reflect.defineProperty(proxy, Placeholder.unobserve, { | ||
value: (key: keyof any, handler: Function) => { | ||
value: (key: PropertyKey, handler: Function) => { | ||
const map = Reflect.get(proxy, handlers); | ||
@@ -1495,3 +1513,3 @@ | ||
function checkReactivityMap(obj: any, key: keyof any, val: any, oldVal: any) { | ||
function checkReactivityMap(obj: any, key: PropertyKey, val: any, oldVal: any) { | ||
const keyToNodeMap = reactivityMap.get(obj)!; | ||
@@ -1498,0 +1516,0 @@ const nodeToChangeMap = keyToNodeMap.get(String(key)); |
Sorry, the diff of this file is too big to display
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
0
276420
14
5037