@manyducks.co/dolla 0.71.0 → 0.73.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/lib/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { $, $$, observe, unwrap, isReadable, isWritable, type Readable, type Wri
3
3
  export { m, cond, repeat, portal } from "./markup.js";
4
4
  export { Fragment } from "./views/fragment.js";
5
5
  export { StoreScope, type StoreScopeProps } from "./views/store-scope.js";
6
- export { RouterStore } from "./stores/router.js";
6
+ export { RouterStore, type RouteMatchContext } from "./stores/router.js";
7
7
  export { LanguageStore } from "./stores/language.js";
8
8
  export { HTTPStore, type HTTPMiddleware } from "./stores/http.js";
9
9
  export { DialogStore, type DialogProps } from "./stores/dialog.js";
package/lib/index.js CHANGED
@@ -708,11 +708,11 @@ var Conditional = class {
708
708
  get connected() {
709
709
  return this.node.parentNode != null;
710
710
  }
711
- connect(parent, after) {
711
+ connect(parent2, after) {
712
712
  if (!this.connected) {
713
- parent.insertBefore(this.node, after?.nextSibling ?? null);
713
+ parent2.insertBefore(this.node, after?.nextSibling ?? null);
714
714
  if (this.appContext.mode === "development") {
715
- parent.insertBefore(this.endNode, this.node.nextSibling);
715
+ parent2.insertBefore(this.endNode, this.node.nextSibling);
716
716
  }
717
717
  this.stopCallback = observe(this.$predicate, (value) => {
718
718
  this.update(value);
@@ -816,9 +816,9 @@ var HTML = class {
816
816
  this.appContext = appContext;
817
817
  this.elementContext = elementContext;
818
818
  }
819
- connect(parent, after) {
820
- if (parent == null) {
821
- throw new Error(`HTML element requires a parent element as the first argument to connect. Got: ${parent}`);
819
+ connect(parent2, after) {
820
+ if (parent2 == null) {
821
+ throw new Error(`HTML element requires a parent element as the first argument to connect. Got: ${parent2}`);
822
822
  }
823
823
  if (!this.connected) {
824
824
  for (const child of this.children) {
@@ -830,7 +830,7 @@ var HTML = class {
830
830
  if (this.props.class)
831
831
  this.applyClasses(this.node, this.props.class, this.stopCallbacks);
832
832
  }
833
- parent.insertBefore(this.node, after?.nextSibling ?? null);
833
+ parent2.insertBefore(this.node, after?.nextSibling ?? null);
834
834
  setTimeout(() => {
835
835
  this.canClickAway = true;
836
836
  }, 0);
@@ -1237,9 +1237,9 @@ var Observer = class {
1237
1237
  }
1238
1238
  };
1239
1239
  }
1240
- connect(parent, after) {
1240
+ connect(parent2, after) {
1241
1241
  if (!this.connected) {
1242
- parent.insertBefore(this.node, after?.nextSibling ?? null);
1242
+ parent2.insertBefore(this.node, after?.nextSibling ?? null);
1243
1243
  this.observerControls.start();
1244
1244
  }
1245
1245
  }
@@ -1310,9 +1310,9 @@ var Outlet = class {
1310
1310
  get connected() {
1311
1311
  return this.node?.parentNode != null;
1312
1312
  }
1313
- connect(parent, after) {
1313
+ connect(parent2, after) {
1314
1314
  if (!this.connected) {
1315
- parent.insertBefore(this.node, after?.nextSibling ?? null);
1315
+ parent2.insertBefore(this.node, after?.nextSibling ?? null);
1316
1316
  this.stopCallback = observe(this.$children, (children) => {
1317
1317
  this.update(children);
1318
1318
  });
@@ -1335,12 +1335,12 @@ var Outlet = class {
1335
1335
  for (const child of this.connectedChildren) {
1336
1336
  child.disconnect();
1337
1337
  }
1338
- this.connectedChildren = newChildren;
1339
- for (let i = 0; i < this.connectedChildren.length; i++) {
1340
- const child = this.connectedChildren[i];
1341
- const previous = i > 0 ? this.connectedChildren[i] : void 0;
1338
+ for (let i = 0; i < newChildren.length; i++) {
1339
+ const child = newChildren[i];
1340
+ const previous = i > 0 ? newChildren[i] : void 0;
1342
1341
  child.connect(this.node.parentElement, previous?.node);
1343
1342
  }
1343
+ this.connectedChildren = newChildren;
1344
1344
  if (this.appContext.mode === "development") {
1345
1345
  this.node.textContent = `Outlet (${newChildren.length} ${newChildren.length === 1 ? "child" : "children"})`;
1346
1346
  this.node.parentElement?.insertBefore(
@@ -1350,6 +1350,7 @@ var Outlet = class {
1350
1350
  }
1351
1351
  }
1352
1352
  setChildren(children) {
1353
+ throw new Error(`setChildren is not supported on Outlet`);
1353
1354
  }
1354
1355
  };
1355
1356
 
@@ -1367,7 +1368,7 @@ var Portal = class {
1367
1368
  this.config = config;
1368
1369
  }
1369
1370
  connect(_parent, _after) {
1370
- const { content, parent } = this.config;
1371
+ const { content, parent: parent2 } = this.config;
1371
1372
  if (isDOMHandle(content)) {
1372
1373
  this.handle = content;
1373
1374
  } else if (isMarkup(content)) {
@@ -1375,7 +1376,7 @@ var Portal = class {
1375
1376
  } else {
1376
1377
  this.handle = getRenderHandle(renderMarkupToDOM(toMarkup(content), this.config));
1377
1378
  }
1378
- this.handle.connect(parent);
1379
+ this.handle.connect(parent2);
1379
1380
  }
1380
1381
  disconnect() {
1381
1382
  if (this.handle?.connected) {
@@ -1531,7 +1532,7 @@ function initView(config) {
1531
1532
  get connected() {
1532
1533
  return isConnected;
1533
1534
  },
1534
- connect(parent, after) {
1535
+ connect(parent2, after) {
1535
1536
  const wasConnected = isConnected;
1536
1537
  if (!wasConnected) {
1537
1538
  initialize();
@@ -1541,7 +1542,7 @@ function initView(config) {
1541
1542
  }
1542
1543
  }
1543
1544
  if (rendered) {
1544
- rendered.connect(parent, after);
1545
+ rendered.connect(parent2, after);
1545
1546
  }
1546
1547
  if (!wasConnected) {
1547
1548
  isConnected = true;
@@ -1572,6 +1573,7 @@ function initView(config) {
1572
1573
  }
1573
1574
  },
1574
1575
  async setChildren(children) {
1576
+ console.log("setChildren", { name: ctx.name, children });
1575
1577
  $$children.set(children);
1576
1578
  }
1577
1579
  };
@@ -1606,9 +1608,9 @@ var Repeat = class {
1606
1608
  this.endNode = document.createTextNode("");
1607
1609
  }
1608
1610
  }
1609
- connect(parent, after) {
1611
+ connect(parent2, after) {
1610
1612
  if (!this.connected) {
1611
- parent.insertBefore(this.node, after?.nextSibling ?? null);
1613
+ parent2.insertBefore(this.node, after?.nextSibling ?? null);
1612
1614
  this.stopCallback = observe(this.$items, (value) => {
1613
1615
  this._update(Array.from(value));
1614
1616
  });
@@ -1704,7 +1706,7 @@ var Text = class {
1704
1706
  constructor({ value }) {
1705
1707
  this.value = value;
1706
1708
  }
1707
- async connect(parent, after = null) {
1709
+ async connect(parent2, after = null) {
1708
1710
  if (!this.connected) {
1709
1711
  if (isReadable(this.value)) {
1710
1712
  this.stopCallback = observe(this.value, (value) => {
@@ -1714,7 +1716,7 @@ var Text = class {
1714
1716
  this.update(this.value);
1715
1717
  }
1716
1718
  }
1717
- parent.insertBefore(this.node, after?.nextSibling ?? null);
1719
+ parent2.insertBefore(this.node, after?.nextSibling ?? null);
1718
1720
  }
1719
1721
  async disconnect() {
1720
1722
  if (this.connected) {
@@ -1788,8 +1790,8 @@ function repeat(items, keyFn, renderFn) {
1788
1790
  const $items = $(items);
1789
1791
  return m("$repeat", { $items, keyFn, renderFn });
1790
1792
  }
1791
- function portal(content, parent) {
1792
- return m("$portal", { content, parent });
1793
+ function portal(content, parent2) {
1794
+ return m("$portal", { content, parent: parent2 });
1793
1795
  }
1794
1796
  var NodeHandle = class {
1795
1797
  node;
@@ -1799,8 +1801,8 @@ var NodeHandle = class {
1799
1801
  constructor(node) {
1800
1802
  this.node = node;
1801
1803
  }
1802
- async connect(parent, after) {
1803
- parent.insertBefore(this.node, after?.nextSibling ?? null);
1804
+ async connect(parent2, after) {
1805
+ parent2.insertBefore(this.node, after?.nextSibling ?? null);
1804
1806
  }
1805
1807
  async disconnect() {
1806
1808
  if (this.node.parentNode) {
@@ -1909,15 +1911,15 @@ function getRenderHandle(handles) {
1909
1911
  get connected() {
1910
1912
  return isConnected;
1911
1913
  },
1912
- async connect(parent, after) {
1913
- parent.insertBefore(node, after ? after : null);
1914
+ connect(parent2, after) {
1915
+ parent2.insertBefore(node, after ? after : null);
1914
1916
  for (const handle of handles) {
1915
1917
  const previous = handles[handles.length - 1]?.node ?? node;
1916
- await handle.connect(parent, previous);
1918
+ handle.connect(parent2, previous);
1917
1919
  }
1918
1920
  isConnected = true;
1919
1921
  },
1920
- async disconnect() {
1922
+ disconnect() {
1921
1923
  if (isConnected) {
1922
1924
  for (const handle of handles) {
1923
1925
  handle.disconnect();
@@ -1926,7 +1928,8 @@ function getRenderHandle(handles) {
1926
1928
  }
1927
1929
  isConnected = false;
1928
1930
  },
1929
- async setChildren() {
1931
+ setChildren() {
1932
+ throw new Error(`setChildren not supported on renderHandle`);
1930
1933
  }
1931
1934
  };
1932
1935
  }
@@ -3144,7 +3147,7 @@ function RouterStore(ctx) {
3144
3147
  history = createBrowserHistory();
3145
3148
  }
3146
3149
  let layerId = 0;
3147
- function prepareRoute(route, layers = []) {
3150
+ function prepareRoute(route, parents = [], layers = []) {
3148
3151
  if (!(typeof route === "object" && !Array.isArray(route)) || !(typeof route.path === "string")) {
3149
3152
  throw new TypeError(`Route configs must be objects with a 'path' string property. Got: ${route}`);
3150
3153
  }
@@ -3155,7 +3158,11 @@ function RouterStore(ctx) {
3155
3158
  } else if (!route.view && !route.routes && !route.redirect) {
3156
3159
  throw new Error(`Route must have a 'view', a 'redirect', or a set of nested 'routes'.`);
3157
3160
  }
3158
- const parts = splitPath(route.path);
3161
+ let parts = [];
3162
+ for (const parent2 of parents) {
3163
+ parts.push(...splitPath(parent2.path));
3164
+ }
3165
+ parts.push(...splitPath(route.path));
3159
3166
  if (parts[parts.length - 1] === "*") {
3160
3167
  parts.pop();
3161
3168
  }
@@ -3168,8 +3175,9 @@ function RouterStore(ctx) {
3168
3175
  redirect = "/" + redirect;
3169
3176
  }
3170
3177
  }
3178
+ console.log({ parts, route, parents, layers });
3171
3179
  routes2.push({
3172
- pattern: route.path,
3180
+ pattern: "/" + joinPath([...parts, ...splitPath(route.path)]),
3173
3181
  meta: {
3174
3182
  redirect
3175
3183
  }
@@ -3182,18 +3190,19 @@ function RouterStore(ctx) {
3182
3190
  } else if (route.view) {
3183
3191
  throw new TypeError(`Route '${route.path}' expected a view function or undefined. Got: ${route.view}`);
3184
3192
  }
3193
+ const markup = m(view);
3194
+ const layer = { id: layerId++, markup };
3185
3195
  if (route.routes) {
3186
3196
  for (const subroute of route.routes) {
3187
- routes2.push(...prepareRoute(subroute));
3197
+ routes2.push(...prepareRoute(subroute, [...parents, route], [...layers, layer]));
3188
3198
  }
3189
3199
  } else {
3190
- const markup = m(view);
3191
- const layer = { id: layerId++, markup };
3192
3200
  routes2.push({
3193
- pattern: route.path,
3201
+ pattern: parent ? joinPath([...parents.map((p) => p.path), route.path]) : route.path,
3194
3202
  meta: {
3195
3203
  pattern: route.path,
3196
- layers: [...layers, layer]
3204
+ layers: [...layers, layer],
3205
+ beforeMatch: route.beforeMatch
3197
3206
  }
3198
3207
  });
3199
3208
  }
@@ -3227,7 +3236,7 @@ function RouterStore(ctx) {
3227
3236
  }
3228
3237
  }
3229
3238
  ctx.onConnected(() => {
3230
- ctx.info(`Total routes: ${routes.length}`);
3239
+ ctx.info("Routes registered:", routes);
3231
3240
  });
3232
3241
  const $$pattern = $$(null);
3233
3242
  const $$path = $$("");
@@ -3276,7 +3285,45 @@ function RouterStore(ctx) {
3276
3285
  });
3277
3286
  return;
3278
3287
  }
3279
- ctx.info(`Matched route: '${matched.pattern}'`);
3288
+ if (matched.meta.beforeMatch) {
3289
+ await matched.meta.beforeMatch({
3290
+ getStore(store) {
3291
+ let name;
3292
+ if (typeof store === "string") {
3293
+ name = store;
3294
+ } else {
3295
+ name = store.name;
3296
+ }
3297
+ if (typeof store !== "string") {
3298
+ let ec = elementContext;
3299
+ while (ec) {
3300
+ if (ec.stores.has(store)) {
3301
+ return ec.stores.get(store)?.instance.exports;
3302
+ }
3303
+ ec = ec.parent;
3304
+ }
3305
+ }
3306
+ if (appContext.stores.has(store)) {
3307
+ const _store = appContext.stores.get(store);
3308
+ if (!_store.instance) {
3309
+ appContext.crashCollector.crash({
3310
+ componentName: ctx.name,
3311
+ error: new Error(`Store '${name}' is not registered on this app.`)
3312
+ });
3313
+ }
3314
+ return _store.instance.exports;
3315
+ }
3316
+ appContext.crashCollector.crash({
3317
+ componentName: ctx.name,
3318
+ error: new Error(`Store '${name}' is not registered on this app.`)
3319
+ });
3320
+ },
3321
+ redirect: (path) => {
3322
+ throw new Error(`Redirect not yet implemented.`);
3323
+ }
3324
+ });
3325
+ }
3326
+ ctx.info(`Matched route: '${matched.pattern}' ('${matched.path}')`);
3280
3327
  if (matched.meta.redirect != null) {
3281
3328
  if (typeof matched.meta.redirect === "string") {
3282
3329
  let path = matched.meta.redirect;
@@ -3300,22 +3347,21 @@ function RouterStore(ctx) {
3300
3347
  const matchedLayer = layers[i];
3301
3348
  const activeLayer = activeLayers[i];
3302
3349
  if (activeLayer?.id !== matchedLayer.id) {
3303
- ctx.info(`Replacing layer ${i} (active ID: ${activeLayer?.id}, matched ID: ${matchedLayer.id})`);
3350
+ ctx.info(`Replacing layer @${i} (active ID: ${activeLayer?.id}, matched ID: ${matchedLayer.id})`);
3304
3351
  activeLayers = activeLayers.slice(0, i);
3305
3352
  const parentLayer = activeLayers[activeLayers.length - 1];
3306
3353
  const renderContext = { appContext, elementContext };
3307
3354
  const rendered = renderMarkupToDOM(matchedLayer.markup, renderContext);
3308
3355
  const handle = getRenderHandle(rendered);
3309
- render.update(() => {
3310
- if (activeLayer && activeLayer.handle.connected) {
3311
- activeLayer.handle.disconnect();
3312
- }
3313
- if (parentLayer) {
3314
- parentLayer.handle.setChildren(rendered);
3315
- } else {
3316
- appContext.rootView.setChildren(rendered);
3317
- }
3318
- }, "dolla-router-change");
3356
+ if (activeLayer && activeLayer.handle.connected) {
3357
+ activeLayer.handle.disconnect();
3358
+ }
3359
+ if (parentLayer) {
3360
+ parentLayer.handle.setChildren(rendered);
3361
+ ctx.log({ rendered, markup: matchedLayer.markup });
3362
+ } else {
3363
+ appContext.rootView.setChildren(rendered);
3364
+ }
3319
3365
  activeLayers.push({ id: matchedLayer.id, handle });
3320
3366
  }
3321
3367
  }