swup 3.1.0 → 4.0.0-rc.14

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 (71) hide show
  1. package/dist/Swup.cjs +1 -1
  2. package/dist/Swup.cjs.map +1 -1
  3. package/dist/Swup.modern.js +1 -1
  4. package/dist/Swup.modern.js.map +1 -1
  5. package/dist/Swup.module.js +1 -1
  6. package/dist/Swup.module.js.map +1 -1
  7. package/dist/Swup.umd.js +1 -1
  8. package/dist/Swup.umd.js.map +1 -1
  9. package/dist/types/Swup.d.ts +20 -20
  10. package/dist/types/helpers/delegateEvent.d.ts +4 -2
  11. package/dist/types/helpers/fetch.d.ts +2 -2
  12. package/dist/types/helpers.d.ts +10 -10
  13. package/dist/types/index.d.ts +5 -5
  14. package/dist/types/modules/Cache.d.ts +2 -2
  15. package/dist/types/modules/__test__/fetchPage.test.d.ts +1 -0
  16. package/dist/types/modules/destroy.d.ts +2 -0
  17. package/dist/types/modules/enable.d.ts +2 -0
  18. package/dist/types/modules/enterPage.d.ts +2 -2
  19. package/dist/types/modules/events.d.ts +6 -6
  20. package/dist/types/modules/fetchPage.d.ts +3 -3
  21. package/dist/types/modules/getAnchorElement.d.ts +0 -7
  22. package/dist/types/modules/getAnimationPromises.d.ts +1 -1
  23. package/dist/types/modules/getPageData.d.ts +2 -2
  24. package/dist/types/modules/handleLinkToSamePage.d.ts +2 -0
  25. package/dist/types/modules/isSameResolvedUrl.d.ts +8 -0
  26. package/dist/types/modules/leavePage.d.ts +2 -2
  27. package/dist/types/modules/linkClickHandler.d.ts +3 -0
  28. package/dist/types/modules/loadPage.d.ts +2 -5
  29. package/dist/types/modules/off.d.ts +3 -0
  30. package/dist/types/modules/on.d.ts +5 -0
  31. package/dist/types/modules/plugins.d.ts +1 -1
  32. package/dist/types/modules/popStateHandler.d.ts +2 -0
  33. package/dist/types/modules/renderPage.d.ts +2 -2
  34. package/dist/types/modules/resolveUrl.d.ts +7 -0
  35. package/dist/types/modules/shouldIgnoreVisit.d.ts +4 -0
  36. package/dist/types/modules/transitions.d.ts +1 -1
  37. package/dist/types/modules/triggerEvent.d.ts +3 -0
  38. package/dist/types/modules/triggerWillOpenNewWindow.d.ts +2 -0
  39. package/dist/types/modules/updateTransition.d.ts +2 -0
  40. package/dist/types/utils.d.ts +1 -1
  41. package/package.json +8 -5
  42. package/readme.md +30 -12
  43. package/src/Swup.ts +115 -143
  44. package/src/__test__/index.test.ts +6 -1
  45. package/src/helpers/Location.ts +10 -7
  46. package/src/helpers/__test__/matchPath.test.ts +54 -0
  47. package/src/helpers/delegateEvent.ts +1 -0
  48. package/src/helpers/matchPath.ts +22 -0
  49. package/src/helpers.ts +2 -4
  50. package/src/index.ts +7 -4
  51. package/src/modules/Cache.ts +43 -33
  52. package/src/modules/Context.ts +91 -0
  53. package/src/modules/Hooks.ts +393 -0
  54. package/src/modules/__test__/cache.test.ts +142 -0
  55. package/src/modules/__test__/hooks.test.ts +192 -0
  56. package/src/modules/enterPage.ts +19 -17
  57. package/src/modules/fetchPage.ts +74 -29
  58. package/src/modules/getAnchorElement.ts +8 -4
  59. package/src/modules/getAnimationPromises.ts +66 -75
  60. package/src/modules/leavePage.ts +22 -20
  61. package/src/modules/loadPage.ts +72 -54
  62. package/src/modules/renderPage.ts +41 -32
  63. package/src/modules/replaceContent.ts +26 -17
  64. package/src/utils/index.ts +24 -3
  65. package/src/helpers/fetch.ts +0 -33
  66. package/src/helpers/getDataFromHtml.ts +0 -39
  67. package/src/helpers/markSwupElements.ts +0 -16
  68. package/src/modules/__test__/events.test.ts +0 -72
  69. package/src/modules/events.ts +0 -92
  70. package/src/modules/getPageData.ts +0 -24
  71. package/src/modules/transitions.ts +0 -10
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { matchPath } from '../../index.js';
3
+ import { pathToRegexp } from 'path-to-regexp';
4
+
5
+ describe('matchPath', () => {
6
+ it('should return false if not matching', () => {
7
+ const urlMatch = matchPath('/users/:user');
8
+ const match = urlMatch('/posts/');
9
+ expect(match).toBe(false);
10
+ });
11
+
12
+ it('should return an object if matching', () => {
13
+ const urlMatch = matchPath('/users/:user');
14
+ const match = urlMatch('/users/bob');
15
+ expect(match).toEqual({
16
+ path: '/users/bob',
17
+ index: 0,
18
+ params: { user: 'bob' }
19
+ });
20
+ });
21
+
22
+ it('should work with primitive strings', () => {
23
+ const urlMatch = matchPath<{ user: string }>('/users/:user');
24
+ const match = urlMatch('/users/bob');
25
+ const params = !match ? false : match.params;
26
+ expect(params).toEqual({ user: 'bob' });
27
+ });
28
+
29
+ it('should work with an array of paths', () => {
30
+ const urlMatch = matchPath<{ user: string }>(['/users/', '/users/:user']);
31
+
32
+ const { params: withParams } = urlMatch('/users/bob') || {};
33
+ expect(withParams).toEqual({ user: 'bob' });
34
+
35
+ const { params: withoutParams } = urlMatch('/users/') || {};
36
+ expect(withoutParams).toEqual({});
37
+ });
38
+
39
+ /**
40
+ * When passing a regex to `match`, the params in the response are sorted by appearance.
41
+ * Only helpful for falsy/truthy detection
42
+ */
43
+ it('should work with regex', () => {
44
+ const re = pathToRegexp('/users/:user');
45
+ const urlMatch = matchPath(re);
46
+ const { params } = urlMatch('/users/bob') || {};
47
+ expect(params).toEqual({ '0': 'bob' });
48
+ });
49
+
50
+ it('should throw with malformed paths', () => {
51
+ // prettier-ignore
52
+ expect(() => matchPath('/\?user=:user')).toThrowError('[swup] Error parsing path');
53
+ });
54
+ });
@@ -12,6 +12,7 @@ export const delegateEvent = <Selector extends string, TEvent extends EventType>
12
12
  options?: DelegateOptions
