@reykjavik/webtools 0.1.18 → 0.1.20

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/CHANGELOG.md CHANGED
@@ -4,12 +4,22 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
- ## 0.1.18
7
+ ## 0.1.20
8
+
9
+ _2024-03-11_
10
+
11
+ - fix: Make all pached `Intl.*` methods bound to their instances
12
+
13
+ ## 0.1.18 – 0.1.19
8
14
 
9
15
  _2024-03-11_
10
16
 
11
17
  - `@reykjavik/webtools/fixIcelandicLocale`:
12
18
  - feat: Patch `Intl.PluralRules` and `Intl.ListFormat`
19
+ - fix: Incorrect alphabetization of accented characters as part of a word …
20
+ (not just a single character) This fix corrects the sorting of initial
21
+ letters, but characters inside the string stay mixed in with their
22
+ unaccented base character.
13
23
 
14
24
  ## 0.1.16 – 0.1.17
15
25
 
package/README.md CHANGED
@@ -462,10 +462,11 @@ detection test.)
462
462
 
463
463
  **`Intl.Collator` and `localeCompare`:**
464
464
 
465
- - When the `sensitivty` option is set to `"base"` or `"accent"`, it will
466
- incorrectly treat `ð` and `d` as the same letter, and the acute-accented
467
- characters `á`, `é`, `í`, `ó`, `ú` and `ý` get lumped in with their
468
- non-accented counterparts.
465
+ - It incorrectly treats `ð` and `d` as the same letter (most of the time), and
466
+ the acute-accented characters `á`, `é`, `í`, `ó`, `ú` and `ý` get lumped in
467
+ with their non-accented counterparts (unless the compared).
468
+ We fix this only for the first letter in the string, but not for the rest of
469
+ it.
469
470
 
470
471
  **`Intl.NumberFormat` and `toLocaleString`:**
471
472
 
@@ -1,4 +1,4 @@
1
- var _a;
1
+ var _a, _b;
2
2
  const _Collator = Intl.Collator;
