@manyducks.co/dolla 2.0.0-alpha.60 → 2.0.0-alpha.61

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 (50) hide show
  1. package/dist/core/context.d.ts +0 -2
  2. package/dist/core/context.test.d.ts +1 -0
  3. package/dist/core/index.d.ts +4 -5
  4. package/dist/core/logger.test.d.ts +0 -0
  5. package/dist/core/markup.d.ts +16 -37
  6. package/dist/core/markup.test.d.ts +0 -0
  7. package/dist/core/mount.test.d.ts +0 -0
  8. package/dist/core/nodes/_markup.d.ts +36 -0
  9. package/dist/core/nodes/dom.d.ts +5 -6
  10. package/dist/core/nodes/dynamic.d.ts +6 -13
  11. package/dist/core/nodes/{html.d.ts → element.d.ts} +9 -8
  12. package/dist/core/nodes/portal.d.ts +6 -7
  13. package/dist/core/nodes/repeat.d.ts +7 -5
  14. package/dist/core/nodes/view.d.ts +6 -5
  15. package/dist/core/ref.test.d.ts +1 -0
  16. package/dist/core/signals.d.ts +17 -2
  17. package/dist/core/views/default-crash-view.d.ts +5 -2
  18. package/dist/hooks/index.d.ts +64 -0
  19. package/dist/hooks/index.test.d.ts +1 -0
  20. package/dist/hooks.js +69 -0
  21. package/dist/hooks.js.map +1 -0
  22. package/dist/i18n.js +49 -49
  23. package/dist/i18n.js.map +1 -1
  24. package/dist/index.js +50 -60
  25. package/dist/index.js.map +1 -1
  26. package/dist/jsx-dev-runtime.js +1 -1
  27. package/dist/jsx-runtime.js +1 -1
  28. package/dist/logger-Bl496yfY.js +91 -0
  29. package/dist/logger-Bl496yfY.js.map +1 -0
  30. package/dist/markup-CX27GJ1M.js +1030 -0
  31. package/dist/markup-CX27GJ1M.js.map +1 -0
  32. package/dist/ref-BD79iqlg.js +15 -0
  33. package/dist/ref-BD79iqlg.js.map +1 -0
  34. package/dist/router/router.d.ts +29 -18
  35. package/dist/{router-BpuJQ6OA.js → router-CjCkk4dA.js} +217 -190
  36. package/dist/router-CjCkk4dA.js.map +1 -0
  37. package/dist/router.js +1 -1
  38. package/dist/signals-gCwiIe5X.js +450 -0
  39. package/dist/signals-gCwiIe5X.js.map +1 -0
  40. package/dist/types.d.ts +1 -1
  41. package/docs/hooks.md +211 -0
  42. package/docs/i18n.md +1 -1
  43. package/package.json +5 -1
  44. package/vite.config.js +1 -0
  45. package/dist/core/symbols.d.ts +0 -2
  46. package/dist/logger-MPwl-Xqu.js +0 -524
  47. package/dist/logger-MPwl-Xqu.js.map +0 -1
  48. package/dist/markup-BGlfQYQk.js +0 -996
  49. package/dist/markup-BGlfQYQk.js.map +0 -1
  50. package/dist/router-BpuJQ6OA.js.map +0 -1
package/dist/i18n.js CHANGED
@@ -4,8 +4,9 @@ var D = (o) => {
4
4
  };
