@withl5e/l5e 0.1.0-alpha.0

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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +24 -0
  3. package/dist/action.js +10 -0
  4. package/dist/action.js.map +1 -0
  5. package/dist/client-D67hK4Yy.js +9 -0
  6. package/dist/client-D67hK4Yy.js.map +1 -0
  7. package/dist/entry-server-Ckh6zfgm.js +258 -0
  8. package/dist/entry-server-Ckh6zfgm.js.map +1 -0
  9. package/dist/entry-server.js +12 -0
  10. package/dist/entry-server.js.map +1 -0
  11. package/dist/generateMetadata-C5QsMS-H.js +144 -0
  12. package/dist/generateMetadata-C5QsMS-H.js.map +1 -0
  13. package/dist/index-BIt7MJT9.js +163 -0
  14. package/dist/index-BIt7MJT9.js.map +1 -0
  15. package/dist/index.js +49 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/island/client.js +5 -0
  18. package/dist/island/client.js.map +1 -0
  19. package/dist/island/runtime.js +98 -0
  20. package/dist/island/runtime.js.map +1 -0
  21. package/dist/island.js +39 -0
  22. package/dist/island.js.map +1 -0
  23. package/dist/jsx-runtime-C2Vw67N2.js +256 -0
  24. package/dist/jsx-runtime-C2Vw67N2.js.map +1 -0
  25. package/dist/jsx-runtime.js +26 -0
  26. package/dist/jsx-runtime.js.map +1 -0
  27. package/dist/middleware.js +9 -0
  28. package/dist/middleware.js.map +1 -0
  29. package/dist/seo.js +7 -0
  30. package/dist/seo.js.map +1 -0
  31. package/dist/server.js +489 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/swap/server.js +15 -0
  34. package/dist/swap/server.js.map +1 -0
  35. package/dist/swap.js +121 -0
  36. package/dist/swap.js.map +1 -0
  37. package/dist/tooltip.js +129 -0
  38. package/dist/tooltip.js.map +1 -0
  39. package/dist/vite-plugin.js +381 -0
  40. package/dist/vite-plugin.js.map +1 -0
  41. package/index.ts +1 -0
  42. package/package.json +129 -0
  43. package/src/action/define-action.ts +8 -0
  44. package/src/action/index.ts +2 -0
  45. package/src/action/types.ts +21 -0
  46. package/src/core/bundler.ts +275 -0
  47. package/src/core/const.ts +2 -0
  48. package/src/core/entry-server.d.ts +1 -0
  49. package/src/core/entry-server.ts +381 -0
  50. package/src/core/exceptions.ts +80 -0
  51. package/src/core/head-priority.ts +15 -0
  52. package/src/core/index.ts +40 -0
  53. package/src/core/jsx-runtime.ts +325 -0
  54. package/src/core/jsx-types.d.ts +548 -0
  55. package/src/core/render.ts +181 -0
  56. package/src/core/request.ts +31 -0
  57. package/src/core/server.ts +740 -0
  58. package/src/core/vite-plugin.ts +779 -0
  59. package/src/island/ClientIsland.ts +71 -0
  60. package/src/island/client.ts +3 -0
  61. package/src/island/index.ts +3 -0
  62. package/src/island/runtime.ts +149 -0
  63. package/src/island/strategy-registry.ts +10 -0
  64. package/src/island/types.ts +28 -0
  65. package/src/middleware/defineMiddleware.ts +5 -0
  66. package/src/middleware/index.ts +133 -0
  67. package/src/middleware/sequence.ts +105 -0
  68. package/src/middleware/types.ts +28 -0
  69. package/src/seo/generateMetadata.tsx +559 -0
  70. package/src/seo/index.ts +10 -0
  71. package/src/seo/mergeMetadata.ts +200 -0
  72. package/src/seo/types.ts +316 -0
  73. package/src/swap/SwapResponse.tsx +16 -0
  74. package/src/swap/create-swap.ts +121 -0
  75. package/src/swap/index.ts +8 -0
  76. package/src/swap/parse.ts +12 -0
  77. package/src/swap/server.ts +1 -0
  78. package/src/swap/swap.ts +57 -0
  79. package/src/swap/types.ts +47 -0
  80. package/src/swap/utils.ts +7 -0
  81. package/src/tooltip/index.ts +2 -0
  82. package/src/tooltip/tooltip-loader.ts +108 -0
  83. package/src/tooltip/tooltip-runtime.ts +173 -0
  84. package/types.d.ts +14 -0
