vike 0.4.199-commit-5883046 → 0.4.199-commit-dc15087

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.
@@ -605,7 +605,9 @@ async function write(urlOriginal, pageContext, fileExtension, fileContent, root,
605
605
  (0, utils_js_1.assertPosixPath)(fileUrl);
606
606
  (0, utils_js_1.assert)(fileUrl.startsWith('/'));
607
607
  const filePathRelative = fileUrl.slice(1);
608
- (0, utils_js_1.assert)(!filePathRelative.startsWith('/'));
608
+ (0, utils_js_1.assert)(!filePathRelative.startsWith('/'),
609
+ // Let's remove this debug info after we add a assertUsage() avoiding https://github.com/vikejs/vike/issues/1929
610
+ { urlOriginal, fileUrl });
609
611
  (0, utils_js_1.assertPosixPath)(outDirClient);
610
612
  (0, utils_js_1.assertPosixPath)(filePathRelative);
611
613
  const filePath = path_1.default.posix.join(outDirClient, filePathRelative);
@@ -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-5883046';
5
+ exports.PROJECT_VERSION = '0.4.199-commit-dc15087';
@@ -1,4 +1,8 @@
1
- export { getHistoryState, enhanceHistoryState, pushHistoryState, type ScrollPosition, saveScrollPosition };
1
+ export { pushHistoryState };
2
+ export { onPopStateBegin };
3
+ export { saveScrollPosition };
4
+ export type { HistoryInfo };
5
+ export type { ScrollPosition };
2
6
  type StateEnhanced = {
3
7
  timestamp: number;
4
8
  scrollPosition: null | ScrollPosition;
@@ -9,7 +13,14 @@ type ScrollPosition = {
9
13
  x: number;
10
14
  y: number;
11
15
  };
12
- declare function enhanceHistoryState(): void;
13
- declare function getHistoryState(): StateEnhanced;
14
16
  declare function saveScrollPosition(): void;
15
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,7 +1,9 @@
1
- export { getHistoryState, enhanceHistoryState, pushHistoryState, saveScrollPosition };
2
- import { assert, assertUsage, hasProp, isObject } from './utils.js';
3
- let initStateEnhanced;
1
+ export { pushHistoryState };
2
+ export { onPopStateBegin };
3
+ export { saveScrollPosition };
4
+ import { assert, assertUsage, getCurrentUrl, getGlobalObject, hasProp, isObject } from './utils.js';
4
5
  init();
6
+ const globalObject = getGlobalObject('history.ts', { previous: getHistoryInfo() });
5
7
  // `window.history.state === null` when:
6
8
  // - The very first render
7
9
  // - Click on `<a href="#some-hash" />`
@@ -38,11 +40,13 @@ function enhance(stateNotEnhanced) {
38
40
  assert(isVikeEnhanced(stateVikeEnhanced));
39
41
  return stateVikeEnhanced;
40
42
  }
41
- function getStateEnhanced() {
43
+ function getState() {
42
44
  const state = getStateNotEnhanced();
43
- // This assert() will most likely eventually cause issues. Let's then:
44
- // - Replace the assert() call with enhanceHistoryState()
45
- // - Remove the race condition buster `initStateEnhanced` as it won't be needed anymore
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()
46
50
  assert(isVikeEnhanced(state));
47
51
  return state;
48
52
  }
@@ -50,11 +54,6 @@ function getStateNotEnhanced() {
50
54
  const state = window.history.state;
51
55
  return state;
52
56
  }
53
- function getHistoryState() {
54
- if (!initStateEnhanced)
55
- enhanceHistoryState(); // avoid race condition
56
- return getStateEnhanced();
57
- }
58
57
  function getScrollPosition() {
59
58
  const scrollPosition = { x: window.scrollX, y: window.scrollY };
60
59
  return scrollPosition;
@@ -64,48 +63,54 @@ function getTimestamp() {
64
63
  }
65
64
  function saveScrollPosition() {
66
65
  const scrollPosition = getScrollPosition();
67
- const state = getStateEnhanced();
66
+ const state = getState();
68
67
  replaceHistoryState({ ...state, scrollPosition });
69
68
  }
70
69
  function pushHistoryState(url, overwriteLastHistoryEntry) {
71
70
  if (!overwriteLastHistoryEntry) {
72
- const timestamp = getTimestamp();
73
- pushState({
74
- timestamp,
71
+ const state = {
72
+ timestamp: getTimestamp(),
75
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.
76
74
  scrollPosition: null,
77
75
  triggeredBy: 'vike',
78
76
  _isVikeEnhanced: true
79
- }, 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);
80
81
  }
81
82
  else {
82
- replaceHistoryState(getStateEnhanced(), url);
83
+ replaceHistoryState(getState(), url);
83
84
  }
84
85
  }
85
86
  function replaceHistoryState(state, url) {
86
87
  const url_ = url ?? null; // Passing `undefined` chokes older Edge versions.
87
88
  window.history.replaceState(state, '', url_);
88
89
  }
89
- function pushState(state, url) {
90
- // 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.
91
- window.history.pushState(state, '', url);
92
- }
93
- function monkeyPatchHistoryPushState() {
94
- const pushStateOriginal = window.history.pushState.bind(window.history);
95
- window.history.pushState = (stateOriginal = {}, ...rest) => {
96
- assertUsage(stateOriginal === undefined || stateOriginal === null || isObject(stateOriginal), 'history.pushState(state) argument state must be an object');
97
- const stateEnhanced = isVikeEnhanced(stateOriginal)
98
- ? stateOriginal
99
- : {
100
- _isVikeEnhanced: true,
101
- scrollPosition: getScrollPosition(),
102
- timestamp: getTimestamp(),
103
- triggeredBy: 'user',
104
- ...stateOriginal
105
- };
106
- assert(isVikeEnhanced(stateEnhanced));
107
- return pushStateOriginal(stateEnhanced, ...rest);
108
- };
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
+ });
109
114
  }
110
115
  function isVikeEnhanced(state) {
111
116
  const yes = isObject(state) && '_isVikeEnhanced' in state;
@@ -126,6 +131,20 @@ function assertStateVikeEnhanced(state) {
126
131
  }
127
132
  function init() {
128
133
  enhanceHistoryState();
129
- initStateEnhanced = true;
130
- monkeyPatchHistoryPushState();
134
+ monkeyPatchHistoryAPI();
135
+ }
136
+ function getHistoryInfo() {
137
+ return {
138
+ url: getCurrentUrl(),
139
+ state: getState()
140
+ };
141
+ }
142
+ function onPopStateBegin() {
143
+ const { previous } = globalObject;
144
+ const isNewState = window.history.state === null;
145
+ if (isNewState)
146
+ enhanceHistoryState();
147
+ const current = getHistoryInfo();
148
+ globalObject.previous = current;
149
+ return { isNewState, previous, current };
131
150
  }
@@ -1,22 +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 getInfo>;
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 getInfo(): {
14
- url: `/${string}`;
15
- state: {
16
- timestamp: number;
17
- scrollPosition: null | import("./history.js").ScrollPosition;
18
- triggeredBy: "user" | "vike" | "browser";
19
- _isVikeEnhanced: true;
20
- };
21
- };
22
- declare function updateState(): void;
@@ -1,11 +1,10 @@
1
1
  export { initOnPopState };
2
- export { updateState };
3
2
  export { onPopState };
4
- import { 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', { previous: getInfo(), 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,18 +16,14 @@ 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 isNewState = window.history.state === null;
21
- if (isNewState)
22
- enhanceHistoryState();
23
- const { previous } = globalObject;
24
- const current = getInfo();
25
- globalObject.previous = current;
19
+ const { isNewState, previous, current } = onPopStateBegin();
26
20
  const scrollTarget = current.state.scrollPosition || undefined;
27
- const isHashNavigation = removeHash(current.url) === removeHash(previous.url);
28
- // - `history.state === null` when:
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.
26
+ // - `isNewState === false` when `popstate` was triggered by the user clicking on his browser's forward/backward history button.
32
27
  const isHashNavigationNew = isHashNavigation && isNewState;
33
28
  const isBackwardNavigation = !current.state.timestamp || !previous.state.timestamp ? null : current.state.timestamp < previous.state.timestamp;
34
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:
@@ -49,31 +44,27 @@ function initOnPopState() {
49
44
  }
50
45
  return;
51
46
  }
47
+ let doNotRenderIfSamePage = isUserPushStateNavigation;
52
48
  let abort;
53
49
  globalObject.listeners.forEach((listener) => {
54
50
  abort || (abort = listener({ previous }));
55
51
  });
56
- if (abort)
52
+ if (abort) {
57
53
  return;
58
- await renderPageClientSide({ scrollTarget, isBackwardNavigation });
54
+ }
55
+ if (abort === false) {
56
+ doNotRenderIfSamePage = false;
57
+ }
58
+ await renderPageClientSide({ scrollTarget, isBackwardNavigation, doNotRenderIfSamePage });
59
59
  });
60
60
  }
61
- /** Control client-side navigation.
61
+ /** Control back-/forward navigation.
62
62
  *
63
63
  * https://vike.dev/onPopState
64
64
  */
65
65
  function onPopState(listener) {
66
66
  globalObject.listeners.push(listener);
67
67
  }
68
- function getInfo() {
69
- return {
70
- url: getCurrentUrl(),
71
- state: getHistoryState()
72
- };
73
- }
74
68
  function removeHash(url) {
75
69
  return url.split('#')[0];
76
70
  }
77
- function updateState() {
78
- globalObject.previous = getInfo();
79
- }
@@ -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>;
@@ -15,7 +15,6 @@ import { assertNoInfiniteAbortLoop, getPageContextFromAllRewrites, isAbortError,
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);
@@ -470,7 +476,6 @@ function changeUrl(url, overwriteLastHistoryEntry) {
470
476
  return;
471
477
  browserNativeScrollRestoration_disable();
472
478
  pushHistoryState(url, overwriteLastHistoryEntry);
473
- updateState();
474
479
  }
475
480
  function handleErrorFetchingStaticAssets(err, pageContext, isFirstRender) {
476
481
  if (!isErrorFetchingStaticAssets(err)) {
@@ -577,7 +577,9 @@ async function write(urlOriginal, pageContext, fileExtension, fileContent, root,
577
577
  assertPosixPath(fileUrl);
578
578
  assert(fileUrl.startsWith('/'));
579
579
  const filePathRelative = fileUrl.slice(1);
580
- assert(!filePathRelative.startsWith('/'));
580
+ assert(!filePathRelative.startsWith('/'),
581
+ // Let's remove this debug info after we add a assertUsage() avoiding https://github.com/vikejs/vike/issues/1929
582
+ { urlOriginal, fileUrl });
581
583
  assertPosixPath(outDirClient);
582
584
  assertPosixPath(filePathRelative);
583
585
  const filePath = path.posix.join(outDirClient, filePathRelative);
@@ -1 +1 @@
1
- export declare const PROJECT_VERSION: "0.4.199-commit-5883046";
1
+ export declare const PROJECT_VERSION: "0.4.199-commit-dc15087";
@@ -1,2 +1,2 @@
1
1
  // Automatically updated by @brillout/release-me
2
- export const PROJECT_VERSION = '0.4.199-commit-5883046';
2
+ export const PROJECT_VERSION = '0.4.199-commit-dc15087';
@@ -1,4 +1,4 @@
1
1
  export declare const projectInfo: {
2
2
  projectName: "Vike";
3
- projectVersion: "0.4.199-commit-5883046";
3
+ projectVersion: "0.4.199-commit-dc15087";
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vike",
3
- "version": "0.4.199-commit-5883046",
3
+ "version": "0.4.199-commit-dc15087",
4
4
  "repository": "https://github.com/vikejs/vike",
5
5
  "exports": {
6
6
  "./server": {