swup 4.3.4 → 4.4.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 +10 -5
- package/dist/types/Swup.d.ts.map +1 -0
- package/dist/types/config/version.d.ts +1 -0
- package/dist/types/config/version.d.ts.map +1 -0
- package/dist/types/helpers/Location.d.ts +1 -0
- package/dist/types/helpers/Location.d.ts.map +1 -0
- package/dist/types/helpers/classify.d.ts +1 -0
- package/dist/types/helpers/classify.d.ts.map +1 -0
- package/dist/types/helpers/createHistoryRecord.d.ts +1 -0
- package/dist/types/helpers/createHistoryRecord.d.ts.map +1 -0
- package/dist/types/helpers/delegateEvent.d.ts +3 -2
- package/dist/types/helpers/delegateEvent.d.ts.map +1 -0
- package/dist/types/helpers/getCurrentUrl.d.ts +1 -0
- package/dist/types/helpers/getCurrentUrl.d.ts.map +1 -0
- package/dist/types/helpers/matchPath.d.ts +2 -1
- package/dist/types/helpers/matchPath.d.ts.map +1 -0
- package/dist/types/helpers/updateHistoryRecord.d.ts +1 -0
- package/dist/types/helpers/updateHistoryRecord.d.ts.map +1 -0
- package/dist/types/helpers.d.ts +1 -0
- package/dist/types/helpers.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/modules/Cache.d.ts +3 -2
- package/dist/types/modules/Cache.d.ts.map +1 -0
- package/dist/types/modules/Classes.d.ts +2 -1
- package/dist/types/modules/Classes.d.ts.map +1 -0
- package/dist/types/modules/Hooks.d.ts +10 -4
- package/dist/types/modules/Hooks.d.ts.map +1 -0
- package/dist/types/modules/Visit.d.ts +4 -2
- package/dist/types/modules/Visit.d.ts.map +1 -0
- package/dist/types/modules/animatePageIn.d.ts +2 -1
- package/dist/types/modules/animatePageIn.d.ts.map +1 -0
- package/dist/types/modules/animatePageOut.d.ts +2 -1
- package/dist/types/modules/animatePageOut.d.ts.map +1 -0
- package/dist/types/modules/awaitAnimations.d.ts +3 -1
- package/dist/types/modules/awaitAnimations.d.ts.map +1 -0
- package/dist/types/modules/fetchPage.d.ts +10 -3
- package/dist/types/modules/fetchPage.d.ts.map +1 -0
- package/dist/types/modules/getAnchorElement.d.ts +1 -0
- package/dist/types/modules/getAnchorElement.d.ts.map +1 -0
- package/dist/types/modules/navigate.d.ts +4 -3
- package/dist/types/modules/navigate.d.ts.map +1 -0
- package/dist/types/modules/plugins.d.ts +2 -1
- package/dist/types/modules/plugins.d.ts.map +1 -0
- package/dist/types/modules/renderPage.d.ts +3 -2
- package/dist/types/modules/renderPage.d.ts.map +1 -0
- package/dist/types/modules/replaceContent.d.ts +4 -2
- package/dist/types/modules/replaceContent.d.ts.map +1 -0
- package/dist/types/modules/resolveUrl.d.ts +2 -1
- package/dist/types/modules/resolveUrl.d.ts.map +1 -0
- package/dist/types/modules/scrollToContent.d.ts +2 -1
- package/dist/types/modules/scrollToContent.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/index.d.ts.map +1 -0
- package/dist/types/utils.d.ts +1 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/package.json +13 -17
- package/src/Swup.ts +18 -7
- package/src/helpers/delegateEvent.ts +6 -2
- package/src/helpers/matchPath.ts +1 -1
- package/src/helpers/updateHistoryRecord.ts +1 -1
- package/src/modules/Cache.ts +2 -2
- package/src/modules/Classes.ts +1 -1
- package/src/modules/Hooks.ts +9 -4
- package/src/modules/Visit.ts +3 -2
- package/src/modules/animatePageIn.ts +1 -1
- package/src/modules/animatePageOut.ts +1 -1
- package/src/modules/awaitAnimations.ts +2 -1
- package/src/modules/fetchPage.ts +48 -9
- package/src/modules/navigate.ts +26 -17
- package/src/modules/plugins.ts +1 -1
- package/src/modules/renderPage.ts +2 -2
- package/src/modules/replaceContent.ts +3 -2
- package/src/modules/resolveUrl.ts +1 -1
- package/src/modules/scrollToContent.ts +1 -1
- package/dist/types/__test__/index.test.d.ts +0 -1
- package/dist/types/helpers/__test__/matchPath.test.d.ts +0 -1
- package/dist/types/modules/__test__/cache.test.d.ts +0 -1
- package/dist/types/modules/__test__/delegateEvent.test.d.ts +0 -1
- package/dist/types/modules/__test__/hooks.test.d.ts +0 -1
- package/dist/types/modules/__test__/plugins.test.d.ts +0 -1
- package/dist/types/modules/__test__/replaceContent.test.d.ts +0 -1
- package/dist/types/modules/__test__/visit.test.d.ts +0 -1
- package/src/__test__/index.test.ts +0 -83
- package/src/helpers/__test__/matchPath.test.ts +0 -54
- package/src/modules/__test__/cache.test.ts +0 -159
- package/src/modules/__test__/delegateEvent.test.ts +0 -36
- package/src/modules/__test__/hooks.test.ts +0 -319
- package/src/modules/__test__/plugins.test.ts +0 -89
- package/src/modules/__test__/replaceContent.test.ts +0 -91
- package/src/modules/__test__/visit.test.ts +0 -92
package/src/modules/Cache.ts
CHANGED
package/src/modules/Classes.ts
CHANGED
package/src/modules/Hooks.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DelegateEvent } from 'delegate-it';
|
|
1
|
+
import type { DelegateEvent } from 'delegate-it';
|
|
2
2
|
|
|
3
|
-
import Swup from '../Swup.js';
|
|
3
|
+
import type Swup from '../Swup.js';
|
|
4
4
|
import { isPromise, runAsPromise } from '../utils.js';
|
|
5
|
-
import { Visit } from './Visit.js';
|
|
6
|
-
import { FetchOptions, PageData } from './fetchPage.js';
|
|
5
|
+
import type { Visit } from './Visit.js';
|
|
6
|
+
import type { FetchOptions, PageData } from './fetchPage.js';
|
|
7
7
|
|
|
8
8
|
export interface HookDefinitions {
|
|
9
9
|
'animation:out:start': undefined;
|
|
@@ -21,6 +21,7 @@ export interface HookDefinitions {
|
|
|
21
21
|
'disable': undefined;
|
|
22
22
|
'fetch:request': { url: string; options: FetchOptions };
|
|
23
23
|
'fetch:error': { url: string; status: number; response: Response };
|
|
24
|
+
'fetch:timeout': { url: string };
|
|
24
25
|
'history:popstate': { event: PopStateEvent };
|
|
25
26
|
'link:click': { el: HTMLAnchorElement; event: DelegateEvent<MouseEvent> };
|
|
26
27
|
'link:self': undefined;
|
|
@@ -31,6 +32,7 @@ export interface HookDefinitions {
|
|
|
31
32
|
'scroll:top': { options: ScrollIntoViewOptions };
|
|
32
33
|
'scroll:anchor': { hash: string; options: ScrollIntoViewOptions };
|
|
33
34
|
'visit:start': undefined;
|
|
35
|
+
'visit:transition': undefined;
|
|
34
36
|
'visit:end': undefined;
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -40,6 +42,7 @@ export interface HookReturnValues {
|
|
|
40
42
|
'page:load': Promise<PageData>;
|
|
41
43
|
'scroll:top': boolean;
|
|
42
44
|
'scroll:anchor': boolean;
|
|
45
|
+
'visit:transition': Promise<boolean>;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
export type HookArguments<T extends HookName> = HookDefinitions[T];
|
|
@@ -131,6 +134,7 @@ export class Hooks {
|
|
|
131
134
|
'disable',
|
|
132
135
|
'fetch:request',
|
|
133
136
|
'fetch:error',
|
|
137
|
+
'fetch:timeout',
|
|
134
138
|
'history:popstate',
|
|
135
139
|
'link:click',
|
|
136
140
|
'link:self',
|
|
@@ -141,6 +145,7 @@ export class Hooks {
|
|
|
141
145
|
'scroll:top',
|
|
142
146
|
'scroll:anchor',
|
|
143
147
|
'visit:start',
|
|
148
|
+
'visit:transition',
|
|
144
149
|
'visit:end'
|
|
145
150
|
];
|
|
146
151
|
|
package/src/modules/Visit.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import Swup
|
|
2
|
-
import {
|
|
1
|
+
import type Swup from '../Swup.js';
|
|
2
|
+
import type { Options } from '../Swup.js';
|
|
3
|
+
import type { HistoryAction, HistoryDirection } from './navigate.js';
|
|
3
4
|
|
|
4
5
|
/** An object holding details about the current visit. */
|
|
5
6
|
export interface Visit {
|
package/src/modules/fetchPage.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Swup from '../Swup.js';
|
|
1
|
+
import type Swup from '../Swup.js';
|
|
2
2
|
import { Location } from '../helpers.js';
|
|
3
3
|
|
|
4
4
|
/** A page object as used by swup and its cache. */
|
|
@@ -15,16 +15,25 @@ export interface FetchOptions extends Omit<RequestInit, 'cache'> {
|
|
|
15
15
|
method?: 'GET' | 'POST';
|
|
16
16
|
/** The body of the request: raw string, form data object or URL params. */
|
|
17
17
|
body?: string | FormData | URLSearchParams;
|
|
18
|
+
/** The request timeout in milliseconds. */
|
|
19
|
+
timeout?: number;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export class FetchError extends Error {
|
|
21
23
|
url: string;
|
|
22
|
-
status
|
|
23
|
-
|
|
24
|
+
status?: number;
|
|
25
|
+
aborted: boolean;
|
|
26
|
+
timedOut: boolean;
|
|
27
|
+
constructor(
|
|
28
|
+
message: string,
|
|
29
|
+
details: { url: string; status?: number; aborted?: boolean; timedOut?: boolean }
|
|
30
|
+
) {
|
|
24
31
|
super(message);
|
|
25
32
|
this.name = 'FetchError';
|
|
26
33
|
this.url = details.url;
|
|
27
34
|
this.status = details.status;
|
|
35
|
+
this.aborted = details.aborted || false;
|
|
36
|
+
this.timedOut = details.timedOut || false;
|
|
28
37
|
}
|
|
29
38
|
}
|
|
30
39
|
|
|
@@ -39,14 +48,44 @@ export async function fetchPage(
|
|
|
39
48
|
url = Location.fromUrl(url).url;
|
|
40
49
|
|
|
41
50
|
const headers = { ...this.options.requestHeaders, ...options.headers };
|
|
42
|
-
|
|
51
|
+
const timeout = options.timeout ?? this.options.timeout;
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const { signal } = controller;
|
|
54
|
+
options = { ...options, headers, signal };
|
|
55
|
+
|
|
56
|
+
let timedOut = false;
|
|
57
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
58
|
+
if (timeout && timeout > 0) {
|
|
59
|
+
timeoutId = setTimeout(() => {
|
|
60
|
+
timedOut = true;
|
|
61
|
+
controller.abort('timeout');
|
|
62
|
+
}, timeout);
|
|
63
|
+
}
|
|
43
64
|
|
|
44
65
|
// Allow hooking before this and returning a custom response-like object (e.g. custom fetch implementation)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
let response: Response;
|
|
67
|
+
try {
|
|
68
|
+
response = await this.hooks.call(
|
|
69
|
+
'fetch:request',
|
|
70
|
+
{ url, options },
|
|
71
|
+
(visit, { url, options }) => fetch(url, options)
|
|
72
|
+
);
|
|
73
|
+
if (timeoutId) {
|
|
74
|
+
clearTimeout(timeoutId);
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (timedOut) {
|
|
78
|
+
this.hooks.call('fetch:timeout', { url });
|
|
79
|
+
throw new FetchError(`Request timed out: ${url}`, { url, timedOut });
|
|
80
|
+
}
|
|
81
|
+
if ((error as Error)?.name === 'AbortError' || signal.aborted) {
|
|
82
|
+
throw new FetchError(`Request aborted: ${url}`, {
|
|
83
|
+
url: url,
|
|
84
|
+
aborted: true
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
50
89
|
|
|
51
90
|
const { status, url: responseUrl } = response;
|
|
52
91
|
const html = await response.text();
|
package/src/modules/navigate.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import Swup from '../Swup.js';
|
|
1
|
+
import type Swup from '../Swup.js';
|
|
2
2
|
import { createHistoryRecord, updateHistoryRecord, getCurrentUrl, Location } from '../helpers.js';
|
|
3
|
-
import { FetchOptions, PageData } from './fetchPage.js';
|
|
4
|
-
import { VisitInitOptions } from './Visit.js';
|
|
3
|
+
import { FetchError, type FetchOptions, type PageData } from './fetchPage.js';
|
|
4
|
+
import type { VisitInitOptions } from './Visit.js';
|
|
5
5
|
|
|
6
6
|
export type HistoryAction = 'push' | 'replace';
|
|
7
7
|
export type HistoryDirection = 'forwards' | 'backwards';
|
|
@@ -62,6 +62,7 @@ export async function performNavigation(
|
|
|
62
62
|
this: Swup,
|
|
63
63
|
options: NavigationOptions & FetchOptions = {}
|
|
64
64
|
): Promise<void> {
|
|
65
|
+
this.navigating = true;
|
|
65
66
|
// Save this localy to a) allow ignoring the visit if a new one was started in the meantime
|
|
66
67
|
// and b) avoid unintended modifications to any newer visits
|
|
67
68
|
const visit = this.visit;
|
|
@@ -137,20 +138,27 @@ export async function performNavigation(
|
|
|
137
138
|
visit.to.html = html;
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
// perform the actual transition: animate and replace content
|
|
142
|
+
await this.hooks.call('visit:transition', undefined, async (visit) => {
|
|
143
|
+
// Start leave animation
|
|
144
|
+
const animationPromise = this.animatePageOut();
|
|
143
145
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
// Wait for page to load and leave animation to finish
|
|
147
|
+
const [page] = await Promise.all([pagePromise, animationPromise]);
|
|
148
|
+
|
|
149
|
+
// Abort if another visit was started in the meantime
|
|
150
|
+
if (visit.id !== this.visit.id) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
148
153
|
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
// Render page: replace content and scroll to top/fragment
|
|
155
|
+
await this.renderPage(page);
|
|
151
156
|
|
|
152
|
-
|
|
153
|
-
|
|
157
|
+
// Wait for enter animation
|
|
158
|
+
await this.animatePageIn();
|
|
159
|
+
|
|
160
|
+
return true;
|
|
161
|
+
});
|
|
154
162
|
|
|
155
163
|
// Finalize visit
|
|
156
164
|
await this.hooks.call('visit:end', undefined, () => this.classes.clear());
|
|
@@ -159,9 +167,10 @@ export async function performNavigation(
|
|
|
159
167
|
// if (visit.to && this.isSameResolvedUrl(visit.to.url, requestedUrl)) {
|
|
160
168
|
// this.visit = this.createVisit({ to: undefined });
|
|
161
169
|
// }
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if
|
|
170
|
+
this.navigating = false;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
// Return early if error is undefined or signals an aborted request
|
|
173
|
+
if (!error || (error as FetchError)?.aborted) {
|
|
165
174
|
return;
|
|
166
175
|
}
|
|
167
176
|
|
package/src/modules/plugins.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { updateHistoryRecord, getCurrentUrl, classify } from '../helpers.js';
|
|
2
|
-
import Swup from '../Swup.js';
|
|
3
|
-
import { PageData } from './fetchPage.js';
|
|
2
|
+
import type Swup from '../Swup.js';
|
|
3
|
+
import type { PageData } from './fetchPage.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Render the next page: replace the content and update scroll position.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import Swup
|
|
1
|
+
import type Swup from '../Swup.js';
|
|
2
|
+
import type { Options } from '../Swup.js';
|
|
2
3
|
import { query, queryAll } from '../utils.js';
|
|
3
|
-
import { PageData } from './fetchPage.js';
|
|
4
|
+
import type { PageData } from './fetchPage.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Perform the replacement of content after loading a page.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { DelegateEvent } from 'delegate-it';
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
-
|
|
4
|
-
import pckg from '../../package.json';
|
|
5
|
-
import Swup, { Options, Plugin } from '../index.js';
|
|
6
|
-
import * as SwupTS from '../Swup.js';
|
|
7
|
-
|
|
8
|
-
const baseUrl = window.location.origin;
|
|
9
|
-
|
|
10
|
-
describe('Exports', () => {
|
|
11
|
-
it('should export Swup and Options/Plugin types', () => {
|
|
12
|
-
class SwupPlugin implements Plugin {
|
|
13
|
-
name = 'SwupPlugin';
|
|
14
|
-
isSwupPlugin = true as const;
|
|
15
|
-
mount = () => {};
|
|
16
|
-
unmount = () => {};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const options: Partial<Options> = {
|
|
20
|
-
animateHistoryBrowsing: false,
|
|
21
|
-
animationSelector: '[class*="transition-"]',
|
|
22
|
-
cache: true,
|
|
23
|
-
containers: ['#swup'],
|
|
24
|
-
ignoreVisit: (url, { el } = {}) => !!el?.closest('[data-no-swup]'),
|
|
25
|
-
linkSelector: 'a[href]',
|
|
26
|
-
plugins: [new SwupPlugin()],
|
|
27
|
-
resolveUrl: (url) => url,
|
|
28
|
-
requestHeaders: {
|
|
29
|
-
'X-Requested-With': 'swup',
|
|
30
|
-
'Accept': 'text/html, application/xhtml+xml'
|
|
31
|
-
},
|
|
32
|
-
skipPopStateHandling: (event) => event.state?.source !== 'swup'
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const swup = new Swup(options);
|
|
36
|
-
expect(swup).toBeInstanceOf(Swup);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should define a version', () => {
|
|
40
|
-
const swup = new Swup();
|
|
41
|
-
expect(swup.version).not.toBeUndefined();
|
|
42
|
-
expect(swup.version).toEqual(pckg.version);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('UMD compatibility: Swup.ts should only have a default export', () => {
|
|
46
|
-
expect(Object.keys(SwupTS)).toEqual(['default']);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('ignoreVisit', () => {
|
|
51
|
-
it('should be called with relative URL', () => {
|
|
52
|
-
const ignoreVisit = vi.fn(() => true);
|
|
53
|
-
const swup = new Swup({ ignoreVisit });
|
|
54
|
-
swup.shouldIgnoreVisit(`${baseUrl}/path/?query#hash`);
|
|
55
|
-
|
|
56
|
-
expect(ignoreVisit.mock.calls).toHaveLength(1);
|
|
57
|
-
expect((ignoreVisit.mock.lastCall as any)[0]).toEqual('/path/?query#hash');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should have access to element and event params', () => {
|
|
61
|
-
const el = document.createElement('a');
|
|
62
|
-
el.href = `${baseUrl}/path/?query#hash`;
|
|
63
|
-
const event = new MouseEvent('click') as DelegateEvent<MouseEvent>;
|
|
64
|
-
event.delegateTarget = el;
|
|
65
|
-
|
|
66
|
-
const ignoreVisit = vi.fn(() => true);
|
|
67
|
-
const swup = new Swup({ ignoreVisit });
|
|
68
|
-
swup.navigate(el.href, {}, { el, event });
|
|
69
|
-
|
|
70
|
-
expect(ignoreVisit.mock.calls).toHaveLength(1);
|
|
71
|
-
expect((ignoreVisit.mock.lastCall as any)[1]).toEqual(
|
|
72
|
-
expect.objectContaining({ el, event })
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should be called from visit method', () => {
|
|
77
|
-
const ignoreVisit = vi.fn(() => true);
|
|
78
|
-
const swup = new Swup({ ignoreVisit });
|
|
79
|
-
swup.navigate('/path/');
|
|
80
|
-
|
|
81
|
-
expect(ignoreVisit.mock.calls).toHaveLength(1);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import Swup from '../../Swup.js';
|
|
3
|
-
import { Cache, CacheData } from '../Cache.js';
|
|
4
|
-
import { Visit } from '../Visit.js';
|
|
5
|
-
|
|
6
|
-
interface CacheTtlData {
|
|
7
|
-
ttl: number;
|
|
8
|
-
created: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface CacheIndexData {
|
|
12
|
-
index: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface AugmentedCacheData extends CacheData, CacheTtlData, CacheIndexData {}
|
|
16
|
-
|
|
17
|
-
const swup = new Swup();
|
|
18
|
-
const visit = swup.visit;
|
|
19
|
-
const cache = new Cache(swup);
|
|
20
|
-
|
|
21
|
-
const page1 = { url: '/page-1', html: '1' };
|
|
22
|
-
const page2 = { url: '/page-2', html: '2' };
|
|
23
|
-
const page3 = { url: '/page-3', html: '3' };
|
|
24
|
-
|
|
25
|
-
describe('Cache', () => {
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
cache.clear();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should be empty', () => {
|
|
31
|
-
expect(cache.size).toBe(0);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should append pages', () => {
|
|
35
|
-
cache.set(page1.url, page1);
|
|
36
|
-
expect(cache.size).toBe(1);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should have pages', () => {
|
|
40
|
-
cache.set(page1.url, page1);
|
|
41
|
-
expect(cache.has(page1.url)).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should get pages', () => {
|
|
45
|
-
cache.set(page1.url, page1);
|
|
46
|
-
expect(cache.get(page1.url)).toEqual(page1);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should delete pages', () => {
|
|
50
|
-
cache.set(page1.url, page1);
|
|
51
|
-
expect(cache.has(page1.url)).toBe(true);
|
|
52
|
-
cache.delete(page1.url);
|
|
53
|
-
expect(cache.has(page1.url)).toBe(false);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should clear', () => {
|
|
57
|
-
cache.set(page1.url, page1);
|
|
58
|
-
expect(cache.size).toBe(1);
|
|
59
|
-
cache.clear();
|
|
60
|
-
expect(cache.size).toBe(0);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should overwrite identical pages', () => {
|
|
64
|
-
cache.set(page1.url, page1);
|
|
65
|
-
expect(cache.size).toBe(1);
|
|
66
|
-
cache.set(page1.url, page1);
|
|
67
|
-
expect(cache.size).toBe(1);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should not overwrite different pages', () => {
|
|
71
|
-
cache.set(page1.url, page1);
|
|
72
|
-
expect(cache.size).toBe(1);
|
|
73
|
-
cache.set(page2.url, page2);
|
|
74
|
-
expect(cache.size).toBe(2);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should trigger a hook on set', () => {
|
|
78
|
-
const handler = vi.fn();
|
|
79
|
-
|
|
80
|
-
swup.hooks.on('cache:set', handler);
|
|
81
|
-
|
|
82
|
-
cache.set(page1.url, page1);
|
|
83
|
-
|
|
84
|
-
expect(handler).toBeCalledTimes(1);
|
|
85
|
-
expect(handler).toBeCalledWith(visit, { page: page1 }, undefined);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should allow augmenting cache entries on save', () => {
|
|
89
|
-
const now = Date.now();
|
|
90
|
-
|
|
91
|
-
swup.hooks.on('cache:set', (_, { page }) => {
|
|
92
|
-
const ttl: CacheTtlData = { ttl: 1000, created: now };
|
|
93
|
-
cache.update(page.url, ttl);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
cache.set('/page', { url: '/page', html: '' });
|
|
97
|
-
|
|
98
|
-
const page = cache.get('/page');
|
|
99
|
-
|
|
100
|
-
expect(page).toEqual({ url: '/page', html: '', ttl: 1000, created: now });
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should allow manual pruning', () => {
|
|
104
|
-
swup.hooks.on('cache:set', (_, { page }) => {
|
|
105
|
-
cache.update(page.url, { index: cache.size });
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
cache.set(page1.url, page1);
|
|
109
|
-
cache.set(page2.url, page2);
|
|
110
|
-
cache.set(page3.url, page3);
|
|
111
|
-
|
|
112
|
-
cache.prune((url, page) => (page as AugmentedCacheData).index > 2);
|
|
113
|
-
|
|
114
|
-
expect(cache.size).toBe(2);
|
|
115
|
-
expect(cache.has(page1.url)).toBe(true);
|
|
116
|
-
expect(cache.has(page2.url)).toBe(true);
|
|
117
|
-
expect(cache.has(page3.url)).toBe(false);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should return a copy from cache.get()', () => {
|
|
121
|
-
cache.set(page1.url, page1);
|
|
122
|
-
const page = cache.get(page1.url);
|
|
123
|
-
page!.html = 'new';
|
|
124
|
-
expect(cache.get(page1.url)?.html).toEqual(page1.html);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should return a new Map with shallow copies from cache.all', () => {
|
|
128
|
-
cache.set(page1.url, page1);
|
|
129
|
-
cache.set(page2.url, page2);
|
|
130
|
-
|
|
131
|
-
const all = cache.all;
|
|
132
|
-
all.get(page1.url)!.html = 'new';
|
|
133
|
-
|
|
134
|
-
expect(cache.get(page1.url)?.html).toEqual(page1.html);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe('Types', () => {
|
|
139
|
-
it('error when necessary', async () => {
|
|
140
|
-
const swup = new Swup();
|
|
141
|
-
const cache = new Cache(swup);
|
|
142
|
-
|
|
143
|
-
// @ts-expect-no-error
|
|
144
|
-
swup.hooks.on('history:popstate', (visit: Visit, { event: PopStateEvent }) => {});
|
|
145
|
-
// @ts-expect-no-error
|
|
146
|
-
await swup.hooks.call('history:popstate', { event: new PopStateEvent('') });
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
// @ts-expect-error
|
|
150
|
-
cache.set();
|
|
151
|
-
// @ts-expect-error
|
|
152
|
-
cache.set(url);
|
|
153
|
-
// @ts-expect-error
|
|
154
|
-
cache.set(url, {});
|
|
155
|
-
// @ts-expect-error
|
|
156
|
-
cache.set({ url: '/test' });
|
|
157
|
-
} catch (error) {}
|
|
158
|
-
});
|
|
159
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { DelegateEvent } from 'delegate-it';
|
|
2
|
-
import { describe, it } from 'vitest';
|
|
3
|
-
|
|
4
|
-
import { delegateEvent } from '../../helpers/delegateEvent.js';
|
|
5
|
-
|
|
6
|
-
describe('delegateEvent', () => {
|
|
7
|
-
it('should return correct types', () => {
|
|
8
|
-
delegateEvent('form', 'submit', (event) => {});
|
|
9
|
-
|
|
10
|
-
// @ts-expect-no-error
|
|
11
|
-
delegateEvent('form', 'submit', (event: SubmitEvent) => {});
|
|
12
|
-
// @ts-expect-error
|
|
13
|
-
delegateEvent('form', 'submit', (event: MouseEvent) => {});
|
|
14
|
-
|
|
15
|
-
// @ts-expect-no-error
|
|
16
|
-
delegateEvent('form', 'submit', (event: DelegateEvent<SubmitEvent>) => {});
|
|
17
|
-
// @ts-expect-error
|
|
18
|
-
delegateEvent('form', 'submit', (event: DelegateEvent<MouseEvent>) => {});
|
|
19
|
-
|
|
20
|
-
// @ts-expect-no-error
|
|
21
|
-
delegateEvent('form', 'submit', (event: DelegateEvent<SubmitEvent, HTMLFormElement>) => {});
|
|
22
|
-
delegateEvent(
|
|
23
|
-
'form',
|
|
24
|
-
'submit',
|
|
25
|
-
// @ts-expect-error
|
|
26
|
-
(event: DelegateEvent<MouseEvent, HTMLAnchorElement>) => {}
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
delegateEvent('form', 'submit', (event) => {
|
|
30
|
-
// @ts-expect-no-error
|
|
31
|
-
const el: HTMLFormElement = event.delegateTarget;
|
|
32
|
-
});
|
|
33
|
-
// @ts-expect-error
|
|
34
|
-
delegateEvent('form', 'submit', (event: MouseEvent) => {});
|
|
35
|
-
});
|
|
36
|
-
});
|