@occluze/tether 0.6.7 → 0.7.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.
- package/README.md +11 -11
- package/dist/esm/astro.js +2 -2
- package/dist/esm/chunks/account-UIPWJQ3Y.js +1 -0
- package/dist/esm/chunks/account-pages-6ROA6Y4V.js +1 -0
- package/dist/esm/chunks/analytics-2V632IRV.js +1 -0
- package/dist/esm/chunks/chunk-4GCXM764.js +1 -0
- package/dist/esm/chunks/chunk-56FCLJJD.js +1 -0
- package/dist/esm/chunks/chunk-5Z5O3767.js +1 -0
- package/dist/esm/chunks/chunk-6OZJCAGZ.js +1 -0
- package/dist/esm/chunks/chunk-A5SYCVQM.js +1 -0
- package/dist/esm/chunks/chunk-BA4LQJJQ.js +1 -0
- package/dist/esm/chunks/chunk-BFTZR4OB.js +4 -0
- package/dist/esm/chunks/chunk-C5JYQCWH.js +1 -0
- package/dist/esm/chunks/chunk-E5YO6274.js +1 -0
- package/dist/esm/chunks/chunk-MPDWM2NN.js +1 -0
- package/dist/esm/chunks/chunk-T44MUL4Y.js +1 -0
- package/dist/esm/chunks/chunk-YLUULATY.js +1 -0
- package/dist/esm/chunks/diagnose-3FEKRIEK.js +14 -0
- package/dist/esm/chunks/extras-6POFBNOP.js +1 -0
- package/dist/esm/chunks/markets-ui-FD6NI2OG.js +1 -0
- package/dist/esm/chunks/search-UJ73YBQ5.js +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/server.js +2 -1
- package/dist/presets/barba.esm.js +55 -39
- package/dist/tether.account-pages.js +781 -495
- package/dist/tether.account-pages.min.js +1 -1
- package/dist/tether.account.js +462 -381
- package/dist/tether.account.min.js +1 -1
- package/dist/tether.analytics.js +299 -194
- package/dist/tether.analytics.min.js +1 -1
- package/dist/tether.diagnose.js +354 -262
- package/dist/tether.diagnose.min.js +2 -2
- package/dist/tether.extras.js +82 -45
- package/dist/tether.extras.min.js +1 -1
- package/dist/tether.js +3653 -2116
- package/dist/tether.markets.js +238 -150
- package/dist/tether.markets.min.js +1 -1
- package/dist/tether.min.js +3 -3
- package/dist/tether.min.js.map +4 -4
- package/dist/tether.search.js +207 -136
- package/dist/tether.search.min.js +1 -1
- package/dist/types/server/src/shared/account-orders.d.ts +44 -0
- package/dist/types/server/src/shared/account-routes.d.ts +153 -0
- package/dist/types/server/src/shared/api-versions.d.ts +13 -0
- package/dist/types/server/src/shared/error-message.d.ts +2 -0
- package/dist/types/server/src/shared/json.d.ts +25 -0
- package/dist/types/server/src/shared/market-codes.d.ts +16 -0
- package/dist/types/src/account/addresses-view.d.ts +5 -0
- package/dist/types/src/account/answers.d.ts +40 -0
- package/dist/types/src/account/markup.d.ts +19 -0
- package/dist/types/src/account/operations.d.ts +41 -0
- package/dist/types/src/account/order-lines.d.ts +13 -0
- package/dist/types/src/account/order-list.d.ts +12 -0
- package/dist/types/src/account/order-page.d.ts +6 -0
- package/dist/types/src/account/page-answers.d.ts +182 -0
- package/dist/types/src/account/page-events.d.ts +2 -0
- package/dist/types/src/account/server-client.d.ts +60 -0
- package/dist/types/src/account/subscriptions-view.d.ts +2 -0
- package/dist/types/src/account-api.d.ts +18 -0
- package/dist/types/src/account-marks.d.ts +15 -0
- package/dist/types/src/account-pages.d.ts +8 -0
- package/dist/types/src/account.d.ts +64 -0
- package/dist/types/src/amounts.d.ts +12 -0
- package/dist/types/src/analytics-shopify.d.ts +41 -0
- package/dist/types/src/analytics.d.ts +2 -0
- package/dist/types/src/api-version.d.ts +28 -0
- package/dist/types/src/astro.d.ts +42 -0
- package/dist/types/src/cart/cart-shape.d.ts +43 -0
- package/dist/types/src/cart/checkout.d.ts +16 -0
- package/dist/types/src/cart/drawer.d.ts +18 -0
- package/dist/types/src/cart/mutations.d.ts +79 -0
- package/dist/types/src/cart/render.d.ts +17 -0
- package/dist/types/src/cart/store.d.ts +182 -0
- package/dist/types/src/cart.d.ts +106 -0
- package/dist/types/src/client.d.ts +100 -0
- package/dist/types/src/collection.d.ts +25 -0
- package/dist/types/src/config-sections.d.ts +40 -0
- package/dist/types/src/config.d.ts +185 -0
- package/dist/types/src/controls/cart-controls.d.ts +2 -0
- package/dist/types/src/controls/discount.d.ts +2 -0
- package/dist/types/src/controls/extras.d.ts +5 -0
- package/dist/types/src/controls/navigation.d.ts +12 -0
- package/dist/types/src/diagnose-markup.d.ts +19 -0
- package/dist/types/src/diagnose.d.ts +60 -0
- package/dist/types/src/dom.d.ts +75 -0
- package/dist/types/src/extras.d.ts +21 -0
- package/dist/types/src/fragments.d.ts +158 -0
- package/dist/types/src/globals.d.ts +15 -0
- package/dist/types/src/images.d.ts +20 -0
- package/dist/types/src/index.d.ts +167 -0
- package/dist/types/src/initial.d.ts +56 -0
- package/dist/types/src/lazy.d.ts +28 -0
- package/dist/types/src/loader.d.ts +84 -0
- package/dist/types/src/market-codes.d.ts +6 -0
- package/dist/types/src/markets-ui.d.ts +32 -0
- package/dist/types/src/markets.d.ts +72 -0
- package/dist/types/src/markup.d.ts +131 -0
- package/dist/types/src/mounting.d.ts +46 -0
- package/dist/types/src/page-market.d.ts +27 -0
- package/dist/types/src/presets/barba.d.ts +56 -0
- package/dist/types/src/product-events.d.ts +2 -0
- package/dist/types/src/product-render.d.ts +17 -0
- package/dist/types/src/product-shape.d.ts +88 -0
- package/dist/types/src/product-state.d.ts +110 -0
- package/dist/types/src/product.d.ts +66 -0
- package/dist/types/src/repeaters.d.ts +16 -0
- package/dist/types/src/search.d.ts +33 -0
- package/dist/types/src/selling-plans.d.ts +64 -0
- package/dist/types/src/server.d.ts +88 -0
- package/dist/types/src/shopify-values.d.ts +46 -0
- package/dist/types/src/status.d.ts +33 -0
- package/dist/types/src/storage.d.ts +12 -0
- package/dist/types/src/store-html.d.ts +39 -0
- package/dist/types/src/storefront/catalog.d.ts +38 -0
- package/dist/types/src/storefront/json-ld.d.ts +20 -0
- package/dist/types/src/variant-render.d.ts +14 -0
- package/package.json +42 -22
- package/src/account/addresses-view.ts +58 -0
- package/src/account/answers.ts +79 -0
- package/src/account/markup.ts +36 -0
- package/src/account/operations.ts +145 -0
- package/src/account/order-lines.ts +75 -0
- package/src/account/order-list.ts +107 -0
- package/src/account/order-page.ts +137 -0
- package/src/account/page-answers.ts +317 -0
- package/src/account/page-events.ts +253 -0
- package/src/account/server-client.ts +183 -0
- package/src/account/subscriptions-view.ts +115 -0
- package/src/account-api.ts +98 -0
- package/src/account-marks.ts +35 -0
- package/src/account-pages.ts +81 -0
- package/src/account.ts +284 -0
- package/src/amounts.ts +64 -0
- package/src/analytics-shopify.ts +246 -0
- package/src/analytics.ts +315 -0
- package/src/api-version.ts +79 -0
- package/src/astro.ts +272 -0
- package/src/cart/cart-shape.ts +25 -0
- package/src/cart/checkout.ts +101 -0
- package/src/cart/drawer.ts +125 -0
- package/src/cart/mutations.ts +237 -0
- package/src/cart/render.ts +266 -0
- package/src/cart/store.ts +279 -0
- package/src/cart.ts +345 -0
- package/src/client.ts +391 -0
- package/src/collection.ts +175 -0
- package/src/config-sections.ts +284 -0
- package/src/config.ts +480 -0
- package/src/controls/cart-controls.ts +123 -0
- package/src/controls/discount.ts +75 -0
- package/src/controls/extras.ts +54 -0
- package/src/controls/navigation.ts +77 -0
- package/src/diagnose-markup.ts +298 -0
- package/src/diagnose.ts +294 -0
- package/src/dom.ts +268 -0
- package/src/extras.ts +136 -0
- package/src/fragments.ts +261 -0
- package/src/globals.d.ts +15 -0
- package/src/images.ts +91 -0
- package/src/index.ts +415 -0
- package/src/initial.ts +101 -0
- package/src/lazy-iife.ts +197 -0
- package/src/lazy.ts +42 -0
- package/src/loader.ts +363 -0
- package/src/market-codes.ts +6 -0
- package/src/markets-ui.ts +227 -0
- package/src/markets.ts +246 -0
- package/src/markup.ts +127 -0
- package/src/mounting.ts +264 -0
- package/src/page-market.ts +110 -0
- package/src/part-integrity.ts +7 -0
- package/src/parts/account-pages.ts +7 -0
- package/src/parts/account.ts +7 -0
- package/src/parts/analytics.ts +7 -0
- package/src/parts/diagnose.ts +7 -0
- package/src/parts/extras.ts +7 -0
- package/src/parts/markets.ts +7 -0
- package/src/parts/search.ts +7 -0
- package/src/presets/barba.ts +194 -0
- package/src/product-events.ts +115 -0
- package/src/product-render.ts +218 -0
- package/src/product-shape.ts +49 -0
- package/src/product-state.ts +318 -0
- package/src/product.ts +308 -0
- package/src/repeaters.ts +161 -0
- package/src/search.ts +305 -0
- package/src/selling-plans.ts +145 -0
- package/src/server.ts +211 -0
- package/src/shopify-values.ts +91 -0
- package/src/status.ts +106 -0
- package/src/storage.ts +34 -0
- package/src/store-html.ts +359 -0
- package/src/storefront/catalog.ts +196 -0
- package/src/storefront/json-ld.ts +144 -0
- package/src/variant-render.ts +139 -0
- package/dist/esm/chunks/account-Q6E2TFEV.js +0 -1
- package/dist/esm/chunks/account-pages-UZXR4R5F.js +0 -1
- package/dist/esm/chunks/analytics-IJBKATUP.js +0 -1
- package/dist/esm/chunks/chunk-4QJKLPYR.js +0 -1
- package/dist/esm/chunks/chunk-64TUD3QR.js +0 -1
- package/dist/esm/chunks/chunk-AJLPU2JC.js +0 -4
- package/dist/esm/chunks/chunk-FIOZPPC5.js +0 -1
- package/dist/esm/chunks/chunk-K2YEQAIH.js +0 -1
- package/dist/esm/chunks/chunk-KIDH76B7.js +0 -1
- package/dist/esm/chunks/chunk-PAYT5ZD7.js +0 -1
- package/dist/esm/chunks/chunk-RREHGH4A.js +0 -1
- package/dist/esm/chunks/chunk-SSJT43CJ.js +0 -1
- package/dist/esm/chunks/chunk-VC46IEJQ.js +0 -1
- package/dist/esm/chunks/diagnose-3WVAWNTX.js +0 -14
- package/dist/esm/chunks/extras-7I7WJX35.js +0 -1
- package/dist/esm/chunks/markets-ui-545XLFTE.js +0 -1
- package/dist/esm/chunks/search-NILMGYXG.js +0 -1
- package/src/account-pages.js +0 -318
- package/src/account.js +0 -385
- package/src/analytics.js +0 -232
- package/src/api-version.js +0 -35
- package/src/astro.js +0 -101
- package/src/cart.js +0 -559
- package/src/client.js +0 -131
- package/src/collection.js +0 -114
- package/src/config.js +0 -85
- package/src/diagnose.js +0 -246
- package/src/dom.js +0 -288
- package/src/extras.js +0 -63
- package/src/fragments.js +0 -144
- package/src/index.js +0 -395
- package/src/initial.js +0 -75
- package/src/lazy-iife.js +0 -46
- package/src/lazy.js +0 -18
- package/src/loader.js +0 -198
- package/src/markets-ui.js +0 -90
- package/src/markets.js +0 -133
- package/src/mounting.js +0 -154
- package/src/parts/account-pages.js +0 -3
- package/src/parts/account.js +0 -3
- package/src/parts/analytics.js +0 -3
- package/src/parts/diagnose.js +0 -3
- package/src/parts/extras.js +0 -3
- package/src/parts/markets.js +0 -3
- package/src/parts/search.js +0 -3
- package/src/presets/barba.js +0 -100
- package/src/product.js +0 -570
- package/src/search.js +0 -153
- package/src/selling-plans.js +0 -94
- package/src/server.js +0 -252
- package/src/status.js +0 -66
- package/types/astro.d.ts +0 -17
- package/types/barba.d.ts +0 -16
- package/types/index.d.ts +0 -426
- package/types/server.d.ts +0 -110
package/src/index.js
DELETED
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tether — Shopify <-> Webflow (or any HTML) sync via the Storefront API.
|
|
3
|
-
*
|
|
4
|
-
* <script>window.TETHER_CONFIG = { domain: 'x.myshopify.com', token: '...' }</script>
|
|
5
|
-
* <script src="tether.min.js" defer></script>
|
|
6
|
-
*
|
|
7
|
-
* With page transitions (Barba, Swup): set autoMount: false, call
|
|
8
|
-
* Tether.init() once and Tether.mount(container) after every page enter.
|
|
9
|
-
*/
|
|
10
|
-
import { cfg, configure, isConfigured, log, warn, reportError } from './config.js';
|
|
11
|
-
import { gql, TetherError, withContext } from './client.js';
|
|
12
|
-
import { Cart, focusables } from './cart.js';
|
|
13
|
-
import { scopes } from './product.js';
|
|
14
|
-
import { ListScope, listSelector } from './collection.js';
|
|
15
|
-
import {
|
|
16
|
-
getProduct, getProducts, getRecommendations, clearProductCache, primeProduct, normalizeProductKey,
|
|
17
|
-
} from './loader.js';
|
|
18
|
-
import * as plans from './selling-plans.js';
|
|
19
|
-
import { resolveProductKey } from './status.js';
|
|
20
|
-
import { attr, sel, get, qsa, emit, formatMoney, busy, parseJSONAttr, toAttributes, imageUrl } from './dom.js';
|
|
21
|
-
import { loadPart, coreModules } from './lazy.js';
|
|
22
|
-
import { mountedLists, liveLists, pruneLive, within, mountProduct, retryFailed, hydrateFromPage, settleMarket, trackMarket } from './mounting.js';
|
|
23
|
-
|
|
24
|
-
export const VERSION = '0.6.7';
|
|
25
|
-
|
|
26
|
-
let initPromise = null;
|
|
27
|
-
let observer = null;
|
|
28
|
-
let mountedBody = null; // the page body mount(document) last ran on (Astro's first page-load repeats it)
|
|
29
|
-
|
|
30
|
-
/** Mount every product scope and product list inside `root`. Safe to call repeatedly. */
|
|
31
|
-
export function mount(root = document) {
|
|
32
|
-
if (!isConfigured()) return 0;
|
|
33
|
-
if (root === document) mountedBody = document.body;
|
|
34
|
-
let count = 0;
|
|
35
|
-
pruneLive(); // page transitions detach old roots: don't keep them alive
|
|
36
|
-
hydrateFromPage(root); // products the server already rendered: no request needed
|
|
37
|
-
|
|
38
|
-
within(root, listSelector()).forEach((el) => {
|
|
39
|
-
if (mountedLists.has(el)) return;
|
|
40
|
-
mountedLists.add(el);
|
|
41
|
-
count++;
|
|
42
|
-
const list = new ListScope(el);
|
|
43
|
-
liveLists.add(list);
|
|
44
|
-
el.addEventListener(`${cfg.prefix}:list-rendered`, (e) => mount(e.detail.container));
|
|
45
|
-
trackMarket(list.load());
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
within(root, sel('product')).forEach((el) => { if (mountProduct(el)) count++; });
|
|
49
|
-
|
|
50
|
-
// The country select lives in the optional markets part: load it only when the page uses it.
|
|
51
|
-
if (within(root, `${sel('country-select')},${sel('country-bind')}`).length) {
|
|
52
|
-
loadPart('markets').then((m) => m.renderCountrySelects(root)).catch((err) => reportError(err, 'markets'));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Customer accounts: their own optional part, when configured and the page (or a login) needs it.
|
|
56
|
-
if ((cfg.account?.clientId || cfg.account?.server) && (within(root, ['account-login', 'account-bind', 'account-link', 'orders', 'order', 'addresses', 'subscriptions', 'profile-form', 'address-form'].map((n) => `[${attr(n)}]`).join(',') + `,[${attr('show')}^="logged-"]`).length
|
|
57
|
-
|| /[?&]code=/.test(window.location.search) || hasAccountSession())) {
|
|
58
|
-
loadPart('account').then((m) => m.mountAccount(root)).catch((err) => reportError(err, 'account'));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Search as you type: its own optional part, only for pages that have it.
|
|
62
|
-
if (within(root, sel('search')).length) {
|
|
63
|
-
loadPart('search').then((m) => m.mountSearch(root)).catch((err) => reportError(err, 'search'));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
settleMarket();
|
|
68
|
-
Cart.render();
|
|
69
|
-
log(`mounted ${count} scope(s)`, root);
|
|
70
|
-
return count;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ---------------------------------------------------------------------------
|
|
74
|
-
// Markets: country select + switching
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
|
|
77
|
-
/** Call before a page transition removes `root` (closes the drawer etc.). */
|
|
78
|
-
export function unmount() {
|
|
79
|
-
Cart.close();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function getScope(el) {
|
|
83
|
-
const root = el?.closest?.(sel('product'));
|
|
84
|
-
return root ? scopes.get(root) || null : null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// ---------------------------------------------------------------------------
|
|
88
|
-
// Document-wide controls (delegated, so they survive page swaps)
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
|
-
|
|
91
|
-
let controlsBound = false;
|
|
92
|
-
|
|
93
|
-
function bindControls() {
|
|
94
|
-
if (controlsBound) return;
|
|
95
|
-
controlsBound = true;
|
|
96
|
-
|
|
97
|
-
document.addEventListener('click', (e) => {
|
|
98
|
-
const t = e.target;
|
|
99
|
-
if (!t?.closest) return;
|
|
100
|
-
|
|
101
|
-
if (t.closest(sel('cart-open'))) { e.preventDefault(); Cart.toggle(); return; }
|
|
102
|
-
if (t.closest(sel('cart-close'))) { e.preventDefault(); Cart.close(); return; }
|
|
103
|
-
|
|
104
|
-
const clear = t.closest(sel('cart-clear'));
|
|
105
|
-
if (clear) { e.preventDefault(); busy(clear, () => Cart.clear().catch((err) => reportError(err, 'cart clear'))); return; }
|
|
106
|
-
|
|
107
|
-
const checkout = t.closest(sel('checkout'));
|
|
108
|
-
if (checkout) { e.preventDefault(); busy(checkout, () => Cart.checkout()); return; }
|
|
109
|
-
|
|
110
|
-
const line = t.closest(dataKeySel());
|
|
111
|
-
if (line && line.closest(sel('cart-lines'))) {
|
|
112
|
-
const id = line.getAttribute(`data-${cfg.prefix}-key`);
|
|
113
|
-
const current = Cart.lines.find((l) => l.id === id);
|
|
114
|
-
const run = (fn) => { e.preventDefault(); fn().catch((err) => reportError(err, 'cart line')); };
|
|
115
|
-
if (t.closest(sel('line-remove'))) { line.classList.add('is-busy'); return run(() => Cart.removeLine(id)); }
|
|
116
|
-
if (current && t.closest(sel('line-plus'))) return run(() => Cart.changeQuantity(id, 1));
|
|
117
|
-
if (current && t.closest(sel('line-minus'))) return run(() => Cart.changeQuantity(id, -1));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const removeCode = t.closest(sel('discount-remove'));
|
|
121
|
-
if (removeCode) {
|
|
122
|
-
e.preventDefault();
|
|
123
|
-
const code = removeCode.closest(sel('discount-code'))?.getAttribute(attr('discount-code'));
|
|
124
|
-
if (code) Cart.removeDiscount(code).catch((err) => reportError(err, 'discount'));
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const groupBtn = t.closest(sel('group-add'));
|
|
129
|
-
if (groupBtn) { e.preventDefault(); busy(groupBtn, () => addGroup(groupBtn)); }
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
document.addEventListener('change', (e) => {
|
|
133
|
-
const t = e.target;
|
|
134
|
-
if (!t?.closest) return;
|
|
135
|
-
const lineEl = t.closest(dataKeySel());
|
|
136
|
-
const id = lineEl?.getAttribute(`data-${cfg.prefix}-key`);
|
|
137
|
-
// The line shows is-busy / aria-busy until Shopify has answered (removed by the next render).
|
|
138
|
-
const busyLine = (p, what) => { lineEl.classList.add('is-busy'); lineEl.setAttribute('aria-busy', 'true'); p.catch((err) => reportError(err, what)); };
|
|
139
|
-
if (id && t.matches(`input${sel('line', 'quantity')}`)) {
|
|
140
|
-
busyLine(Cart.updateLine(id, parseInt(t.value, 10) || 0), 'cart line');
|
|
141
|
-
} else if (id && t.matches(`select${sel('line-plan')}`)) {
|
|
142
|
-
busyLine(Cart.updateLine(id, { sellingPlanId: t.value || null }), 'cart line plan');
|
|
143
|
-
} else if (t.matches(`select${sel('country-select')}`)) {
|
|
144
|
-
if (t.value) setCountry(t.value).catch((err) => reportError(err, 'country'));
|
|
145
|
-
} else if (t.matches(sel('cart-note'))) {
|
|
146
|
-
Cart.setNote(t.value).catch((err) => reportError(err, 'cart note'));
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
document.addEventListener('submit', (e) => {
|
|
151
|
-
const form = e.target.closest?.(sel('discount-form'));
|
|
152
|
-
if (!form) return;
|
|
153
|
-
e.preventDefault();
|
|
154
|
-
const input = form.querySelector(sel('discount-input')) || form.querySelector('input');
|
|
155
|
-
const code = input?.value.trim();
|
|
156
|
-
if (!code) return;
|
|
157
|
-
busy(form, async () => {
|
|
158
|
-
const mark = (bad) => {
|
|
159
|
-
form.classList.toggle('is-invalid', bad);
|
|
160
|
-
if (input) { if (bad) input.setAttribute('aria-invalid', 'true'); else input.removeAttribute('aria-invalid'); }
|
|
161
|
-
};
|
|
162
|
-
try {
|
|
163
|
-
await Cart.applyDiscount(code);
|
|
164
|
-
mark(false);
|
|
165
|
-
emit('discount-applied', { code }, form);
|
|
166
|
-
if (input) input.value = '';
|
|
167
|
-
} catch (err) {
|
|
168
|
-
mark(true);
|
|
169
|
-
if (err?.code === 'INVALID_DISCOUNT') emit('discount-invalid', { code }, form); else reportError(err, 'discount');
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
}, true); // capture: before a router (Astro's ClientRouter) turns the submit into a navigation
|
|
173
|
-
document.addEventListener('input', (e) => {
|
|
174
|
-
const f = e.target.closest?.(sel('discount-form'));
|
|
175
|
-
if (f) { f.classList.remove('is-invalid'); e.target.removeAttribute?.('aria-invalid'); }
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// Other tabs: the cart or the market changed there (handled by the extras part, loaded then).
|
|
179
|
-
window.addEventListener('storage', (e) => {
|
|
180
|
-
if (cfg.domain && e.key?.startsWith(`${cfg.prefix}:`)) loadPart('extras').then((m) => m.onStorage(e)).catch(() => {});
|
|
181
|
-
});
|
|
182
|
-
// Shareable variant links: only when turned on.
|
|
183
|
-
document.addEventListener(`${cfg.prefix}:variant-changed`, (e) => {
|
|
184
|
-
const root = e.target;
|
|
185
|
-
if (cfg.variantUrl || root.hasAttribute?.(attr('variant-url'))) loadPart('extras').then((m) => m.writeVariantUrl(root, e.detail)).catch(() => {});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// Astro View Transitions (<ClientRouter />): mount every new page, close the cart while leaving.
|
|
189
|
-
if (cfg.astro !== false) {
|
|
190
|
-
// Astro fires this on the first load too: wait for init (the visitor's saved market) first.
|
|
191
|
-
document.addEventListener('astro:page-load', () => (initPromise || Promise.resolve()).then(() => {
|
|
192
|
-
// Navigated to a page of another market (e.g. /de/): switch first, then mount in that market.
|
|
193
|
-
// Back on a normal page after one: the visitor's own choice, else the default market.
|
|
194
|
-
let target = pageMarket();
|
|
195
|
-
if (!target && onPageMarket) {
|
|
196
|
-
try { target = JSON.parse(localStorage.getItem(`${cfg.prefix}:market:${cfg.domain}`) || 'null'); } catch { target = null; }
|
|
197
|
-
target = target?.country ? target : baseMarket;
|
|
198
|
-
}
|
|
199
|
-
onPageMarket = Boolean(pageMarket());
|
|
200
|
-
const differs = target?.country && (String(target.country).toUpperCase() !== String(cfg.country || '').toUpperCase()
|
|
201
|
-
|| String(target.language || '').toUpperCase() !== String(cfg.language || '').toUpperCase());
|
|
202
|
-
if (differs) setCountry(target.country, { language: target.language ?? null, locale: target.locale, remember: false }).then(() => mount(document), () => mount(document));
|
|
203
|
-
else if (mountedBody !== document.body) mount(document); // the first load is already mounted: no second round of requests
|
|
204
|
-
}));
|
|
205
|
-
document.addEventListener('astro:before-swap', () => { mountedBody = null; unmount(); });
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Back online after a failed load: try the failed products and lists again.
|
|
209
|
-
window.addEventListener('online', () => {
|
|
210
|
-
retryFailed();
|
|
211
|
-
liveLists.forEach((l) => { if (l.root.getAttribute(attr('state')) === 'error') l.load(); });
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
document.addEventListener('keydown', (e) => {
|
|
215
|
-
if (!Cart.isOpen()) return;
|
|
216
|
-
if (e.key === 'Escape') { Cart.close(); return; }
|
|
217
|
-
if (e.key !== 'Tab' || cfg.cartFocus === false) return;
|
|
218
|
-
// Keep Tab inside the open cart.
|
|
219
|
-
const d = Cart.drawers().find((x) => x.classList.contains(cfg.cartDrawerOpenClass));
|
|
220
|
-
const f = d ? focusables(d) : [];
|
|
221
|
-
if (!f.length) return;
|
|
222
|
-
const first = f[0];
|
|
223
|
-
const last = f[f.length - 1];
|
|
224
|
-
if (!d.contains(document.activeElement) || (e.shiftKey && document.activeElement === first) || (!e.shiftKey && document.activeElement === last)) {
|
|
225
|
-
e.preventDefault();
|
|
226
|
-
(e.shiftKey ? last : first).focus();
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function dataKeySel() { return `[data-${cfg.prefix}-key]`; }
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* [tether-group] + [tether-group-add]: adds every product scope inside the
|
|
235
|
-
* group in one request. Bundles, build-a-box, "complete the set".
|
|
236
|
-
* Lines get attribute _group (and the group's tether-line-attributes).
|
|
237
|
-
*/
|
|
238
|
-
export const addGroup = (btn) => loadPart('extras').then((m) => m.addGroup(btn));
|
|
239
|
-
|
|
240
|
-
// ---------------------------------------------------------------------------
|
|
241
|
-
// Init
|
|
242
|
-
// ---------------------------------------------------------------------------
|
|
243
|
-
|
|
244
|
-
let analyticsQueue = null;
|
|
245
|
-
|
|
246
|
-
export function init(overrides = {}) {
|
|
247
|
-
if (initPromise && !Object.keys(overrides).length) return initPromise;
|
|
248
|
-
configure({ ...(window.TETHER_CONFIG || {}), ...overrides });
|
|
249
|
-
|
|
250
|
-
if (!isConfigured()) {
|
|
251
|
-
warn('TETHER_CONFIG is missing domain or token. Tether is idle.');
|
|
252
|
-
initPromise = Promise.resolve(null);
|
|
253
|
-
return initPromise;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
bindControls();
|
|
257
|
-
if (cfg.observe) startObserver();
|
|
258
|
-
// Analytics (optional part): buffer Tether's events until it has loaded, so none are missed.
|
|
259
|
-
if (cfg.analytics && !analyticsQueue) {
|
|
260
|
-
analyticsQueue = [];
|
|
261
|
-
['product-ready', 'list-ready', 'cart-updated', 'cart-toggled', 'checkout', 'search'].forEach((n) => document.addEventListener(`${cfg.prefix}:${n}`, (e) => analyticsQueue.push(e)));
|
|
262
|
-
document.addEventListener('astro:page-load', () => analyticsQueue.push(new CustomEvent(`${cfg.prefix}:page`)));
|
|
263
|
-
loadPart('analytics').then((m) => m.startAnalytics(analyticsQueue)).catch((err) => reportError(err, 'analytics'));
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
initPromise = applyMarket(overrides).then(() => Cart.init()).then(async (cart) => {
|
|
267
|
-
// A saved cart from another market: move it to this one, so prices match the page.
|
|
268
|
-
const want = cfg.country ? String(cfg.country).toUpperCase() : null;
|
|
269
|
-
if (cart && want && cart.buyerIdentity?.countryCode && cart.buyerIdentity.countryCode !== want) {
|
|
270
|
-
try { cart = await Cart.setBuyerIdentity({ countryCode: want }, { create: false }); } catch (err) { reportError(err, 'cart country'); }
|
|
271
|
-
}
|
|
272
|
-
emit('ready', { cart });
|
|
273
|
-
return api;
|
|
274
|
-
});
|
|
275
|
-
return initPromise;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Detected market (saved choice, server hint, Webflow locale) wins over the static config.
|
|
280
|
-
* Without markets.detect only the visitor's saved choice applies, read here without loading
|
|
281
|
-
* the markets part.
|
|
282
|
-
*/
|
|
283
|
-
/**
|
|
284
|
-
* A page built for one market (e.g. /de/ pages): <meta name="tether-page-country" content="DE">,
|
|
285
|
-
* tether-page-language, tether-page-locale. Wins over the visitor's saved choice, isn't remembered.
|
|
286
|
-
*/
|
|
287
|
-
function pageMarket(doc = document) {
|
|
288
|
-
const get = (n) => doc.querySelector(`meta[name="${cfg.prefix}-page-${n}"]`)?.getAttribute('content') || undefined;
|
|
289
|
-
const country = get('country');
|
|
290
|
-
return country ? { country: country.toUpperCase(), language: get('language'), locale: get('locale') } : null;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
let baseMarket = null; // the configured market, before a visitor's or page's market applied
|
|
294
|
-
let onPageMarket = false;
|
|
295
|
-
|
|
296
|
-
async function applyMarket(overrides) {
|
|
297
|
-
const m = cfg.markets || {};
|
|
298
|
-
baseMarket = { country: cfg.country, language: cfg.language, locale: cfg.locale };
|
|
299
|
-
const page = pageMarket();
|
|
300
|
-
onPageMarket = Boolean(page);
|
|
301
|
-
if (page) {
|
|
302
|
-
cfg.country = page.country;
|
|
303
|
-
cfg.language = page.language ? page.language.toUpperCase() : undefined;
|
|
304
|
-
if (page.locale) cfg.locale = page.locale;
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
if (m.detect === false) return;
|
|
308
|
-
let market = null;
|
|
309
|
-
if (m.detect) {
|
|
310
|
-
try { market = (await loadPart('markets')).detectMarket(m); } catch (err) { reportError(err, 'markets'); return; }
|
|
311
|
-
} else if (m.remember !== false) {
|
|
312
|
-
try { market = JSON.parse(localStorage.getItem(`${cfg.prefix}:market:${cfg.domain}`) || 'null'); } catch { /* none */ }
|
|
313
|
-
if (market && !/^[A-Z]{2}$/.test(market.country || '')) market = null;
|
|
314
|
-
}
|
|
315
|
-
if (!market) return;
|
|
316
|
-
if (market.country) cfg.country = market.country;
|
|
317
|
-
if (market.language) cfg.language = market.language;
|
|
318
|
-
if (market.locale && !overrides.locale) cfg.locale = market.locale; // the visitor's market wins over the default
|
|
319
|
-
log('market', market);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function hasAccountSession() {
|
|
323
|
-
try { return Boolean(sessionStorage[`${cfg.prefix}:account:${cfg.domain}`] || localStorage[`${cfg.prefix}:account:${cfg.domain}`] || localStorage[`${cfg.prefix}:account-hint:${cfg.domain}`]); } catch { return false; }
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/** Customer accounts (optional part, loaded on first use). See src/account.js. */
|
|
327
|
-
const accountApi = new Proxy({}, { get: (_, n) => (...a) => loadPart('account').then((m) => m[n](...a)) });
|
|
328
|
-
|
|
329
|
-
/** Tether.setCountry('DE'): loads the markets part on first use. */
|
|
330
|
-
export function setCountry(code, opts) {
|
|
331
|
-
return loadPart('markets').then((m) => m.setCountry(code, opts));
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function startObserver() {
|
|
335
|
-
if (observer || typeof MutationObserver === 'undefined') return;
|
|
336
|
-
let t = null;
|
|
337
|
-
const queued = new Set();
|
|
338
|
-
observer = new MutationObserver((muts) => {
|
|
339
|
-
muts.forEach((m) => m.addedNodes.forEach((n) => { if (n.nodeType === 1) queued.add(n); }));
|
|
340
|
-
clearTimeout(t);
|
|
341
|
-
t = setTimeout(() => { queued.forEach((n) => { if (n.isConnected) mount(n); }); queued.clear(); }, 50);
|
|
342
|
-
});
|
|
343
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
export const api = {
|
|
347
|
-
VERSION,
|
|
348
|
-
init,
|
|
349
|
-
mount,
|
|
350
|
-
unmount,
|
|
351
|
-
get config() { return cfg; },
|
|
352
|
-
get ready() { return initPromise || Promise.resolve(null); },
|
|
353
|
-
query: gql,
|
|
354
|
-
withContext,
|
|
355
|
-
cart: Cart,
|
|
356
|
-
getProduct,
|
|
357
|
-
getProducts,
|
|
358
|
-
getRecommendations,
|
|
359
|
-
getScope,
|
|
360
|
-
addGroup,
|
|
361
|
-
account: accountApi,
|
|
362
|
-
primeProduct,
|
|
363
|
-
normalizeProductKey,
|
|
364
|
-
clearProductCache,
|
|
365
|
-
formatMoney,
|
|
366
|
-
imageUrl,
|
|
367
|
-
plans,
|
|
368
|
-
TetherError,
|
|
369
|
-
resolveProductKey,
|
|
370
|
-
setCountry,
|
|
371
|
-
detectMarket: (markets) => loadPart('markets').then((m) => m.detectMarket(markets || cfg.markets)),
|
|
372
|
-
getLocalization: () => loadPart('markets').then((m) => m.getLocalization()),
|
|
373
|
-
diagnose: (opts) => loadPart('diagnose').then((m) => m.diagnose({ ...opts, version: VERSION })),
|
|
374
|
-
__core: coreModules,
|
|
375
|
-
__parts: {},
|
|
376
|
-
on(name, fn) {
|
|
377
|
-
const h = (e) => fn(e.detail, e);
|
|
378
|
-
document.addEventListener(`${cfg.prefix}:${name}`, h);
|
|
379
|
-
return () => document.removeEventListener(`${cfg.prefix}:${name}`, h);
|
|
380
|
-
},
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
export default api;
|
|
384
|
-
|
|
385
|
-
// Auto-start for the plain <script> setup.
|
|
386
|
-
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
387
|
-
window.Tether = api;
|
|
388
|
-
// Not with a framework integration (Astro), which starts Tether itself.
|
|
389
|
-
const auto = window.TETHER_CONFIG && window.TETHER_CONFIG.autoMount !== false && !window.TETHER_INTEGRATION;
|
|
390
|
-
if (auto) {
|
|
391
|
-
const start = () => init().then(() => mount(document));
|
|
392
|
-
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start, { once: true });
|
|
393
|
-
else start();
|
|
394
|
-
}
|
|
395
|
-
}
|
package/src/initial.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What the browser script will show first for a product, so a server can render the same
|
|
3
|
-
* (no flash when Tether takes over): the selected variant, price, purchase mode and conditions.
|
|
4
|
-
* Same rules as src/product.js: first available variant (or the one in ?variant=), subscription
|
|
5
|
-
* when the product requires a plan or defaultPurchase is 'subscription', low stock at 5 or fewer.
|
|
6
|
-
*/
|
|
7
|
-
import { GID, allPlans, allocationFor, findPlan, planPrice, planSavings } from './selling-plans.js';
|
|
8
|
-
|
|
9
|
-
export function initialState(product, { variant, sellingPlan, defaultPurchase = 'one-time', lowStock = 5 } = {}) {
|
|
10
|
-
const vs = product?.variants?.nodes || [];
|
|
11
|
-
const gid = variant ? GID.variant(variant) : null;
|
|
12
|
-
const v = (gid && vs.find((x) => x.id === gid)) || vs.find((x) => x.availableForSale) || vs[0] || null;
|
|
13
|
-
if (!v) return null;
|
|
14
|
-
const selected = Object.fromEntries((v.selectedOptions || []).map((o) => [o.name, o.value]));
|
|
15
|
-
|
|
16
|
-
const plans = allPlans(product);
|
|
17
|
-
const fromUrl = findPlan(product, sellingPlan);
|
|
18
|
-
let plan = plans.length ? (fromUrl || plans[0]) : null;
|
|
19
|
-
if (plan && !allocationFor(v, plan.id)) plan = plans.find((p) => allocationFor(v, p.id)) || plan;
|
|
20
|
-
const wantsSub = product.requiresSellingPlan || fromUrl || defaultPurchase === 'subscription';
|
|
21
|
-
const subscriptionAvailable = plans.some((p) => allocationFor(v, p.id));
|
|
22
|
-
const purchase = plans.length && wantsSub ? 'subscription' : 'one-time';
|
|
23
|
-
const sub = purchase === 'subscription' && subscriptionAvailable;
|
|
24
|
-
|
|
25
|
-
const price = sub ? planPrice(v, plan.id) : v.price;
|
|
26
|
-
const compareAt = sub ? (allocationFor(v, plan.id)?.priceAdjustments?.[0]?.compareAtPrice || v.compareAtPrice) : v.compareAtPrice;
|
|
27
|
-
const onSale = Boolean(compareAt && parseFloat(compareAt.amount) > parseFloat(price.amount));
|
|
28
|
-
const q = v.quantityAvailable;
|
|
29
|
-
const low = q != null && q > 0 && q <= lowStock;
|
|
30
|
-
const savingsNow = sub ? planSavings(v, plan.id).percentage : 0;
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
variant: v,
|
|
34
|
-
selected,
|
|
35
|
-
purchase,
|
|
36
|
-
plan: sub ? plan : null,
|
|
37
|
-
/** The plan list (tether-plan-list="all"): selected one gets is-selected, unavailable ones is-unavailable. */
|
|
38
|
-
plans: plans.map((p) => ({
|
|
39
|
-
id: p.id, name: p.name, price: planPrice(v, p.id), savings: planSavings(v, p.id).percentage,
|
|
40
|
-
selected: sub && p.id === plan.id, available: Boolean(allocationFor(v, p.id)),
|
|
41
|
-
})),
|
|
42
|
-
/** Savings of the selected plan in % (tether-bind="subscription-savings"), 0 without one. */
|
|
43
|
-
savings: plan ? planSavings(v, plan.id).percentage : 0,
|
|
44
|
-
price,
|
|
45
|
-
compareAtPrice: onSale ? compareAt : null,
|
|
46
|
-
available: v.availableForSale,
|
|
47
|
-
/** The tether-show conditions, e.g. hidden={!state.show['out-of-stock']}. */
|
|
48
|
-
show: {
|
|
49
|
-
'in-stock': v.availableForSale,
|
|
50
|
-
'out-of-stock': !v.availableForSale,
|
|
51
|
-
backorder: Boolean(v.availableForSale && v.currentlyNotInStock),
|
|
52
|
-
'on-sale': onSale,
|
|
53
|
-
'not-on-sale': !onSale,
|
|
54
|
-
'has-variants': vs.length > 1,
|
|
55
|
-
'single-variant': vs.length === 1,
|
|
56
|
-
'low-stock': low,
|
|
57
|
-
'plenty-in-stock': v.availableForSale && !low,
|
|
58
|
-
'subscription-available': subscriptionAvailable,
|
|
59
|
-
'subscription-unavailable': !subscriptionAvailable,
|
|
60
|
-
'subscription-only': Boolean(product.requiresSellingPlan),
|
|
61
|
-
'one-time-available': !product.requiresSellingPlan,
|
|
62
|
-
'subscription-selected': sub,
|
|
63
|
-
'one-time-selected': !sub,
|
|
64
|
-
'has-savings': sub ? savingsNow > 0 : onSale,
|
|
65
|
-
},
|
|
66
|
-
/** Option values without an available variant, as plain data (for frameworks that serialise props). */
|
|
67
|
-
unavailable: Object.fromEntries((product.options || []).map((o) => [o.name, o.optionValues.map((x) => x.name).filter((n) => isUnavail(o.name, n))])),
|
|
68
|
-
/** Option values with no available variant (given the other selected options): class is-unavailable. */
|
|
69
|
-
isUnavailable: (name, value) => isUnavail(name, value),
|
|
70
|
-
};
|
|
71
|
-
function isUnavail(name, value) {
|
|
72
|
-
return !vs.some((x) => x.availableForSale && x.selectedOptions.every((o) =>
|
|
73
|
-
(o.name === name ? o.value === value : selected[o.name] === o.value)));
|
|
74
|
-
}
|
|
75
|
-
}
|
package/src/lazy-iife.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <script> build: optional parts are separate files next to tether(.min).js, e.g.
|
|
3
|
-
* tether.markets.min.js, loaded on first use. Override the folder with config.partsUrl.
|
|
4
|
-
*/
|
|
5
|
-
import * as config from './config.js';
|
|
6
|
-
import * as client from './client.js';
|
|
7
|
-
import * as dom from './dom.js';
|
|
8
|
-
import * as fragments from './fragments.js';
|
|
9
|
-
import * as loader from './loader.js';
|
|
10
|
-
import * as sellingPlans from './selling-plans.js';
|
|
11
|
-
import * as status from './status.js';
|
|
12
|
-
import * as cart from './cart.js';
|
|
13
|
-
import * as product from './product.js';
|
|
14
|
-
import * as collection from './collection.js';
|
|
15
|
-
import * as mounting from './mounting.js';
|
|
16
|
-
|
|
17
|
-
const { cfg } = config;
|
|
18
|
-
|
|
19
|
-
/** The modules a part may use, shared instead of bundled twice (scripts/build.mjs maps imports to these). */
|
|
20
|
-
export const coreModules = {
|
|
21
|
-
'config.js': config, 'client.js': client, 'dom.js': dom, 'fragments.js': fragments, 'loader.js': loader,
|
|
22
|
-
'selling-plans.js': sellingPlans, 'status.js': status, 'cart.js': cart, 'product.js': product,
|
|
23
|
-
'collection.js': collection, 'mounting.js': mounting,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const self = typeof document !== 'undefined' ? document.currentScript?.src || '' : '';
|
|
27
|
-
const parts = {};
|
|
28
|
-
|
|
29
|
-
export function loadPart(name) {
|
|
30
|
-
if (parts[name]) return parts[name];
|
|
31
|
-
const base = cfg.partsUrl || self.replace(/[^/]*$/, '');
|
|
32
|
-
const min = /\.min\.js(\?|$)/.test(self) || !self ? '.min' : '';
|
|
33
|
-
const url = `${base.replace(/\/?$/, '/')}tether.${name}${min}.js`;
|
|
34
|
-
parts[name] = new Promise((resolve, reject) => {
|
|
35
|
-
const s = document.createElement('script');
|
|
36
|
-
s.src = url;
|
|
37
|
-
s.async = true;
|
|
38
|
-
s.onload = () => (window.Tether?.__parts?.[name] ? resolve(window.Tether.__parts[name]) : reject(new Error(`${url} did not register`)));
|
|
39
|
-
s.onerror = () => { delete parts[name]; reject(new Error(`Could not load ${url}. Host it next to the Tether script, or set partsUrl.`)); };
|
|
40
|
-
document.head.appendChild(s);
|
|
41
|
-
});
|
|
42
|
-
return parts[name];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Parts that load other parts (account -> account-pages) use this same loader.
|
|
46
|
-
coreModules['lazy.js'] = { loadPart, coreModules };
|
package/src/lazy.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Optional parts that most pages never need, loaded on first use.
|
|
3
|
-
* ESM / Node: dynamic import (bundlers split these into their own chunks).
|
|
4
|
-
* The <script> build swaps this file for lazy-iife.js (see scripts/build.mjs).
|
|
5
|
-
*/
|
|
6
|
-
const PARTS = {
|
|
7
|
-
diagnose: () => import('./diagnose.js'),
|
|
8
|
-
markets: () => import('./markets-ui.js'),
|
|
9
|
-
search: () => import('./search.js'),
|
|
10
|
-
extras: () => import('./extras.js'),
|
|
11
|
-
account: () => import('./account.js'),
|
|
12
|
-
'account-pages': () => import('./account-pages.js'),
|
|
13
|
-
analytics: () => import('./analytics.js'),
|
|
14
|
-
};
|
|
15
|
-
export const loadPart = (name) => PARTS[name]();
|
|
16
|
-
|
|
17
|
-
// Only the <script> build shares its modules with separately loaded parts.
|
|
18
|
-
export const coreModules = undefined;
|