5
5
  var J = (o, t, e) => t in o ? B(o, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[t] = e;
6
6
  var O = (o, t, e) => J(o, typeof t != "symbol" ? t + "" : t, e), _ = (o, t, e) => t.has(o) || D("Cannot " + e);
7
- var i = (o, t, e) => (_(o, t, "read from private field"), e ? e.call(o) : t.get(o)), m = (o, t, e) => t.has(o) ? D("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(o) : t.set(o, e), N = (o, t, e, n) => (_(o, t, "write to private field"), n ? n.call(o, e) : t.set(o, e), e), g = (o, t, e) => (_(o, t, "access private method"), e);
8
- import { $, h as K, d as Q, g as x } from "./logger-MPwl-Xqu.js";
7
+ var r = (o, t, e) => (_(o, t, "read from private field"), e ? e.call(o) : t.get(o)), m = (o, t, e) => t.has(o) ? D("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(o) : t.set(o, e), N = (o, t, e, n) => (_(o, t, "write to private field"), n ? n.call(o, e) : t.set(o, e), e), g = (o, t, e) => (_(o, t, "access private method"), e);
8
+ import { c as K } from "./logger-Bl496yfY.js";
9
+ import { $, d as Q, g as x } from "./signals-gCwiIe5X.js";
9
10
  import { b as C, i as j, c as U, t as P } from "./typeChecking-CbltMOUt.js";
10
11
  var I, F, p, S, G;
11
12
  class W {
@@ -18,7 +19,7 @@ class W {
18
19
  }
19
20
  async load() {
20
21
  let t;
21
- if (!i(this, I)) {
22
+ if (!r(this, I)) {
22
23
  if (j(this.config.strings))
23
24
  t = this.config.strings;
24
25
  else if (U(this.config.fetch)) {
@@ -41,17 +42,17 @@ class W {
41
42
  if (t) {
42
43
  const e = g(this, p, S).call(this, t);
43
44
  for (const n of e)
44
- i(this, F).set(n[0], n[1]);
45
+ r(this, F).set(n[0], n[1]);
45
46
  } else
46
47
  throw new Error("Language could not be loaded.");
47
48
  }
48
49
  getTemplate(t) {
49
- return i(this, F).get(t) ?? {
50
+ return r(this, F).get(t) ?? {
50
51
  segments: [{ type: 0, text: `[MISSING: ${t}]` }]
51
52
  };
52
53
  }
53
54
  hasTemplate(t) {
54
- return i(this, F).has(t);
55
+ return r(this, F).has(t);
55
56
  }
56
57
  }
57
58
  I = new WeakMap(), F = new WeakMap(), p = new WeakSet(), S = function(t, e = []) {
@@ -78,7 +79,7 @@ I = new WeakMap(), F = new WeakMap(), p = new WeakSet(), S = function(t, e = [])
78
79
  const n = {
79
80
  segments: []
80
81
  };
81
- let a = "", s = 0, r = 0, c, w, l;
82
+ let a = "", s = 0, i = 0, c, w, l;
82
83
  const k = () => {
83
84
  c = {
84
85
  type: 1,
@@ -92,32 +93,32 @@ I = new WeakMap(), F = new WeakMap(), p = new WeakSet(), S = function(t, e = [])
92
93
  };
93
94
  };
94
95
  for (; s < t.length; ) {
95
- if (r !== 0 && t[s] === " ") {
96
+ if (i !== 0 && t[s] === " ") {
96
97
  s++;
97
98
  continue;
98
99
  }
99
- switch (r) {
100
+ switch (i) {
100
101
  case 0:
101
- t[s] === "{" && t[s + 1] === "{" ? (r = 1, s += 2, a.length > 0 && (n.segments.push({ type: 0, text: a }), a = ""), k()) : (a += t[s], s++);
102
+ t[s] === "{" && t[s + 1] === "{" ? (i = 1, s += 2, a.length > 0 && (n.segments.push({ type: 0, text: a }), a = ""), k()) : (a += t[s], s++);
102
103
  break;
103
104
  case 1:
104
- t[s] === "|" ? (r = 2, s += 1, c.name = a, a = "", y()) : t[s] === "}" && t[s + 1] === "}" ? (r = 0, s += 2, c.name = a, a = "", n.segments.push(c)) : (a += t[s], s++);
105
+ t[s] === "|" ? (i = 2, s += 1, c.name = a, a = "", y()) : t[s] === "}" && t[s + 1] === "}" ? (i = 0, s += 2, c.name = a, a = "", n.segments.push(c)) : (a += t[s], s++);
105
106
  break;
106
107
  case 2:
107
- t[s] === "(" ? (r = 3, s += 1, w.name = a, a = "") : t[s] === "}" && t[s + 1] === "}" ? (r = 0, s += 2, c.formats.push(w), n.segments.push(c)) : (a += t[s], s++);
108
+ t[s] === "(" ? (i = 3, s += 1, w.name = a, a = "") : t[s] === "}" && t[s + 1] === "}" ? (i = 0, s += 2, c.formats.push(w), n.segments.push(c)) : (a += t[s], s++);
108
109
  break;
109
110
  case 3:
110
- t[s] === ")" || (t[s] === ":" ? (r = 4, s += 1, l = a, a = "") : t[s] === "}" && t[s + 1] === "}" || (a += t[s], s++));
111
+ t[s] === ")" || (t[s] === ":" ? (i = 4, s += 1, l = a, a = "") : t[s] === "}" && t[s + 1] === "}" || (a += t[s], s++));
111
112
  break;
112
113
  case 4:
113
- t[s] === ")" ? (r = 5, s += 1, w.options[l] = a, a = "", c.formats.push(w)) : t[s] === "," ? (r = 3, s += 1, w.options[l] = a, a = "") : t[s] === "}" && t[s + 1] === "}" || (a += t[s], s++);
114
+ t[s] === ")" ? (i = 5, s += 1, w.options[l] = a, a = "", c.formats.push(w)) : t[s] === "," ? (i = 3, s += 1, w.options[l] = a, a = "") : t[s] === "}" && t[s + 1] === "}" || (a += t[s], s++);
114
115
  break;
115
116
  case 5:
116
- t[s] === "|" ? (r = 2, s += 1, y()) : t[s] === "}" && t[s + 1] === "}" && (r = 0, s += 2, n.segments.push(c));
117
+ t[s] === "|" ? (i = 2, s += 1, y()) : t[s] === "}" && t[s + 1] === "}" && (i = 0, s += 2, n.segments.push(c));
117
118
  break;
118
119
  }
119
120
  }
120
- return r === 0 && a.length > 0 && n.segments.push({ type: 0, text: a }), n;
121
+ return i === 0 && a.length > 0 && n.segments.push({ type: 0, text: a }), n;
121
122
  };
122
123
  var b, h, E, T, v, d, u, H, L, V, M, A;
123
124
  class X {
@@ -129,23 +130,23 @@ class X {
129
130
  m(this, T, /* @__PURE__ */ new Map());
130
131
  m(this, v, "auto");
131
132
  m(this, d, $("en"));
132
- O(this, "$locale", $(i(this, d)));
133
+ O(this, "$locale", $(r(this, d)));
133
134
  N(this, b, K("dolla.i18n")), this.addFormat("number", (t, e, n) => g(this, u, L).call(this, Number(e), n)), this.addFormat("datetime", (t, e, n) => g(this, u, V).call(this, e, n)), this.addFormat("list", (t, e, n) => g(this, u, M).call(this, e, n));
134
135
  }
135
136
  get locales() {
136
- return [...i(this, h).keys()];
137
+ return [...r(this, h).keys()];
137
138
  }
138
139
  async setup(t) {
139
140
  if (t.translations.forEach((e) => {
140
- i(this, h).set(e.locale, new W(e));
141
+ r(this, h).set(e.locale, new W(e));
141
142
  }), t.locale && t.locale !== "auto") {
142
143
  if (!t.translations.some((n) => n.locale === t.locale))
143
144
  throw new Error(`Initial locale '${t.locale}' is not registered in the locales array.`);
144
145
  N(this, v, t.locale);
145
146
  }
146
- i(this, b).info(
147
- `${i(this, h).size} language${i(this, h).size === 1 ? "" : "s"} supported: '${[...i(this, h).keys()].join("', '")}'`
148
- ), i(this, h).size > 0 && await this.setLocale(i(this, v));
147
+ r(this, b).info(
148
+ `${r(this, h).size} language${r(this, h).size === 1 ? "" : "s"} supported: '${[...r(this, h).keys()].join("', '")}'`
149
+ ), r(this, h).size > 0 && await this.setLocale(r(this, v));
149
150
  }
150
151
  async setLocale(t) {
151
152
  var a;
@@ -153,24 +154,24 @@ class X {
153
154
  if (t === "auto") {
154
155
  let s = [];
155
156
  if (typeof navigator < "u") {
156
- const r = navigator;
157
- ((a = r.languages) == null ? void 0 : a.length) > 0 ? s.push(...r.languages) : r.language ? s.push(r.language) : r.browserLanguage ? s.push(r.browserLanguage) : r.userLanguage && s.push(r.userLanguage);
157
+ const i = navigator;
158
+ ((a = i.languages) == null ? void 0 : a.length) > 0 ? s.push(...i.languages) : i.language ? s.push(i.language) : i.browserLanguage ? s.push(i.browserLanguage) : i.userLanguage && s.push(i.userLanguage);
158
159
  }
159
- for (const r of s)
160
- i(this, h).has(r) && (e = r);
160
+ for (const i of s)
161
+ r(this, h).has(i) && (e = i);
161
162
  } else
162
- i(this, h).has(t) && (e = t);
163
+ r(this, h).has(t) && (e = t);
163
164
  if (e == null) {
164
- const s = i(this, h).keys().next().value;
165
+ const s = r(this, h).keys().next().value;
165
166
  s && (e = s);
166
167
  }
167
- if (!e || !i(this, h).has(e))
168
+ if (!e || !r(this, h).has(e))
168
169
  throw new Error(`Locale '${t}' has no translation.`);
169
- const n = i(this, h).get(e);
170
+ const n = r(this, h).get(e);
170
171
  try {
171
- await n.load(), N(this, E, []), i(this, d).call(this, e), i(this, b).info("set language to " + e);
172
+ await n.load(), N(this, E, []), r(this, d).call(this, e), r(this, b).info("set language to " + e);
172
173
  } catch (s) {
173
- s instanceof Error && i(this, b).crash(s);
174
+ s instanceof Error && r(this, b).crash(s);
174
175
  }
175
176
  }
176
177
  /**
@@ -191,7 +192,7 @@ class X {
191
192
  const n = {};
192
193
  for (const a in e)
193
194
  n[a] = x(e[a]);
194
- return g(this, u, H).call(this, i(this, d).call(this), t, n);
195
+ return g(this, u, H).call(this, r(this, d).call(this), t, n);
195
196
  });
196
197
  }
197
198
  /**
@@ -209,7 +210,7 @@ class X {
209
210
  * t("greeting", {name: "world"}); // State<"Hello, WORLD!">
210
211
  */
211
212
  addFormat(t, e) {
212
- i(this, T).set(t, e);
213
+ r(this, T).set(t, e);
213
214
  }
214
215
  /**
215
216
  * Creates an `Intl.Collator` configured for the current locale.
@@ -218,7 +219,7 @@ class X {
218
219
  * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator#options
219
220
  */
220
221
  collator(t) {
221
- return new Intl.Collator(i(this, d).call(this), t);
222
+ return new Intl.Collator(r(this, d).call(this), t);
222
223
  }
223
224
  /**
224
225
  * Formats a number for the current locale. Uses `Intl.NumberFormat` under the hood.
@@ -257,7 +258,7 @@ b = new WeakMap(), h = new WeakMap(), E = new WeakMap(), T = new WeakMap(), v =
257
258
  var w;
258
259
  const a = g(this, u, A).call(this, e, n);
259
260
  if (a) return a;
260
- const s = i(this, h).get(t);
261
+ const s = r(this, h).get(t);
261
262
  if (n.context != null && (e += "_" + n.context), n.count != null)
262
263
  if (n.ordinal) {
263
264
  const l = `${e}_ordinal_(=${n.count})`;
@@ -266,9 +267,9 @@ b = new WeakMap(), h = new WeakMap(), E = new WeakMap(), T = new WeakMap(), v =
266
267
  const l = `${e}_(=${n.count})`;
267
268
  s.hasTemplate(l) ? e = l : e += "_" + new Intl.PluralRules(t).select(n.count);
268
269
  }
269
- const r = s.getTemplate(e);
270
+ const i = s.getTemplate(e);
270
271
  let c = "";
271
- for (const l of r.segments)
272
+ for (const l of i.segments)
272
273
  if (l.type === 0)
273
274
  c += l.text;
274
275
  else if (l.type === 1) {
@@ -276,12 +277,12 @@ b = new WeakMap(), h = new WeakMap(), E = new WeakMap(), T = new WeakMap(), v =
276
277
  const y = ((w = n.formatOverrides) == null ? void 0 : w[l.name]) ?? [...l.formats];
277
278
  l.name === "count" && y.length === 0 && y.push({ name: "number", options: {} });
278
279
  for (const f of y) {
279
- const R = i(this, T).get(f.name);
280
+ const R = r(this, T).get(f.name);
280
281
  if (R == null) {
281
282
  const z = new Error(
282
- `Failed to load format '${f.name}' when processing '${e}', template: ${r}`
283
+ `Failed to load format '${f.name}' when processing '${e}', template: ${i}`
283
284
  );
284
- throw i(this, b).crash(z), z;
285
+ throw r(this, b).crash(z), z;
285
286
  }
286
287
  k = R(t, k, f.options);
287
288
  }
@@ -289,15 +290,14 @@ b = new WeakMap(), h = new WeakMap(), E = new WeakMap(), T = new WeakMap(), v =
289
290
  }
290
291
  return c;
291
292
  }, L = function(t, e) {
292
- return new Intl.NumberFormat(i(this, d).call(this), e).format(t);
293
+ return new Intl.NumberFormat(r(this, d).call(this), e).format(t);
293
294
  }, V = function(t, e) {
294
- return new Intl.DateTimeFormat(i(this, d).call(this), e).format(C(t) ? new Date(t) : t);
295
+ return new Intl.DateTimeFormat(r(this, d).call(this), e).format(C(t) ? new Date(t) : t);
295
296
  }, M = function(t, e) {
296
- return new Intl.ListFormat(i(this, d).call(this), e).format(t);
297
- }, // relativeTime(): State<string> {
298
- // }
297
+ return new Intl.ListFormat(r(this, d).call(this), e).format(t);
298
+ }, // relativeTime(date?: MaybeSignal<string | number | Date | undefined>): Signal<string> {}
299
299
  A = function(t, e) {
300
- for (const n of i(this, E))
300
+ for (const n of r(this, E))
301
301
  if (n[0] === t && Q(n[1], e))
302
302
  return n[2];
303
303
  };
@@ -310,9 +310,9 @@ function Y(o, t) {
310
310
  }
311
311
  return n;
312
312
  }
313
- const q = new X(), st = q.t.bind(q);
313
+ const q = new X(), nt = q.t.bind(q);
314
314
  export {
315
315
  q as i18n,
316
- st as t
316
+ nt as t
317
317
  };
318
318
  //# sourceMappingURL=i18n.js.map
package/dist/i18n.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","sources":["../src/i18n/index.ts"],"sourcesContent":["import { createLogger, type Logger } from \"../core/logger.js\";\nimport { $, get, type MaybeSignal, type Signal } from \"../core/signals.js\";\nimport { isFunction, isObject, isString, typeOf } from \"../typeChecking.js\";\nimport { deepEqual } from \"../utils.js\";\n\n// ----- Types ----- //\n\n/**\n * A JSON object of translated strings. Values can be string templates or nested objects.\n */\ninterface LocalizedStrings extends Record<string, string | LocalizedStrings> {}\n\nenum SegmentType {\n Static,\n Variable,\n}\ntype StringTemplate = { segments: (StaticSegment | VariableSegment)[] };\n/**\n * A string segment with literal text to be appended without processing.\n */\ntype StaticSegment = {\n type: SegmentType.Static;\n text: string;\n};\n/**\n * A variable passed to the t() function. Needs to be formatted before it is appended.\n */\ntype VariableSegment = {\n type: SegmentType.Variable;\n name: string;\n formats: Format[];\n};\n/**\n * A formatter to be applied to a variable.\n */\ntype Format = {\n name: string;\n options: Record<string, any>;\n};\n\nexport interface TranslationConfig {\n /**\n * Name of the locale this translation is for (BCP 47 locale names recommended).\n */\n locale: string;\n\n /**\n * An object with translated strings for this language.\n */\n strings?: LocalizedStrings;\n\n /**\n * A callback function that returns a Promise that resolves to the translation object for this language.\n */\n fetch?: () => Promise<LocalizedStrings>;\n\n /**\n * Path to a JSON file with translated strings for this language.\n */\n path?: string;\n}\n\nexport type I18nSetupOptions = {\n /**\n * Default locale to load on startup\n */\n locale?: string | null;\n\n translations: TranslationConfig[];\n};\n\nexport type TOptions = {\n /**\n *\n */\n count?: MaybeSignal<number>;\n\n /**\n *\n */\n context?: MaybeSignal<string>;\n\n /**\n * Override formats specified in the template with the ones in the array for each named variable.\n *\n * @example\n * t(\"example_key\", {\n * count: 5,\n * formatOverrides: {\n * count: [ { name: \"datetime\", options: { style: \"currency\", currency: \"JPY\" } } ]\n * }\n * });\n */\n formatOverrides?: MaybeSignal<Record<string, Record<string, Format[]>>>;\n\n [value: string]: MaybeSignal<any>;\n};\n\nexport type Formatter = (locale: string, value: unknown, options: Record<string, any>) => string;\n\n// ----- Code ----- //\n\nclass Translation {\n config: TranslationConfig;\n\n #isLoaded = false;\n\n #templates = new Map<string, StringTemplate>();\n\n constructor(config: TranslationConfig) {\n this.config = config;\n }\n\n async load(): Promise<void> {\n let strings: LocalizedStrings | undefined;\n\n if (!this.#isLoaded) {\n if (isObject(this.config.strings)) {\n strings = this.config.strings;\n } else if (isFunction(this.config.fetch)) {\n strings = await this.config.fetch();\n if (!isObject(strings)) {\n throw new Error(`Fetch function did not return an object of language strings: ${strings}`);\n }\n } else if (isString(this.config.path)) {\n const res = await fetch(this.config.path);\n if (res.ok) {\n const body = await res.json();\n if (isObject(body)) {\n strings = body as LocalizedStrings;\n } else {\n throw new Error(\n `Language path '${this.config.path}' did not return an object of language strings: ${body}`,\n );\n }\n } else {\n throw new Error(`HTTP request failed.`);\n }\n }\n }\n\n if (strings) {\n const entries = this.#compile(strings);\n for (const entry of entries) {\n this.#templates.set(entry[0], entry[1]);\n }\n } else {\n throw new Error(`Language could not be loaded.`);\n }\n }\n\n getTemplate(selector: string): StringTemplate {\n return (\n this.#templates.get(selector) ?? {\n segments: [{ type: SegmentType.Static, text: `[MISSING: ${selector}]` }],\n }\n );\n }\n\n hasTemplate(selector: string): boolean {\n return this.#templates.has(selector);\n }\n\n #compile(strings: { [key: string]: any }, path: string[] = []): [string, StringTemplate][] {\n const entries: [string, StringTemplate][] = [];\n\n for (const key in strings) {\n switch (typeOf(strings[key])) {\n case \"string\":\n entries.push([[...path, key].join(\".\"), this.#parseTemplate(strings[key])]);\n break;\n case \"object\":\n entries.push(...this.#compile(strings[key], [...path, key]));\n break;\n default:\n throw new Error(\n `Expected to find a string or object at ${[...path, key].join(\".\")}. Got: ${typeOf(strings[key])}`,\n );\n }\n }\n\n return entries;\n }\n\n #parseTemplate(template: string): StringTemplate {\n // \"{{itemName}} costs {{amount | number(style: currency, currency: USD)}}.\"\n\n enum Loc {\n /**\n * Outside value braces.\n */\n Static,\n /**\n * Inside value braces; currently parsing the name of the value. e.g. `{{ [name] | number(style: currency, currency: USD) }}`\n */\n ValueName,\n /**\n * Inside value braces; currently parsing the name of a format function. e.g. `{{ name | [number](style: currency, currency: USD) }}`\n */\n FormatName,\n /**\n * Inside value braces; currently parsing the name of a format option. e.g. `{{ name | number([style]: currency, currency: USD) }}`\n */\n FormatOptionName,\n /**\n * Inside value braces; currently parsing the value of a format option. e.g. `{{ name | number(style: [currency], currency: USD ) }}`\n */\n FormatOptionValue,\n /**\n * Inside value braces; just reached the closing bracket of a format option. e.g. `{{ name | number(style: [currency], currency: USD) [] }}`\n */\n FormatOptionEnd,\n }\n\n const parsed: StringTemplate = {\n segments: [],\n };\n\n let buffer = \"\";\n let i = 0;\n let loc: Loc = Loc.Static;\n let segment!: VariableSegment;\n let format!: VariableSegment[\"formats\"][0];\n\n let formatOptionName!: string;\n\n const startSegment = () => {\n segment = {\n type: SegmentType.Variable,\n name: \"\",\n formats: [],\n };\n };\n\n const startFormat = () => {\n format = {\n name: \"\",\n options: {},\n };\n };\n\n while (i < template.length) {\n // Skip spaces (unless we're in static)\n if (loc !== Loc.Static && template[i] === \" \") {\n i++;\n continue;\n }\n\n switch (loc) {\n case Loc.Static:\n if (template[i] === \"{\" && template[i + 1] === \"{\") {\n loc = Loc.ValueName;\n i += 2;\n // close static segment\n if (buffer.length > 0) {\n parsed.segments.push({ type: SegmentType.Static, text: buffer });\n buffer = \"\";\n }\n startSegment();\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.ValueName:\n if (template[i] === \"|\") {\n loc = Loc.FormatName;\n i += 1;\n // add name to value segment\n segment.name = buffer;\n buffer = \"\";\n startFormat();\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n loc = Loc.Static;\n i += 2;\n // close value segment\n segment.name = buffer;\n buffer = \"\";\n parsed.segments.push(segment);\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.FormatName:\n if (template[i] === \"(\") {\n loc = Loc.FormatOptionName;\n i += 1;\n // add name to format object\n format.name = buffer;\n buffer = \"\";\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n loc = Loc.Static;\n i += 2;\n // close format and value segment\n segment.formats.push(format);\n parsed.segments.push(segment);\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.FormatOptionName:\n if (template[i] === \")\") {\n // TODO: error - no value provided for option\n } else if (template[i] === \":\") {\n loc = Loc.FormatOptionValue;\n i += 1;\n // add name to format option object\n formatOptionName = buffer;\n buffer = \"\";\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n // TODO: error - format options parenthesis not closed\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.FormatOptionValue:\n if (template[i] === \")\") {\n loc = Loc.FormatOptionEnd;\n i += 1;\n // add value to format option object\n // add format option to format object; we're done with this format\n format.options[formatOptionName] = buffer;\n buffer = \"\";\n segment.formats.push(format);\n } else if (template[i] === \",\") {\n loc = Loc.FormatOptionName;\n i += 1;\n // add value to format option object\n // add format option to format object; we're adding another option\n format.options[formatOptionName] = buffer;\n buffer = \"\";\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n // TODO: error - format options parenthesis not closed\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.FormatOptionEnd:\n if (template[i] === \"|\") {\n loc = Loc.FormatName;\n i += 1;\n startFormat();\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n loc = Loc.Static;\n i += 2;\n // add value segment\n parsed.segments.push(segment);\n } else {\n // TODO: error - no other valid characters\n }\n break;\n }\n }\n\n if (loc === Loc.Static && buffer.length > 0) {\n parsed.segments.push({ type: SegmentType.Static, text: buffer });\n }\n\n return parsed;\n }\n}\n\n/**\n * Dolla's I(nternationalizatio)n module. Manages language translations and locale-based formatting.\n */\nclass I18n {\n #logger: Logger;\n #translations = new Map<string, Translation>();\n #cache: [key: string, values: Record<string, any> | undefined, output: string][] = [];\n #formats = new Map<string, Formatter>();\n\n #initialLocale = \"auto\";\n\n #locale = $<string>(\"en\");\n\n readonly $locale = $(this.#locale);\n\n constructor() {\n this.#logger = createLogger(\"dolla.i18n\");\n\n this.addFormat(\"number\", (_, value, options) => {\n return this.#formatNumber(Number(value), options);\n });\n this.addFormat(\"datetime\", (_, value, options) => {\n return this.#formatDateTime(value as any, options);\n });\n this.addFormat(\"list\", (_, value, options) => {\n return this.#formatList(value as any, options);\n });\n }\n\n get locales() {\n return [...this.#translations.keys()];\n }\n\n async setup(options: I18nSetupOptions) {\n // Convert languages into Language instances.\n options.translations.forEach((entry) => {\n this.#translations.set(entry.locale, new Translation(entry));\n });\n\n // Check that initialLanguage is actually registered.\n if (options.locale && options.locale !== \"auto\") {\n const isRegistered = options.translations.some((entry) => entry.locale === options.locale);\n if (!isRegistered) {\n throw new Error(`Initial locale '${options.locale}' is not registered in the locales array.`);\n }\n this.#initialLocale = options.locale;\n }\n\n this.#logger.info(\n `${this.#translations.size} language${this.#translations.size === 1 ? \"\" : \"s\"} supported: '${[...this.#translations.keys()].join(\"', '\")}'`,\n );\n\n if (this.#translations.size > 0) {\n await this.setLocale(this.#initialLocale);\n }\n }\n\n async setLocale(name: string) {\n let realName!: string;\n\n if (name === \"auto\") {\n let names = [];\n\n if (typeof navigator !== \"undefined\") {\n const nav = navigator as any;\n\n if (nav.languages?.length > 0) {\n names.push(...nav.languages);\n } else if (nav.language) {\n names.push(nav.language);\n } else if (nav.browserLanguage) {\n names.push(nav.browserLanguage);\n } else if (nav.userLanguage) {\n names.push(nav.userLanguage);\n }\n }\n\n for (const name of names) {\n if (this.#translations.has(name)) {\n // Found a matching language.\n realName = name;\n }\n }\n } else {\n // Tag is the actual tag to set.\n if (this.#translations.has(name)) {\n realName = name;\n }\n }\n\n if (realName == null) {\n const firstLanguage = this.#translations.keys().next().value;\n if (firstLanguage) {\n realName = firstLanguage;\n }\n }\n\n if (!realName || !this.#translations.has(realName)) {\n throw new Error(`Locale '${name}' has no translation.`);\n }\n\n const translation = this.#translations.get(realName)!;\n\n try {\n await translation.load();\n\n this.#cache = [];\n this.#locale(realName);\n\n this.#logger.info(\"set language to \" + realName);\n } catch (error) {\n if (error instanceof Error) {\n this.#logger.crash(error);\n }\n }\n }\n\n /**\n * Returns a State containing the value at `key`.\n\n * @param selector - Key to the translated value.\n * @param options - A map of `{{placeholder}}` names and the values to replace them with.\n *\n * @example\n * const $value = t(\"your.key.here\", { count: 5 });\n */\n t(selector: string, options?: TOptions): Signal<string> {\n if (this === undefined) {\n throw new Error(\n `The 't' function cannot be destructured. If you need a standalone version you can import it like so: 'import { t } from \"@manyducks.co/dolla\"'`,\n );\n }\n\n return $(() => {\n const values: Record<string, any> = {};\n\n // Track all option values.\n for (const key in options) {\n values[key] = get(options[key]);\n }\n\n return this.#getValue(this.#locale(), selector, values);\n });\n }\n\n #getValue(locale: string, selector: string, options: Record<string, any>): string {\n const cached = this.#getCached(selector, options);\n if (cached) return cached;\n\n const translation = this.#translations.get(locale)!;\n\n // Handle count (pluralization) and context. Keys become \"key_context_pluralization\".\n\n if (options.context != null) {\n selector += \"_\" + options.context;\n }\n if (options.count != null) {\n if (options.ordinal) {\n // Try to match the exact number key if there is one (e.g. \"myExampleKey_ordinal_(=2)\" when count is 2).\n const exact = `${selector}_ordinal_(=${options.count})`;\n if (translation.hasTemplate(exact)) {\n selector = exact;\n } else {\n selector += \"_ordinal_\" + new Intl.PluralRules(locale, { type: \"ordinal\" }).select(options.count);\n }\n } else {\n // Try to match the exact number key if there is one (e.g. \"myExampleKey_(=2)\" when count is 2).\n const exact = `${selector}_(=${options.count})`;\n if (translation.hasTemplate(exact)) {\n selector = exact;\n } else {\n selector += \"_\" + new Intl.PluralRules(locale).select(options.count);\n }\n }\n }\n\n const template = translation.getTemplate(selector);\n let output = \"\";\n\n for (const segment of template.segments) {\n if (segment.type === SegmentType.Static) {\n output += segment.text;\n } else if (segment.type === SegmentType.Variable) {\n let value = resolve(options, segment.name);\n\n const formats = options.formatOverrides?.[segment.name] ?? [...segment.formats];\n\n if (segment.name === \"count\" && formats.length === 0) {\n formats.push({ name: \"number\", options: {} });\n }\n\n for (const format of formats) {\n const fn = this.#formats.get(format.name);\n if (fn == null) {\n const error = new Error(\n `Failed to load format '${format.name}' when processing '${selector}', template: ${template}`,\n );\n this.#logger.crash(error);\n throw error;\n }\n value = fn(locale, value, format.options);\n }\n\n output += value;\n }\n }\n\n return output;\n }\n\n /**\n * Add a custom format callback.\n *\n * @example\n * Dolla.i18n.addFormat(\"uppercase\", (locale, value, options) => {\n * return value.toUpperCase();\n * });\n *\n * {\n * \"greeting\": \"Hello, {{name|uppercase}}!\"\n * }\n *\n * t(\"greeting\", {name: \"world\"}); // State<\"Hello, WORLD!\">\n */\n addFormat(name: string, callback: (locale: string, value: unknown, options: Record<string, any>) => string) {\n this.#formats.set(name, callback);\n }\n\n /**\n * Creates an `Intl.Collator` configured for the current locale.\n * NOTE: The locale is tracked if called within a signal tracking context.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator#options\n */\n collator(options?: Intl.CollatorOptions) {\n return new Intl.Collator(this.#locale(), options);\n }\n\n /**\n * Formats a number for the current locale. Uses `Intl.NumberFormat` under the hood.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options\n */\n number(count: MaybeSignal<number | bigint>, options?: Intl.NumberFormatOptions): Signal<string> {\n return $(() => this.#formatNumber(get(count), options));\n }\n\n #formatNumber(count: number | bigint, options?: Intl.NumberFormatOptions): string {\n // NOTE: Locale is tracked if called within a tracking context.\n return new Intl.NumberFormat(this.#locale(), options).format(count);\n }\n\n /**\n * Formats a date for the current locale. Uses `Intl.DateTimeFormat` under the hood.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options\n *\n * @example\n * const date = new Date();\n * const $formatted = Dolla.i18n.dateTime(date, { dateFormat: \"short\" });\n */\n dateTime(\n date?: MaybeSignal<string | number | Date | undefined>,\n options?: Intl.DateTimeFormatOptions,\n ): Signal<string> {\n return $(() => this.#formatDateTime(get(date), options));\n }\n\n #formatDateTime(date?: string | number | Date, options?: Intl.DateTimeFormatOptions): string {\n // NOTE: Locale is tracked if called within a tracking context.\n return new Intl.DateTimeFormat(this.#locale(), options).format(isString(date) ? new Date(date) : date);\n }\n\n /**\n * Formats a list for the current locale. Uses `Intl.ListFormat` under the hood.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options\n *\n * @example\n * const list = new Date();\n * const $formatted = Dolla.i18n.list(list, { });\n */\n list(list: MaybeSignal<Iterable<string>>, options?: Intl.ListFormatOptions): Signal<string> {\n return $(() => this.#formatList(get(list), options));\n }\n\n #formatList(list: Iterable<string>, options?: Intl.ListFormatOptions): string {\n // NOTE: Locale is tracked if called within a tracking context.\n return new Intl.ListFormat(this.#locale(), options).format(list);\n }\n\n // relativeTime(): State<string> {\n\n // }\n\n #getCached(key: string, values?: Record<string, any>): string | undefined {\n for (const entry of this.#cache) {\n if (entry[0] === key && deepEqual(entry[1], values)) {\n return entry[2];\n }\n }\n }\n}\n\nfunction resolve(object: any, key: string) {\n const parsed = String(key)\n .split(/[\\.\\[\\]]/)\n .filter((part) => part.trim() !== \"\");\n let value = object;\n\n while (parsed.length > 0) {\n const part = parsed.shift()!;\n\n if (value != null) {\n value = value[part];\n } else {\n value = undefined;\n }\n }\n\n return value;\n}\n\nexport const i18n = new I18n();\nexport const t = i18n.t.bind(i18n);\n"],"names":["Translation","config","__privateAdd","_Translation_instances","__publicField","_isLoaded","_templates","strings","__privateGet","isObject","isFunction","isString","res","body","entries","__privateMethod","compile_fn","entry","selector","path","key","typeOf","parseTemplate_fn","template","Loc","Loc2","parsed","buffer","i","loc","segment","format","formatOptionName","startSegment","startFormat","I18n","_I18n_instances","_logger","_translations","_cache","_formats","_initialLocale","_locale","__privateSet","createLogger","_","value","options","formatNumber_fn","formatDateTime_fn","formatList_fn","name","realName","names","nav","_a","firstLanguage","translation","error","values","get","getValue_fn","callback","count","date","list","locale","cached","getCached_fn","exact","output","resolve","formats","fn","deepEqual","object","part","i18n","t"],"mappings":"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"}
1
+ {"version":3,"file":"i18n.js","sources":["../src/i18n/index.ts"],"sourcesContent":["import { createLogger, type Logger } from \"../core/logger.js\";\nimport { $, get, type MaybeSignal, type Signal } from \"../core/signals.js\";\nimport { isFunction, isObject, isString, typeOf } from \"../typeChecking.js\";\nimport { deepEqual } from \"../utils.js\";\n\n// ----- Types ----- //\n\n/**\n * A JSON object of translated strings. Values can be string templates or nested objects.\n */\ninterface LocalizedStrings extends Record<string, string | LocalizedStrings> {}\n\nenum SegmentType {\n Static,\n Variable,\n}\ntype StringTemplate = { segments: (StaticSegment | VariableSegment)[] };\n/**\n * A string segment with literal text to be appended without processing.\n */\ntype StaticSegment = {\n type: SegmentType.Static;\n text: string;\n};\n/**\n * A variable passed to the t() function. Needs to be formatted before it is appended.\n */\ntype VariableSegment = {\n type: SegmentType.Variable;\n name: string;\n formats: Format[];\n};\n/**\n * A formatter to be applied to a variable.\n */\ntype Format = {\n name: string;\n options: Record<string, any>;\n};\n\nexport interface TranslationConfig {\n /**\n * Name of the locale this translation is for (BCP 47 locale names recommended).\n */\n locale: string;\n\n /**\n * An object with translated strings for this language.\n */\n strings?: LocalizedStrings;\n\n /**\n * A callback function that returns a Promise that resolves to the translation object for this language.\n */\n fetch?: () => Promise<LocalizedStrings>;\n\n /**\n * Path to a JSON file with translated strings for this language.\n */\n path?: string;\n}\n\nexport type I18nSetupOptions = {\n /**\n * Default locale to load on startup\n */\n locale?: string | null;\n\n translations: TranslationConfig[];\n};\n\nexport type TOptions = {\n /**\n *\n */\n count?: MaybeSignal<number>;\n\n /**\n *\n */\n context?: MaybeSignal<string>;\n\n /**\n * Override formats specified in the template with the ones in the array for each named variable.\n *\n * @example\n * t(\"example_key\", {\n * count: 5,\n * formatOverrides: {\n * count: [ { name: \"datetime\", options: { style: \"currency\", currency: \"JPY\" } } ]\n * }\n * });\n */\n formatOverrides?: MaybeSignal<Record<string, Record<string, Format[]>>>;\n\n [value: string]: MaybeSignal<any>;\n};\n\nexport type Formatter = (locale: string, value: unknown, options: Record<string, any>) => string;\n\n// ----- Code ----- //\n\nclass Translation {\n config: TranslationConfig;\n\n #isLoaded = false;\n\n #templates = new Map<string, StringTemplate>();\n\n constructor(config: TranslationConfig) {\n this.config = config;\n }\n\n async load(): Promise<void> {\n let strings: LocalizedStrings | undefined;\n\n if (!this.#isLoaded) {\n if (isObject(this.config.strings)) {\n strings = this.config.strings;\n } else if (isFunction(this.config.fetch)) {\n strings = await this.config.fetch();\n if (!isObject(strings)) {\n throw new Error(`Fetch function did not return an object of language strings: ${strings}`);\n }\n } else if (isString(this.config.path)) {\n const res = await fetch(this.config.path);\n if (res.ok) {\n const body = await res.json();\n if (isObject(body)) {\n strings = body as LocalizedStrings;\n } else {\n throw new Error(\n `Language path '${this.config.path}' did not return an object of language strings: ${body}`,\n );\n }\n } else {\n throw new Error(`HTTP request failed.`);\n }\n }\n }\n\n if (strings) {\n const entries = this.#compile(strings);\n for (const entry of entries) {\n this.#templates.set(entry[0], entry[1]);\n }\n } else {\n throw new Error(`Language could not be loaded.`);\n }\n }\n\n getTemplate(selector: string): StringTemplate {\n return (\n this.#templates.get(selector) ?? {\n segments: [{ type: SegmentType.Static, text: `[MISSING: ${selector}]` }],\n }\n );\n }\n\n hasTemplate(selector: string): boolean {\n return this.#templates.has(selector);\n }\n\n #compile(strings: { [key: string]: any }, path: string[] = []): [string, StringTemplate][] {\n const entries: [string, StringTemplate][] = [];\n\n for (const key in strings) {\n switch (typeOf(strings[key])) {\n case \"string\":\n entries.push([[...path, key].join(\".\"), this.#parseTemplate(strings[key])]);\n break;\n case \"object\":\n entries.push(...this.#compile(strings[key], [...path, key]));\n break;\n default:\n throw new Error(\n `Expected to find a string or object at ${[...path, key].join(\".\")}. Got: ${typeOf(strings[key])}`,\n );\n }\n }\n\n return entries;\n }\n\n #parseTemplate(template: string): StringTemplate {\n // \"{{itemName}} costs {{amount | number(style: currency, currency: USD)}}.\"\n\n enum Loc {\n /**\n * Outside value braces.\n */\n Static,\n /**\n * Inside value braces; currently parsing the name of the value. e.g. `{{ [name] | number(style: currency, currency: USD) }}`\n */\n ValueName,\n /**\n * Inside value braces; currently parsing the name of a format function. e.g. `{{ name | [number](style: currency, currency: USD) }}`\n */\n FormatName,\n /**\n * Inside value braces; currently parsing the name of a format option. e.g. `{{ name | number([style]: currency, currency: USD) }}`\n */\n FormatOptionName,\n /**\n * Inside value braces; currently parsing the value of a format option. e.g. `{{ name | number(style: [currency], currency: USD ) }}`\n */\n FormatOptionValue,\n /**\n * Inside value braces; just reached the closing bracket of a format option. e.g. `{{ name | number(style: [currency], currency: USD) [] }}`\n */\n FormatOptionEnd,\n }\n\n const parsed: StringTemplate = {\n segments: [],\n };\n\n let buffer = \"\";\n let i = 0;\n let loc: Loc = Loc.Static;\n let segment!: VariableSegment;\n let format!: VariableSegment[\"formats\"][0];\n\n let formatOptionName!: string;\n\n const startSegment = () => {\n segment = {\n type: SegmentType.Variable,\n name: \"\",\n formats: [],\n };\n };\n\n const startFormat = () => {\n format = {\n name: \"\",\n options: {},\n };\n };\n\n while (i < template.length) {\n // Skip spaces (unless we're in static)\n if (loc !== Loc.Static && template[i] === \" \") {\n i++;\n continue;\n }\n\n switch (loc) {\n case Loc.Static:\n if (template[i] === \"{\" && template[i + 1] === \"{\") {\n loc = Loc.ValueName;\n i += 2;\n // close static segment\n if (buffer.length > 0) {\n parsed.segments.push({ type: SegmentType.Static, text: buffer });\n buffer = \"\";\n }\n startSegment();\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.ValueName:\n if (template[i] === \"|\") {\n loc = Loc.FormatName;\n i += 1;\n // add name to value segment\n segment.name = buffer;\n buffer = \"\";\n startFormat();\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n loc = Loc.Static;\n i += 2;\n // close value segment\n segment.name = buffer;\n buffer = \"\";\n parsed.segments.push(segment);\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.FormatName:\n if (template[i] === \"(\") {\n loc = Loc.FormatOptionName;\n i += 1;\n // add name to format object\n format.name = buffer;\n buffer = \"\";\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n loc = Loc.Static;\n i += 2;\n // close format and value segment\n segment.formats.push(format);\n parsed.segments.push(segment);\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.FormatOptionName:\n if (template[i] === \")\") {\n // TODO: error - no value provided for option\n } else if (template[i] === \":\") {\n loc = Loc.FormatOptionValue;\n i += 1;\n // add name to format option object\n formatOptionName = buffer;\n buffer = \"\";\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n // TODO: error - format options parenthesis not closed\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.FormatOptionValue:\n if (template[i] === \")\") {\n loc = Loc.FormatOptionEnd;\n i += 1;\n // add value to format option object\n // add format option to format object; we're done with this format\n format.options[formatOptionName] = buffer;\n buffer = \"\";\n segment.formats.push(format);\n } else if (template[i] === \",\") {\n loc = Loc.FormatOptionName;\n i += 1;\n // add value to format option object\n // add format option to format object; we're adding another option\n format.options[formatOptionName] = buffer;\n buffer = \"\";\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n // TODO: error - format options parenthesis not closed\n } else {\n buffer += template[i];\n i++;\n }\n break;\n case Loc.FormatOptionEnd:\n if (template[i] === \"|\") {\n loc = Loc.FormatName;\n i += 1;\n startFormat();\n } else if (template[i] === \"}\" && template[i + 1] === \"}\") {\n loc = Loc.Static;\n i += 2;\n // add value segment\n parsed.segments.push(segment);\n } else {\n // TODO: error - no other valid characters\n }\n break;\n }\n }\n\n if (loc === Loc.Static && buffer.length > 0) {\n parsed.segments.push({ type: SegmentType.Static, text: buffer });\n }\n\n return parsed;\n }\n}\n\n/**\n * Dolla's I(nternationalizatio)n module. Manages language translations and locale-based formatting.\n */\nclass I18n {\n #logger: Logger;\n #translations = new Map<string, Translation>();\n #cache: [key: string, values: Record<string, any> | undefined, output: string][] = [];\n #formats = new Map<string, Formatter>();\n\n #initialLocale = \"auto\";\n\n #locale = $<string>(\"en\");\n\n readonly $locale = $(this.#locale);\n\n constructor() {\n this.#logger = createLogger(\"dolla.i18n\");\n\n this.addFormat(\"number\", (_, value, options) => {\n return this.#formatNumber(Number(value), options);\n });\n this.addFormat(\"datetime\", (_, value, options) => {\n return this.#formatDateTime(value as any, options);\n });\n this.addFormat(\"list\", (_, value, options) => {\n return this.#formatList(value as any, options);\n });\n }\n\n get locales() {\n return [...this.#translations.keys()];\n }\n\n async setup(options: I18nSetupOptions) {\n // Convert languages into Language instances.\n options.translations.forEach((entry) => {\n this.#translations.set(entry.locale, new Translation(entry));\n });\n\n // Check that initialLanguage is actually registered.\n if (options.locale && options.locale !== \"auto\") {\n const isRegistered = options.translations.some((entry) => entry.locale === options.locale);\n if (!isRegistered) {\n throw new Error(`Initial locale '${options.locale}' is not registered in the locales array.`);\n }\n this.#initialLocale = options.locale;\n }\n\n this.#logger.info(\n `${this.#translations.size} language${this.#translations.size === 1 ? \"\" : \"s\"} supported: '${[...this.#translations.keys()].join(\"', '\")}'`,\n );\n\n if (this.#translations.size > 0) {\n await this.setLocale(this.#initialLocale);\n }\n }\n\n async setLocale(name: string) {\n let realName!: string;\n\n if (name === \"auto\") {\n let names = [];\n\n if (typeof navigator !== \"undefined\") {\n const nav = navigator as any;\n\n if (nav.languages?.length > 0) {\n names.push(...nav.languages);\n } else if (nav.language) {\n names.push(nav.language);\n } else if (nav.browserLanguage) {\n names.push(nav.browserLanguage);\n } else if (nav.userLanguage) {\n names.push(nav.userLanguage);\n }\n }\n\n for (const name of names) {\n if (this.#translations.has(name)) {\n // Found a matching language.\n realName = name;\n }\n }\n } else {\n // Tag is the actual tag to set.\n if (this.#translations.has(name)) {\n realName = name;\n }\n }\n\n if (realName == null) {\n const firstLanguage = this.#translations.keys().next().value;\n if (firstLanguage) {\n realName = firstLanguage;\n }\n }\n\n if (!realName || !this.#translations.has(realName)) {\n throw new Error(`Locale '${name}' has no translation.`);\n }\n\n const translation = this.#translations.get(realName)!;\n\n try {\n await translation.load();\n\n this.#cache = [];\n this.#locale(realName);\n\n this.#logger.info(\"set language to \" + realName);\n } catch (error) {\n if (error instanceof Error) {\n this.#logger.crash(error);\n }\n }\n }\n\n /**\n * Returns a State containing the value at `key`.\n\n * @param selector - Key to the translated value.\n * @param options - A map of `{{placeholder}}` names and the values to replace them with.\n *\n * @example\n * const $value = t(\"your.key.here\", { count: 5 });\n */\n t(selector: string, options?: TOptions): Signal<string> {\n if (this === undefined) {\n throw new Error(\n `The 't' function cannot be destructured. If you need a standalone version you can import it like so: 'import { t } from \"@manyducks.co/dolla\"'`,\n );\n }\n\n return $(() => {\n const values: Record<string, any> = {};\n\n // Track all option values.\n for (const key in options) {\n values[key] = get(options[key]);\n }\n\n return this.#getValue(this.#locale(), selector, values);\n });\n }\n\n #getValue(locale: string, selector: string, options: Record<string, any>): string {\n const cached = this.#getCached(selector, options);\n if (cached) return cached;\n\n const translation = this.#translations.get(locale)!;\n\n // Handle count (pluralization) and context. Keys become \"key_context_pluralization\".\n\n if (options.context != null) {\n selector += \"_\" + options.context;\n }\n if (options.count != null) {\n if (options.ordinal) {\n // Try to match the exact number key if there is one (e.g. \"myExampleKey_ordinal_(=2)\" when count is 2).\n const exact = `${selector}_ordinal_(=${options.count})`;\n if (translation.hasTemplate(exact)) {\n selector = exact;\n } else {\n selector += \"_ordinal_\" + new Intl.PluralRules(locale, { type: \"ordinal\" }).select(options.count);\n }\n } else {\n // Try to match the exact number key if there is one (e.g. \"myExampleKey_(=2)\" when count is 2).\n const exact = `${selector}_(=${options.count})`;\n if (translation.hasTemplate(exact)) {\n selector = exact;\n } else {\n selector += \"_\" + new Intl.PluralRules(locale).select(options.count);\n }\n }\n }\n\n const template = translation.getTemplate(selector);\n let output = \"\";\n\n for (const segment of template.segments) {\n if (segment.type === SegmentType.Static) {\n output += segment.text;\n } else if (segment.type === SegmentType.Variable) {\n let value = resolve(options, segment.name);\n\n const formats = options.formatOverrides?.[segment.name] ?? [...segment.formats];\n\n if (segment.name === \"count\" && formats.length === 0) {\n formats.push({ name: \"number\", options: {} });\n }\n\n for (const format of formats) {\n const fn = this.#formats.get(format.name);\n if (fn == null) {\n const error = new Error(\n `Failed to load format '${format.name}' when processing '${selector}', template: ${template}`,\n );\n this.#logger.crash(error);\n throw error;\n }\n value = fn(locale, value, format.options);\n }\n\n output += value;\n }\n }\n\n return output;\n }\n\n /**\n * Add a custom format callback.\n *\n * @example\n * Dolla.i18n.addFormat(\"uppercase\", (locale, value, options) => {\n * return value.toUpperCase();\n * });\n *\n * {\n * \"greeting\": \"Hello, {{name|uppercase}}!\"\n * }\n *\n * t(\"greeting\", {name: \"world\"}); // State<\"Hello, WORLD!\">\n */\n addFormat(name: string, callback: (locale: string, value: unknown, options: Record<string, any>) => string) {\n this.#formats.set(name, callback);\n }\n\n /**\n * Creates an `Intl.Collator` configured for the current locale.\n * NOTE: The locale is tracked if called within a signal tracking context.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator#options\n */\n collator(options?: Intl.CollatorOptions) {\n return new Intl.Collator(this.#locale(), options);\n }\n\n /**\n * Formats a number for the current locale. Uses `Intl.NumberFormat` under the hood.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options\n */\n number(count: MaybeSignal<number | bigint>, options?: Intl.NumberFormatOptions): Signal<string> {\n return $(() => this.#formatNumber(get(count), options));\n }\n\n #formatNumber(count: number | bigint, options?: Intl.NumberFormatOptions): string {\n // NOTE: Locale is tracked if called within a tracking context.\n return new Intl.NumberFormat(this.#locale(), options).format(count);\n }\n\n /**\n * Formats a date for the current locale. Uses `Intl.DateTimeFormat` under the hood.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options\n *\n * @example\n * const date = new Date();\n * const $formatted = Dolla.i18n.dateTime(date, { dateFormat: \"short\" });\n */\n dateTime(\n date?: MaybeSignal<string | number | Date | undefined>,\n options?: Intl.DateTimeFormatOptions,\n ): Signal<string> {\n return $(() => this.#formatDateTime(get(date), options));\n }\n\n #formatDateTime(date?: string | number | Date, options?: Intl.DateTimeFormatOptions): string {\n // NOTE: Locale is tracked if called within a tracking context.\n return new Intl.DateTimeFormat(this.#locale(), options).format(isString(date) ? new Date(date) : date);\n }\n\n /**\n * Formats a list for the current locale. Uses `Intl.ListFormat` under the hood.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options\n *\n * @example\n * const list = new Date();\n * const $formatted = Dolla.i18n.list(list, { });\n */\n list(list: MaybeSignal<Iterable<string>>, options?: Intl.ListFormatOptions): Signal<string> {\n return $(() => this.#formatList(get(list), options));\n }\n\n #formatList(list: Iterable<string>, options?: Intl.ListFormatOptions): string {\n // NOTE: Locale is tracked if called within a tracking context.\n return new Intl.ListFormat(this.#locale(), options).format(list);\n }\n\n // relativeTime(date?: MaybeSignal<string | number | Date | undefined>): Signal<string> {}\n\n #getCached(key: string, values?: Record<string, any>): string | undefined {\n for (const entry of this.#cache) {\n if (entry[0] === key && deepEqual(entry[1], values)) {\n return entry[2];\n }\n }\n }\n}\n\nfunction resolve(object: any, key: string) {\n const parsed = String(key)\n .split(/[\\.\\[\\]]/)\n .filter((part) => part.trim() !== \"\");\n let value = object;\n\n while (parsed.length > 0) {\n const part = parsed.shift()!;\n\n if (value != null) {\n value = value[part];\n } else {\n value = undefined;\n }\n }\n\n return value;\n}\n\nexport const i18n = new I18n();\nexport const t = i18n.t.bind(i18n);\n"],"names":["Translation","config","__privateAdd","_Translation_instances","__publicField","_isLoaded","_templates","strings","__privateGet","isObject","isFunction","isString","res","body","entries","__privateMethod","compile_fn","entry","selector","path","key","typeOf","parseTemplate_fn","template","Loc","Loc2","parsed","buffer","i","loc","segment","format","formatOptionName","startSegment","startFormat","I18n","_I18n_instances","_logger","_translations","_cache","_formats","_initialLocale","_locale","__privateSet","createLogger","_","value","options","formatNumber_fn","formatDateTime_fn","formatList_fn","name","realName","names","nav","_a","firstLanguage","translation","error","values","get","getValue_fn","callback","count","date","list","locale","cached","getCached_fn","exact","output","resolve","formats","fn","deepEqual","object","part","i18n","t"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,24 +1,13 @@
1
- import { o as h } from "./logger-MPwl-Xqu.js";
2
- import { $ as k, b as D, h as I, d as V, e as F, g as _, c as q, f as v, i as B, j as $, s as P, a as S, u as W } from "./logger-MPwl-Xqu.js";
3
- import { m as t, w as y, C as n, L as m, V as f } from "./markup-BGlfQYQk.js";
4
- import { c as A, p as Y, r as j, a as G, u as H } from "./markup-BGlfQYQk.js";
5
- import { R as w, M as p, U as b } from "./router-BpuJQ6OA.js";
6
- import { a as x } from "./typeChecking-CbltMOUt.js";
7
- const u = Symbol("Ref.EMPTY");
8
- function C(e = u) {
9
- return function() {
10
- if (arguments.length === 0) {
11
- if (e === u)
12
- throw new Error("Ref getter was called, but ref has no value! Be sure to set your refs before accessing them.");
13
- } else if (arguments.length === 1)
14
- e = arguments[0];
15
- else
16
- throw new Error("Ref called with too many arguments. Expected 0 or 1 arguments.");
17
- return e;
18
- };
19
- }
20
- function E(e) {
21
- return t("div", {
1
+ import { $ as T, b as D, d as E, e as V, g as v, s as I, a as R, u as q } from "./signals-gCwiIe5X.js";
2
+ import { m as a, w as g, C as s, L as m, V as d } from "./markup-CX27GJ1M.js";
3
+ import { M as _, c as $, p as B, r as W, a as z, u as A } from "./markup-CX27GJ1M.js";
4
+ import { r as G } from "./ref-BD79iqlg.js";
5
+ import { R as h, M as p, U as y } from "./router-CjCkk4dA.js";
6
+ import { a as w } from "./typeChecking-CbltMOUt.js";
7
+ import { o as x } from "./logger-Bl496yfY.js";
8
+ import { c as j, g as H, s as J, a as K, b as Q } from "./logger-Bl496yfY.js";
9
+ function N(e) {
10
+ return a("div", {
22
11
  style: {
23
12
  backgroundColor: "#880000",
24
13
  color: "#fff",
@@ -28,17 +17,17 @@ function E(e) {
28
17
  fontSize: "20px"
29
18
  },
30
19
  children: [
31
- t("h1", { style: { marginBottom: "0.5rem" }, children: "The app has crashed" }),
32
- t("p", {
20
+ a("h1", { style: { marginBottom: "0.5rem" }, children: "The app has crashed" }),
21
+ a("p", {
33
22
  style: { marginBottom: "0.25rem" },
34
23
  children: [
35
- t("span", {
24
+ a("span", {
36
25
  style: { fontFamily: "monospace" },
37
26
  children: e.loggerName
38
27
  }),
39
- y(
28
+ g(
40
29
  e.tag,
41
- t("span", {
30
+ a("span", {
42
31
  style: { fontFamily: "monospace", opacity: 0.5 },
43
32
  children: ` [${e.tagName ? `${e.tagName}: ` : ""}${e.tag}]`
44
33
  })
@@ -46,7 +35,7 @@ function E(e) {
46
35
  " says:"
47
36
  ]
48
37
  }),
49
- t("blockquote", {
38
+ a("blockquote", {
50
39
  style: {
51
40
  backgroundColor: "#991111",
52
41
  padding: "0.25em",
@@ -55,7 +44,7 @@ function E(e) {
55
44
  marginBottom: "1rem"
56
45
  },
57
46
  children: [
58
- t("span", {
47
+ a("span", {
59
48
  style: {
60
49
  display: "inline-block",
61
50
  backgroundColor: "red",
@@ -70,47 +59,48 @@ function E(e) {
70
59
  e.error.message
71
60
  ]
72
61
  }),
73
- t("p", { children: "Please see the browser console for details." })
62
+ a("p", { children: "Please see the browser console for details." })
74
63
  ]
75
64
  });
76
65
  }
77
- let s = !1;
78
- async function R(e, o, r) {
79
- if (x(Element, o, "Expected an element or a selector string. Got type: %t, value: %v"), s)
66
+ let o = !1;
67
+ async function C(e, n, r) {
68
+ if (w(Element, n, "Expected an element or a selector string. Got type: %t, value: %v"), o)
80
69
  throw new Error("A Dolla app is already mounted.");
81
- let l, i, d = (r == null ? void 0 : r.crashView) ?? E;
82
- const a = (r == null ? void 0 : r.context) ?? new n("App");
83
- h((g) => {
84
- s && c(), new f(a, d, g).mount(o);
85
- }), n.emit(a, m.WILL_MOUNT), e instanceof w ? (i = e, l = await i[p](o, a)) : l = new f(a, e, {}), l.mount(o), s = !0, n.emit(a, m.DID_MOUNT);
70
+ let l, i, f = (r == null ? void 0 : r.crashView) ?? N;
71
+ const t = (r == null ? void 0 : r.context) ?? new s("App");
72
+ x((u) => {
73
+ o && c(), new d(t, f, u).mount(n);
74
+ }), s.emit(t, m.WILL_MOUNT), e instanceof h ? (i = e, l = await i[p](n, t)) : l = new d(t, e, {}), l.mount(n), o = !0, s.emit(t, m.DID_MOUNT);
86
75
  async function c() {
87
- s && (n.emit(a, m.WILL_UNMOUNT), l.unmount(!1), i && await i[b](), s = !1, n.emit(a, m.DID_UNMOUNT));
76
+ o && (s.emit(t, m.WILL_UNMOUNT), l.unmount(!1), i && await i[y](), o = !1, s.emit(t, m.DID_UNMOUNT));
88
77
  }
89
78
  return c;
90
79
  }
91
80
  export {
92
- k as $,
81
+ T as $,
82
+ _ as MarkupNode,
93
83
  D as batch,
94
- A as createContext,
95
- I as createLogger,
96
- V as deepEqual,
97
- F as effect,
98
- _ as get,
99
- q as getEnv,
100
- t as m,
101
- R as mount,
102
- h as onLoggerCrash,
103
- Y as portal,
104
- C as ref,
105
- j as render,
106
- G as repeat,
107
- v as setEnv,
108
- B as setLogFilter,
109
- $ as setLogLevels,
110
- P as shallowEqual,
111
- S as strictEqual,
112
- H as unless,
113
- W as untracked,
114
- y as when
84
+ $ as createContext,
85
+ j as createLogger,
86
+ E as deepEqual,
87
+ V as effect,
88
+ v as get,
89
+ H as getEnv,
90
+ a as m,
91
+ C as mount,
92
+ x as onLoggerCrash,
93
+ B as portal,
94
+ G as ref,
95
+ W as render,
96
+ z as repeat,
97
+ J as setEnv,
98
+ K as setLogFilter,
99
+ Q as setLogLevels,
100
+ I as shallowEqual,
101
+ R as strictEqual,
102
+ A as unless,
103
+ q as untracked,
104
+ g as when
115
105
  };
116
106
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/core/ref.ts","../src/core/views/default-crash-view.ts","../src/core/mount.ts"],"sourcesContent":["const EMPTY_REF = Symbol(\"Ref.EMPTY\");\n\n/**\n * A hybrid getter/setter function that stores the last value it was called with.\n * Guarantees a value is held at runtime by throwing an error if no value is set.\n */\nexport interface Ref<T> {\n /**\n * Returns the currently stored value of the ref, or throws an error if no value has been set.\n */\n (): T;\n\n /**\n * Stores a new value to the ref and returns that value.\n */\n (value: T): T;\n}\n\n/**\n * Creates a Ref.\n */\nexport function ref<T>(value?: T): Ref<T>;\n\nexport function ref(value = EMPTY_REF) {\n return function () {\n if (arguments.length === 0) {\n if (value === EMPTY_REF) {\n throw new Error(`Ref getter was called, but ref has no value! Be sure to set your refs before accessing them.`);\n }\n } else if (arguments.length === 1) {\n value = arguments[0];\n } else {\n throw new Error(`Ref called with too many arguments. Expected 0 or 1 arguments.`);\n }\n\n return value;\n };\n}\n","import { when, m } from \"../markup.js\";\n\n/**\n * Props passed to the crash view when a crash occurs.\n */\nexport type CrashViewProps = {\n /**\n * JavaScript Error object.\n */\n error: Error;\n\n /**\n * A string to identify the logger that reported this error.\n */\n loggerName: string;\n\n /**\n * Unique identifier to pinpoint the specific view that reported the crash.\n */\n tag?: string;\n\n /**\n * Label for the tag.\n */\n tagName?: string;\n};\n\nexport function DefaultCrashView(props: CrashViewProps) {\n return m(\"div\", {\n style: {\n backgroundColor: \"#880000\",\n color: \"#fff\",\n padding: \"2rem\",\n position: \"fixed\",\n inset: 0,\n fontSize: \"20px\",\n },\n children: [\n m(\"h1\", { style: { marginBottom: \"0.5rem\" }, children: \"The app has crashed\" }),\n m(\"p\", {\n style: { marginBottom: \"0.25rem\" },\n children: [\n m(\"span\", {\n style: { fontFamily: \"monospace\" },\n children: props.loggerName,\n }),\n when(\n props.tag,\n m(\"span\", {\n style: { fontFamily: \"monospace\", opacity: 0.5 },\n children: ` [${props.tagName ? `${props.tagName}: ` : \"\"}${props.tag}]`,\n }),\n ),\n \" says:\",\n ],\n }),\n m(\"blockquote\", {\n style: {\n backgroundColor: \"#991111\",\n padding: \"0.25em\",\n borderRadius: \"6px\",\n fontFamily: \"monospace\",\n marginBottom: \"1rem\",\n },\n children: [\n m(\"span\", {\n style: {\n display: \"inline-block\",\n backgroundColor: \"red\",\n padding: \"0.1em 0.4em\",\n marginRight: \"0.5em\",\n borderRadius: \"4px\",\n fontSize: \"0.9em\",\n fontWeight: \"bold\",\n },\n children: props.error.name,\n }),\n props.error.message,\n ],\n }),\n m(\"p\", { children: \"Please see the browser console for details.\" }),\n ],\n });\n}\n","import { MOUNT, Router, UNMOUNT } from \"../router/router\";\nimport { assertInstanceOf } from \"../typeChecking\";\nimport type { View } from \"../types\";\nimport { Context, LifecycleEvent } from \"./context\";\nimport { type LoggerCrashProps, onLoggerCrash } from \"./logger\";\nimport { type MarkupNode } from \"./markup\";\nimport { ViewInstance } from \"./nodes/view\";\nimport { DefaultCrashView } from \"./views/default-crash-view\";\n\nlet isMounted = false;\n\nexport type UnmountFn = () => Promise<void>;\nexport interface MountOptions {\n crashView?: View<LoggerCrashProps>;\n\n /**\n * An existing Context to use as the root, otherwise a new one will be created.\n * Use this to provide top-level stores and state to the whole app.\n */\n context?: Context;\n}\n\nexport async function mount(view: View<{}>, domNode: Element, options?: MountOptions): Promise<UnmountFn>;\nexport async function mount(router: Router, domNode: Element, options?: MountOptions): Promise<UnmountFn>;\n\nexport async function mount(view: any, rootElement: Element, options?: MountOptions): Promise<UnmountFn> {\n assertInstanceOf(Element, rootElement, \"Expected an element or a selector string. Got type: %t, value: %v\");\n\n if (isMounted) {\n throw new Error(`A Dolla app is already mounted.`);\n }\n\n let rootView: MarkupNode;\n let router: Router | undefined;\n let crashView = options?.crashView ?? DefaultCrashView;\n\n const rootContext = options?.context ?? new Context(\"App\");\n\n onLoggerCrash((props) => {\n if (isMounted) {\n unmount();\n }\n\n // Mount the crash page\n new ViewInstance(rootContext, crashView, props).mount(rootElement);\n });\n\n Context.emit(rootContext, LifecycleEvent.WILL_MOUNT);\n\n if (view instanceof Router) {\n // Store router reference so we can unmount it with the app.\n router = view;\n rootView = await router[MOUNT](rootElement, rootContext);\n } else {\n rootView = new ViewInstance(rootContext, view, {});\n }\n rootView.mount(rootElement);\n isMounted = true;\n\n Context.emit(rootContext, LifecycleEvent.DID_MOUNT);\n\n async function unmount() {\n if (!isMounted) return;\n\n Context.emit(rootContext, LifecycleEvent.WILL_UNMOUNT);\n\n rootView.unmount(false);\n if (router) {\n await router[UNMOUNT]();\n }\n isMounted = false;\n\n Context.emit(rootContext, LifecycleEvent.DID_UNMOUNT);\n }\n\n return unmount;\n}\n"],"names":["EMPTY_REF","ref","value","DefaultCrashView","props","m","when","isMounted","mount","view","rootElement","options","assertInstanceOf","rootView","router","crashView","rootContext","Context","onLoggerCrash","unmount","ViewInstance","LifecycleEvent","Router","MOUNT","UNMOUNT"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/core/views/default-crash-view.ts","../src/core/mount.ts"],"sourcesContent":["import { when, m } from \"../markup.js\";\n\n/**\n * Props passed to the crash view when a crash occurs.\n */\nexport interface CrashViewProps {\n /**\n * JavaScript Error object.\n */\n error: Error;\n\n /**\n * A string to identify the logger that reported this error.\n */\n loggerName: string;\n\n /**\n * Unique identifier to pinpoint the specific view that reported the crash.\n */\n tag?: string;\n\n /**\n * Label for the tag.\n */\n tagName?: string;\n}\n\n/**\n * The crash view displayed unless you specify your own.\n */\nexport function DefaultCrashView(props: CrashViewProps) {\n return m(\"div\", {\n style: {\n backgroundColor: \"#880000\",\n color: \"#fff\",\n padding: \"2rem\",\n position: \"fixed\",\n inset: 0,\n fontSize: \"20px\",\n },\n children: [\n m(\"h1\", { style: { marginBottom: \"0.5rem\" }, children: \"The app has crashed\" }),\n m(\"p\", {\n style: { marginBottom: \"0.25rem\" },\n children: [\n m(\"span\", {\n style: { fontFamily: \"monospace\" },\n children: props.loggerName,\n }),\n when(\n props.tag,\n m(\"span\", {\n style: { fontFamily: \"monospace\", opacity: 0.5 },\n children: ` [${props.tagName ? `${props.tagName}: ` : \"\"}${props.tag}]`,\n }),\n ),\n \" says:\",\n ],\n }),\n m(\"blockquote\", {\n style: {\n backgroundColor: \"#991111\",\n padding: \"0.25em\",\n borderRadius: \"6px\",\n fontFamily: \"monospace\",\n marginBottom: \"1rem\",\n },\n children: [\n m(\"span\", {\n style: {\n display: \"inline-block\",\n backgroundColor: \"red\",\n padding: \"0.1em 0.4em\",\n marginRight: \"0.5em\",\n borderRadius: \"4px\",\n fontSize: \"0.9em\",\n fontWeight: \"bold\",\n },\n children: props.error.name,\n }),\n props.error.message,\n ],\n }),\n m(\"p\", { children: \"Please see the browser console for details.\" }),\n ],\n });\n}\n","import { MOUNT, Router, UNMOUNT } from \"../router/router\";\nimport { assertInstanceOf } from \"../typeChecking\";\nimport type { View } from \"../types\";\nimport { Context, LifecycleEvent } from \"./context\";\nimport { type LoggerCrashProps, onLoggerCrash } from \"./logger\";\nimport { type MarkupNode } from \"./markup\";\nimport { ViewNode } from \"./nodes/view\";\nimport { DefaultCrashView } from \"./views/default-crash-view\";\n\nlet isMounted = false;\n\nexport type UnmountFn = () => Promise<void>;\nexport interface MountOptions {\n crashView?: View<LoggerCrashProps>;\n\n /**\n * An existing Context to use as the root, otherwise a new one will be created.\n * Use this to provide top-level stores and state to the whole app.\n */\n context?: Context;\n}\n\nexport async function mount(view: View<{}>, domNode: Element, options?: MountOptions): Promise<UnmountFn>;\nexport async function mount(router: Router, domNode: Element, options?: MountOptions): Promise<UnmountFn>;\n\nexport async function mount(view: any, rootElement: Element, options?: MountOptions): Promise<UnmountFn> {\n assertInstanceOf(Element, rootElement, \"Expected an element or a selector string. Got type: %t, value: %v\");\n\n if (isMounted) {\n throw new Error(`A Dolla app is already mounted.`);\n }\n\n let rootView: MarkupNode;\n let router: Router | undefined;\n let crashView = options?.crashView ?? DefaultCrashView;\n\n const rootContext = options?.context ?? new Context(\"App\");\n\n onLoggerCrash((props) => {\n if (isMounted) {\n unmount();\n }\n\n // Mount the crash page\n new ViewNode(rootContext, crashView, props).mount(rootElement);\n });\n\n Context.emit(rootContext, LifecycleEvent.WILL_MOUNT);\n\n if (view instanceof Router) {\n // Store router reference so we can unmount it with the app.\n router = view;\n rootView = await router[MOUNT](rootElement, rootContext);\n } else {\n rootView = new ViewNode(rootContext, view, {});\n }\n rootView.mount(rootElement);\n isMounted = true;\n\n Context.emit(rootContext, LifecycleEvent.DID_MOUNT);\n\n async function unmount() {\n if (!isMounted) return;\n\n Context.emit(rootContext, LifecycleEvent.WILL_UNMOUNT);\n\n rootView.unmount(false);\n if (router) {\n await router[UNMOUNT]();\n }\n isMounted = false;\n\n Context.emit(rootContext, LifecycleEvent.DID_UNMOUNT);\n }\n\n return unmount;\n}\n"],"names":["DefaultCrashView","props","m","when","isMounted","mount","view","rootElement","options","assertInstanceOf","rootView","router","crashView","rootContext","Context","onLoggerCrash","unmount","ViewNode","LifecycleEvent","Router","MOUNT","UNMOUNT"],"mappings":";;;;;;;;AA8BO,SAASA,EAAiBC,GAAuB;AACtD,SAAOC,EAAE,OAAO;AAAA,IACd,OAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,SAAS;AAAA,MACT,UAAU;AAAA,MACV,OAAO;AAAA,MACP,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACRA,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,SAAS,GAAG,UAAU,uBAAuB;AAAA,MAC9EA,EAAE,KAAK;AAAA,QACL,OAAO,EAAE,cAAc,UAAU;AAAA,QACjC,UAAU;AAAA,UACRA,EAAE,QAAQ;AAAA,YACR,OAAO,EAAE,YAAY,YAAY;AAAA,YACjC,UAAUD,EAAM;AAAA,UAAA,CACjB;AAAA,UACDE;AAAA,YACEF,EAAM;AAAA,YACNC,EAAE,QAAQ;AAAA,cACR,OAAO,EAAE,YAAY,aAAa,SAAS,IAAI;AAAA,cAC/C,UAAU,KAAKD,EAAM,UAAU,GAAGA,EAAM,OAAO,OAAO,EAAE,GAAGA,EAAM,GAAG;AAAA,YACrE,CAAA;AAAA,UACH;AAAA,UACA;AAAA,QAAA;AAAA,MACF,CACD;AAAA,MACDC,EAAE,cAAc;AAAA,QACd,OAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,SAAS;AAAA,UACT,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,cAAc;AAAA,QAChB;AAAA,QACA,UAAU;AAAA,UACRA,EAAE,QAAQ;AAAA,YACR,OAAO;AAAA,cACL,SAAS;AAAA,cACT,iBAAiB;AAAA,cACjB,SAAS;AAAA,cACT,aAAa;AAAA,cACb,cAAc;AAAA,cACd,UAAU;AAAA,cACV,YAAY;AAAA,YACd;AAAA,YACA,UAAUD,EAAM,MAAM;AAAA,UAAA,CACvB;AAAA,UACDA,EAAM,MAAM;AAAA,QAAA;AAAA,MACd,CACD;AAAA,MACDC,EAAE,KAAK,EAAE,UAAU,8CAA+C,CAAA;AAAA,IAAA;AAAA,EACpE,CACD;AACH;AC7EA,IAAIE,IAAY;AAgBM,eAAAC,EAAMC,GAAWC,GAAsBC,GAA4C;AAGvG,MAFiBC,EAAA,SAASF,GAAa,mEAAmE,GAEtGH;AACI,UAAA,IAAI,MAAM,iCAAiC;AAG/C,MAAAM,GACAC,GACAC,KAAYJ,KAAA,gBAAAA,EAAS,cAAaR;AAEtC,QAAMa,KAAcL,KAAA,gBAAAA,EAAS,YAAW,IAAIM,EAAQ,KAAK;AAEzD,EAAAC,EAAc,CAACd,MAAU;AACvB,IAAIG,KACMY,EAAA,GAIV,IAAIC,EAASJ,GAAaD,GAAWX,CAAK,EAAE,MAAMM,CAAW;AAAA,EAAA,CAC9D,GAEOO,EAAA,KAAKD,GAAaK,EAAe,UAAU,GAE/CZ,aAAgBa,KAETR,IAAAL,GACTI,IAAW,MAAMC,EAAOS,CAAK,EAAEb,GAAaM,CAAW,KAEvDH,IAAW,IAAIO,EAASJ,GAAaP,GAAM,CAAA,CAAE,GAE/CI,EAAS,MAAMH,CAAW,GACdH,IAAA,IAEJU,EAAA,KAAKD,GAAaK,EAAe,SAAS;AAElD,iBAAeF,IAAU;AACvB,IAAKZ,MAEGU,EAAA,KAAKD,GAAaK,EAAe,YAAY,GAErDR,EAAS,QAAQ,EAAK,GAClBC,KACI,MAAAA,EAAOU,CAAO,EAAE,GAEZjB,IAAA,IAEJU,EAAA,KAAKD,GAAaK,EAAe,WAAW;AAAA,EAAA;AAG/C,SAAAF;AACT;"}
@@ -1,4 +1,4 @@
1
- import { b as n } from "./markup-BGlfQYQk.js";
1
+ import { d as n } from "./markup-CX27GJ1M.js";
2
2
  import { F as m } from "./fragment-BahD_BJA.js";
3
3
  function u(e, r, t, o, a, i) {
4
4
  return new n(e, t != null ? { ...r, key: t } : r);
@@ -1,4 +1,4 @@
1
- import { b as u } from "./markup-BGlfQYQk.js";
1
+ import { d as u } from "./markup-CX27GJ1M.js";
2
2
  import { F as m } from "./fragment-BahD_BJA.js";
3
3
  function o(t, n, r) {
4
4
  return new u(t, r != null ? { ...n, key: r } : n);