vike 0.4.199-commit-43cc34a → 0.4.199-commit-5d1b353

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PROJECT_VERSION = void 0;
4
4
  // Automatically updated by @brillout/release-me
5
- exports.PROJECT_VERSION = '0.4.199-commit-43cc34a';
5
+ exports.PROJECT_VERSION = '0.4.199-commit-5d1b353';
@@ -1,5 +1,9 @@
1
- export { enhanceHistoryState, getHistoryState, pushHistory, type ScrollPosition, saveScrollPosition, monkeyPatchHistoryPushState };
2
- type StateVikeEnhanced = {
1
+ export { pushHistoryState };
2
+ export { onPopStateBegin };
3
+ export { saveScrollPosition };
4
+ export type { HistoryInfo };
5
+ export type { ScrollPosition };
6
+ type StateEnhanced = {
3
7
  timestamp: number;
4
8
  scrollPosition: null | ScrollPosition;
5
9
  triggeredBy: 'user' | 'vike' | 'browser';
@@ -9,9 +13,14 @@ type ScrollPosition = {
9
13
  x: number;
10
14
  y: number;
11
15
  };
12
- type StateNotInitialized = null | undefined | Partial<StateVikeEnhanced> | StateVikeEnhanced;
13
- declare function enhanceHistoryState(): void;
14
- declare function getHistoryState(): StateNotInitialized;
15
16
  declare function saveScrollPosition(): void;
16
- declare function pushHistory(url: string, overwriteLastHistoryEntry: boolean): void;
17
- declare function monkeyPatchHistoryPushState(): void;
17
+ declare function pushHistoryState(url: string, overwriteLastHistoryEntry: boolean): void;
18
+ type HistoryInfo = {
19
+ url: `/${string}`;
20
+ state: StateEnhanced;
21
+ };
22
+ declare function onPopStateBegin(): {
23
+ isNewState: boolean;
24
+ previous: HistoryInfo;
25
+ current: HistoryInfo;
26
+ };
@@ -1,22 +1,26 @@
1
- export { enhanceHistoryState, getHistoryState, pushHistory, saveScrollPosition, monkeyPatchHistoryPushState };
2
- import { assert, assertUsage, hasProp, isObject } from './utils.js';
1
+ export { pushHistoryState };
2
+ export { onPopStateBegin };
3
+ export { saveScrollPosition };
4
+ import { assert, assertUsage, getCurrentUrl, getGlobalObject, hasProp, isObject } from './utils.js';
5
+ init();
6
+ const globalObject = getGlobalObject('history.ts', { previous: getHistoryInfo() });
3
7
  // `window.history.state === null` when:
4
8
  // - The very first render
5
9
  // - Click on `<a href="#some-hash" />`
6
10
  // - `location.hash = 'some-hash'`
7
11
  function enhanceHistoryState() {
8
- const stateNotInitialized = window.history.state;
9
- if (isVikeEnhanced(stateNotInitialized))
12
+ const stateNotEnhanced = getStateNotEnhanced();
13
+ if (isVikeEnhanced(stateNotEnhanced))
10
14
  return;
11
- const stateVikeEnhanced = enhance(stateNotInitialized);
15
+ const stateVikeEnhanced = enhance(stateNotEnhanced);
12
16
  replaceHistoryState(stateVikeEnhanced);
13
17
  }
14
- function enhance(stateNotInitialized) {
18
+ function enhance(stateNotEnhanced) {
15
19
  const timestamp = getTimestamp();
16
20
  const scrollPosition = getScrollPosition();
17
21
  const triggeredBy = 'browser';
18
22
  let stateVikeEnhanced;
19
- if (!stateNotInitialized) {
23
+ if (!stateNotEnhanced) {
20
24
  stateVikeEnhanced = {
21
25
  timestamp,
22
26
  scrollPosition,
@@ -27,21 +31,26 @@ function enhance(stateNotInitialized) {
27
31
  else {
28
32
  // State information may be incomplete if `window.history.state` is set by an old Vike version. (E.g. `state.timestamp` was introduced for `pageContext.isBackwardNavigation` in `0.4.19`.)
29
33
  stateVikeEnhanced = {
30
- timestamp: stateNotInitialized.timestamp ?? timestamp,
31
- scrollPosition: stateNotInitialized.scrollPosition ?? scrollPosition,
32
- triggeredBy: stateNotInitialized.triggeredBy ?? triggeredBy,
34
+ timestamp: stateNotEnhanced.timestamp ?? timestamp,
35
+ scrollPosition: stateNotEnhanced.scrollPosition ?? scrollPosition,
36
+ triggeredBy: stateNotEnhanced.triggeredBy ?? triggeredBy,
33
37
  _isVikeEnhanced: true
34
38
  };
35
39
  }
36
40
  assert(isVikeEnhanced(stateVikeEnhanced));
37
41
  return stateVikeEnhanced;
38
42
  }
39
- function getState_alreadyEnhanced() {
40
- const state = getHistoryState();
43
+ function getState() {
44
+ const state = getStateNotEnhanced();
45
+ // *Every* state added to the history needs to go through Vike.
46
+ // - Otherwise Vike's `popstate` listener won't work. (Because, for example, if globalObject.previous is outdated => isHashNavigation faulty => client-side navigation wrongfully skipped.)
47
+ // - Therefore, we monkey patch history.pushState() and history.replaceState()
48
+ // - Therefore, we assert() below that history.state has been enhanced by Vike
49
+ // - If users stumble upon this assert() then make it a assertUsage()
41
50
  assert(isVikeEnhanced(state));
42
51
  return state;
43
52
  }
44
- function getHistoryState() {
53
+ function getStateNotEnhanced() {
45
54
  const state = window.history.state;
46
55
  return state;
47
56
  }
@@ -54,48 +63,54 @@ function getTimestamp() {
54
63
  }
55
64
  function saveScrollPosition() {
56
65
  const scrollPosition = getScrollPosition();
57
- const state = getState_alreadyEnhanced();
66
+ const state = getState();
58
67
  replaceHistoryState({ ...state, scrollPosition });
59
68
  }
60
- function pushHistory(url, overwriteLastHistoryEntry) {
69
+ function pushHistoryState(url, overwriteLastHistoryEntry) {
61
70
  if (!overwriteLastHistoryEntry) {
62
- const timestamp = getTimestamp();
63
- pushHistoryState({
64
- timestamp,
71
+ const state = {
72
+ timestamp: getTimestamp(),
65
73
  // I don't remember why I set it to `null`, maybe because setting it now would be too early? (Maybe there is a delay between renderPageClientSide() is finished and the browser updating the scroll position.) Anyways, it seems like autoSaveScrollPosition() is enough.
66
74
  scrollPosition: null,
67
75
  triggeredBy: 'vike',
68
76
  _isVikeEnhanced: true
69
- }, url);
77
+ };
78
+ // Calling the monkey patched history.pushState() (and not the orignal) so that other tools (e.g. user tracking) can listen to Vike's pushState() calls
79
+ // https://github.com/vikejs/vike/issues/1582
80
+ window.history.pushState(state, '', url);
70
81
  }
71
82
  else {
72
- replaceHistoryState(getState_alreadyEnhanced(), url);
83
+ replaceHistoryState(getState(), url);
73
84
  }
74
85
  }
75
86
  function replaceHistoryState(state, url) {
76
87
  const url_ = url ?? null; // Passing `undefined` chokes older Edge versions.
77
88
  window.history.replaceState(state, '', url_);
78
89
  }
79
- function pushHistoryState(state, url) {
80
- // Vike should call window.history.pushState() (and not the orignal `pushStateOriginal()`) so that other tools (e.g. user tracking) can listen to Vike's pushState() calls, see https://github.com/vikejs/vike/issues/1582.
81
- window.history.pushState(state, '', url);
82
- }
83
- function monkeyPatchHistoryPushState() {
84
- const pushStateOriginal = window.history.pushState.bind(window.history);
85
- window.history.pushState = (stateOriginal = {}, ...rest) => {
86
- assertUsage(stateOriginal === undefined || stateOriginal === null || isObject(stateOriginal), 'history.pushState(state) argument state must be an object');
87
- const stateEnhanced = isVikeEnhanced(stateOriginal)
88
- ? stateOriginal
89
- : {
90
- _isVikeEnhanced: true,
91
- scrollPosition: getScrollPosition(),
92
- timestamp: getTimestamp(),
93
- triggeredBy: 'user',
94
- ...stateOriginal
95
- };
96
- assert(isVikeEnhanced(stateEnhanced));
97
- return pushStateOriginal(stateEnhanced, ...rest);
98
- };
90
+ // Monkey patch:
91
+ // - history.pushState()
92
+ // - history.replaceState()
93
+ function monkeyPatchHistoryAPI() {
94
+ ;
95
+ ['pushState', 'replaceState'].forEach((funcName) => {
96
+ const funcOriginal = window.history[funcName].bind(window.history);
97
+ window.history[funcName] = (stateOriginal = {}, ...rest) => {
98
+ assertUsage(stateOriginal === undefined || stateOriginal === null || isObject(stateOriginal), `history.${funcName}(state) argument state must be an object`);
99
+ const stateEnhanced = isVikeEnhanced(stateOriginal)
100
+ ? stateOriginal
101
+ : {
102
+ _isVikeEnhanced: true,
103
+ scrollPosition: getScrollPosition(),
104
+ timestamp: getTimestamp(),
105
+ triggeredBy: 'user',
106
+ ...stateOriginal
107
+ };
108
+ assert(isVikeEnhanced(stateEnhanced));
109
+ const ret = funcOriginal(stateEnhanced, ...rest);
110
+ globalObject.previous = getHistoryInfo();
111
+ return ret;
112
+ };
113
+ });
99
114
  }
100
115
  function isVikeEnhanced(state) {
101
116
  const yes = isObject(state) && '_isVikeEnhanced' in state;
@@ -114,3 +129,22 @@ function assertStateVikeEnhanced(state) {
114
129
  assert(hasProp(state.scrollPosition, 'x', 'number') && hasProp(state.scrollPosition, 'y', 'number'));
115
130
  }
116
131
  }
132
+ function init() {
133
+ enhanceHistoryState();
134
+ monkeyPatchHistoryAPI();
135
+ }
136
+ function getHistoryInfo() {
137
+ return {
138
+ url: getCurrentUrl(),
139
+ state: getState()
140
+ };
141
+ }
142
+ function onPopStateBegin() {
143
+ const isNewState = window.history.state === null;
144
+ if (isNewState)
145
+ enhanceHistoryState();
146
+ const { previous } = globalObject;
147
+ const current = getHistoryInfo();
148
+ globalObject.previous = current;
149
+ return { isNewState, previous, current };
150
+ }
@@ -1,6 +1,5 @@
1
1
  export { initClientRouter };
2
2
  import { assert } from './utils.js';
3
- import { enhanceHistoryState, monkeyPatchHistoryPushState } from './history.js';
4
3
  import { getRenderCount, renderPageClientSide } from './renderPageClientSide.js';
5
4
  import { initOnPopState } from './initOnPopState.js';
6
5
  import { initOnLinkClick } from './initOnLinkClick.js';
@@ -29,9 +28,7 @@ async function renderFirstPage() {
29
28
  }
30
29
  function initHistoryAndScroll() {
31
30
  setupNativeScrollRestoration();
32
- enhanceHistoryState();
33
31
  autoSaveScrollPosition();
34
- monkeyPatchHistoryPushState();
35
32
  // Handle back-/forward navigation
36
33
  initOnPopState();
37
34
  }
@@ -1,27 +1,12 @@
1
1
  export { initOnPopState };
2
- export { updateState };
3
2
  export { onPopState };
3
+ import { type HistoryInfo } from './history.js';
4
4
  declare function initOnPopState(): void;
5
5
  type Listener = (arg: {
6
- previous: ReturnType<typeof getState>;
6
+ previous: HistoryInfo;
7
7
  }) => void | boolean;
8
- /** Control client-side navigation.
8
+ /** Control back-/forward navigation.
9
9
  *
10
10
  * https://vike.dev/onPopState
11
11
  */
12
12
  declare function onPopState(listener: Listener): void;
13
- declare function getState(): {
14
- url: `/${string}`;
15
- state: {
16
- timestamp: number;
17
- scrollPosition: null | import("./history.js").ScrollPosition;
18
- triggeredBy: "user" | "vike" | "browser";
19
- _isVikeEnhanced: true;
20
- } | Partial<{
21
- timestamp: number;
22
- scrollPosition: null | import("./history.js").ScrollPosition;
23
- triggeredBy: "user" | "vike" | "browser";
24
- _isVikeEnhanced: true;
25
- }> | null | undefined;
26
- };
27
- declare function updateState(): void;
@@ -1,11 +1,10 @@
1
1
  export { initOnPopState };
2
- export { updateState };
3
2
  export { onPopState };
4
- import { assert, getCurrentUrl, getGlobalObject } from './utils.js';
5
- import { enhanceHistoryState, getHistoryState } from './history.js';
3
+ import { getGlobalObject } from './utils.js';
4
+ import { onPopStateBegin } from './history.js';
6
5
  import { renderPageClientSide } from './renderPageClientSide.js';
7
6
  import { setScrollPosition } from './setScrollPosition.js';
8
- const globalObject = getGlobalObject('initOnPopState.ts', { previousState: getState(), listeners: [] });
7
+ const globalObject = getGlobalObject('initOnPopState.ts', { listeners: [] });
9
8
  function initOnPopState() {
10
9
  // - The popstate event is trigged upon:
11
10
  // - Back-/forward navigation.
@@ -17,25 +16,16 @@ function initOnPopState() {
17
16
  // - `location.hash = 'some-hash'`
18
17
  // - The `event` argument of `window.addEventListener('popstate', (event) => /*...*/)` is useless: the History API doesn't provide the previous state (the popped state), see https://stackoverflow.com/questions/48055323/is-history-state-always-the-same-as-popstate-event-state
19
18
  window.addEventListener('popstate', async () => {
20
- const previous = globalObject.previousState;
21
- const current = getState();
22
- globalObject.previousState = current;
23
- const scrollTarget = current.state?.scrollPosition || undefined;
24
- const isHashNavigation = removeHash(current.url) === removeHash(previous.url);
25
- const isBackwardNavigation = !current.state?.timestamp || !previous.state?.timestamp
26
- ? null
27
- : current.state.timestamp < previous.state.timestamp;
28
- // - `history.state === null` when:
19
+ const { isNewState, previous, current } = onPopStateBegin();
20
+ const scrollTarget = current.state.scrollPosition || undefined;
21
+ const isUserPushStateNavigation = current.state.triggeredBy === 'user' || previous.state.triggeredBy === 'user';
22
+ const isHashNavigation = current.url !== previous.url && removeHash(current.url) === removeHash(previous.url);
23
+ // - `isNewState === true` when:
29
24
  // - Click on `<a href="#some-hash" />` (note that Vike's `initOnLinkClick()` handler skips hash links)
30
25
  // - `location.hash = 'some-hash'`
31
- // - `history.state !== null` when `popstate` was triggered by the user clicking on his browser's forward/backward history button.
32
- let isHashNavigationNew = isHashNavigation && window.history.state === null;
33
- if (window.history.state === null) {
34
- assert(isHashNavigation);
35
- // The browser already scrolled to `#${hash}` => the current scroll position is the right one => we save it with `enhanceHistoryState()`.
36
- enhanceHistoryState();
37
- globalObject.previousState = getState();
38
- }
26
+ // - `isNewState === false` when `popstate` was triggered by the user clicking on his browser's forward/backward history button.
27
+ const isHashNavigationNew = isHashNavigation && isNewState;
28
+ const isBackwardNavigation = !current.state.timestamp || !previous.state.timestamp ? null : current.state.timestamp < previous.state.timestamp;
39
29
  // We have to scroll ourselves because we use `window.history.scrollRestoration = 'manual'`. So far this seems to work. Alternatives in case it doesn't work:
40
30
  // - Alternative: we use `window.history.scrollRestoration = 'auto'`
41
31
  // - Problem: I don't think it's possbible to set `window.history.scrollRestoration = 'auto'` only for hash navigation and not for non-hash navigations?
@@ -49,33 +39,32 @@ function initOnPopState() {
49
39
  if (!isHashNavigationNew) {
50
40
  setScrollPosition(scrollTarget);
51
41
  }
42
+ else {
43
+ // The browser already scrolled to `#${hash}` => the current scroll position is the right one => we saved it with `enhanceHistoryState()`.
44
+ }
52
45
  return;
53
46
  }
47
+ let doNotRenderIfSamePage = isUserPushStateNavigation;
54
48
  let abort;
55
49
  globalObject.listeners.forEach((listener) => {
56
50
  abort || (abort = listener({ previous }));
57
51
  });
58
- if (abort)
52
+ if (abort) {
59
53
  return;
60
- await renderPageClientSide({ scrollTarget, isBackwardNavigation });
54
+ }
55
+ if (abort === false) {
56
+ doNotRenderIfSamePage = false;
57
+ }
58
+ await renderPageClientSide({ scrollTarget, isBackwardNavigation, doNotRenderIfSamePage });
61
59
  });
62
60
  }
63
- /** Control client-side navigation.
61
+ /** Control back-/forward navigation.
64
62
  *
65
63
  * https://vike.dev/onPopState
66
64
  */
67
65
  function onPopState(listener) {
68
66
  globalObject.listeners.push(listener);
69
67
  }
70
- function getState() {
71
- return {
72
- url: getCurrentUrl(),
73
- state: getHistoryState()
74
- };
75
- }
76
68
  function removeHash(url) {
77
69
  return url.split('#')[0];
78
70
  }
79
- function updateState() {
80
- globalObject.previousState = getState();
81
- }
@@ -12,6 +12,7 @@ type RenderArgs = {
12
12
  overwriteLastHistoryEntry?: boolean;
13
13
  pageContextsFromRewrite?: PageContextFromRewrite[];
14
14
  redirectCount?: number;
15
+ doNotRenderIfSamePage?: boolean;
15
16
  isClientSideNavigation?: boolean;
16
17
  };
17
18
  declare function renderPageClientSide(renderArgs: RenderArgs): Promise<void>;
@@ -10,12 +10,11 @@ import { assertInfo, assertWarning, isReact } from './utils.js';
10
10
  import { executeOnRenderClientHook } from '../shared/executeOnRenderClientHook.js';
11
11
  import { assertHook, getHook } from '../../shared/hooks/getHook.js';
12
12
  import { isErrorFetchingStaticAssets, loadUserFilesClientSide } from '../shared/loadUserFilesClientSide.js';
13
- import { pushHistory } from './history.js';
13
+ import { pushHistoryState } from './history.js';
14
14
  import { assertNoInfiniteAbortLoop, getPageContextFromAllRewrites, isAbortError, logAbortErrorHandled } from '../../shared/route/abort.js';
15
15
  import { route } from '../../shared/route/index.js';
16
16
  import { isClientSideRoutable } from './isClientSideRoutable.js';
17
17
  import { setScrollPosition } from './setScrollPosition.js';
18
- import { updateState } from './initOnPopState.js';
19
18
  import { browserNativeScrollRestoration_disable, setInitialRenderIsDone } from './scrollRestoration.js';
20
19
  import { getErrorPageId } from '../../shared/error-page.js';
21
20
  import { setPageContextCurrent } from './getPageContextCurrent.js';
@@ -30,7 +29,7 @@ const globalObject = getGlobalObject('renderPageClientSide.ts', (() => {
30
29
  })());
31
30
  const { firstRenderStartPromise } = globalObject;
32
31
  async function renderPageClientSide(renderArgs) {
33
- const { urlOriginal = getCurrentUrl(), overwriteLastHistoryEntry = false, isBackwardNavigation, pageContextsFromRewrite = [], redirectCount = 0, isClientSideNavigation = true } = renderArgs;
32
+ const { urlOriginal = getCurrentUrl(), overwriteLastHistoryEntry = false, isBackwardNavigation, pageContextsFromRewrite = [], redirectCount = 0, doNotRenderIfSamePage, isClientSideNavigation = true } = renderArgs;
34
33
  let { scrollTarget } = renderArgs;
35
34
  const { previousPageContext } = globalObject;
36
35
  addLinkPrefetchHandlers_unwatch();
@@ -117,6 +116,13 @@ async function renderPageClientSide(renderArgs) {
117
116
  redirectHard(urlOriginal);
118
117
  return;
119
118
  }
119
+ const isSamePage = pageContextFromRoute.pageId &&
120
+ previousPageContext?.pageId &&
121
+ pageContextFromRoute.pageId === previousPageContext.pageId;
122
+ if (doNotRenderIfSamePage && isSamePage) {
123
+ // Skip's Vike's rendering; let the user handle the navigation
124
+ return;
125
+ }
120
126
  // TODO/eventually: create helper assertPageContextFromHook()
121
127
  assert(!('urlOriginal' in pageContextFromRoute));
122
128
  objectAssign(pageContext, pageContextFromRoute);
@@ -469,8 +475,7 @@ function changeUrl(url, overwriteLastHistoryEntry) {
469
475
  if (getCurrentUrl() === url)
470
476
  return;
471
477
  browserNativeScrollRestoration_disable();
472
- pushHistory(url, overwriteLastHistoryEntry);
473
- updateState();
478
+ pushHistoryState(url, overwriteLastHistoryEntry);
474
479
  }
475
480
  function handleErrorFetchingStaticAssets(err, pageContext, isFirstRender) {
476
481
  if (!isErrorFetchingStaticAssets(err)) {
@@ -1 +1 @@
1
- export declare const PROJECT_VERSION: "0.4.199-commit-43cc34a";
1
+ export declare const PROJECT_VERSION: "0.4.199-commit-5d1b353";
@@ -1,2 +1,2 @@
1
1
  // Automatically updated by @brillout/release-me
2
- export const PROJECT_VERSION = '0.4.199-commit-43cc34a';
2
+ export const PROJECT_VERSION = '0.4.199-commit-5d1b353';
@@ -1,4 +1,4 @@
1
1
  export declare const projectInfo: {
2
2
  projectName: "Vike";
3
- projectVersion: "0.4.199-commit-43cc34a";
3
+ projectVersion: "0.4.199-commit-5d1b353";
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vike",
3
- "version": "0.4.199-commit-43cc34a",
3
+ "version": "0.4.199-commit-5d1b353",
4
4
  "repository": "https://github.com/vikejs/vike",
5
5
  "exports": {
6
6
  "./server": {