swup 4.0.0-rc.14 → 4.0.0-rc.21

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.
Files changed (83) hide show
  1. package/README.md +100 -0
  2. package/dist/Swup.cjs +1 -1
  3. package/dist/Swup.cjs.map +1 -1
  4. package/dist/Swup.modern.js +1 -1
  5. package/dist/Swup.modern.js.map +1 -1
  6. package/dist/Swup.module.js +1 -1
  7. package/dist/Swup.module.js.map +1 -1
  8. package/dist/Swup.umd.js +1 -1
  9. package/dist/Swup.umd.js.map +1 -1
  10. package/dist/types/Swup.d.ts +62 -54
  11. package/dist/types/helpers/Location.d.ts +10 -7
  12. package/dist/types/helpers/delegateEvent.d.ts +3 -5
  13. package/dist/types/helpers/matchPath.d.ts +3 -0
  14. package/dist/types/helpers.d.ts +7 -10
  15. package/dist/types/index.d.ts +9 -6
  16. package/dist/types/modules/Cache.d.ts +15 -15
  17. package/dist/types/modules/Classes.d.ts +13 -0
  18. package/dist/types/modules/Context.d.ts +73 -0
  19. package/dist/types/modules/Hooks.d.ts +241 -0
  20. package/dist/types/modules/__test__/hooks.test.d.ts +1 -0
  21. package/dist/types/modules/__test__/replaceContent.test.d.ts +1 -0
  22. package/dist/types/modules/awaitAnimations.d.ts +21 -0
  23. package/dist/types/modules/enterPage.d.ts +6 -3
  24. package/dist/types/modules/fetchPage.d.ts +24 -4
  25. package/dist/types/modules/getAnchorElement.d.ts +8 -0
  26. package/dist/types/modules/leavePage.d.ts +6 -3
  27. package/dist/types/modules/plugins.d.ts +12 -5
  28. package/dist/types/modules/renderPage.d.ts +7 -7
  29. package/dist/types/modules/replaceContent.d.ts +8 -11
  30. package/dist/types/modules/visit.d.ts +33 -0
  31. package/dist/types/utils/index.d.ts +3 -1
  32. package/dist/types/utils.d.ts +1 -1
  33. package/package.json +7 -6
  34. package/src/Swup.ts +83 -65
  35. package/src/__test__/index.test.ts +3 -3
  36. package/src/helpers/Location.ts +2 -2
  37. package/src/helpers/delegateEvent.ts +2 -2
  38. package/src/helpers.ts +0 -1
  39. package/src/index.ts +34 -4
  40. package/src/modules/Cache.ts +2 -2
  41. package/src/modules/Classes.ts +48 -0
  42. package/src/modules/Context.ts +49 -19
  43. package/src/modules/Hooks.ts +103 -83
  44. package/src/modules/__test__/cache.test.ts +6 -6
  45. package/src/modules/__test__/hooks.test.ts +111 -40
  46. package/src/modules/__test__/replaceContent.test.ts +92 -0
  47. package/src/modules/{getAnimationPromises.ts → awaitAnimations.ts} +13 -18
  48. package/src/modules/enterPage.ts +21 -17
  49. package/src/modules/fetchPage.ts +12 -12
  50. package/src/modules/getAnchorElement.ts +2 -1
  51. package/src/modules/leavePage.ts +16 -12
  52. package/src/modules/plugins.ts +11 -8
  53. package/src/modules/renderPage.ts +28 -18
  54. package/src/modules/replaceContent.ts +24 -16
  55. package/src/modules/visit.ts +143 -0
  56. package/src/utils/index.ts +1 -2
  57. package/dist/types/helpers/cleanupAnimationClasses.d.ts +0 -2
  58. package/dist/types/helpers/fetch.d.ts +0 -5
  59. package/dist/types/helpers/getDataFromHtml.d.ts +0 -7
  60. package/dist/types/helpers/markSwupElements.d.ts +0 -1
  61. package/dist/types/modules/destroy.d.ts +0 -2
  62. package/dist/types/modules/enable.d.ts +0 -2
  63. package/dist/types/modules/events.d.ts +0 -33
  64. package/dist/types/modules/getAnimationPromises.d.ts +0 -7
  65. package/dist/types/modules/getPageData.d.ts +0 -6
  66. package/dist/types/modules/handleLinkToSamePage.d.ts +0 -2
  67. package/dist/types/modules/isSameResolvedUrl.d.ts +0 -8
  68. package/dist/types/modules/linkClickHandler.d.ts +0 -3
  69. package/dist/types/modules/loadPage.d.ts +0 -12
  70. package/dist/types/modules/off.d.ts +0 -3
  71. package/dist/types/modules/on.d.ts +0 -5
  72. package/dist/types/modules/popStateHandler.d.ts +0 -2
  73. package/dist/types/modules/resolveUrl.d.ts +0 -7
  74. package/dist/types/modules/shouldIgnoreVisit.d.ts +0 -4
  75. package/dist/types/modules/transitions.d.ts +0 -6
  76. package/dist/types/modules/triggerEvent.d.ts +0 -3
  77. package/dist/types/modules/triggerWillOpenNewWindow.d.ts +0 -2
  78. package/dist/types/modules/updateTransition.d.ts +0 -2
  79. package/readme.md +0 -78
  80. package/src/helpers/cleanupAnimationClasses.ts +0 -8
  81. package/src/modules/loadPage.ts +0 -99
  82. /package/dist/types/{modules/__test__/events.test.d.ts → helpers/__test__/matchPath.test.d.ts} +0 -0
  83. /package/dist/types/modules/__test__/{fetchPage.test.d.ts → cache.test.d.ts} +0 -0
