@fable-org/fable-library-js 2.0.0 → 2.1.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 +28 -1
- package/Date.js +80 -8
- package/Decimal.js +6 -4
- package/Reflection.js +20 -1
- package/String.js +17 -4
- package/System.Text.js +33 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
last_commit_released: 63bcd3d90f37cb3934edcc59b5f54f49ffab3896
|
|
3
|
+
updaters:
|
|
4
|
+
- package.json:
|
|
5
|
+
file: package.json
|
|
6
|
+
- regex:
|
|
7
|
+
file: ./../Fable.Transforms/Global/Compiler.fs
|
|
8
|
+
pattern: (?<=let JS_LIBRARY_VERSION = ").*(?=")
|
|
9
|
+
---
|
|
10
|
+
|
|
1
11
|
# Changelog
|
|
2
12
|
|
|
3
13
|
All notable changes to this project will be documented in this file.
|
|
@@ -5,7 +15,24 @@ All notable changes to this project will be documented in this file.
|
|
|
5
15
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
16
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
17
|
|
|
8
|
-
##
|
|
18
|
+
## 2.1.0 - 2026-05-28
|
|
19
|
+
|
|
20
|
+
### 🚀 Features
|
|
21
|
+
|
|
22
|
+
* *(js/ts/python)* Add missing standard DateTime format specifiers for JS/TS and Python (#4547) ([90f5eb7](https://github.com/fable-compiler/Fable/commit/90f5eb7f61710bc679f6fdec30ff5d864984ac08))
|
|
23
|
+
* *(js/ts/python)* Add missing StringBuilder.Append overloads for numeric types (#4568) ([b402c30](https://github.com/fable-compiler/Fable/commit/b402c30d04bfca8644314ba4b21894abfff6a713))
|
|
24
|
+
|
|
25
|
+
### 🐞 Bug Fixes
|
|
26
|
+
|
|
27
|
+
* [JS/TS] Fix DatetimeOffset.ToString("s") (#4596) ([3ce6f3f](https://github.com/fable-compiler/Fable/commit/3ce6f3fd88cc7f67e34e2bbc311e722c55ffbf45))
|
|
28
|
+
* *(js/ts)* Fix datetime custom format off by one year (#4558) ([83bdbb5](https://github.com/fable-compiler/Fable/commit/83bdbb5b34e70eae203831a3a442d477d15911e1))
|
|
29
|
+
* *(js/ts)* Hex format specifier uses no padding unless precision is specified (#4603) ([ba9857a](https://github.com/fable-compiler/Fable/commit/ba9857a4dcbf7ab1f936241f6619fda43e039040))
|
|
30
|
+
* *(js/ts)* Fix Decimal.GetBits returning incorrect mantissa after round arithmetic result (#4561) ([345bcd4](https://github.com/fable-compiler/Fable/commit/345bcd4b99417b304c71059966126fad74cf3a97))
|
|
31
|
+
* *(js/ts)* Fix G/g format specifier corrupting exponential notation when trimming trailing zeros (#4587) ([773c098](https://github.com/fable-compiler/Fable/commit/773c098a8f7ac7e8db1d4e490dec1294a80321ee))
|
|
32
|
+
* *(js/ts/python)* Fix FSharpOption not recognized as union type in F# reflection (#4529) ([d78a37d](https://github.com/fable-compiler/Fable/commit/d78a37db9f4c25eb51fac8afcd320b4ea36c60a7))
|
|
33
|
+
* *(ts)* Enforce browser-only compatibility in fable-library-ts tsconfig (#4563) ([10c81c1](https://github.com/fable-compiler/Fable/commit/10c81c1361208eb61f5fa78c6704e6fccc068fb1))
|
|
34
|
+
|
|
35
|
+
<strong><small>[View changes on Github](https://github.com/fable-compiler/Fable/compare/b471dc16fc3b5132af77b5974d1669c9b8220cca..63bcd3d90f37cb3934edcc59b5f54f49ffab3896)</small></strong>
|
|
9
36
|
|
|
10
37
|
## 2.0.0 - 2026-04-21
|
|
11
38
|
|
package/Date.js
CHANGED
|
@@ -254,13 +254,13 @@ function dateToStringWithCustomFormat(date, format, utc) {
|
|
|
254
254
|
cursorPos += tokenLength;
|
|
255
255
|
switch (tokenLength) {
|
|
256
256
|
case 1:
|
|
257
|
-
result += localizedDate
|
|
257
|
+
result += year(localizedDate) % 100;
|
|
258
258
|
break;
|
|
259
259
|
case 2:
|
|
260
|
-
result += padWithZeros(localizedDate
|
|
260
|
+
result += padWithZeros(year(localizedDate) % 100, 2);
|
|
261
261
|
break;
|
|
262
262
|
default:
|
|
263
|
-
result += padWithZeros(localizedDate
|
|
263
|
+
result += padWithZeros(year(localizedDate), tokenLength);
|
|
264
264
|
break;
|
|
265
265
|
}
|
|
266
266
|
break;
|
|
@@ -384,10 +384,32 @@ function dateToStringWithOffset(date, format) {
|
|
|
384
384
|
switch (format) {
|
|
385
385
|
case "D": return dateToString_D(d);
|
|
386
386
|
case "d": return dateToString_d(d);
|
|
387
|
-
case "
|
|
388
|
-
case "
|
|
387
|
+
case "F": return dateToString_D(d) + " " + dateToString_T(d);
|
|
388
|
+
case "f": return dateToString_D(d) + " " + dateToString_t(d);
|
|
389
|
+
case "G": return dateToString_d(d) + " " + dateToString_T(d);
|
|
390
|
+
case "g": return dateToString_d(d) + " " + dateToString_t(d);
|
|
391
|
+
case "M":
|
|
392
|
+
case "m": return dateToString_M(d);
|
|
389
393
|
case "O":
|
|
390
394
|
case "o": return dateToISOStringWithOffset(d, (date.offset ?? 0));
|
|
395
|
+
case "R":
|
|
396
|
+
case "r": {
|
|
397
|
+
const utcDate = DateTime(date.getTime(), DateTimeKind.Utc);
|
|
398
|
+
return dateToString_R(utcDate);
|
|
399
|
+
}
|
|
400
|
+
case "s": return dateToString_s(toUniversalTime(d));
|
|
401
|
+
case "T": return dateToString_T(toUniversalTime(d));
|
|
402
|
+
case "t": return dateToString_t(toUniversalTime(d));
|
|
403
|
+
case "u": {
|
|
404
|
+
const utcDate = DateTime(date.getTime(), DateTimeKind.Utc);
|
|
405
|
+
return dateToString_u(utcDate);
|
|
406
|
+
}
|
|
407
|
+
case "U": {
|
|
408
|
+
const utcDate = DateTime(date.getTime(), DateTimeKind.Utc);
|
|
409
|
+
return dateToString_D(utcDate) + " " + dateToString_T(utcDate);
|
|
410
|
+
}
|
|
411
|
+
case "Y":
|
|
412
|
+
case "y": return dateToString_Y(d);
|
|
391
413
|
default: throw new Exception("Unrecognized Date print format");
|
|
392
414
|
}
|
|
393
415
|
}
|
|
@@ -415,6 +437,44 @@ function dateToString_t(date) {
|
|
|
415
437
|
return padWithZeros(hour(date), 2)
|
|
416
438
|
+ ":" + padWithZeros(minute(date), 2);
|
|
417
439
|
}
|
|
440
|
+
// RFC 1123: "Thu, 01 Jan 2009 00:00:00 GMT" — always UTC
|
|
441
|
+
function dateToString_R(date) {
|
|
442
|
+
const utcDate = toUniversalTime(date);
|
|
443
|
+
return shortDays[dayOfWeek(utcDate)] + ", "
|
|
444
|
+
+ padWithZeros(day(utcDate), 2) + " "
|
|
445
|
+
+ shortMonths[month(utcDate) - 1] + " "
|
|
446
|
+
+ year(utcDate) + " "
|
|
447
|
+
+ padWithZeros(hour(utcDate), 2) + ":"
|
|
448
|
+
+ padWithZeros(minute(utcDate), 2) + ":"
|
|
449
|
+
+ padWithZeros(second(utcDate), 2) + " GMT";
|
|
450
|
+
}
|
|
451
|
+
// Sortable ISO 8601, no timezone: "2009-06-15T13:45:30"
|
|
452
|
+
function dateToString_s(date) {
|
|
453
|
+
return padWithZeros(year(date), 4) + "-"
|
|
454
|
+
+ padWithZeros(month(date), 2) + "-"
|
|
455
|
+
+ padWithZeros(day(date), 2) + "T"
|
|
456
|
+
+ padWithZeros(hour(date), 2) + ":"
|
|
457
|
+
+ padWithZeros(minute(date), 2) + ":"
|
|
458
|
+
+ padWithZeros(second(date), 2);
|
|
459
|
+
}
|
|
460
|
+
// Universal sortable: "2009-06-15 13:45:30Z" — always UTC
|
|
461
|
+
function dateToString_u(date) {
|
|
462
|
+
const utcDate = toUniversalTime(date);
|
|
463
|
+
return padWithZeros(year(utcDate), 4) + "-"
|
|
464
|
+
+ padWithZeros(month(utcDate), 2) + "-"
|
|
465
|
+
+ padWithZeros(day(utcDate), 2) + " "
|
|
466
|
+
+ padWithZeros(hour(utcDate), 2) + ":"
|
|
467
|
+
+ padWithZeros(minute(utcDate), 2) + ":"
|
|
468
|
+
+ padWithZeros(second(utcDate), 2) + "Z";
|
|
469
|
+
}
|
|
470
|
+
// Month/day (InvariantCulture "MMMM dd"): "June 15"
|
|
471
|
+
function dateToString_M(date) {
|
|
472
|
+
return longMonths[month(date) - 1] + " " + padWithZeros(day(date), 2);
|
|
473
|
+
}
|
|
474
|
+
// Year/month (InvariantCulture "yyyy MMMM"): "2009 June"
|
|
475
|
+
function dateToString_Y(date) {
|
|
476
|
+
return year(date) + " " + longMonths[month(date) - 1];
|
|
477
|
+
}
|
|
418
478
|
function dateToStringWithKind(date, format) {
|
|
419
479
|
const utc = date.kind === DateTimeKind.Utc;
|
|
420
480
|
if (typeof format !== "string") {
|
|
@@ -424,11 +484,23 @@ function dateToStringWithKind(date, format) {
|
|
|
424
484
|
switch (format) {
|
|
425
485
|
case "D": return dateToString_D(date);
|
|
426
486
|
case "d": return dateToString_d(date);
|
|
487
|
+
case "F": return dateToString_D(date) + " " + dateToString_T(date);
|
|
488
|
+
case "f": return dateToString_D(date) + " " + dateToString_t(date);
|
|
489
|
+
case "G": return dateToString_d(date) + " " + dateToString_T(date);
|
|
490
|
+
case "g": return dateToString_d(date) + " " + dateToString_t(date);
|
|
491
|
+
case "M":
|
|
492
|
+
case "m": return dateToString_M(date);
|
|
493
|
+
case "O":
|
|
494
|
+
case "o": return dateToISOString(date, utc);
|
|
495
|
+
case "R":
|
|
496
|
+
case "r": return dateToString_R(date);
|
|
497
|
+
case "s": return dateToString_s(date);
|
|
427
498
|
case "T": return dateToString_T(date);
|
|
428
499
|
case "t": return dateToString_t(date);
|
|
429
|
-
case "
|
|
430
|
-
case "
|
|
431
|
-
|
|
500
|
+
case "u": return dateToString_u(date);
|
|
501
|
+
case "U": return dateToString_D(toUniversalTime(date)) + " " + dateToString_T(toUniversalTime(date));
|
|
502
|
+
case "Y":
|
|
503
|
+
case "y": return dateToString_Y(date);
|
|
432
504
|
default:
|
|
433
505
|
throw new Exception("Unrecognized Date print format");
|
|
434
506
|
}
|
package/Decimal.js
CHANGED
|
@@ -207,14 +207,16 @@ export function fromParts(low, mid, high, isNegative, scale) {
|
|
|
207
207
|
}
|
|
208
208
|
export function getBits(d) {
|
|
209
209
|
const bitSize = 96;
|
|
210
|
-
const
|
|
210
|
+
const decStr = d.toString();
|
|
211
|
+
const absStr = decStr[0] === "-" ? decStr.slice(1) : decStr;
|
|
212
|
+
const dotPos = absStr.indexOf(".");
|
|
213
|
+
const scale = dotPos < 0 ? 0 : absStr.length - dotPos - 1;
|
|
214
|
+
const mantissaStr = absStr.replace(".", "");
|
|
215
|
+
const decDigits = Uint8Array.from(mantissaStr, ch => ch.charCodeAt(0) - 48);
|
|
211
216
|
const hexDigits = decimalToHex(decDigits, bitSize);
|
|
212
217
|
const low = getInt32Bits(hexDigits, 0);
|
|
213
218
|
const mid = getInt32Bits(hexDigits, 8);
|
|
214
219
|
const high = getInt32Bits(hexDigits, 16);
|
|
215
|
-
const decStr = d.toString();
|
|
216
|
-
const dotPos = decStr.indexOf(".");
|
|
217
|
-
const scale = dotPos < 0 ? 0 : decStr.length - dotPos - 1;
|
|
218
220
|
const signExp = ((scale & 0x7F) << 16) | (d.s < 0 ? 0x80000000 : 0);
|
|
219
221
|
return [low, mid, high, signExp];
|
|
220
222
|
}
|
package/Reflection.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Record, Union } from "./Types.js";
|
|
2
2
|
import { Exception, combineHashCodes, equalArraysWith, stringHash } from "./Util.js";
|
|
3
3
|
import Decimal from "./Decimal.js";
|
|
4
|
+
import { Some, some } from "./Option.js";
|
|
4
5
|
export class CaseInfo {
|
|
5
6
|
constructor(declaringType, tag, name, fields) {
|
|
6
7
|
this.declaringType = declaringType;
|
|
@@ -85,7 +86,11 @@ export function lambda_type(argType, returnType) {
|
|
|
85
86
|
return new TypeInfo("Microsoft.FSharp.Core.FSharpFunc`2", [argType, returnType]);
|
|
86
87
|
}
|
|
87
88
|
export function option_type(generic) {
|
|
88
|
-
|
|
89
|
+
const t = new TypeInfo("Microsoft.FSharp.Core.FSharpOption`1", [generic], undefined, undefined, undefined, () => [
|
|
90
|
+
new CaseInfo(t, 0, "None"),
|
|
91
|
+
new CaseInfo(t, 1, "Some", [["value", generic]])
|
|
92
|
+
]);
|
|
93
|
+
return t;
|
|
89
94
|
}
|
|
90
95
|
export function list_type(generic) {
|
|
91
96
|
return new TypeInfo("Microsoft.FSharp.Collections.FSharpList`1", [generic]);
|
|
@@ -351,6 +356,16 @@ export function isFunction(t) {
|
|
|
351
356
|
// FSharpValue
|
|
352
357
|
export function getUnionFields(v, t) {
|
|
353
358
|
const cases = getUnionCases(t);
|
|
359
|
+
// Special handling for option types (None is undefined, Some is the value or a Some wrapper)
|
|
360
|
+
if (t.fullname === "Microsoft.FSharp.Core.FSharpOption`1") {
|
|
361
|
+
if (v == null) {
|
|
362
|
+
return [cases[0], []]; // None case
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
const innerValue = v instanceof Some ? v.value : v;
|
|
366
|
+
return [cases[1], [innerValue]]; // Some case
|
|
367
|
+
}
|
|
368
|
+
}
|
|
354
369
|
const case_ = cases[v.tag];
|
|
355
370
|
if (case_ == null) {
|
|
356
371
|
throw new Exception(`Cannot find case ${v.name} in union type`);
|
|
@@ -380,6 +395,10 @@ export function makeUnion(uci, values) {
|
|
|
380
395
|
if (values.length !== expectedLength) {
|
|
381
396
|
throw new Exception(`Expected an array of length ${expectedLength} but got ${values.length}`);
|
|
382
397
|
}
|
|
398
|
+
// Special handling for option types
|
|
399
|
+
if (uci.declaringType.fullname === "Microsoft.FSharp.Core.FSharpOption`1") {
|
|
400
|
+
return uci.tag === 0 ? undefined : some(values[0]);
|
|
401
|
+
}
|
|
383
402
|
const construct = uci.declaringType.construct;
|
|
384
403
|
if (construct == null) {
|
|
385
404
|
return {};
|
package/String.js
CHANGED
|
@@ -382,11 +382,24 @@ export function format(str, ...args) {
|
|
|
382
382
|
}
|
|
383
383
|
break;
|
|
384
384
|
case "g":
|
|
385
|
-
case "G":
|
|
385
|
+
case "G": {
|
|
386
386
|
rep = precision != null ? toPrecision(rep, precision) : toPrecision(rep);
|
|
387
|
-
//
|
|
388
|
-
|
|
387
|
+
// Handle exponential notation: only trim trailing zeros from mantissa, not from exponent.
|
|
388
|
+
// .NET G format guarantees an exponent of at least 2 digits with an explicit sign (e.g. "E-07").
|
|
389
|
+
const eIdx = rep.indexOf("e");
|
|
390
|
+
if (eIdx >= 0) {
|
|
391
|
+
const mantissa = trimEnd(trimEnd(rep.slice(0, eIdx), "0"), ".");
|
|
392
|
+
const expSign = rep[eIdx + 1]; // toPrecision always emits "+" or "-"
|
|
393
|
+
const expDigits = rep.slice(eIdx + 2);
|
|
394
|
+
const paddedExpDigits = expDigits.length < 2 ? "0" + expDigits : expDigits;
|
|
395
|
+
const eChar = format === "G" ? "E" : "e";
|
|
396
|
+
rep = mantissa + eChar + expSign + paddedExpDigits;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
rep = trimEnd(trimEnd(rep, "0"), ".");
|
|
400
|
+
}
|
|
389
401
|
break;
|
|
402
|
+
}
|
|
390
403
|
case "n":
|
|
391
404
|
case "N":
|
|
392
405
|
precision = precision != null ? precision : 2;
|
|
@@ -419,7 +432,7 @@ export function format(str, ...args) {
|
|
|
419
432
|
if (!isIntegral(rep)) {
|
|
420
433
|
throw new Exception("Format specifier was invalid.");
|
|
421
434
|
}
|
|
422
|
-
precision = precision != null ? precision :
|
|
435
|
+
precision = precision != null ? precision : 1;
|
|
423
436
|
rep = padLeft(toHex(rep), precision, "0");
|
|
424
437
|
if (format === "X") {
|
|
425
438
|
rep = rep.toUpperCase();
|
package/System.Text.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { replace, format, replicate, substring, isNullOrEmpty, join } from "./String.js";
|
|
2
2
|
import { class_type } from "./Reflection.js";
|
|
3
3
|
import { toString } from "./Types.js";
|
|
4
|
-
import { Exception, clear, int32ToString } from "./Util.js";
|
|
4
|
+
import { Exception, clear, int64ToString, int32ToString, int16ToString } from "./Util.js";
|
|
5
5
|
import { setItem, item } from "./Array.js";
|
|
6
6
|
export class StringBuilder {
|
|
7
7
|
constructor(value, capacity) {
|
|
@@ -47,10 +47,42 @@ export function StringBuilder__Append_61B1CA(x, c, repeatCount) {
|
|
|
47
47
|
void (x.buf.push(s));
|
|
48
48
|
return x;
|
|
49
49
|
}
|
|
50
|
+
export function StringBuilder__Append_Z510FF069(x, o) {
|
|
51
|
+
void (x.buf.push(o.toString()));
|
|
52
|
+
return x;
|
|
53
|
+
}
|
|
54
|
+
export function StringBuilder__Append_244D3E44(x, o) {
|
|
55
|
+
void (x.buf.push(o.toString()));
|
|
56
|
+
return x;
|
|
57
|
+
}
|
|
58
|
+
export function StringBuilder__Append_Z524259E6(x, o) {
|
|
59
|
+
void (x.buf.push(int16ToString(o)));
|
|
60
|
+
return x;
|
|
61
|
+
}
|
|
62
|
+
export function StringBuilder__Append_Z6EF82811(x, o) {
|
|
63
|
+
void (x.buf.push(o.toString()));
|
|
64
|
+
return x;
|
|
65
|
+
}
|
|
50
66
|
export function StringBuilder__Append_Z524259A4(x, o) {
|
|
51
67
|
void (x.buf.push(int32ToString(o)));
|
|
52
68
|
return x;
|
|
53
69
|
}
|
|
70
|
+
export function StringBuilder__Append_Z6EF827D7(x, o) {
|
|
71
|
+
void (x.buf.push(o.toString()));
|
|
72
|
+
return x;
|
|
73
|
+
}
|
|
74
|
+
export function StringBuilder__Append_Z524259C1(x, o) {
|
|
75
|
+
void (x.buf.push(int64ToString(o)));
|
|
76
|
+
return x;
|
|
77
|
+
}
|
|
78
|
+
export function StringBuilder__Append_Z6EF827B6(x, o) {
|
|
79
|
+
void (x.buf.push(o.toString()));
|
|
80
|
+
return x;
|
|
81
|
+
}
|
|
82
|
+
export function StringBuilder__Append_Z7138B98C(x, o) {
|
|
83
|
+
void (x.buf.push(o.toString()));
|
|
84
|
+
return x;
|
|
85
|
+
}
|
|
54
86
|
export function StringBuilder__Append_5E38073B(x, o) {
|
|
55
87
|
void (x.buf.push(o.toString()));
|
|
56
88
|
return x;
|
package/package.json
CHANGED