3
3
  const mapLocales = (locales) => {
4
4
  locales = typeof locales === 'string' ? [locales] : locales || [];
@@ -11,6 +11,8 @@ const mapLocales = (locales) => {
11
11
  // or "accent" then `ð` is consiered a variant of `d` and the letters
12
12
  // á, é, í, ó, ú, and ý are not treated as separate letters but simply
13
13
  // variants of a, e, i, o, u, and y.
14
+ // Also when the accented characters are part of a word, they are treated
15
+ // as fully equal to the base letter.
14
16
  return ['da'];
15
17
  }
16
18
  if (_Collator.supportedLocalesOf(loc).length) {
@@ -19,17 +21,30 @@ const mapLocales = (locales) => {
19
21
  }
20
22
  };
21
23
  const combineParts = (parts) => parts.map(({ value }) => value).join('');
22
- // ===========================================================================
23
- // Collator
24
- // ===========================================================================
25
24
  const PatchedCollator = function Collator(locales, options) {
26
- locales = mapLocales(locales) || locales;
27
- const instance = new _Collator(locales, options);
28
- Object.setPrototypeOf(instance, PatchedCollator.prototype);
29
- return instance;
25
+ if (!(this instanceof PatchedCollator)) {
26
+ // @ts-expect-error (YOLO! Can't be arsed)
27
+ return new PatchedCollator(locales, options);
28
+ }
29
+ const mappedLocales = mapLocales(locales);
30
+ const parent = _Collator(mappedLocales || locales, options);
31
+ const mapped = !!mappedLocales;
32
+ this.compare = (a, b) => {
33
+ const res1 = parent.compare(a, b);
34
+ if (!mapped) {
35
+ return res1;
36
+ }
37
+ const a0 = a.charAt(0);
38
+ const b0 = b.charAt(0);
39
+ if (/\d/.test(a0 + b0)) {
40
+ return res1;
41
+ }
42
+ const res2 = parent.compare(a0, b0);
43
+ return res2 !== 0 ? res2 : res1;
44
+ };
45
+ this.resolvedOptions = () => parent.resolvedOptions();
30
46
  };
31
- PatchedCollator.prototype = Object.create(_Collator.prototype);
32
- PatchedCollator.prototype.constructor = PatchedCollator;
47
+ PatchedCollator.prototype = { constructor: PatchedCollator };
33
48
  // Static methods (not patched since "is" is not ACTUALLY supported.)
34
49
  PatchedCollator.supportedLocalesOf = _Collator.supportedLocalesOf;
35
50
  PatchedCollator.$original = _Collator;
@@ -44,11 +59,8 @@ _patchedLocaleCompare.$original = _localeCompare;
44
59
  // NumberFormat
45
60
  // ===========================================================================
46
61
  const _NumberFormat = Intl.NumberFormat;
47
- const reformatNumberParts = function (parts) {
48
- if (!this.mapped) {
49
- return parts;
50
- }
51
- const options = this.super.resolvedOptions();
62
+ const reformatNumberParts = function (parent, parts) {
63
+ const options = parent.resolvedOptions();
52
64
  if (options.style === 'currency' && options.currencyDisplay === 'symbol') {
53
65
  const currency = options.currency;
54
66
  if (currency === 'DKK' || currency === 'ISK') {
@@ -67,31 +79,21 @@ const PatchedNumberFormat = function NumberFormat(locales, options) {
67
79
  return new PatchedNumberFormat(locales, options);
68
80
  }
69
81
  const mappedLocales = mapLocales(locales);
70
- this.super = _NumberFormat(mappedLocales || locales, options);
71
- this.mapped = !!mappedLocales;
72
- };
73
- // This is all very hacky, but extending the class *AND* preseving the
74
- // ability to instantiate without `new` is a bit of a pain.
75
- // Eagerly interested in finding a better way to do this.
76
- const numberFormatProto = {
77
- constructor: PatchedNumberFormat,
78
- format(value) {
79
- return combineParts(this.formatToParts(value));
80
- },
81
- formatRange(value1, value2) {
82
- return combineParts(this.formatRangeToParts(value1, value2));
83
- },
84
- formatToParts(value) {
85
- return reformatNumberParts.call(this, this.super.formatToParts(value));
86
- },
87
- formatRangeToParts(value1, value2) {
88
- return reformatNumberParts.call(this, this.super.formatRangeToParts(value1, value2));
89
- },
90
- resolvedOptions() {
91
- return this.super.resolvedOptions();
92
- },
82
+ const parent = _NumberFormat(mappedLocales || locales, options);
83
+ const mapped = !!mappedLocales;
84
+ this.format = (value) => combineParts(this.formatToParts(value));
85
+ this.formatRange = (value1, value2) => combineParts(this.formatRangeToParts(value1, value2));
86
+ this.formatToParts = (value) => {
87
+ const parts = parent.formatToParts(value);
88
+ return mapped ? reformatNumberParts(parent, parts) : parts;
89
+ };
90
+ this.formatRangeToParts = (value1, value2) => {
91
+ const parts = parent.formatRangeToParts(value1, value2);
92
+ return mapped ? reformatNumberParts(parent, parts) : parts;
93
+ };
94
+ this.resolvedOptions = () => parent.resolvedOptions();
93
95
  };
94
- PatchedNumberFormat.prototype = numberFormatProto;
96
+ PatchedNumberFormat.prototype = { constructor: PatchedNumberFormat };
95
97
  // Static methods (not patched since "is" is not ACTUALLY supported.)
96
98
  PatchedNumberFormat.supportedLocalesOf = _NumberFormat.supportedLocalesOf;
97
99
  PatchedNumberFormat.$original = _NumberFormat;
@@ -177,11 +179,8 @@ const partMappers = {
177
179
  }
178
180
  },
179
181
  };
180
- const reformatDateTimeParts = function (parts) {
181
- if (!this.mapped) {
182
- return parts;
183
- }
184
- const options = this.super.resolvedOptions();
182
+ const reformatDateTimeParts = function (parent, parts) {
183
+ const options = parent.resolvedOptions();
185
184
  parts.forEach((part, idx) => {
186
185
  var _a;
187
186
  const mapper = partMappers[part.type];
@@ -206,31 +205,21 @@ const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
206
205
  hourCycle: 'h11',
207
206
  };
208
207
  }
209
- this.super = _DateTimeFormat(mappedLocales || locales, options);
210
- this.mapped = !!mappedLocales;
211
- };
212
- // This is all very hacky, but extending the class *AND* preseving the
213
- // ability to instantiate without `new` is a bit of a pain.
214
- // Eagerly interested in finding a better way to do this.
215
- const dateTimeFormatProto = {
216
- constructor: PatchedDateTimeFormat,
217
- format(value) {
218
- return combineParts(this.formatToParts(value));
219
- },
220
- formatRange(value1, value2) {
221
- return combineParts(this.formatRangeToParts(value1, value2));
222
- },
223
- formatToParts(value) {
224
- return reformatDateTimeParts.call(this, this.super.formatToParts(value));
225
- },
226
- formatRangeToParts(value1, value2) {
227
- return reformatDateTimeParts.call(this, this.super.formatRangeToParts(value1, value2));
228
- },
229
- resolvedOptions() {
230
- return this.super.resolvedOptions();
231
- },
208
+ const parent = _DateTimeFormat(mappedLocales || locales, options);
209
+ const mapped = !!mappedLocales;
210
+ this.format = (value) => combineParts(this.formatToParts(value));
211
+ this.formatRange = (value1, value2) => combineParts(this.formatRangeToParts(value1, value2));
212
+ this.formatToParts = (value) => {
213
+ const parts = parent.formatToParts(value);
214
+ return mapped ? reformatDateTimeParts(parent, parts) : parts;
215
+ };
216
+ this.formatRangeToParts = (value1, value2) => {
217
+ const parts = parent.formatRangeToParts(value1, value2);
218
+ return mapped ? reformatDateTimeParts(parent, parts) : parts;
219
+ };
220
+ this.resolvedOptions = () => parent.resolvedOptions();
232
221
  };
233
- PatchedDateTimeFormat.prototype = dateTimeFormatProto;
222
+ PatchedDateTimeFormat.prototype = { constructor: PatchedDateTimeFormat };
234
223
  // Static methods (not patched since "is" is not ACTUALLY supported.)
235
224
  PatchedDateTimeFormat.supportedLocalesOf = _DateTimeFormat.supportedLocalesOf;
236
225
  PatchedDateTimeFormat.$original = _DateTimeFormat;
@@ -257,6 +246,8 @@ if (_PluralRules) {
257
246
  super(mappedLocales || locales, options);
258
247
  this.mapped = !!mappedLocales;
259
248
  this.ord = (options === null || options === void 0 ? void 0 : options.type) === 'ordinal';
249
+ this.select = this.select.bind(this);
250
+ this.selectRange = this.selectRange.bind(this);
260
251
  }
261
252
  select(n) {
262
253
  if (this.mapped) {
@@ -285,28 +276,31 @@ const _ListFormat = Intl.ListFormat;
285
276
  let PatchedListFormat;
286
277
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
287
278
  if (_ListFormat) {
288
- PatchedListFormat = class ListFormat extends _ListFormat {
289
- constructor(locales, options) {
290
- const mappedLocales = mapLocales(locales);
291
- super(mappedLocales || locales, options);
292
- this.$original = _ListFormat;
293
- this.mapped = !!mappedLocales;
294
- }
295
- format(list) {
296
- return this.mapped ? combineParts(this.formatToParts(list)) : super.format(list);
297
- }
298
- formatToParts(list) {
299
- const parts = super.formatToParts(list);
300
- if (this.mapped) {
301
- for (const item of parts) {
302
- const { value } = item;
303
- if (item.type === 'literal' && (value === ', el.' || value === ', eller')) {
304
- item.value = ', eða';
279
+ PatchedListFormat = (_b = class ListFormat extends _ListFormat {
280
+ constructor(locales, options) {
281
+ const mappedLocales = mapLocales(locales);
282
+ super(mappedLocales || locales, options);
283
+ this.mapped = !!mappedLocales;
284
+ this.format = this.format.bind(this);
285
+ this.formatToParts = this.formatToParts.bind(this);
286
+ }
287
+ format(list) {
288
+ return this.mapped ? combineParts(this.formatToParts(list)) : super.format(list);
289
+ }
290
+ formatToParts(list) {
291
+ const parts = super.formatToParts(list);
292
+ if (this.mapped) {
293
+ for (const item of parts) {
294
+ const { value } = item;
295
+ if (item.type === 'literal' && (value === ' el. ' || value === ' eller ')) {
296
+ item.value = ' eða ';
297
+ }
305
298
  }
306
299
  }
300
+ return parts;
307
301
  }
308
- return parts;
309
- }
310
- };
302
+ },
303
+ _b.$original = _ListFormat,
304
+ _b);
311
305
  }
312
306
  export const _PatchedListFormat = PatchedListFormat;
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- var _a;
2
+ var _a, _b;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports._PatchedListFormat = exports._PatchedPluralRules = exports._patchedToLocaleDateString = exports._PatchedDateTimeFormat = exports._patchedToLocaleString = exports._PatchedNumberFormat = exports._patchedLocaleCompare = exports._PatchedCollator = void 0;
5
5
  const _Collator = Intl.Collator;
@@ -14,6 +14,8 @@ const mapLocales = (locales) => {
14
14
  // or "accent" then `ð` is consiered a variant of `d` and the letters
15
15
  // á, é, í, ó, ú, and ý are not treated as separate letters but simply
16
16
  // variants of a, e, i, o, u, and y.
17
+ // Also when the accented characters are part of a word, they are treated
18
+ // as fully equal to the base letter.
17
19
  return ['da'];
18
20
  }
19
21
  if (_Collator.supportedLocalesOf(loc).length) {
@@ -22,17 +24,30 @@ const mapLocales = (locales) => {
22
24
  }
23
25
  };
24
26
  const combineParts = (parts) => parts.map(({ value }) => value).join('');
25
- // ===========================================================================
26
- // Collator
27
- // ===========================================================================
28
27
  const PatchedCollator = function Collator(locales, options) {
29
- locales = mapLocales(locales) || locales;
30
- const instance = new _Collator(locales, options);
31
- Object.setPrototypeOf(instance, PatchedCollator.prototype);
32
- return instance;
28
+ if (!(this instanceof PatchedCollator)) {
29
+ // @ts-expect-error (YOLO! Can't be arsed)
30
+ return new PatchedCollator(locales, options);
31
+ }
32
+ const mappedLocales = mapLocales(locales);
33
+ const parent = _Collator(mappedLocales || locales, options);
34
+ const mapped = !!mappedLocales;
35
+ this.compare = (a, b) => {
36
+ const res1 = parent.compare(a, b);
37
+ if (!mapped) {
38
+ return res1;
39
+ }
40
+ const a0 = a.charAt(0);
41
+ const b0 = b.charAt(0);
42
+ if (/\d/.test(a0 + b0)) {
43
+ return res1;
44
+ }
45
+ const res2 = parent.compare(a0, b0);
46
+ return res2 !== 0 ? res2 : res1;
47
+ };
48
+ this.resolvedOptions = () => parent.resolvedOptions();
33
49
  };
34
- PatchedCollator.prototype = Object.create(_Collator.prototype);
35
- PatchedCollator.prototype.constructor = PatchedCollator;
50
+ PatchedCollator.prototype = { constructor: PatchedCollator };
36
51
  // Static methods (not patched since "is" is not ACTUALLY supported.)
37
52
  PatchedCollator.supportedLocalesOf = _Collator.supportedLocalesOf;
38
53
  PatchedCollator.$original = _Collator;
@@ -48,11 +63,8 @@ exports._patchedLocaleCompare.$original = _localeCompare;
48
63
  // NumberFormat
49
64
  // ===========================================================================
50
65
  const _NumberFormat = Intl.NumberFormat;
51
- const reformatNumberParts = function (parts) {
52
- if (!this.mapped) {
53
- return parts;
54
- }
55
- const options = this.super.resolvedOptions();
66
+ const reformatNumberParts = function (parent, parts) {
67
+ const options = parent.resolvedOptions();
56
68
  if (options.style === 'currency' && options.currencyDisplay === 'symbol') {
57
69
  const currency = options.currency;
58
70
  if (currency === 'DKK' || currency === 'ISK') {
@@ -71,31 +83,21 @@ const PatchedNumberFormat = function NumberFormat(locales, options) {
71
83
  return new PatchedNumberFormat(locales, options);
72
84
  }
73
85
  const mappedLocales = mapLocales(locales);
74
- this.super = _NumberFormat(mappedLocales || locales, options);
75
- this.mapped = !!mappedLocales;
76
- };
77
- // This is all very hacky, but extending the class *AND* preseving the
78
- // ability to instantiate without `new` is a bit of a pain.
79
- // Eagerly interested in finding a better way to do this.
80
- const numberFormatProto = {
81
- constructor: PatchedNumberFormat,
82
- format(value) {
83
- return combineParts(this.formatToParts(value));
84
- },
85
- formatRange(value1, value2) {
86
- return combineParts(this.formatRangeToParts(value1, value2));
87
- },
88
- formatToParts(value) {
89
- return reformatNumberParts.call(this, this.super.formatToParts(value));
90
- },
91
- formatRangeToParts(value1, value2) {
92
- return reformatNumberParts.call(this, this.super.formatRangeToParts(value1, value2));
93
- },
94
- resolvedOptions() {
95
- return this.super.resolvedOptions();
96
- },
86
+ const parent = _NumberFormat(mappedLocales || locales, options);
87
+ const mapped = !!mappedLocales;
88
+ this.format = (value) => combineParts(this.formatToParts(value));
89
+ this.formatRange = (value1, value2) => combineParts(this.formatRangeToParts(value1, value2));
90
+ this.formatToParts = (value) => {
91
+ const parts = parent.formatToParts(value);
92
+ return mapped ? reformatNumberParts(parent, parts) : parts;
93
+ };
94
+ this.formatRangeToParts = (value1, value2) => {
95
+ const parts = parent.formatRangeToParts(value1, value2);
96
+ return mapped ? reformatNumberParts(parent, parts) : parts;
97
+ };
98
+ this.resolvedOptions = () => parent.resolvedOptions();
97
99
  };
98
- PatchedNumberFormat.prototype = numberFormatProto;
100
+ PatchedNumberFormat.prototype = { constructor: PatchedNumberFormat };
99
101
  // Static methods (not patched since "is" is not ACTUALLY supported.)
100
102
  PatchedNumberFormat.supportedLocalesOf = _NumberFormat.supportedLocalesOf;
101
103
  PatchedNumberFormat.$original = _NumberFormat;
@@ -182,11 +184,8 @@ const partMappers = {
182
184
  }
183
185
  },
184
186
  };
185
- const reformatDateTimeParts = function (parts) {
186
- if (!this.mapped) {
187
- return parts;
188
- }
189
- const options = this.super.resolvedOptions();
187
+ const reformatDateTimeParts = function (parent, parts) {
188
+ const options = parent.resolvedOptions();
190
189
  parts.forEach((part, idx) => {
191
190
  var _a;
192
191
  const mapper = partMappers[part.type];
@@ -211,31 +210,21 @@ const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
211
210
  hourCycle: 'h11',
212
211
  };
213
212
  }
214
- this.super = _DateTimeFormat(mappedLocales || locales, options);
215
- this.mapped = !!mappedLocales;
216
- };
217
- // This is all very hacky, but extending the class *AND* preseving the
218
- // ability to instantiate without `new` is a bit of a pain.
219
- // Eagerly interested in finding a better way to do this.
220
- const dateTimeFormatProto = {
221
- constructor: PatchedDateTimeFormat,
222
- format(value) {
223
- return combineParts(this.formatToParts(value));
224
- },
225
- formatRange(value1, value2) {
226
- return combineParts(this.formatRangeToParts(value1, value2));
227
- },
228
- formatToParts(value) {
229
- return reformatDateTimeParts.call(this, this.super.formatToParts(value));
230
- },
231
- formatRangeToParts(value1, value2) {
232
- return reformatDateTimeParts.call(this, this.super.formatRangeToParts(value1, value2));
233
- },
234
- resolvedOptions() {
235
- return this.super.resolvedOptions();
236
- },
213
+ const parent = _DateTimeFormat(mappedLocales || locales, options);
214
+ const mapped = !!mappedLocales;
215
+ this.format = (value) => combineParts(this.formatToParts(value));
216
+ this.formatRange = (value1, value2) => combineParts(this.formatRangeToParts(value1, value2));
217
+ this.formatToParts = (value) => {
218
+ const parts = parent.formatToParts(value);
219
+ return mapped ? reformatDateTimeParts(parent, parts) : parts;
220
+ };
221
+ this.formatRangeToParts = (value1, value2) => {
222
+ const parts = parent.formatRangeToParts(value1, value2);
223
+ return mapped ? reformatDateTimeParts(parent, parts) : parts;
224
+ };
225
+ this.resolvedOptions = () => parent.resolvedOptions();
237
226
  };
238
- PatchedDateTimeFormat.prototype = dateTimeFormatProto;
227
+ PatchedDateTimeFormat.prototype = { constructor: PatchedDateTimeFormat };
239
228
  // Static methods (not patched since "is" is not ACTUALLY supported.)
240
229
  PatchedDateTimeFormat.supportedLocalesOf = _DateTimeFormat.supportedLocalesOf;
241
230
  PatchedDateTimeFormat.$original = _DateTimeFormat;
@@ -263,6 +252,8 @@ if (_PluralRules) {
263
252
  super(mappedLocales || locales, options);
264
253
  this.mapped = !!mappedLocales;
265
254
  this.ord = (options === null || options === void 0 ? void 0 : options.type) === 'ordinal';
255
+ this.select = this.select.bind(this);
256
+ this.selectRange = this.selectRange.bind(this);
266
257
  }
267
258
  select(n) {
268
259
  if (this.mapped) {
@@ -291,28 +282,31 @@ const _ListFormat = Intl.ListFormat;
291
282
  let PatchedListFormat;
292
283
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
293
284
  if (_ListFormat) {
294
- PatchedListFormat = class ListFormat extends _ListFormat {
295
- constructor(locales, options) {
296
- const mappedLocales = mapLocales(locales);
297
- super(mappedLocales || locales, options);
298
- this.$original = _ListFormat;
299
- this.mapped = !!mappedLocales;
300
- }
301
- format(list) {
302
- return this.mapped ? combineParts(this.formatToParts(list)) : super.format(list);
303
- }
304
- formatToParts(list) {
305
- const parts = super.formatToParts(list);
306
- if (this.mapped) {
307
- for (const item of parts) {
308
- const { value } = item;
309
- if (item.type === 'literal' && (value === ', el.' || value === ', eller')) {
310
- item.value = ', eða';
285
+ PatchedListFormat = (_b = class ListFormat extends _ListFormat {
286
+ constructor(locales, options) {
287
+ const mappedLocales = mapLocales(locales);
288
+ super(mappedLocales || locales, options);
289
+ this.mapped = !!mappedLocales;
290
+ this.format = this.format.bind(this);
291
+ this.formatToParts = this.formatToParts.bind(this);
292
+ }
293
+ format(list) {
294
+ return this.mapped ? combineParts(this.formatToParts(list)) : super.format(list);
295
+ }
296
+ formatToParts(list) {
297
+ const parts = super.formatToParts(list);
298
+ if (this.mapped) {
299
+ for (const item of parts) {
300
+ const { value } = item;
301
+ if (item.type === 'literal' && (value === ' el. ' || value === ' eller ')) {
302
+ item.value = ' eða ';
303
+ }
311
304
  }
312
305
  }
306
+ return parts;
313
307
  }
314
- return parts;
315
- }
316
- };
308
+ },
309
+ _b.$original = _ListFormat,
310
+ _b);
317
311
  }
318
312
  exports._PatchedListFormat = PatchedListFormat;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/webtools",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "Misc. JS/TS helpers used by Reykjavík City's web dev teams.",
5
5
  "main": "index.js",
6
6
  "repository": "ssh://git@github.com:reykjavikcity/webtools.git",