@@ -4,33 +4,33 @@ import Swup, { Options } from '../Swup.js';
4
4
  import { isPromise, runAsPromise } from '../utils.js';
5
5
  import { Context } from './Context.js';
6
6
  import { FetchOptions, PageData } from './fetchPage.js';
7
- import { AnimationDirection } from './getAnimationPromises.js';
7
+ import { AnimationDirection } from './awaitAnimations.js';
8
8
 
9
9
  export interface HookDefinitions {
10
- animationInDone: undefined;
11
- animationInStart: undefined;
12
- animationOutDone: undefined;
13
- animationOutStart: undefined;
14
- animationSkipped: undefined;
15
- awaitAnimation: { selector: Options['animationSelector']; direction: AnimationDirection };
16
- cacheCleared: undefined;
17
- clickLink: { el: HTMLAnchorElement; event: DelegateEvent<MouseEvent> };
18
- disabled: undefined;
19
- enabled: undefined;
20
- fetchPage: { url: string; options: FetchOptions; response?: Response | Promise<Response> };
21
- loadPage: { url: string; options: FetchOptions; page?: PageData | Promise<PageData> };
22
- openPageInNewTab: { href: string };
23
- pageCached: { page: PageData };
24
- pageLoaded: { page: PageData; cache?: boolean };
25
- pageView: { url: string; title: string };
26
- popState: { event: PopStateEvent };
27
- replaceContent: { page: PageData; containers: Options['containers'] };
28
- samePage: undefined;
29
- samePageWithHash: { hash: string; options: ScrollIntoViewOptions };
30
- scrollToContent: { options: ScrollIntoViewOptions };
31
- serverError: { url: string; status: number; response: Response };
32
- transitionStart: undefined;
33
- transitionEnd: undefined;
10
+ 'animation:out:start': undefined;
11
+ 'animation:out:end': undefined;
12
+ 'animation:in:start': undefined;
13
+ 'animation:in:end': undefined;
14
+ 'animation:skip': undefined;
15
+ 'animation:await': { direction: AnimationDirection };
16
+ 'cache:clear': undefined;
17
+ 'cache:set': { page: PageData };
18
+ 'content:replace': { page: PageData };
19
+ 'content:scroll': { options: ScrollIntoViewOptions };
20
+ 'enable': undefined;
21
+ 'disable': undefined;
22
+ 'fetch:request': { url: string; options: FetchOptions };
23
+ 'fetch:error': { url: string; status: number; response: Response };
24
+ 'history:popstate': { event: PopStateEvent };
25
+ 'link:click': { el: HTMLAnchorElement; event: DelegateEvent<MouseEvent> };
26
+ 'link:self': undefined;
27
+ 'link:anchor': { hash: string; options: ScrollIntoViewOptions };
28
+ 'link:newtab': { href: string };
29
+ 'page:request': { url: string; options: FetchOptions };
30
+ 'page:load': { page: PageData; cache?: boolean };
31
+ 'page:view': { url: string; title: string };
32
+ 'visit:start': undefined;
33
+ 'visit:end': undefined;
34
34
  }
