From c3cdad9dd35ed1f0918438407e3efed0cff9c720 Mon Sep 17 00:00:00 2001 From: Nikolas Savvidis Date: Wed, 22 Dec 2021 20:44:11 +0100 Subject: [PATCH 1/9] Fixes --- src/app/store.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/app/store.ts b/src/app/store.ts index 478a3a0..412cc77 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -4,7 +4,7 @@ import { nanoid } from 'nanoid'; import { object } from './object'; import { IPage } from '../types/page'; import { IOptions } from '../types/options'; -import { assign } from '../constants/native'; +import { assign, isArray } from '../constants/native'; import { dispatchEvent } from './events'; import * as scroll from '../observers/scroll'; import * as progress from './progress'; @@ -150,17 +150,22 @@ export function hydrate (state: IPage, snapshot: string): IPage { * Removes cached records. Optionally pass in URL * to remove specific record. */ -export function clear (url?: string): void { +export function clear (url?: string[] | string): void { - if (typeof url === 'string') { + if (typeof url === 'undefined') { + + pages.clear(); + snaps.clear(); + + } else if (typeof url === 'string') { snaps.delete(pages.get(url).snapshot); pages.delete(url); - } else { + } else if (isArray(url)) { - pages.clear(); - snaps.clear(); + const saved = pages.clear(url); + snaps.clear(saved as any); } @@ -201,5 +206,5 @@ export function history (): string { browser.replace(location, updated); - return location.state.url; + return (location.state as any).url; } From bbce5a8efd1dc0fad949eb7ecaee961fd0a481f7 Mon Sep 17 00:00:00 2001 From: Nikolas Savvidis Date: Wed, 22 Dec 2021 20:44:24 +0100 Subject: [PATCH 2/9] various --- src/observers/hrefs.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/observers/hrefs.ts b/src/observers/hrefs.ts index ddfd3ce..0bf488f 100644 --- a/src/observers/hrefs.ts +++ b/src/observers/hrefs.ts @@ -70,25 +70,27 @@ function handleTrigger (event: MouseEvent): void { const target = getLink(event.target, 'a:not([data-pjax-disable]):not([href^="#"])'); if (!target) return undefined; - if (!dispatchEvent('pjax:trigger', { target }, true)) return undefined; - const { url, location } = path.get(target, true); - const click = handleClick(target); + if (dispatchEvent('pjax:trigger', { target }, true)) { - if (request.transit.has(url)) { + const { url, location } = path.get(target, true); + const click = handleClick(target); - target.addEventListener('click', click(url), false); + if (request.transit.has(url)) { - } else { - - const state = attrparse(target, { url, location, position: y0x0() }); + target.addEventListener('click', click(url), false); - if (store.has(url)) { - const page = store.pages.update(state.url, state); - target.addEventListener('click', click(page), false); } else { - request.get(state); // TRIGGERS FETCH - target.addEventListener('click', click(url), false); + + const state = attrparse(target, { url, location, position: y0x0() }); + + if (store.has(url)) { + const page = store.pages.update(state.url, state); + target.addEventListener('click', click(page), false); + } else { + request.get(state); // TRIGGERS FETCH + target.addEventListener('click', click(url), false); + } } } } From ef8cac60e87a9aa7f38b39dacca597aa913e1d86 Mon Sep 17 00:00:00 2001 From: Nikolas Savvidis Date: Wed, 22 Dec 2021 20:44:38 +0100 Subject: [PATCH 3/9] no destruct --- src/observers/history.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/observers/history.ts b/src/observers/history.ts index 4f7d14a..65de2a0 100644 --- a/src/observers/history.ts +++ b/src/observers/history.ts @@ -78,12 +78,9 @@ export function stop (): void { */ export function updateState (): IPage { - const { location } = browser; - const updated = merge(location.state, { position }); + const updated = merge(browser.location.state as IPage, { position }); - console.log(location); - - browser.replace(location, updated); + browser.replace(browser.location, updated); return updated; From 51a57c1e95ddb8d13eb364cf7e003f31161be663 Mon Sep 17 00:00:00 2001 From: Nikolas Savvidis Date: Wed, 22 Dec 2021 20:44:44 +0100 Subject: [PATCH 4/9] values --- src/constants/native.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/native.ts b/src/constants/native.ts index 4ac440e..4969ae7 100644 --- a/src/constants/native.ts +++ b/src/constants/native.ts @@ -6,6 +6,7 @@ export const { assign, is, keys, + values, defineProperty, getOwnPropertyNames } = Object; From 9f6363c97ff3ec5f956b6d601528db9968594e72 Mon Sep 17 00:00:00 2001 From: Nikolas Savvidis Date: Wed, 22 Dec 2021 20:45:00 +0100 Subject: [PATCH 5/9] faster replacements --- src/app/render.ts | 86 ++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/src/app/render.ts b/src/app/render.ts index 2c6d995..c2d6901 100644 --- a/src/app/render.ts +++ b/src/app/render.ts @@ -4,7 +4,7 @@ import { dispatchEvent } from './events'; import { progress } from './progress'; import { IPage } from '../types/page'; import history from 'history/browser'; -import { from, is } from '../constants/native'; +import { from } from '../constants/native'; import * as store from './store'; import * as mouseover from '../observers/hover'; import * as intersect from '../observers/intersect'; @@ -134,29 +134,6 @@ function appendTrackedNode (node: Element): void { } -/** - * Apply actions to the documents target fragments - * with the request response. - */ -function replaceTarget (target: Element, state: IPage): (DOM: Element) => void { - - return DOM => { - - if (dispatchEvent('pjax:render', { target }, true)) { - - DOM.innerHTML = target.innerHTML; - - if (state.append || state.prepend) { - const fragment = document.createElement('div'); - forEach(fragment.appendChild)(from(target.childNodes)); - if (state.append) DOM.appendChild(fragment); - else DOM.insertBefore(fragment, DOM.firstChild); - } - } - - }; -} - /** * Parse HTML document string from request response * using `parser()` method. Cached pages will pass @@ -184,17 +161,47 @@ export async function capture (state: IPage) { } } -function renderNodes (state: IPage, target: Document, nodes: string[]) { +function renderNodes (state: IPage, target: Document) { + + const nodes = state.replace ? [ ...state.targets, ...state.replace ] : state.targets; + const selector = nodes.join(','); + const current = document.body.querySelectorAll(selector); + + if (current.length === 0) return document.body.replaceWith(target.body); + + const fetched = target.body.querySelectorAll(selector); - let fallback = 1; + current.forEach((node, i) => { - forEach(element => { - const node = target.body.querySelector(element); - if (node) forEach(replaceTarget(node, state))(document.body.querySelectorAll(element)); - else fallback++; - }, nodes); + if (!node.matches(nodes[i])) return; + if (!dispatchEvent('pjax:render', { target: nodes[i] }, true)) return; - if (is(fallback, state.targets.length)) replaceTarget(target.body, state)(document.body); + node.replaceWith(fetched[i]); + + if (state.append || state.prepend) { + + const fragment = document.createElement('div'); + target.childNodes.forEach(fragment.appendChild); + + return state.append + ? node.appendChild(fragment) + : node.insertBefore(fragment, node.firstChild); + + } + + }); + +} + +function hydrateNodes (target: Document, selectors: string[]) { + + const nodes = selectors.join(','); + const current = document.body.querySelectorAll(nodes); + + if (current.length > 0) { + const fetched = target.body.querySelectorAll(nodes); + current.forEach((node, i) => node.replaceWith(fetched[i])); + } } @@ -229,21 +236,16 @@ export function update (state: IPage, popstate?: boolean): IPage { if (state.hydrate) { - renderNodes(state, target, state.hydrate); - - const capture = { ...state, hydrate: undefined }; - store.clear(); + hydrateNodes(target, state.hydrate); - const page = store.capture(capture, document.documentElement.outerHTML); + const { url } = store.pages.update(state.url, { hydrate: undefined }); + store.snaps.clear(store.pages.clear([ url ])); - history.replace(capture.location, page); + console.log(store.pages.all); } else { - renderNodes(state, target, state.replace ? [ - ...state.targets, - ...state.replace - ] : state.targets); + renderNodes(state, target); target.body.querySelectorAll('[data-pjax-track]').forEach(appendTrackedNode); From cf68295af0737e637fcdd67497307fc56bcb3d2f Mon Sep 17 00:00:00 2001 From: Nikolas Savvidis Date: Wed, 22 Dec 2021 20:45:07 +0100 Subject: [PATCH 6/9] types --- src/app/controller.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/app/controller.ts b/src/app/controller.ts index a215bb8..2dba7d4 100644 --- a/src/app/controller.ts +++ b/src/app/controller.ts @@ -10,6 +10,7 @@ import { isArray } from '../constants/native'; import { connect } from './connects'; import * as store from './store'; import { forEach } from './utils'; +import { ILocation } from '../types/location'; /** * Sets initial page state executing on intial load. @@ -31,12 +32,16 @@ function onload (): void { } if (store.config.cache.reverse) { - const previous = browser.location.state.location; - if (previous.lastpath) request.get(previous.lastpath, 'reverse'); + const previous: { location?: ILocation } = browser.location.state; + if (previous?.location?.lastpath) { + request.get(previous.location.lastpath, 'reverse').then(() => { + browser.replace(window.location, page); + }); + } + } else { + browser.replace(window.location, page); } - browser.replace(window.location, page); - removeEventListener('load', onload); } From b8a66c982f3b6cf084b170f52655781d8d3427c7 Mon Sep 17 00:00:00 2001 From: Nikolas Savvidis Date: Wed, 22 Dec 2021 20:45:22 +0100 Subject: [PATCH 7/9] Configurable --- src/app/object.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/app/object.ts b/src/app/object.ts index d7092ab..fb2e11b 100644 --- a/src/app/object.ts +++ b/src/app/object.ts @@ -1,5 +1,9 @@ import merge from 'mergerino'; -import { assign, defineProperty, getOwnPropertyNames, create } from '../constants/native'; +import { + defineProperty, + getOwnPropertyNames, + create +} from '../constants/native'; /** * Object @@ -10,7 +14,7 @@ import { assign, defineProperty, getOwnPropertyNames, create } from '../constant */ export function object ({ writable, - configurable = true, + configurable, enumerable }: Omit = { writable: false, @@ -68,7 +72,9 @@ export function object ({ */ update (prop: K, ...value: T[K][]): T[K] { - return assign(o[prop], merge(o[prop] as any, ...value)); + o[prop] = merge(o[prop] || {} as any, value); + + return o[prop]; }, @@ -109,13 +115,16 @@ export function object ({ * will only clear that specific record. Returns * boolean when sucessful clear is executed. */ - clear (exclude: string[] = []): boolean { + clear (exclude: string[] = []): any[] { + + const snapshots = []; getOwnPropertyNames(o).forEach(url => { - if (exclude.indexOf(url) !== -1) delete o[url]; + if (!exclude.includes(url)) delete o[url]; + else snapshots.push(o[url].snapshot); }); - return true; + return snapshots; } From 898612eadd542d2e2964cf32d2f59a365dcddfee Mon Sep 17 00:00:00 2001 From: Nikolas Savvidis Date: Wed, 22 Dec 2021 20:45:25 +0100 Subject: [PATCH 8/9] Update changelog.md --- changelog.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 85dca74..5b2d500 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # CHANGELOG -### 18/12/2012 +### 21/12/2012 | v0.3.0-beta.1 ##### Internal Improvements @@ -16,7 +16,7 @@ This is a new render method that will override defined `targets` which is helpfu This version provides a preemptive fetches. There are 2 additions exposed in the version, which need a little more work but can be used. A new options for **reverse** caching is available which will execute a preemptive fetch of the previous page in the history stack when no snapshot exists in cache. Snapshot cache is purged if a browser refresh occurs and when navigating backwards or pages will need to be re-fetched resulting in minor delays if a refresh was triggered between browsing. By default, the last known previous page in the history stack is now fetched. -In addition to the reverse cache feature, a **preemptive** option is now available to the `prefetch` config. This option currently accepts a string list of paths only. Values defined here will be fetched preemptively and saved to cache either upon initial load anf those pages will be visited in background asynchronously in the order they were passed. In the next release preemptive fetches will be made possible according to a specific path. +In addition to the reverse cache feature, a **preemptive** option is now available to the `prefetch` config. This option currently accepts a string list of paths only. Values defined here will be fetched preemptively and saved to cache either upon initial load and those pages will be visited in background asynchronously in the order they were passed. In the next release preemptive fetches will be made possible according to a specific path. ##### Methods @@ -33,6 +33,10 @@ Below is a the complete list of changes applied in this release - Fixed scroll position preservation between no-cached navigations - Promise methods resolve page state correctly. - Improved `` node replacements between navigations. +- Replaced how fragments are replaced between visits. +- Added a hydration support for partial replacements +- Overhauled the store engine +- Clean up various other areas. #### 10/08/2021 | v0.2.0-beta.1 From 3709636d7d63a8a109a41c12c57b88fbd5886078 Mon Sep 17 00:00:00 2001 From: Nikolas Savvidis Date: Wed, 22 Dec 2021 20:45:58 +0100 Subject: [PATCH 9/9] v 0.3.0-beta.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 69dd753..64c2f95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@brixtol/pjax", - "version": "0.2.0-beta.1", + "version": "0.3.0-beta.1", "private": false, "description": "A modern next generation pjax solution for SSR web applications", "author": "ΝΙΚΟΛΑΣ ΣΑΒΒΙΔΗΣ",