13
13
  ): Unsubscribe => {
14
14
  const controller = new AbortController();
15
+ options = { ...options, signal: controller.signal };
15
16
  delegate<string, ParseSelector<Selector, HTMLElement>, TEvent>(
16
17
  selector,
17
18
  type,
@@ -0,0 +1,22 @@
1
+ import { match } from 'path-to-regexp';
2
+
3
+ import type {
4
+ Path,
5
+ ParseOptions,
6
+ TokensToRegexpOptions,
7
+ RegexpToFunctionOptions,
8
+ MatchFunction
9
+ } from 'path-to-regexp';
10
+
11
+ export { Path };
12
+
13
+ export const matchPath = <P extends object = object>(
14
+ path: Path,
15
+ options?: ParseOptions & TokensToRegexpOptions & RegexpToFunctionOptions
16
+ ): MatchFunction<P> => {
17
+ try {
18
+ return match<P>(path, options);
19
+ } catch (error) {
20
+ throw new Error(`[swup] Error parsing path "${path}":\n${error}`);
21
+ }
22
+ };
package/src/helpers.ts CHANGED
@@ -1,13 +1,11 @@
1
1
  // Re-export all helpers to allow custom package export path
2
- // e.g. import { getPageData } from 'swup/helpers'
2
+ // e.g. import { updateHistoryRecord } from 'swup'
3
3
 
4
4
  export { classify } from './helpers/classify.js';
5
5
  export { createHistoryRecord } from './helpers/createHistoryRecord.js';
6
6
  export { updateHistoryRecord } from './helpers/updateHistoryRecord.js';
7
7
  export { delegateEvent } from './helpers/delegateEvent.js';
8
- export { getDataFromHtml } from './helpers/getDataFromHtml.js';
9
- export { fetch } from './helpers/fetch.js';
10
8
  export { getCurrentUrl } from './helpers/getCurrentUrl.js';
11
9
  export { Location } from './helpers/Location.js';
12
- export { markSwupElements } from './helpers/markSwupElements.js';
13
10
  export { cleanupAnimationClasses } from './helpers/cleanupAnimationClasses.js';
11
+ export { matchPath } from './helpers/matchPath.js';
package/src/index.ts CHANGED
@@ -1,10 +1,13 @@
1
- import Swup, { Options } from './Swup.js';
2
- import { Plugin } from './modules/plugins.js';
3
- import { Handler } from './modules/events.js';
1
+ import Swup, { type Options } from './Swup.js';
2
+ import type { CacheData } from './modules/Cache.js';
3
+ import type { Context, PageContext } from './modules/Context.js';
4
+ import type { Plugin } from './modules/plugins.js';
5
+ import type { HookDefinitions, Handler } from './modules/Hooks.js';
6
+ import type { Path } from 'path-to-regexp';
4
7
 
5
8
  export default Swup;
6
9
 
7
10
  export * from './helpers.js';
8
11
  export * from './utils.js';
9
12
 
10
- export type { Options, Plugin, Handler };
13
+ export type { Options, Plugin, CacheData, Context, PageContext, HookDefinitions, Handler, Path };
@@ -1,55 +1,65 @@
1
- import { getCurrentUrl, Location } from '../helpers.js';
2
1
  import Swup from '../Swup.js';
3
- import { PageData } from './getPageData.js';
2
+ import { Location } from '../helpers.js';
3
+ import { PageData } from './fetchPage.js';
4
+
5
+ export interface CacheData extends PageData {}
4
6
 
5
- export interface PageRecord extends PageData {
6
- url: string;
7
- responseURL: string;
8
- }
9
7
  export class Cache {
10
- pages: Record<string, PageRecord> = {};
11
- last: PageRecord | null = null;
12
- swup: Swup;
8
+ private swup: Swup;
9
+ private pages: Map<string, CacheData> = new Map();
13
10
 
14
11
  constructor(swup: Swup) {
15
12
  this.swup = swup;
16
13
  }
17
14
 
18
- getCacheUrl(url: string): string {
19
- return this.swup.resolveUrl(Location.fromUrl(url).url);
15
+ get size() {
16
+ return this.pages.size;
17
+ }
18
+
19
+ get all() {
20
+ return this.pages;
21
+ }
22
+
23
+ public has(url: string): boolean {
24
+ return this.pages.has(this.resolve(url));
25
+ }
26
+
27
+ public get(url: string): CacheData | undefined {
28
+ return this.pages.get(this.resolve(url));
20
29
  }
21
30
 
22
- cacheUrl(page: PageRecord) {
23
- page.url = this.getCacheUrl(page.url);
24
- if (page.url in this.pages === false) {
25
- this.pages[page.url] = page;
26
- }
27
- page.responseURL = this.getCacheUrl(page.responseURL);
28
- this.last = this.pages[page.url];
29
- this.swup.log(`Cache (${Object.keys(this.pages).length})`, this.pages);
31
+ public set(url: string, page: CacheData) {
32
+ url = this.resolve(url);
33
+ page = { ...page, url };
34
+ this.pages.set(url, page);
35
+ this.swup.hooks.triggerSync('pageCached', { page });
30
36
  }
31
37
 
32
- getPage(url: string): PageRecord {
33
- url = this.getCacheUrl(url);
34
- return this.pages[url];
38
+ public update(url: string, page: CacheData) {
39
+ url = this.resolve(url);
40
+ page = { ...this.get(url), ...page, url };
41
+ this.pages.set(url, page);
35
42
  }
36
43
 
37
- getCurrentPage(): PageRecord {
38
- return this.getPage(getCurrentUrl());
44
+ public delete(url: string): void {
45
+ this.pages.delete(this.resolve(url));
39
46
  }
40
47
 
41
- exists(url: string): boolean {
42
- url = this.getCacheUrl(url);
43
- return url in this.pages;
48
+ public clear(): void {
49
+ this.pages.clear();
50
+ this.swup.hooks.triggerSync('cacheCleared');
44
51
  }
45
52
 
46
- empty(): void {
47
- this.pages = {};
48
- this.last = null;
49
- this.swup.log('Cache cleared');
53
+ public prune(predicate: (url: string, page: CacheData) => boolean): void {
54
+ this.pages.forEach((page, url) => {
55
+ if (predicate(url, page)) {
56
+ this.delete(url);
57
+ }
58
+ });
50
59
  }
51
60
 
52
- remove(url: string): void {
53
- delete this.pages[this.getCacheUrl(url)];
61
+ private resolve(urlToResolve: string): string {
62
+ const { url } = Location.fromUrl(urlToResolve);
63
+ return this.swup.resolveUrl(url);
54
64
  }
55
65
  }
@@ -0,0 +1,91 @@
1
+ import Swup, { Options } from '../Swup.js';
2
+ import { HistoryAction } from './loadPage.js';
3
+
4
+ export interface Context<TEvent = Event> {
5
+ from: PageContext;
6
+ to?: PageContext;
7
+ containers: Options['containers'];
8
+ transition: TransitionContext;
9
+ trigger: TriggerContext<TEvent>;
10
+ history: HistoryContext;
11
+ scroll: ScrollContext;
12
+ }
13
+
14
+ export interface PageContext {
15
+ url: string;
16
+ }
17
+
18
+ export interface TransitionContext {
19
+ animate: boolean;
20
+ name?: string;
21
+ }
22
+
23
+ export interface ScrollContext {
24
+ reset: boolean;
25
+ target?: string;
26
+ }
27
+
28
+ export interface TriggerContext<TEvent = Event> {
29
+ el?: Element;
30
+ event?: TEvent;
31
+ }
32
+
33
+ export interface HistoryContext {
34
+ action: HistoryAction;
35
+ popstate: boolean;
36
+ // direction: 'forward' | 'backward' | undefined
37
+ }
38
+
39
+ export interface ContextInitOptions {
40
+ to: string | undefined;
41
+ from?: string;
42
+ hash?: string;
43
+ containers?: Options['containers'];
44
+ animate?: boolean;
45
+ transition?: string;
46
+ el?: Element;
47
+ event?: Event;
48
+ popstate?: boolean;
49
+ action?: HistoryAction;
50
+ resetScroll?: boolean;
51
+ }
52
+
53
+ export function createContext(
54
+ this: Swup,
55
+ {
56
+ to,
57
+ from = this.currentPageUrl,
58
+ hash: target,
59
+ containers = this.options.containers,
60
+ animate = true,
61
+ transition,
62
+ el,
63
+ event,
64
+ popstate = false,
65
+ action = 'push',
66
+ resetScroll: reset = true
67
+ }: ContextInitOptions
68
+ ): Context {
69
+ return {
70
+ from: { url: from },
71
+ to: to !== undefined ? { url: to } : undefined,
72
+ containers,
73
+ transition: {
74
+ animate,
75
+ name: transition
76
+ },
77
+ trigger: {
78
+ el,
79
+ event
80
+ },
81
+ history: {
82
+ action,
83
+ popstate
84
+ // direction: undefined
85
+ },
86
+ scroll: {
87
+ reset,
88
+ target
89
+ }
90
+ };
91
+ }