35
35
 
36
36
  export type HookArguments<T extends HookName> = HookDefinitions[T];
@@ -38,18 +38,28 @@ export type HookArguments<T extends HookName> = HookDefinitions[T];
38
38
  export type HookName = keyof HookDefinitions;
39
39
 
40
40
  export type Handler<T extends HookName> = (
41
+ /** The global context object for the current visit */
41
42
  context: Context,
42
- args: HookArguments<T>
43
+ /** The local arguments passed into the handler */
44
+ args: HookArguments<T>,
45
+ /** The default handler to be executed, available if replacing an internal hook handler */
46
+ defaultHandler?: Handler<T>
43
47
  ) => Promise<any> | void;
44
48
 
45
49
  export type Handlers = {
46
50
  [K in HookName]: Handler<K>[];
47
51
  };
48
52
 
53
+ export type HookUnregister = () => void;
54
+
49
55
  export type HookOptions = {
56
+ /** Execute the hook once, then remove the handler */
50
57
  once?: boolean;
58
+ /** Execute the hook before the internal default handler */
51
59
  before?: boolean;
60
+ /** Set a priority for when to execute this hook. Lower numbers execute first. Default: `0` */
52
61
  priority?: number;
62
+ /** Replace the internal default handler with this hook handler */
53
63
  replace?: boolean;
54
64
  };
55
65
 
@@ -79,30 +89,30 @@ export class Hooks {
79
89
  // Can we deduplicate this somehow? Or make it error when not in sync with HookDefinitions?
80
90
  // https://stackoverflow.com/questions/53387838/how-to-ensure-an-arrays-values-the-keys-of-a-typescript-interface/53395649
81
91
  readonly hooks: HookName[] = [
82
- 'animationInDone',
83
- 'animationInStart',
84
- 'animationOutDone',
85
- 'animationOutStart',
86
- 'animationSkipped',
87
- 'awaitAnimation',
88
- 'cacheCleared',
89
- 'clickLink',
90
- 'disabled',
91
- 'enabled',
92
- 'fetchPage',
93
- 'loadPage',
94
- 'openPageInNewTab',
95
- 'pageCached',
96
- 'pageLoaded',
97
- 'pageView',
98
- 'popState',
99
- 'replaceContent',
100
- 'samePage',
101
- 'samePageWithHash',
102
- 'scrollToContent',
103
- 'serverError',
104
- 'transitionStart',
105
- 'transitionEnd'
92
+ 'animation:out:start',
93
+ 'animation:out:end',
94
+ 'animation:in:start',
95
+ 'animation:in:end',
96
+ 'animation:skip',
97
+ 'animation:await',
98
+ 'cache:clear',
99
+ 'cache:set',
100
+ 'content:replace',
101
+ 'content:scroll',
102
+ 'enable',
103
+ 'disable',
104
+ 'fetch:request',
105
+ 'fetch:error',
106
+ 'history:popstate',
107
+ 'link:click',
108
+ 'link:self',
109
+ 'link:anchor',
110
+ 'link:newtab',
111
+ 'page:request',
112
+ 'page:load',
113
+ 'page:view',
114
+ 'visit:start',
115
+ 'visit:end'
106
116
  ];
107
117
 
108
118
  constructor(swup: Swup) {
@@ -140,9 +150,8 @@ export class Hooks {
140
150
  const ledger = this.registry.get(hook);
141
151
  if (ledger) {
142
152
  return ledger;
143
- } else {
144
- console.error(`Unknown hook '${hook}'`);
145
153
  }
154
+ console.error(`Unknown hook '${hook}'`);
146
155
  }
147
156
 
148
157
  /**
@@ -162,21 +171,26 @@ export class Hooks {
162
171
  * - `before`: Execute the handler before the default handler
163
172
  * - `priority`: Specify the order in which the handlers are executed
164
173
  * - `replace`: Replace the default handler with this handler
165
- * @returns The handler function
174
+ * @returns A function to unregister the handler
166
175
  */
167
- on<T extends HookName>(hook: T, handler: Handler<T>): Handler<T>;
168
- on<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions): Handler<T>;
169
- on<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions = {}): Handler<T> {
176
+ on<T extends HookName>(hook: T, handler: Handler<T>): HookUnregister;
177
+ on<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions): HookUnregister;
178
+ on<T extends HookName>(
179
+ hook: T,
180
+ handler: Handler<T>,
181
+ options: HookOptions = {}
182
+ ): HookUnregister {
170
183
  const ledger = this.get(hook);
171
184
  if (!ledger) {
172
185
  console.warn(`Hook '${hook}' not found.`);
173
- return handler;
186
+ return () => {};
174
187
  }
175
188
 
176
189
  const id = ledger.size + 1;
177
190
  const registration: HookRegistration<T> = { ...options, id, hook, handler };
178
191
  ledger.set(handler, registration);
179
- return handler;
192
+
193
+ return () => this.off(hook, handler);
180
194
  }
