@fable-org/fable-library-js 2.11.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- last_commit_released: ce886964cb4521dc4c01c8da089b97c9d9742814
2
+ last_commit_released: cac5ddd9a99205e0bb5272f1ffd8f0cca937824c
3
3
  updaters:
4
4
  - package.json:
5
5
  file: package.json
@@ -15,6 +15,15 @@ All notable changes to this project will be documented in this file.
15
15
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
16
16
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
17
17
 
18
+ ## 2.12.0 - 2026-10-29
19
+
20
+ ### 🚀 Features
21
+
22
+ * *(js/ts)* Support the "R" numeric format and choose "G" notation like .NET (#5212) ([24d9c41a](https://github.com/fable-compiler/Fable/commit/24d9c41ad69993f0c4e843558133bcd787a960d4))
23
+ * *(js/ts/python/dart/beam)* Support ArgumentException.ParamName (#5203) ([8ed77788](https://github.com/fable-compiler/Fable/commit/8ed7778893bab48c7dd41d7322d26d2bcaba6d99))
24
+
25
+ <strong><small>[View changes on Github](https://github.com/fable-compiler/Fable/compare/ce886964cb4521dc4c01c8da089b97c9d9742814..cac5ddd9a99205e0bb5272f1ffd8f0cca937824c)</small></strong>
26
+
18
27
  ## 2.11.0 - 2026-10-29
19
28
 
20
29
  ### 🚀 Features
package/String.js CHANGED
@@ -344,6 +344,53 @@ function splitIntAndDecimalPart(value) {
344
344
  function thousandSeparate(value) {
345
345
  return value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
346
346
  }
347
+ function splitExponential(s) {
348
+ const eIdx = s.indexOf("e");
349
+ return [s.slice(0, eIdx).replace(".", ""), Number(s.slice(eIdx + 1))];
350
+ }
351
+ function shortestSingle(x) {
352
+ // 100 digits tell where a float32 lies between two candidates, and 9 digits always round-trip
353
+ const [digits, exponent] = splitExponential(x.toExponential(99));
354
+ for (let p = 1;; p++) {
355
+ const down = Number(digits.slice(0, p));
356
+ const rest = digits.slice(p);
357
+ const half = "5".padEnd(rest.length, "0");
358
+ const roundUp = rest > half || rest === half && down % 2 === 1;
359
+ for (const n of roundUp ? [down + 1, down] : [down, down + 1]) {
360
+ if (Math.fround(Number(n + "e" + (exponent - p + 1))) === x) {
361
+ return [String(n), exponent + String(n).length - p];
362
+ }
363
+ }
364
+ }
365
+ }
366
+ // .NET "G" and "R": fixed notation unless the exponent is below -4 or reaches the precision (17 for the shortest digits, 9 for a float32)
367
+ function formatGeneral(x, format, precision, isSingle = false) {
368
+ if (!Number.isFinite(x)) {
369
+ return String(x);
370
+ }
371
+ const sign = x < 0 || Object.is(x, -0) ? "-" : "";
372
+ x = Math.abs(x);
373
+ // R ignores the precision and G0 is G
374
+ const p = format === "r" || format === "R" ? 0 : precision ?? 0;
375
+ const [significand, exponent] = p > 0 ? splitExponential(x.toExponential(p - 1)) : isSingle ? shortestSingle(x) : splitExponential(x.toExponential());
376
+ const digits = trimEnd(significand, "0") || "0";
377
+ if (exponent < -4 || exponent >= (p > 0 ? p : isSingle ? 9 : 17)) {
378
+ const mantissa = digits.length > 1 ? digits[0] + "." + digits.slice(1) : digits;
379
+ const eChar = format === "G" || format === "R" ? "E" : "e";
380
+ return sign + mantissa + eChar + (exponent < 0 ? "-" : "+") + padLeft(String(Math.abs(exponent)), 2, "0");
381
+ }
382
+ else if (exponent < 0) {
383
+ return sign + "0." + "0".repeat(-exponent - 1) + digits;
384
+ }
385
+ else {
386
+ const decimals = digits.slice(exponent + 1);
387
+ return sign + digits.slice(0, exponent + 1).padEnd(exponent + 1, "0") + (decimals ? "." + decimals : "");
388
+ }
389
+ }
390
+ export function formatSingle(str, x) {
391
+ const m = /^\{0:([GgRr])(\d{0,2})\}$/.exec(str);
392
+ return m != null ? formatGeneral(Math.fround(x), m[1], m[2] ? parseInt(m[2], 10) : null, true) : format(str, x);
393
+ }
347
394
  export function format(str, ...args) {
348
395
  let str2;
349
396
  if (typeof str === "object") {
@@ -423,12 +470,18 @@ export function format(str, ...args) {
423
470
  }
424
471
  break;
425
472
  case "g":
426
- case "G": {
427
- if (precision == null && typeof rep !== "number" && typeof rep !== "bigint") {
473
+ case "G":
474
+ case "r":
475
+ case "R": {
476
+ if (typeof rep === "number") {
477
+ rep = formatGeneral(rep, format, precision);
478
+ break;
479
+ }
480
+ if (precision == null) {
428
481
  rep = String(rep);
429
482
  break;
430
483
  }
431
- rep = precision != null ? toPrecision(rep, precision) : toPrecision(rep);
484
+ rep = toPrecision(rep, precision);
432
485
  // Handle exponential notation: only trim trailing zeros from mantissa, not from exponent.
433
486
  // .NET G format guarantees an exponent of at least 2 digits with an explicit sign (e.g. "E-07").
434
487
  const eIdx = rep.indexOf("e");
@@ -437,7 +490,7 @@ export function format(str, ...args) {
437
490
  const expSign = rep[eIdx + 1]; // toPrecision always emits "+" or "-"
438
491
  const expDigits = rep.slice(eIdx + 2);
439
492
  const paddedExpDigits = expDigits.length < 2 ? "0" + expDigits : expDigits;
440
- const eChar = format === "G" ? "E" : "e";
493
+ const eChar = format === "G" || format === "R" ? "E" : "e";
441
494
  rep = mantissa + eChar + expSign + paddedExpDigits;
442
495
  }
443
496
  else if (rep.indexOf(".") >= 0) {
@@ -469,9 +522,6 @@ export function format(str, ...args) {
469
522
  rep = thousandSeparate(parts.integral) + " %";
470
523
  }
471
524
  break;
472
- case "r":
473
- case "R":
474
- throw new Exception("The round-trip format is not supported by Fable");
475
525
  case "x":
476
526
  case "X":
477
527
  if (!isIntegral(rep)) {
package/System.js CHANGED
@@ -239,16 +239,16 @@ export function ArgumentException_$ctor_Z60A2B367(message, paramName, innerExcep
239
239
  return new ArgumentException(message, paramName, innerException);
240
240
  }
241
241
  export function ArgumentException_$ctor() {
242
- return ArgumentException_$ctor_Z60A2B367(SR_Arg_ArgumentException, "", defaultOf());
242
+ return ArgumentException_$ctor_Z60A2B367(SR_Arg_ArgumentException, defaultOf(), defaultOf());
243
243
  }
244
244
  export function ArgumentException_$ctor_Z721C83C5(message) {
245
- return ArgumentException_$ctor_Z60A2B367(message, "", defaultOf());
245
+ return ArgumentException_$ctor_Z60A2B367(message, defaultOf(), defaultOf());
246
246
  }
247
247
  export function ArgumentException_$ctor_Z384F8060(message, paramName) {
248
248
  return ArgumentException_$ctor_Z60A2B367(message, paramName, defaultOf());
249
249
  }
250
250
  export function ArgumentException_$ctor_68CE3CA2(message, innerException) {
251
- return ArgumentException_$ctor_Z60A2B367(message, "", innerException);
251
+ return ArgumentException_$ctor_Z60A2B367(message, defaultOf(), innerException);
252
252
  }
253
253
  export function ArgumentException__get_ParamName(_) {
254
254
  return _.paramName;
@@ -268,7 +268,7 @@ export function ArgumentNullException_$ctor_Z721C83C5(paramName) {
268
268
  return ArgumentNullException_$ctor_Z384F8060(paramName, SR_ArgumentNull_Generic);
269
269
  }
270
270
  export function ArgumentNullException_$ctor() {
271
- return ArgumentNullException_$ctor_Z721C83C5("");
271
+ return ArgumentNullException_$ctor_Z721C83C5(defaultOf());
272
272
  }
273
273
  export class ArgumentOutOfRangeException extends ArgumentException {
274
274
  constructor(paramName, message) {
@@ -285,5 +285,5 @@ export function ArgumentOutOfRangeException_$ctor_Z721C83C5(paramName) {
285
285
  return ArgumentOutOfRangeException_$ctor_Z384F8060(paramName, SR_Arg_ArgumentOutOfRangeException);
286
286
  }
287
287
  export function ArgumentOutOfRangeException_$ctor() {
288
- return ArgumentOutOfRangeException_$ctor_Z721C83C5("");
288
+ return ArgumentOutOfRangeException_$ctor_Z721C83C5(defaultOf());
289
289
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "private": false,
4
4
  "type": "module",
5
5
  "name": "@fable-org/fable-library-js",
6
- "version": "2.11.0",
6
+ "version": "2.12.0",
7
7
  "description": "Core library used by F# projects compiled with fable.io",
8
8
  "author": "Fable Contributors",
9
9
  "license": "MIT",