swup 4.4.4 → 4.5.1
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.
- package/dist/Swup.cjs +1 -1
- package/dist/Swup.cjs.map +1 -1
- package/dist/Swup.modern.js +1 -1
- package/dist/Swup.modern.js.map +1 -1
- package/dist/Swup.module.js +1 -1
- package/dist/Swup.module.js.map +1 -1
- package/dist/Swup.umd.js +1 -1
- package/dist/Swup.umd.js.map +1 -1
- package/dist/types/Swup.d.ts +8 -4
- package/dist/types/Swup.d.ts.map +1 -1
- package/dist/types/helpers/Location.d.ts.map +1 -1
- package/dist/types/helpers/history.d.ts +14 -0
- package/dist/types/helpers/history.d.ts.map +1 -0
- package/dist/types/helpers.d.ts +1 -2
- package/dist/types/helpers.d.ts.map +1 -1
- package/dist/types/modules/Classes.d.ts.map +1 -1
- package/dist/types/modules/Hooks.d.ts +16 -8
- package/dist/types/modules/Hooks.d.ts.map +1 -1
- package/dist/types/modules/Visit.d.ts +28 -22
- package/dist/types/modules/Visit.d.ts.map +1 -1
- package/dist/types/modules/animatePageIn.d.ts +2 -1
- package/dist/types/modules/animatePageIn.d.ts.map +1 -1
- package/dist/types/modules/animatePageOut.d.ts +2 -1
- package/dist/types/modules/animatePageOut.d.ts.map +1 -1
- package/dist/types/modules/fetchPage.d.ts.map +1 -1
- package/dist/types/modules/navigate.d.ts +2 -2
- package/dist/types/modules/navigate.d.ts.map +1 -1
- package/dist/types/modules/renderPage.d.ts +2 -1
- package/dist/types/modules/renderPage.d.ts.map +1 -1
- package/dist/types/modules/replaceContent.d.ts.map +1 -1
- package/dist/types/modules/scrollToContent.d.ts +2 -1
- package/dist/types/modules/scrollToContent.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/Swup.ts +43 -40
- package/src/helpers/Location.ts +1 -0
- package/src/helpers/getCurrentUrl.ts +1 -1
- package/src/helpers/history.ts +37 -0
- package/src/helpers.ts +1 -2
- package/src/modules/Cache.ts +2 -2
- package/src/modules/Classes.ts +8 -1
- package/src/modules/Hooks.ts +98 -37
- package/src/modules/Visit.ts +86 -47
- package/src/modules/animatePageIn.ts +9 -8
- package/src/modules/animatePageOut.ts +7 -18
- package/src/modules/fetchPage.ts +9 -11
- package/src/modules/navigate.ts +83 -39
- package/src/modules/renderPage.ts +18 -18
- package/src/modules/replaceContent.ts +1 -0
- package/src/modules/scrollToContent.ts +6 -4
- package/dist/types/helpers/createHistoryRecord.d.ts +0 -10
- package/dist/types/helpers/createHistoryRecord.d.ts.map +0 -1
- package/dist/types/helpers/updateHistoryRecord.d.ts +0 -3
- package/dist/types/helpers/updateHistoryRecord.d.ts.map +0 -1
- package/src/helpers/createHistoryRecord.ts +0 -24
- package/src/helpers/updateHistoryRecord.ts +0 -19
package/src/Swup.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { renderPage } from './modules/renderPage.js';
|
|
|
21
21
|
import { use, unuse, findPlugin, type Plugin } from './modules/plugins.js';
|
|
22
22
|
import { isSameResolvedUrl, resolveUrl } from './modules/resolveUrl.js';
|
|
23
23
|
import { nextTick } from './utils.js';
|
|
24
|
-
import { type HistoryState } from './helpers/
|
|
24
|
+
import { type HistoryState } from './helpers/history.js';
|
|
25
25
|
|
|
26
26
|
/** Options for customizing swup's behavior. */
|
|
27
27
|
export type Options = {
|
|
@@ -41,6 +41,8 @@ export type Options = {
|
|
|
41
41
|
linkSelector: string;
|
|
42
42
|
/** How swup handles links to the same page. Default: `scroll` */
|
|
43
43
|
linkToSelf: NavigationToSelfAction;
|
|
44
|
+
/** Enable native animations using the View Transitions API. */
|
|
45
|
+
native: boolean;
|
|
44
46
|
/** Plugins to register on startup. */
|
|
45
47
|
plugins: Plugin[];
|
|
46
48
|
/** Custom headers sent along with fetch requests. */
|
|
@@ -62,6 +64,7 @@ const defaults: Options = {
|
|
|
62
64
|
ignoreVisit: (url, { el } = {}) => !!el?.closest('[data-no-swup]'),
|
|
63
65
|
linkSelector: 'a[href]',
|
|
64
66
|
linkToSelf: 'scroll',
|
|
67
|
+
native: false,
|
|
65
68
|
plugins: [],
|
|
66
69
|
resolveUrl: (url) => url,
|
|
67
70
|
requestHeaders: {
|
|
@@ -98,6 +101,8 @@ export default class Swup {
|
|
|
98
101
|
protected clickDelegate?: DelegateEventUnsubscribe;
|
|
99
102
|
/** Navigation status */
|
|
100
103
|
protected navigating: boolean = false;
|
|
104
|
+
/** Run anytime a visit ends */
|
|
105
|
+
protected onVisitEnd?: () => Promise<unknown>;
|
|
101
106
|
|
|
102
107
|
/** Install a plugin */
|
|
103
108
|
use = use;
|
|
@@ -149,7 +154,7 @@ export default class Swup {
|
|
|
149
154
|
this.hooks = new Hooks(this);
|
|
150
155
|
this.visit = this.createVisit({ to: '' });
|
|
151
156
|
|
|
152
|
-
this.currentHistoryIndex = (history.state as HistoryState)?.index ?? 1;
|
|
157
|
+
this.currentHistoryIndex = (window.history.state as HistoryState)?.index ?? 1;
|
|
153
158
|
|
|
154
159
|
if (!this.checkRequirements()) {
|
|
155
160
|
return;
|
|
@@ -185,11 +190,14 @@ export default class Swup {
|
|
|
185
190
|
// https://github.com/swup/swup/issues/475
|
|
186
191
|
}
|
|
187
192
|
|
|
193
|
+
// Sanitize/check native option
|
|
194
|
+
this.options.native = this.options.native && !!document.startViewTransition;
|
|
195
|
+
|
|
188
196
|
// Mount plugins
|
|
189
197
|
this.options.plugins.forEach((plugin) => this.use(plugin));
|
|
190
198
|
|
|
191
199
|
// Create initial history record
|
|
192
|
-
if ((history.state as HistoryState)?.source !== 'swup') {
|
|
200
|
+
if ((window.history.state as HistoryState)?.source !== 'swup') {
|
|
193
201
|
updateHistoryRecord(null, { index: this.currentHistoryIndex });
|
|
194
202
|
}
|
|
195
203
|
|
|
@@ -197,9 +205,10 @@ export default class Swup {
|
|
|
197
205
|
await nextTick();
|
|
198
206
|
|
|
199
207
|
// Trigger enable hook
|
|
200
|
-
await this.hooks.call('enable', undefined, () => {
|
|
201
|
-
|
|
202
|
-
|
|
208
|
+
await this.hooks.call('enable', undefined, undefined, () => {
|
|
209
|
+
const html = document.documentElement;
|
|
210
|
+
html.classList.add('swup-enabled');
|
|
211
|
+
html.classList.toggle('swup-native', this.options.native);
|
|
203
212
|
});
|
|
204
213
|
}
|
|
205
214
|
|
|
@@ -218,9 +227,10 @@ export default class Swup {
|
|
|
218
227
|
this.options.plugins.forEach((plugin) => this.unuse(plugin));
|
|
219
228
|
|
|
220
229
|
// trigger disable hook
|
|
221
|
-
await this.hooks.call('disable', undefined, () => {
|
|
222
|
-
|
|
223
|
-
|
|
230
|
+
await this.hooks.call('disable', undefined, undefined, () => {
|
|
231
|
+
const html = document.documentElement;
|
|
232
|
+
html.classList.remove('swup-enabled');
|
|
233
|
+
html.classList.remove('swup-native');
|
|
224
234
|
});
|
|
225
235
|
|
|
226
236
|
// remove handlers
|
|
@@ -265,11 +275,11 @@ export default class Swup {
|
|
|
265
275
|
return;
|
|
266
276
|
}
|
|
267
277
|
|
|
268
|
-
|
|
278
|
+
const visit = this.createVisit({ to: url, hash, el, event });
|
|
269
279
|
|
|
270
280
|
// Exit early if control key pressed
|
|
271
281
|
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
272
|
-
this.hooks.
|
|
282
|
+
this.hooks.callSync('link:newtab', visit, { href });
|
|
273
283
|
return;
|
|
274
284
|
}
|
|
275
285
|
|
|
@@ -278,8 +288,8 @@ export default class Swup {
|
|
|
278
288
|
return;
|
|
279
289
|
}
|
|
280
290
|
|
|
281
|
-
this.hooks.callSync('link:click', { el, event }, () => {
|
|
282
|
-
const from =
|
|
291
|
+
this.hooks.callSync('link:click', visit, { el, event }, () => {
|
|
292
|
+
const from = visit.from.url ?? '';
|
|
283
293
|
|
|
284
294
|
event.preventDefault();
|
|
285
295
|
|
|
@@ -287,20 +297,18 @@ export default class Swup {
|
|
|
287
297
|
if (!url || url === from) {
|
|
288
298
|
if (hash) {
|
|
289
299
|
// With hash: scroll to anchor
|
|
290
|
-
this.hooks.callSync('link:anchor', { hash }, () => {
|
|
300
|
+
this.hooks.callSync('link:anchor', visit, { hash }, () => {
|
|
291
301
|
updateHistoryRecord(url + hash);
|
|
292
|
-
this.scrollToContent();
|
|
302
|
+
this.scrollToContent(visit);
|
|
293
303
|
});
|
|
294
304
|
} else {
|
|
295
305
|
// Without hash: scroll to top or load/reload page
|
|
296
|
-
this.hooks.callSync('link:self', undefined, () => {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
updateHistoryRecord(url);
|
|
303
|
-
return this.scrollToContent();
|
|
306
|
+
this.hooks.callSync('link:self', visit, undefined, () => {
|
|
307
|
+
if (this.options.linkToSelf === 'navigate') {
|
|
308
|
+
this.performNavigation(visit);
|
|
309
|
+
} else {
|
|
310
|
+
updateHistoryRecord(url);
|
|
311
|
+
this.scrollToContent(visit);
|
|
304
312
|
}
|
|
305
313
|
});
|
|
306
314
|
}
|
|
@@ -313,12 +321,12 @@ export default class Swup {
|
|
|
313
321
|
}
|
|
314
322
|
|
|
315
323
|
// Finally, proceed with loading the page
|
|
316
|
-
this.performNavigation();
|
|
324
|
+
this.performNavigation(visit);
|
|
317
325
|
});
|
|
318
326
|
}
|
|
319
327
|
|
|
320
328
|
protected handlePopState(event: PopStateEvent) {
|
|
321
|
-
const href: string = (event.state as HistoryState)?.url ?? location.href;
|
|
329
|
+
const href: string = (event.state as HistoryState)?.url ?? window.location.href;
|
|
322
330
|
|
|
323
331
|
// Exit early if this event should be ignored
|
|
324
332
|
if (this.options.skipPopStateHandling(event)) {
|
|
@@ -332,37 +340,32 @@ export default class Swup {
|
|
|
332
340
|
|
|
333
341
|
const { url, hash } = Location.fromUrl(href);
|
|
334
342
|
|
|
335
|
-
|
|
343
|
+
const visit = this.createVisit({ to: url, hash, event });
|
|
336
344
|
|
|
337
345
|
// Mark as history visit
|
|
338
|
-
|
|
346
|
+
visit.history.popstate = true;
|
|
339
347
|
|
|
340
348
|
// Determine direction of history visit
|
|
341
349
|
const index = (event.state as HistoryState)?.index ?? 0;
|
|
342
350
|
if (index && index !== this.currentHistoryIndex) {
|
|
343
351
|
const direction = index - this.currentHistoryIndex > 0 ? 'forwards' : 'backwards';
|
|
344
|
-
|
|
352
|
+
visit.history.direction = direction;
|
|
345
353
|
this.currentHistoryIndex = index;
|
|
346
354
|
}
|
|
347
355
|
|
|
348
356
|
// Disable animation & scrolling for history visits
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
357
|
+
visit.animation.animate = false;
|
|
358
|
+
visit.scroll.reset = false;
|
|
359
|
+
visit.scroll.target = false;
|
|
352
360
|
|
|
353
361
|
// Animated history visit: re-enable animation & scroll reset
|
|
354
362
|
if (this.options.animateHistoryBrowsing) {
|
|
355
|
-
|
|
356
|
-
|
|
363
|
+
visit.animation.animate = true;
|
|
364
|
+
visit.scroll.reset = true;
|
|
357
365
|
}
|
|
358
366
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
// event.preventDefault();
|
|
362
|
-
// }
|
|
363
|
-
|
|
364
|
-
this.hooks.callSync('history:popstate', { event }, () => {
|
|
365
|
-
this.performNavigation();
|
|
367
|
+
this.hooks.callSync('history:popstate', visit, { event }, () => {
|
|
368
|
+
this.performNavigation(visit);
|
|
366
369
|
});
|
|
367
370
|
}
|
|
368
371
|
|
package/src/helpers/Location.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** Get the current page URL: path name + query params. Optionally including hash. */
|
|
2
2
|
export const getCurrentUrl = ({ hash }: { hash?: boolean } = {}): string => {
|
|
3
|
-
return location.pathname + location.search + (hash ? location.hash : '');
|
|
3
|
+
return window.location.pathname + window.location.search + (hash ? window.location.hash : '');
|
|
4
4
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getCurrentUrl } from './getCurrentUrl.js';
|
|
2
|
+
|
|
3
|
+
export interface HistoryState {
|
|
4
|
+
url: string;
|
|
5
|
+
source: 'swup';
|
|
6
|
+
random: number;
|
|
7
|
+
index?: number;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type HistoryData = Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
/** Create a new history record with a custom swup identifier. */
|
|
14
|
+
export const createHistoryRecord = (url: string, data: HistoryData = {}): void => {
|
|
15
|
+
url = url || getCurrentUrl({ hash: true });
|
|
16
|
+
const state: HistoryState = {
|
|
17
|
+
url,
|
|
18
|
+
random: Math.random(),
|
|
19
|
+
source: 'swup',
|
|
20
|
+
...data
|
|
21
|
+
};
|
|
22
|
+
window.history.pushState(state, '', url);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Update the current history record with a custom swup identifier. */
|
|
26
|
+
export const updateHistoryRecord = (url: string | null = null, data: HistoryData = {}): void => {
|
|
27
|
+
url = url || getCurrentUrl({ hash: true });
|
|
28
|
+
const currentState = (window.history.state as HistoryState) || {};
|
|
29
|
+
const state: HistoryState = {
|
|
30
|
+
...currentState,
|
|
31
|
+
url,
|
|
32
|
+
random: Math.random(),
|
|
33
|
+
source: 'swup',
|
|
34
|
+
...data
|
|
35
|
+
};
|
|
36
|
+
window.history.replaceState(state, '', url);
|
|
37
|
+
};
|
package/src/helpers.ts
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
// e.g. import { updateHistoryRecord } from 'swup'
|
|
3
3
|
|
|
4
4
|
export { classify } from './helpers/classify.js';
|
|
5
|
-
export { createHistoryRecord } from './helpers/
|
|
6
|
-
export { updateHistoryRecord } from './helpers/updateHistoryRecord.js';
|
|
5
|
+
export { createHistoryRecord, updateHistoryRecord } from './helpers/history.js';
|
|
7
6
|
export { delegateEvent } from './helpers/delegateEvent.js';
|
|
8
7
|
export { getCurrentUrl } from './helpers/getCurrentUrl.js';
|
|
9
8
|
export { Location } from './helpers/Location.js';
|
package/src/modules/Cache.ts
CHANGED
|
@@ -49,7 +49,7 @@ export class Cache {
|
|
|
49
49
|
url = this.resolve(url);
|
|
50
50
|
page = { ...page, url };
|
|
51
51
|
this.pages.set(url, page);
|
|
52
|
-
this.swup.hooks.callSync('cache:set', { page });
|
|
52
|
+
this.swup.hooks.callSync('cache:set', undefined, { page });
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/** Update a cache record, overwriting or adding custom data. */
|
|
@@ -67,7 +67,7 @@ export class Cache {
|
|
|
67
67
|
/** Empty the cache. */
|
|
68
68
|
clear(): void {
|
|
69
69
|
this.pages.clear();
|
|
70
|
-
this.swup.hooks.callSync('cache:clear', undefined);
|
|
70
|
+
this.swup.hooks.callSync('cache:clear', undefined, undefined);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/** Remove all cache entries that return true for a given predicate function. */
|
package/src/modules/Classes.ts
CHANGED
|
@@ -3,7 +3,14 @@ import { queryAll } from '../utils.js';
|
|
|
3
3
|
|
|
4
4
|
export class Classes {
|
|
5
5
|
protected swup: Swup;
|
|
6
|
-
protected swupClasses = [
|
|
6
|
+
protected swupClasses = [
|
|
7
|
+
'to-',
|
|
8
|
+
'is-changing',
|
|
9
|
+
'is-rendering',
|
|
10
|
+
'is-popstate',
|
|
11
|
+
'is-animating',
|
|
12
|
+
'is-leaving'
|
|
13
|
+
];
|
|
7
14
|
|
|
8
15
|
constructor(swup: Swup) {
|
|
9
16
|
this.swup = swup;
|
package/src/modules/Hooks.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { DelegateEvent } from 'delegate-it';
|
|
|
2
2
|
|
|
3
3
|
import type Swup from '../Swup.js';
|
|
4
4
|
import { isPromise, runAsPromise } from '../utils.js';
|
|
5
|
-
import
|
|
5
|
+
import { Visit } from './Visit.js';
|
|
6
6
|
import type { FetchOptions, PageData } from './fetchPage.js';
|
|
7
7
|
|
|
8
8
|
export interface HookDefinitions {
|
|
@@ -33,16 +33,16 @@ export interface HookDefinitions {
|
|
|
33
33
|
'scroll:anchor': { hash: string; options: ScrollIntoViewOptions };
|
|
34
34
|
'visit:start': undefined;
|
|
35
35
|
'visit:transition': undefined;
|
|
36
|
+
'visit:abort': undefined;
|
|
36
37
|
'visit:end': undefined;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export interface HookReturnValues {
|
|
40
|
-
'content:scroll': Promise<boolean
|
|
41
|
+
'content:scroll': Promise<boolean> | boolean;
|
|
41
42
|
'fetch:request': Promise<Response>;
|
|
42
43
|
'page:load': Promise<PageData>;
|
|
43
44
|
'scroll:top': boolean;
|
|
44
45
|
'scroll:anchor': boolean;
|
|
45
|
-
'visit:transition': Promise<boolean>;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export type HookArguments<T extends HookName> = HookDefinitions[T];
|
|
@@ -154,6 +154,7 @@ export class Hooks {
|
|
|
154
154
|
'scroll:anchor',
|
|
155
155
|
'visit:start',
|
|
156
156
|
'visit:transition',
|
|
157
|
+
'visit:abort',
|
|
157
158
|
'visit:end'
|
|
158
159
|
];
|
|
159
160
|
|
|
@@ -333,20 +334,29 @@ export class Hooks {
|
|
|
333
334
|
* Trigger a hook asynchronously, executing its default handler and all registered handlers.
|
|
334
335
|
* Will execute all handlers in order and `await` any `Promise`s they return.
|
|
335
336
|
* @param hook Name of the hook to trigger
|
|
337
|
+
* @param visit The visit object this hook belongs to
|
|
336
338
|
* @param args Arguments to pass to the handler
|
|
337
339
|
* @param defaultHandler A default implementation of this hook to execute
|
|
338
340
|
* @returns The resolved return value of the executed default handler
|
|
339
341
|
*/
|
|
342
|
+
// Overload: default order of arguments
|
|
343
|
+
async call<T extends HookName>(hook: T, visit: Visit | undefined, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>>; // prettier-ignore
|
|
344
|
+
// Overload: legacy order of arguments, with visit missing
|
|
345
|
+
async call<T extends HookName>(hook: T, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>>; // prettier-ignore
|
|
346
|
+
// Implementation
|
|
340
347
|
async call<T extends HookName>(
|
|
341
348
|
hook: T,
|
|
342
|
-
|
|
343
|
-
|
|
349
|
+
arg1: Visit | HookArguments<T>,
|
|
350
|
+
arg2: HookArguments<T> | HookDefaultHandler<T>,
|
|
351
|
+
arg3?: HookDefaultHandler<T>
|
|
344
352
|
): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>> {
|
|
353
|
+
const [visit, args, defaultHandler] = this.parseCallArgs(hook, arg1, arg2, arg3);
|
|
354
|
+
|
|
345
355
|
const { before, handler, after } = this.getHandlers(hook, defaultHandler);
|
|
346
|
-
await this.run(before, args);
|
|
347
|
-
const [result] = await this.run(handler, args);
|
|
348
|
-
await this.run(after, args);
|
|
349
|
-
this.dispatchDomEvent(hook, args);
|
|
356
|
+
await this.run(before, visit, args);
|
|
357
|
+
const [result] = await this.run(handler, visit, args, true);
|
|
358
|
+
await this.run(after, visit, args);
|
|
359
|
+
this.dispatchDomEvent(hook, visit, args);
|
|
350
360
|
return result;
|
|
351
361
|
}
|
|
352
362
|
|
|
@@ -354,23 +364,51 @@ export class Hooks {
|
|
|
354
364
|
* Trigger a hook synchronously, executing its default handler and all registered handlers.
|
|
355
365
|
* Will execute all handlers in order, but will **not** `await` any `Promise`s they return.
|
|
356
366
|
* @param hook Name of the hook to trigger
|
|
367
|
+
* @param visit The visit object this hook belongs to
|
|
357
368
|
* @param args Arguments to pass to the handler
|
|
358
369
|
* @param defaultHandler A default implementation of this hook to execute
|
|
359
370
|
* @returns The (possibly unresolved) return value of the executed default handler
|
|
360
371
|
*/
|
|
372
|
+
// Overload: default order of arguments
|
|
373
|
+
callSync<T extends HookName>(hook: T, visit: Visit | undefined, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): ReturnType<HookDefaultHandler<T>>; // prettier-ignore
|
|
374
|
+
// Overload: legacy order of arguments, with visit missing
|
|
375
|
+
callSync<T extends HookName>(hook: T, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): ReturnType<HookDefaultHandler<T>>; // prettier-ignore
|
|
376
|
+
// Implementation
|
|
361
377
|
callSync<T extends HookName>(
|
|
362
378
|
hook: T,
|
|
363
|
-
|
|
364
|
-
|
|
379
|
+
arg1: Visit | HookArguments<T>,
|
|
380
|
+
arg2: HookArguments<T> | HookDefaultHandler<T>,
|
|
381
|
+
arg3?: HookDefaultHandler<T>
|
|
365
382
|
): ReturnType<HookDefaultHandler<T>> {
|
|
383
|
+
const [visit, args, defaultHandler] = this.parseCallArgs(hook, arg1, arg2, arg3);
|
|
366
384
|
const { before, handler, after } = this.getHandlers(hook, defaultHandler);
|
|
367
|
-
this.runSync(before, args);
|
|
368
|
-
const [result] = this.runSync(handler, args);
|
|
369
|
-
this.runSync(after, args);
|
|
370
|
-
this.dispatchDomEvent(hook, args);
|
|
385
|
+
this.runSync(before, visit, args);
|
|
386
|
+
const [result] = this.runSync(handler, visit, args, true);
|
|
387
|
+
this.runSync(after, visit, args);
|
|
388
|
+
this.dispatchDomEvent(hook, visit, args);
|
|
371
389
|
return result;
|
|
372
390
|
}
|
|
373
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Parse the call arguments for call() and callSync() to allow legacy argument order.
|
|
394
|
+
*/
|
|
395
|
+
protected parseCallArgs<T extends HookName>(
|
|
396
|
+
hook: T,
|
|
397
|
+
arg1: Visit | HookArguments<T> | undefined,
|
|
398
|
+
arg2: HookArguments<T> | HookDefaultHandler<T>,
|
|
399
|
+
arg3?: HookDefaultHandler<T>
|
|
400
|
+
): [Visit | undefined, HookArguments<T>, HookDefaultHandler<T> | undefined] {
|
|
401
|
+
const isLegacyOrder =
|
|
402
|
+
!(arg1 instanceof Visit) && (typeof arg1 === 'object' || typeof arg2 === 'function');
|
|
403
|
+
if (isLegacyOrder) {
|
|
404
|
+
// Legacy positioning: arguments in second or handler passed in third place
|
|
405
|
+
return [undefined, arg1 as HookArguments<T>, arg2 as HookDefaultHandler<T>];
|
|
406
|
+
} else {
|
|
407
|
+
// Default positioning: visit passed in as first argument
|
|
408
|
+
return [arg1, arg2 as HookArguments<T>, arg3];
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
374
412
|
/**
|
|
375
413
|
* Execute the handlers for a hook, in order, as `Promise`s that will be `await`ed.
|
|
376
414
|
* @param registrations The registrations (handler + options) to execute
|
|
@@ -378,20 +416,29 @@ export class Hooks {
|
|
|
378
416
|
*/
|
|
379
417
|
|
|
380
418
|
// Overload: running HookDefaultHandler: expect HookDefaultHandler return type
|
|
381
|
-
protected async run<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], args: HookArguments<T
|
|
419
|
+
protected async run<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], visit: Visit | undefined, args: HookArguments<T>, rethrow: true): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>[]>; // prettier-ignore
|
|
382
420
|
// Overload: running user handler: expect no specific type
|
|
383
|
-
protected async run<T extends HookName>(registrations: HookRegistration<T>[], args: HookArguments<T>): Promise<unknown[]>; // prettier-ignore
|
|
421
|
+
protected async run<T extends HookName>(registrations: HookRegistration<T>[], visit: Visit | undefined, args: HookArguments<T>): Promise<unknown[]>; // prettier-ignore
|
|
384
422
|
// Implementation
|
|
385
423
|
protected async run<T extends HookName, R extends HookRegistration<T>[]>(
|
|
386
424
|
registrations: R,
|
|
387
|
-
|
|
425
|
+
visit: Visit | undefined = this.swup.visit,
|
|
426
|
+
args: HookArguments<T>,
|
|
427
|
+
rethrow: boolean = false
|
|
388
428
|
): Promise<Awaited<ReturnType<HookDefaultHandler<T>>> | unknown[]> {
|
|
389
429
|
const results = [];
|
|
390
430
|
for (const { hook, handler, defaultHandler, once } of registrations) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
431
|
+
if (visit?.done) continue;
|
|
432
|
+
if (once) this.off(hook, handler);
|
|
433
|
+
try {
|
|
434
|
+
const result = await runAsPromise(handler, [visit, args, defaultHandler]);
|
|
435
|
+
results.push(result);
|
|
436
|
+
} catch (error) {
|
|
437
|
+
if (rethrow) {
|
|
438
|
+
throw error;
|
|
439
|
+
} else {
|
|
440
|
+
console.error(`Error in hook '${hook}':`, error);
|
|
441
|
+
}
|
|
395
442
|
}
|
|
396
443
|
}
|
|
397
444
|
return results;
|
|
@@ -404,26 +451,34 @@ export class Hooks {
|
|
|
404
451
|
*/
|
|
405
452
|
|
|
406
453
|
// Overload: running HookDefaultHandler: expect HookDefaultHandler return type
|
|
407
|
-
protected runSync<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], args: HookArguments<T
|
|
454
|
+
protected runSync<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], visit: Visit | undefined, args: HookArguments<T>, rethrow: true): ReturnType<HookDefaultHandler<T>>[]; // prettier-ignore
|
|
408
455
|
// Overload: running user handler: expect no specific type
|
|
409
|
-
protected runSync<T extends HookName>(registrations: HookRegistration<T>[], args: HookArguments<T>): unknown[]; // prettier-ignore
|
|
456
|
+
protected runSync<T extends HookName>(registrations: HookRegistration<T>[], visit: Visit | undefined, args: HookArguments<T>): unknown[]; // prettier-ignore
|
|
410
457
|
// Implementation
|
|
411
458
|
protected runSync<T extends HookName, R extends HookRegistration<T>[]>(
|
|
412
459
|
registrations: R,
|
|
413
|
-
|
|
460
|
+
visit: Visit | undefined = this.swup.visit,
|
|
461
|
+
args: HookArguments<T>,
|
|
462
|
+
rethrow: boolean = false
|
|
414
463
|
): (ReturnType<HookDefaultHandler<T>> | unknown)[] {
|
|
415
464
|
const results = [];
|
|
416
465
|
for (const { hook, handler, defaultHandler, once } of registrations) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
466
|
+
if (visit?.done) continue;
|
|
467
|
+
if (once) this.off(hook, handler);
|
|
468
|
+
try {
|
|
469
|
+
const result = (handler as HookDefaultHandler<T>)(visit, args, defaultHandler);
|
|
470
|
+
results.push(result);
|
|
471
|
+
if (isPromise(result)) {
|
|
472
|
+
console.warn(
|
|
473
|
+
`Swup will not await Promises in handler for synchronous hook '${hook}'.`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
} catch (error) {
|
|
477
|
+
if (rethrow) {
|
|
478
|
+
throw error;
|
|
479
|
+
} else {
|
|
480
|
+
console.error(`Error in hook '${hook}':`, error);
|
|
481
|
+
}
|
|
427
482
|
}
|
|
428
483
|
}
|
|
429
484
|
return results;
|
|
@@ -500,8 +555,14 @@ export class Hooks {
|
|
|
500
555
|
* Dispatch a custom event on the `document` for a hook. Prefixed with `swup:`
|
|
501
556
|
* @param hook Name of the hook.
|
|
502
557
|
*/
|
|
503
|
-
protected dispatchDomEvent<T extends HookName>(
|
|
504
|
-
|
|
558
|
+
protected dispatchDomEvent<T extends HookName>(
|
|
559
|
+
hook: T,
|
|
560
|
+
visit: Visit | undefined,
|
|
561
|
+
args?: HookArguments<T>
|
|
562
|
+
): void {
|
|
563
|
+
if (visit?.done) return;
|
|
564
|
+
|
|
565
|
+
const detail: HookEventDetail = { hook, args, visit: visit || this.swup.visit };
|
|
505
566
|
document.dispatchEvent(
|
|
506
567
|
new CustomEvent<HookEventDetail>(`swup:any`, { detail, bubbles: true })
|
|
507
568
|
);
|