181
195
 
182
196
  /**
@@ -185,16 +199,16 @@ export class Hooks {
185
199
  * @param hook Name of the hook to listen for
186
200
  * @param handler The handler function to execute
187
201
  * @param options Any other event options (see `hooks.on()` for details)
188
- * @returns The handler function
202
+ * @returns A function to unregister the handler
189
203
  * @see on
190
204
  */
191
- before<T extends HookName>(hook: T, handler: Handler<T>): Handler<T>;
192
- before<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions): Handler<T>;
205
+ before<T extends HookName>(hook: T, handler: Handler<T>): HookUnregister;
206
+ before<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions): HookUnregister;
193
207
  before<T extends HookName>(
194
208
  hook: T,
195
209
  handler: Handler<T>,
196
210
  options: HookOptions = {}
197
- ): Handler<T> {
211
+ ): HookUnregister {
198
212
  return this.on(hook, handler, { ...options, before: true });
199
213
  }
200
214
 
@@ -204,16 +218,16 @@ export class Hooks {
204
218
  * @param hook Name of the hook to listen for
205
219
  * @param handler The handler function to execute instead of the default handler
206
220
  * @param options Any other event options (see `hooks.on()` for details)
207
- * @returns The handler function
221
+ * @returns A function to unregister the handler
208
222
  * @see on
209
223
  */
210
- replace<T extends HookName>(hook: T, handler: Handler<T>): Handler<T>;
211
- replace<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions): Handler<T>;
224
+ replace<T extends HookName>(hook: T, handler: Handler<T>): HookUnregister;
225
+ replace<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions): HookUnregister;
212
226
  replace<T extends HookName>(
213
227
  hook: T,
214
228
  handler: Handler<T>,
215
229
  options: HookOptions = {}
216
- ): Handler<T> {
230
+ ): HookUnregister {
217
231
  return this.on(hook, handler, { ...options, replace: true });
218
232
  }
219
233
 