@@ -0,0 +1,200 @@
1
+ import type { Metadata } from './types';
2
+
3
+ /**
4
+ * Merges parent and child metadata objects
5
+ * Follows Next.js shallow merge pattern with deep merge for nested objects
6
+ *
7
+ * @param parent - Parent metadata (from layout/global loader)
8
+ * @param child - Child metadata (from page/view loader)
9
+ * @returns Merged metadata object
10
+ */
11
+ export function mergeMetadata(
12
+ parent: Metadata | null | undefined,
13
+ child: Metadata | null | undefined,
14
+ ): Metadata {
15
+ // If no parent, return child (or empty object)
16
+ if (!parent) {
17
+ return child || {};
18
+ }
19
+
20
+ // If no child, return parent
21
+ if (!child) {
22
+ return parent;
23
+ }
24
+
25
+ // Shallow merge base properties (child overrides parent)
26
+ const merged: Metadata = {
27
+ ...parent,
28
+ ...child,
29
+ };
30
+
31
+ // Deep merge for nested objects
32
+ // OpenGraph: merge nested properties
33
+ if (child.openGraph || parent.openGraph) {
34
+ if (child.openGraph && parent.openGraph) {
35
+ merged.openGraph = {
36
+ ...parent.openGraph,
37
+ ...child.openGraph,
38
+ // Deep merge for nested arrays/objects in openGraph
39
+ images: child.openGraph.images ?? parent.openGraph.images,
40
+ videos: child.openGraph.videos ?? parent.openGraph.videos,
41
+ audio: child.openGraph.audio ?? parent.openGraph.audio,
42
+ alternateLocale: child.openGraph.alternateLocale ?? parent.openGraph.alternateLocale,
43
+ authors: child.openGraph.authors ?? parent.openGraph.authors,
44
+ tags: child.openGraph.tags ?? parent.openGraph.tags,
45
+ };
46
+ } else {
47
+ merged.openGraph = child.openGraph || parent.openGraph;
48
+ }
49
+ }
50
+
51
+ // Twitter: merge nested properties
52
+ if (child.twitter || parent.twitter) {
53
+ if (child.twitter && parent.twitter) {
54
+ merged.twitter = {
55
+ ...parent.twitter,
56
+ ...child.twitter,
57
+ // Deep merge for nested objects
58
+ images: child.twitter.images ?? parent.twitter.images,
59
+ app: child.twitter.app
60
+ ? {
61
+ ...parent.twitter.app,
62
+ ...child.twitter.app,
63
+ id: {
64
+ ...parent.twitter.app?.id,
65
+ ...child.twitter.app.id,
66
+ },
67
+ url: {
68
+ ...parent.twitter.app?.url,
69
+ ...child.twitter.app.url,
70
+ },
71
+ }
72
+ : parent.twitter.app,
73
+ };
74
+ } else {
75
+ merged.twitter = child.twitter || parent.twitter;
76
+ }
77
+ }
78
+
79
+ // Icons: merge nested properties
80
+ if (child.icons || parent.icons) {
81
+ if (child.icons && parent.icons) {
82
+ merged.icons = {
83
+ icon: child.icons.icon ?? parent.icons.icon,
84
+ shortcut: child.icons.shortcut ?? parent.icons.shortcut,
85
+ apple: child.icons.apple ?? parent.icons.apple,
86
+ other: child.icons.other ?? parent.icons.other,
87
+ };
88
+ } else {
89
+ merged.icons = child.icons || parent.icons;
90
+ }
91
+ }
92
+
93
+ // Verification: merge nested properties
94
+ if (child.verification || parent.verification) {
95
+ if (child.verification && parent.verification) {
96
+ merged.verification = {
97
+ ...parent.verification,
98
+ ...child.verification,
99
+ // Deep merge for other verification tags
100
+ other: child.verification.other
101
+ ? {
102
+ ...parent.verification.other,
103
+ ...child.verification.other,
104
+ }
105
+ : parent.verification.other,
106
+ me: child.verification.me ?? parent.verification.me,
107
+ };
108
+ } else {
109
+ merged.verification = child.verification || parent.verification;
110
+ }
111
+ }
112
+
113
+ // AppLinks: merge nested properties
114
+ if (child.appLinks || parent.appLinks) {
115
+ if (child.appLinks && parent.appLinks) {
116
+ merged.appLinks = {
117
+ ios: child.appLinks.ios
118
+ ? {
119
+ ...parent.appLinks.ios,
120
+ ...child.appLinks.ios,
121
+ }
122
+ : parent.appLinks.ios,
123
+ android: child.appLinks.android
124
+ ? {
125
+ ...parent.appLinks.android,
126
+ ...child.appLinks.android,
127
+ }
128
+ : parent.appLinks.android,
129
+ web: child.appLinks.web
130
+ ? {
131
+ ...parent.appLinks.web,
132
+ ...child.appLinks.web,
133
+ }
134
+ : parent.appLinks.web,
135
+ };
136
+ } else {
137
+ merged.appLinks = child.appLinks || parent.appLinks;
138
+ }
139
+ }
140
+
141
+ // FormatDetection: merge nested properties
142
+ if (child.formatDetection || parent.formatDetection) {
143
+ if (child.formatDetection && parent.formatDetection) {
144
+ merged.formatDetection = {
145
+ ...parent.formatDetection,
146
+ ...child.formatDetection,
147
+ };
148
+ } else {
149
+ merged.formatDetection = child.formatDetection || parent.formatDetection;
150
+ }
151
+ }
152
+
153
+ // Viewport: merge if both are objects
154
+ if (child.viewport && parent.viewport) {
155
+ if (typeof child.viewport === 'object' && typeof parent.viewport === 'object') {
156
+ merged.viewport = {
157
+ ...parent.viewport,
158
+ ...child.viewport,
159
+ } as Metadata['viewport'];
160
+ } else {
161
+ // If either is string, child overrides
162
+ merged.viewport = child.viewport;
163
+ }
164
+ }
165
+
166
+ // Robots: merge if both are objects
167
+ if (child.robots && parent.robots) {
168
+ if (typeof child.robots === 'object' && typeof parent.robots === 'object') {
169
+ merged.robots = {
170
+ ...parent.robots,
171
+ ...child.robots,
172
+ } as Metadata['robots'];
173
+ } else {
174
+ // If either is string, child overrides
175
+ merged.robots = child.robots;
176
+ }
177
+ }
178
+
179
+ // Array fields: child overrides parent (shallow merge behavior)
180
+ // These are already handled by spread operator above
181
+ // But we explicitly handle them for clarity:
182
+ merged.keywords = child.keywords ?? parent.keywords;
183
+ merged.themeColor = child.themeColor ?? parent.themeColor;
184
+ merged.archives = child.archives ?? parent.archives;
185
+ merged.assets = child.assets ?? parent.assets;
186
+
187
+ // Other: merge objects
188
+ if (child.other || parent.other) {
189
+ if (child.other && parent.other) {
190
+ merged.other = {
191
+ ...parent.other,
192
+ ...child.other,
193
+ };
194
+ } else {
195
+ merged.other = child.other || parent.other;
196
+ }
197
+ }
198
+
199
+ return merged;
200
+ }
@@ -0,0 +1,316 @@
1
+ // Basic metadata types
2
+ export type Author = { name: string; url?: string };
3
+
4
+ export interface Metadata {
5
+ // Basic meta tags
6
+ charset?: string;
7
+ title?: string | TemplateString;
8
+ description?: string;
9
+ keywords?: string | string[];
10
+ author?: string | Author | Author[];
11
+
12
+ // URL and canonical
13
+ canonical?: string;
14
+
15
+ // Robots and indexing
16
+ robots?:
17
+ | string
18
+ | {
19
+ index?: boolean;
20
+ follow?: boolean;
21
+ noarchive?: boolean;
22
+ nosnippet?: boolean;
23
+ noimageindex?: boolean;
24
+ nocache?: boolean;
25
+ allowQueryNames?: string | string[];
26
+ };
27
+
28
+ // Viewport
29
+ viewport?:
30
+ | string
31
+ | {
32
+ width?: string | number;
33
+ height?: string | number;
34
+ initialScale?: number;
35
+ maximumScale?: number;
36
+ minimumScale?: number;
37
+ userScalable?: boolean;
38
+ };
39
+
40
+ // Theme and colors
41
+ themeColor?: string | string[];
42
+ colorScheme?: 'light' | 'dark' | 'light dark' | 'dark light';
43
+
44
+ // Icons
45
+ icons?: {
46
+ icon?: string | string[];
47
+ shortcut?: string;
48
+ apple?: string | string[];
49
+ other?: Array<{
50
+ rel?: string;
51
+ url: string;
52
+ sizes?: string;
53
+ type?: string;
54
+ }>;
55
+ };
56
+
57
+ // Application specific
58
+ generator?: string;
59
+ creator?: string;
60
+ publisher?: string;
61
+ referrer?:
62
+ | 'no-referrer'
63
+ | 'origin'
64
+ | 'no-referrer-when-downgrade'
65
+ | 'origin-when-cross-origin'
66
+ | 'same-origin'
67
+ | 'strict-origin'
68
+ | 'strict-origin-when-cross-origin'
69
+ | 'unsafe-url';
70
+
71
+ // Pagination links
72
+ prev?: string;
73
+ next?: string;
74
+
75
+ // Format detection
76
+ formatDetection?: {
77
+ telephone?: boolean;
78
+ date?: boolean;
79
+ address?: boolean;
80
+ email?: boolean;
81
+ };
82
+
83
+ // Open Graph
84
+ openGraph?: {
85
+ type?:
86
+ | 'website'
87
+ | 'article'
88
+ | 'book'
89
+ | 'profile'
90
+ | 'music.song'
91
+ | 'music.album'
92
+ | 'music.playlist'
93
+ | 'music.radio_station'
94
+ | 'video.movie'
95
+ | 'video.episode'
96
+ | 'video.tv_show'
97
+ | 'video.other';
98
+ title?: string;
99
+ description?: string;
100
+ siteName?: string;
101
+ url?: string;
102
+ locale?: string;
103
+ alternateLocale?: string | string[];
104
+ images?: string | OpenGraphImage | Array<string | OpenGraphImage>;
105
+ videos?: string | OpenGraphVideo | Array<string | OpenGraphVideo>;
106
+ audio?: string | OpenGraphAudio | Array<string | OpenGraphAudio>;
107
+ determiner?: 'a' | 'an' | 'the' | 'auto' | '';
108
+
109
+ // Article specific
110
+ publishedTime?: string;
111
+ modifiedTime?: string;
112
+ expirationTime?: string;
113
+ authors?: string | string[];
114
+ section?: string;
115
+ tags?: string | string[];
116
+
117
+ // Book specific
118
+ isbn?: string;
119
+ releaseDate?: string;
120
+
121
+ // Profile specific
122
+ firstName?: string;
123
+ lastName?: string;
124
+ username?: string;
125
+ gender?: string;
126
+ };
127
+
128
+ // Twitter Card
129
+ twitter?: {
130
+ card?: 'summary' | 'summary_large_image' | 'app' | 'player';
131
+ site?: string;
132
+ siteId?: string;
133
+ creator?: string;
134
+ creatorId?: string;
135
+ title?: string;
136
+ description?: string;
137
+ images?: string | TwitterImage | Array<string | TwitterImage>;
138
+ app?: {
139
+ id: {
140
+ iphone?: string;
141
+ ipad?: string;
142
+ googleplay?: string;
143
+ };
144
+ url?: {
145
+ iphone?: string;
146
+ ipad?: string;
147
+ googleplay?: string;
148
+ };
149
+ name?: string;
150
+ };
151
+ };
152
+
153
+ // Verification
154
+ verification?: {
155
+ google?: string;
156
+ yahoo?: string;
157
+ yandex?: string;
158
+ me?: string | string[];
159
+ other?: Record<string, string | string[]>;
160
+ };
161
+
162
+ // App Links
163
+ appLinks?: {
164
+ ios?: {
165
+ url?: string;
166
+ app_store_id?: string;
167
+ app_name?: string;
168
+ };
169
+ android?: {
170
+ package?: string;
171
+ app_name?: string;
172
+ url?: string;
173
+ };
174
+ web?: {
175
+ url?: string;
176
+ should_fallback?: boolean;
177
+ };
178
+ };
179
+
180
+ // Additional meta tags
181
+ other?: Record<string, string | number | string[]>;
182
+
183
+ // Archives (for blog posts, etc.)
184
+ archives?: string | string[];
185
+
186
+ // Assets and manifests
187
+ assets?: string | string[];
188
+ manifest?: string;
189
+
190
+ feed?: string;
191
+
192
+ // Category
193
+ category?: string;
194
+
195
+ // Classification
196
+ classification?: string;
197
+ }
198
+
199
+ export interface OpenGraphImage {
200
+ url: string;
201
+ secureUrl?: string;
202
+ alt?: string;
203
+ type?: string;
204
+ width?: string | number;
205
+ height?: string | number;
206
+ }
207
+
208
+ export interface OpenGraphVideo {
209
+ url: string;
210
+ secureUrl?: string;
211
+ type?: string;
212
+ width?: string | number;
213
+ height?: string | number;
214
+ }
215
+
216
+ export interface OpenGraphAudio {
217
+ url: string;
218
+ secureUrl?: string;
219
+ type?: string;
220
+ }
221
+
222
+ export interface TwitterImage {
223
+ url: string;
224
+ alt?: string;
225
+ }
226
+
227
+ export interface TemplateString {
228
+ default: string;
229
+ template?: string;
230
+ absolute?: boolean;
231
+ }
232
+
233
+ // For dynamic metadata generation
234
+ export interface ResolvingMetadata extends Promise<Metadata> {}
235
+
236
+ // Page props type for generateMetadata
237
+ export interface MetadataProps<
238
+ Params = Record<string, string | string[]>,
239
+ SearchParams = Record<string, string | string[] | undefined>,
240
+ > {
241
+ params?: Params;
242
+ searchParams?: SearchParams;
243
+ }
244
+
245
+ // Schema.org types for structured data
246
+ export interface OrganizationSchema {
247
+ '@context': 'https://schema.org';
248
+ '@type': 'Organization';
249
+ name: string;
250
+ url: string;
251
+ logo?: string;
252
+ description?: string;
253
+ sameAs?: string[]; // Social media profiles
254
+ contactPoint?: {
255
+ '@type': 'ContactPoint';
256
+ email?: string;
257
+ contactType?: string;
258
+ availableLanguage?: string[];
259
+ };
260
+ address?: {
261
+ '@type': 'PostalAddress';
262
+ addressCountry?: string;
263
+ addressLocality?: string;
264
+ };
265
+ }
266
+
267
+ export interface WebSiteSchema {
268
+ '@context': 'https://schema.org';
269
+ '@type': 'WebSite';
270
+ name: string;
271
+ url: string;
272
+ description?: string;
273
+ inLanguage?: string;
274
+ potentialAction?: {
275
+ '@type': 'SearchAction';
276
+ target: {
277
+ '@type': 'EntryPoint';
278
+ urlTemplate: string;
279
+ };
280
+ 'query-input': string;
281
+ };
282
+ }
283
+
284
+ export interface BreadcrumbSchema {
285
+ '@context': 'https://schema.org';
286
+ '@type': 'BreadcrumbList';
287
+ itemListElement: Array<{
288
+ '@type': 'ListItem';
289
+ position: number;
290
+ name: string;
291
+ item?: string;
292
+ }>;
293
+ }
294
+
295
+ export interface WebPageSchema {
296
+ '@context': 'https://schema.org';
297
+ '@type': 'WebPage';
298
+ name: string;
299
+ url: string;
300
+ description?: string;
301
+ inLanguage?: string;
302
+ isPartOf?: {
303
+ '@type': 'WebSite';
304
+ '@id': string;
305
+ };
306
+ about?: {
307
+ '@type': 'Thing';
308
+ name: string;
309
+ };
310
+ primaryImageOfPage?: {
311
+ '@type': 'ImageObject';
312
+ url: string;
313
+ width?: number;
314
+ height?: number;
315
+ };
316
+ }
@@ -0,0 +1,16 @@
1
+ interface SwapResponseProps {
2
+ children: JSX.Element | JSX.Element[];
3
+ hasMore?: boolean;
4
+ meta?: Record<string, string>;
5
+ }
6
+
7
+ export function SwapResponse({ children, hasMore, meta }: SwapResponseProps) {
8
+ return (
9
+ <div
10
+ data-has-more={hasMore !== undefined ? String(hasMore) : undefined}
11
+ {...(meta && Object.fromEntries(Object.entries(meta).map(([k, v]) => [`data-${k}`, v])))}
12
+ >
13
+ {children}
14
+ </div>
15
+ );
16
+ }
@@ -0,0 +1,121 @@
1
+ import { parseHTML } from './parse';
2
+ import { swap } from './swap';
3
+ import type { SwapOptions, SwapInstance, SwapMeta, SwapContext } from './types';
4
+
5
+ const REQUEST_CLASS = 'l5e-request';
6
+
7
+ function getNaturalEvent(el: Element): string {
8
+ const tag = el.tagName.toLowerCase();
9
+ if (tag === 'form') return 'submit';
10
+ if (tag === 'input' || tag === 'textarea' || tag === 'select') return 'change';
11
+ return 'click';
12
+ }
13
+
14
+ export function createSwap(opts: SwapOptions): SwapInstance {
15
+ let destroyed = false;
16
+ const swapMode = opts.swap ?? 'innerHTML';
17
+
18
+ const triggerEls = opts.trigger
19
+ ? Array.from(document.querySelectorAll<HTMLElement>(opts.trigger))
20
+ : [];
21
+
22
+ const targetEl = document.querySelector<HTMLElement>(opts.target);
23
+
24
+ const firstTrigger = triggerEls[0] ?? null;
25
+ const eventName = opts.event ?? (firstTrigger ? getNaturalEvent(firstTrigger) : 'click');
26
+ const shouldPreventDefault = eventName === 'submit';
27
+
28
+ const loadingCfg = opts.loading;
29
+ const loadingClass = loadingCfg?.class ?? REQUEST_CLASS;
30
+
31
+ function applyLoading(currentTrigger: HTMLElement | null): () => void {
32
+ if (!loadingCfg) return () => {};
33
+ const el = loadingCfg.target
34
+ ? document.querySelector<HTMLElement>(loadingCfg.target)
35
+ : currentTrigger;
36
+ if (!el) return () => {};
37
+
38
+ const shouldDisable = loadingCfg.disabled ?? el === currentTrigger;
39
+ const originalHtml = el.innerHTML;
40
+ const originalDisabled = (el as HTMLButtonElement).disabled ?? false;
41
+
42
+ el.classList.add(loadingClass);
43
+ if (loadingCfg.html) el.innerHTML = loadingCfg.html;
44
+ if (shouldDisable && 'disabled' in el) (el as any).disabled = true;
45
+
46
+ return () => {
47
+ el.classList.remove(loadingClass);
48
+ if (loadingCfg.html && el !== targetEl) el.innerHTML = originalHtml;
49
+ if (shouldDisable && 'disabled' in el) (el as any).disabled = originalDisabled;
50
+ };
51
+ }
52
+
53
+ async function exec(input?: HTMLElement | string): Promise<void> {
54
+ if (destroyed) return;
55
+
56
+ if (!targetEl) return;
57
+
58
+ const ctxEl = input instanceof HTMLElement ? input : firstTrigger;
59
+ const ctx: SwapContext = {
60
+ triggerElement: ctxEl,
61
+ triggerDataset: ctxEl?.dataset ?? ({} as DOMStringMap),
62
+ };
63
+ const htmlInput = typeof input === 'string' ? input : undefined;
64
+
65
+ if (opts.onBefore?.(ctx) === false) return;
66
+
67
+ const restoreLoading = applyLoading(ctxEl);
68
+
69
+ try {
70
+ let html: string;
71
+ let res: Response | undefined;
72
+ if (opts.action) {
73
+ res = await opts.action(ctx);
74
+ html = await res.text();
75
+ } else if (htmlInput !== undefined) {
76
+ html = htmlInput;
77
+ } else {
78
+ return;
79
+ }
80
+
81
+ let nodes: Node[];
82
+ if (opts.select) {
83
+ nodes = parseHTML(html, opts.select) as Node[];
84
+ } else {
85
+ nodes = Array.from(parseHTML(html).childNodes);
86
+ }
87
+
88
+ const root = parseHTML(html).firstElementChild as HTMLElement | null;
89
+ const meta: SwapMeta = { root, response: res };
90
+
91
+ if (opts.onNodes) {
92
+ const t = opts.onNodes(nodes, meta);
93
+ if (Array.isArray(t)) nodes = t;
94
+ }
95
+
96
+ const inserted = swap(targetEl, nodes, swapMode);
97
+
98
+ opts.onSwap?.(inserted, meta);
99
+ opts.onAfter?.(inserted, meta, ctx);
100
+ } catch (err: any) {
101
+ opts.onError?.(err, err.status);
102
+ } finally {
103
+ restoreLoading();
104
+ }
105
+ }
106
+
107
+ function handleTrigger(e: Event) {
108
+ if (shouldPreventDefault) e.preventDefault();
109
+ exec((e.currentTarget as HTMLElement) ?? undefined);
110
+ }
111
+
112
+ triggerEls.forEach((el) => el.addEventListener(eventName, handleTrigger));
113
+
114
+ return {
115
+ exec,
116
+ destroy() {
117
+ destroyed = true;
118
+ triggerEls.forEach((el) => el.removeEventListener(eventName, handleTrigger));
119
+ },
120
+ };
121
+ }
@@ -0,0 +1,8 @@
1
+ // Layer 1
2
+ export { parseHTML } from './parse';
3
+ export { swap } from './swap';
4
+ export { debounce } from './utils';
5
+
6
+ // Layer 2
7
+ export { createSwap } from './create-swap';
8
+ export type { SwapOptions, SwapInstance, SwapMeta, SwapMode, SwapContext } from './types';
@@ -0,0 +1,12 @@
1
+ export function parseHTML(html: string): DocumentFragment;
2
+ export function parseHTML(html: string, selector: string): Element[];
3
+ export function parseHTML(html: string, selector?: string): DocumentFragment | Element[] {
4
+ const tpl = document.createElement('template');
5
+ tpl.innerHTML = html;
6
+ const fragment = tpl.content;
7
+
8
+ if (selector) {
9
+ return Array.from(fragment.querySelectorAll(selector));
10
+ }
11
+ return fragment;
12
+ }
@@ -0,0 +1 @@
1
+ export { SwapResponse } from './SwapResponse';