@@ -225,9 +239,13 @@ export class Hooks {
225
239
  * @param options Any other event options (see `hooks.on()` for details)
226
240
  * @see on
227
241
  */
228
- once<T extends HookName>(hook: T, handler: Handler<T>): Handler<T>;
229
- once<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions): Handler<T>;
230
- once<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions = {}): Handler<T> {
242
+ once<T extends HookName>(hook: T, handler: Handler<T>): HookUnregister;
243
+ once<T extends HookName>(hook: T, handler: Handler<T>, options: HookOptions): HookUnregister;
244
+ once<T extends HookName>(
245
+ hook: T,
246
+ handler: Handler<T>,
247
+ options: HookOptions = {}
248
+ ): HookUnregister {
231
249
  return this.on(hook, handler, { ...options, once: true });
232
250
  }
233
251
 
@@ -241,7 +259,6 @@ export class Hooks {
241
259
  off<T extends HookName>(hook: T, handler: Handler<T>): void;
242
260
  off<T extends HookName>(hook: T, handler?: Handler<T>): void {
243
261
  const ledger = this.get(hook);
244
-
245
262
  if (ledger && handler) {
246
263
  const deleted = ledger.delete(handler);
247
264
  if (!deleted) {
@@ -265,11 +282,11 @@ export class Hooks {
265
282
  args?: HookArguments<T>,
266
283
  defaultHandler?: Handler<T>
267
284
  ): Promise<any> {
268
- const { before, handler, after } = this.getHandlers(hook, defaultHandler);
285
+ const { before, handler, after, replaced } = this.getHandlers(hook, defaultHandler);
269
286
  await this.execute(before, args);
270
- const [result] = await this.execute(handler, args);
287
+ const [result] = await this.execute(handler, args, replaced ? defaultHandler : undefined);
271
288
  await this.execute(after, args);
272
- this.dispatchDomEvent(hook);
289
+ this.dispatchDomEvent(hook, args);
273
290
  return result;
274
291
  }
275
292
 
@@ -286,11 +303,11 @@ export class Hooks {
286
303
  args?: HookArguments<T>,
287
304
  defaultHandler?: Handler<T>
288
305
  ): any {
289
- const { before, after, handler } = this.getHandlers(hook, defaultHandler);
306
+ const { before, after, handler, replaced } = this.getHandlers(hook, defaultHandler);
290
307
  this.executeSync(before, args);
291
- const [result] = this.executeSync(handler, args);
308
+ const [result] = this.executeSync(handler, args, replaced ? defaultHandler : undefined);
292
309
  this.executeSync(after, args);
293
- this.dispatchDomEvent(hook);
310
+ this.dispatchDomEvent(hook, args);
294
311
  return result;
295
312
  }
296
313
 
@@ -301,11 +318,12 @@ export class Hooks {
301
318
  */
302
319
  async execute<T extends HookName>(
303
320
  registrations: HookRegistration<T>[],
304
- args?: HookArguments<T>
321
+ args?: HookArguments<T>,
322
+ defaultHandler?: Handler<T>
305
323
  ): Promise<any> {
306
324
  const results = [];
307
325
  for (const { hook, handler, once } of registrations) {
308
- const result = await runAsPromise(handler, [this.swup.context, args]);
326
+ const result = await runAsPromise(handler, [this.swup.context, args, defaultHandler]);
309
327
  results.push(result);
310
328
  if (once) {
311
329
  this.off(hook, handler);
@@ -321,11 +339,12 @@ export class Hooks {
321
339
  */
322
340
  executeSync<T extends HookName>(
323
341
  registrations: HookRegistration<T>[],
324
- args?: HookArguments<T>
342
+ args?: HookArguments<T>,
343
+ defaultHandler?: Handler<T>
325
344
  ): any[] {
326
345
  const results = [];
327
346
  for (const { hook, handler, once } of registrations) {
328
- const result = handler(this.swup.context, args as HookArguments<T>);
347
+ const result = handler(this.swup.context, args as HookArguments<T>, defaultHandler);
329
348
  results.push(result);
330
349
  if (isPromise(result)) {
331
350
  console.warn(
@@ -378,7 +397,7 @@ export class Hooks {
378
397
  * @returns The sort direction
379
398
  */
380
399
  sortRegistrations<T extends HookName>(a: HookRegistration<T>, b: HookRegistration<T>): number {
381
- const priority = (b.priority ?? 0) - (a.priority ?? 0);
400
+ const priority = (a.priority ?? 0) - (b.priority ?? 0);
382
401
  const id = a.id - b.id;
383
402
  return priority || id || 0;
384
403
  }
@@ -387,7 +406,8 @@ export class Hooks {
387
406
  * Trigger a custom event on the `document`. Prefixed with `swup:`
388
407
  * @param hook Name of the hook to trigger.
389
408
  */
390
- dispatchDomEvent<T extends HookName>(hook: T): void {
391
- document.dispatchEvent(new CustomEvent(`swup:${hook}`, { detail: hook }));
409
+ dispatchDomEvent<T extends HookName>(hook: T, args?: HookArguments<T>): void {
410
+ const detail = { hook, args, context: this.swup.context };
411
+ document.dispatchEvent(new CustomEvent(`swup:${hook}`, { detail }));
392
412
  }
393
413
  }
@@ -77,18 +77,18 @@ describe('Cache', () => {
77
77
  it('should trigger a hook on set', () => {
78
78
  const handler = vi.fn();
79
79
 
80
- swup.hooks.on('pageCached', handler);
80
+ swup.hooks.on('cache:set', handler);
81
81
 
82
82
  cache.set(page1.url, page1);
83
83
 
84
84
  expect(handler).toBeCalledTimes(1);
85
- expect(handler).toBeCalledWith(ctx, { page: page1 });
85
+ expect(handler).toBeCalledWith(ctx, { page: page1 }, undefined);
86
86
  });
87
87
 
88
88
  it('should allow augmenting cache entries on save', () => {
89
89
  const now = Date.now();
90
90
 
91
- swup.hooks.on('pageCached', (_, { page }) => {
91
+ swup.hooks.on('cache:set', (_, { page }) => {
92
92
  const ttl: CacheTtlData = { ttl: 1000, created: now };
93
93
  cache.update(page.url, ttl as AugmentedCacheData);
94
94
  });
@@ -101,7 +101,7 @@ describe('Cache', () => {
101
101
  });
102
102
 
103
103
  it('should allow manual pruning', () => {
104
- swup.hooks.on('pageCached', (_, { page }) => {
104
+ swup.hooks.on('cache:set', (_, { page }) => {
105
105
  cache.update(page.url, { index: cache.size } as AugmentedCacheData);
106
106
  });
107
107
 
@@ -124,9 +124,9 @@ describe('Types', () => {
124
124
  const cache = new Cache(swup);
125
125
 
126
126
  // @ts-expect-no-error
127
- swup.hooks.on('popState', (ctx: Context, { event: PopStateEvent }) => {});
127
+ swup.hooks.on('history:popstate', (ctx: Context, { event: PopStateEvent }) => {});
128
128
  // @ts-expect-no-error
129
- await swup.hooks.trigger('popState', { event: new PopStateEvent('') });
129
+ await swup.hooks.trigger('history:popstate', { event: new PopStateEvent('') });
130
130
 
131
131
  try {
132
132
  // @ts-expect-error
@@ -4,7 +4,7 @@ import { Handler, Hooks } from '../Hooks.js';
4
4
  import { Context } from '../Context.js';
5
5
 
6
6
  describe('Hook registry', () => {
7
- it('should add custom handlers', () => {
7
+ it('should add handlers', () => {
8
8
  const swup = new Swup();
9
9
  const handler = vi.fn();
10
10
 
@@ -16,8 +16,8 @@ describe('Hook registry', () => {
16
16
  };
17
17
  const hooks = new HooksWithAccess(swup);
18
18
 
19
- hooks.on('enabled', handler);
20
- const ledger = hooks.getRegistry().get('enabled');
19
+ hooks.on('enable', handler);
20
+ const ledger = hooks.getRegistry().get('enable');
21
21
 
22
22
  expect(ledger).toBeDefined();
23
23
  expect(ledger).toBeInstanceOf(Map);
@@ -29,22 +29,57 @@ describe('Hook registry', () => {
29
29
  expect(registration?.handler).toEqual(handler);
30
30
  });
31
31
 
32
- it('should return the passed handler', () => {
32
+ it('should remove handlers', async () => {
33
33
  const swup = new Swup();
34
- const handler = vi.fn();
34
+ const handler1 = vi.fn();
35
+ const handler2 = vi.fn();
36
+
37
+ swup.hooks.on('enable', handler1);
38
+ swup.hooks.on('enable', handler2);
39
+
40
+ await swup.hooks.trigger('enable');
41
+
42
+ expect(handler1).toBeCalledTimes(1);
43
+ expect(handler2).toBeCalledTimes(1);
44
+
45
+ swup.hooks.off('enable', handler2);
46
+
47
+ await swup.hooks.trigger('enable');
48
+
49
+ expect(handler1).toBeCalledTimes(2);
50
+ expect(handler2).toBeCalledTimes(1);
51
+ });
35
52
 
36
- const handlerReturned = swup.hooks.on('enabled', handler);
53
+ it('should return a function to unregister the handler', async () => {
54
+ const swup = new Swup();
55
+ const handler1 = vi.fn();
56
+ const handler2 = vi.fn();
57
+
58
+ const unregister1 = swup.hooks.on('enable', handler1);
59
+ const unregister2 = swup.hooks.on('enable', handler2);
60
+
61
+ expect(unregister1).toBeTypeOf('function');
62
+
63
+ await swup.hooks.trigger('enable');
37
64
 
38
- expect(handlerReturned).toEqual(handler);
65
+ expect(handler1).toBeCalledTimes(1);
66
+ expect(handler2).toBeCalledTimes(1);
67
+
68
+ unregister2();
69
+
70
+ await swup.hooks.trigger('enable');
71
+
72
+ expect(handler1).toBeCalledTimes(2);
73
+ expect(handler2).toBeCalledTimes(1);
39
74
  });
40
75
 
41
76
  it('should trigger custom handlers', async () => {
42
77
  const swup = new Swup();
43
78
  const handler = vi.fn();
44
79
 
45
- swup.hooks.on('enabled', handler);
80
+ swup.hooks.on('enable', handler);
46
81
 
47
- await swup.hooks.trigger('enabled');
82
+ await swup.hooks.trigger('enable');
48
83
 
49
84
  expect(handler).toBeCalledTimes(1);
50
85
  });
@@ -53,10 +88,10 @@ describe('Hook registry', () => {
53
88
  const swup = new Swup();
54
89
  const handler = vi.fn();
55
90
 
56
- swup.hooks.on('enabled', handler, { once: true });
91
+ swup.hooks.on('enable', handler, { once: true });
57
92
 
58
- await swup.hooks.trigger('enabled', undefined, () => {});
59
- await swup.hooks.trigger('enabled', undefined, () => {});
93
+ await swup.hooks.trigger('enable', undefined, () => {});
94
+ await swup.hooks.trigger('enable', undefined, () => {});
60
95
 
61
96
  expect(handler).toBeCalledTimes(1);
62
97
  });
@@ -65,10 +100,10 @@ describe('Hook registry', () => {
65
100
  const swup = new Swup();
66
101
  const handler = vi.fn();
67
102
 
68
- swup.hooks.once('enabled', handler);
103
+ swup.hooks.once('enable', handler);
69
104
 
70
- await swup.hooks.trigger('enabled', undefined, () => {});
71
- await swup.hooks.trigger('enabled', undefined, () => {});
105
+ await swup.hooks.trigger('enable', undefined, () => {});
106
+ await swup.hooks.trigger('enable', undefined, () => {});
72
107
 
73
108
  expect(handler).toBeCalledTimes(1);
74
109
  });
@@ -77,7 +112,7 @@ describe('Hook registry', () => {
77
112
  const swup = new Swup();
78
113
  const handler = vi.fn();
79
114
 
80
- await swup.hooks.trigger('enabled', undefined, handler);
115
+ await swup.hooks.trigger('enable', undefined, handler);
81
116
 
82
117
  expect(handler).toBeCalledTimes(1);
83
118
  });
@@ -101,11 +136,11 @@ describe('Hook registry', () => {
101
136
  }
102
137
  };
103
138
 
104
- swup.hooks.on('disabled', handlers.before, { before: true });
105
- swup.hooks.on('disabled', handlers.normal, {});
106
- swup.hooks.on('disabled', handlers.after, {});
139
+ swup.hooks.on('disable', handlers.before, { before: true });
140
+ swup.hooks.on('disable', handlers.normal, {});
141
+ swup.hooks.on('disable', handlers.after, {});
107
142
 
108
- await swup.hooks.trigger('disabled', undefined, handlers.original);
143
+ await swup.hooks.trigger('disable', undefined, handlers.original);
109
144
 
110
145
  expect(called).toEqual(['before', 'original', 'normal', 'after']);
111
146
  });
@@ -132,44 +167,77 @@ describe('Hook registry', () => {
132
167
  },
133
168
  6: () => {
134
169
  called.push(6);
170
+ },
171
+ 7: () => {
172
+ called.push(7);
173
+ },
174
+ 8: () => {
175
+ called.push(8);
135
176
  }
136
177
  };
137
178
 
138
- swup.hooks.on('disabled', handlers['1'], { priority: 1, before: true });
139
- swup.hooks.on('disabled', handlers['2'], { priority: 2, before: true });
140
- swup.hooks.on('disabled', handlers['4'], { priority: 5 });
141
- swup.hooks.on('disabled', handlers['6'], { priority: 4 });
142
- swup.hooks.on('disabled', handlers['5'], { priority: 4 });
179
+ swup.hooks.on('disable', handlers['1'], { priority: 2, before: true });
180
+ swup.hooks.on('disable', handlers['2'], { priority: -1, before: true });
181
+ swup.hooks.on('disable', handlers['3'], { priority: 1 });
182
+ swup.hooks.on('disable', handlers['4']);
183
+ swup.hooks.on('disable', handlers['8'], { priority: 4 });
184
+ swup.hooks.on('disable', handlers['7'], { priority: 4 });
143
185
 
144
- await swup.hooks.trigger('disabled', undefined, handlers['3']);
186
+ await swup.hooks.trigger('disable', undefined, handlers['5']);
145
187
 
146
- expect(called).toEqual([2, 1, 3, 4, 6, 5]);
188
+ expect(called).toEqual([2, 1, 5, 4, 3, 8, 7]);
147
189
  });
148
190
 
149
191
  it('should allow replacing original handlers', async () => {
192
+ const swup = new Swup();
193
+ const customHandler = vi.fn();
194
+ const defaultHandler = vi.fn();
195
+
196
+ swup.hooks.on('enable', customHandler, { replace: true });
197
+
198
+ await swup.hooks.trigger('enable', undefined, defaultHandler);
199
+
200
+ expect(customHandler).toBeCalledTimes(1);
201
+ expect(defaultHandler).toBeCalledTimes(0);
202
+ });
203
+
204
+ it('should pass original handler into replacing handlers', async () => {
205
+ const swup = new Swup();
206
+ const customHandler = vi.fn();
207
+ const defaultHandler = vi.fn();
208
+ const ctx = swup.context;
209
+
210
+ swup.hooks.on('enable', customHandler, { replace: true });
211
+
212
+ await swup.hooks.trigger('enable', undefined, defaultHandler);
213
+
214
+ expect(customHandler).toBeCalledWith(ctx, undefined, defaultHandler);
215
+ });
216
+
217
+ it('should not pass original handler into normal handlers', async () => {
150
218
  const swup = new Swup();
151
219
  const listener = vi.fn();
152
220
  const handler = vi.fn();
221
+ const ctx = swup.context;
153
222
 
154
- swup.hooks.on('enabled', listener, { replace: true });
223
+ swup.hooks.on('enable', listener);
155
224
 
156
- await swup.hooks.trigger('enabled', undefined, handler);
225
+ await swup.hooks.trigger('enable', undefined, handler);
157
226
 
158
- expect(handler).toBeCalledTimes(0);
159
- expect(listener).toBeCalledTimes(1);
227
+ expect(listener).toBeCalledWith(ctx, undefined, undefined);
160
228
  });
161
229
 
162
230
  it('should trigger event handler with context and args', async () => {
163
231
  const swup = new Swup();
164
- const handler: Handler<'popState'> = vi.fn();
232
+ const handler: Handler<'history:popstate'> = vi.fn();
165
233
  const ctx = swup.context;
166
234
  const args = { event: new PopStateEvent('') };
167
235
 
168
- swup.hooks.on('popState', handler);
169
- await swup.hooks.trigger('popState', args);
236
+ swup.hooks.on('history:popstate', handler);
237
+ await swup.hooks.trigger('history:popstate', args);
170
238
 
171
239
  expect(handler).toBeCalledTimes(1);
172
- expect(handler).toBeCalledWith(ctx, args);
240
+ expect(handler).toBeCalledWith(ctx, args, undefined);
173
241
  });
174
242
  });
175
243
 
@@ -178,15 +246,18 @@ describe('Types', () => {
178
246
  const swup = new Swup();
179
247
 
180
248
  // @ts-expect-no-error
181
- swup.hooks.on('popState', (ctx: Context, { event }: { event: PopStateEvent }) => {});
249
+ swup.hooks.on(
250
+ 'history:popstate',
251
+ (ctx: Context, { event }: { event: PopStateEvent }) => {}
252
+ );
182
253
  // @ts-expect-no-error
183
- await swup.hooks.trigger('popState', { event: new PopStateEvent('') });
254
+ await swup.hooks.trigger('history:popstate', { event: new PopStateEvent('') });
184
255
 
185
256
  // @ts-expect-error
186
- swup.hooks.on('popState', ({ event: MouseEvent }) => {});
257
+ swup.hooks.on('history:popstate', ({ event: MouseEvent }) => {});
187
258
  // @ts-expect-error
188
- swup.hooks.on('popState', (ctx: Context, { event }: { event: MouseEvent }) => {});
259
+ swup.hooks.on('history:popstate', (ctx: Context, { event }: { event: MouseEvent }) => {});
189
260
  // @ts-expect-error
190
- await swup.hooks.trigger('popState', { event: new MouseEvent('') });
261
+ await swup.hooks.trigger('history:popstate', { event: new MouseEvent('') });
191
262
  });
192
263
  });