@fable-org/fable-library-ts 1.10.0 → 2.0.0-beta.2

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/String.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { toString as dateToString } from "./Date.js";
2
- import { compare as numericCompare, isNumeric, multiply, Numeric, toExponential, toFixed, toHex, toPrecision } from "./Numeric.js";
2
+ import { compare as numericCompare, isNumeric, isIntegral, multiply, Numeric, toExponential, toFixed, toHex, toPrecision } from "./Numeric.js";
3
3
  import { escape } from "./RegExp.js";
4
4
  import { toString } from "./Types.js";
5
5
 
@@ -295,6 +295,19 @@ export function fsFormat(str: string) {
295
295
  };
296
296
  }
297
297
 
298
+ function splitIntAndDecimalPart(value: string) {
299
+ let [repInt, repDecimal] = value.split(".");
300
+ repDecimal === undefined && (repDecimal = "");
301
+ return {
302
+ integral: repInt,
303
+ decimal: repDecimal
304
+ }
305
+ }
306
+
307
+ function thousandSeparate(value: string) {
308
+ return value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
309
+ }
310
+
298
311
  export function format(str: string | object, ...args: any[]) {
299
312
  let str2: string;
300
313
  if (typeof str === "object") {
@@ -310,31 +323,89 @@ export function format(str: string | object, ...args: any[]) {
310
323
  throw new Error("Index must be greater or equal to zero and less than the arguments' length.")
311
324
  }
312
325
  let rep = args[idx];
326
+ let parts;
313
327
  if (isNumeric(rep)) {
314
- precision = precision == null ? null : parseInt(precision, 10);
328
+ precision = precision == "" ? null : parseInt(precision, 10);
315
329
  switch (format) {
330
+ case "b": case "B":
331
+ if (!isIntegral(rep)) {
332
+ throw new Error("Format specifier was invalid.");
333
+ }
334
+ rep = (rep >>> 0).toString(2).replace(/^0+/, "").padStart(precision || 1, "0");
335
+ break;
336
+ case "c": case "C":
337
+ const isNegative = isLessThan(rep, 0);
338
+ if (isLessThan(rep, 0)) {
339
+ rep = multiply(rep, -1);
340
+ }
341
+ precision = precision == null ? 2 : precision;
342
+ rep = toFixed(rep, precision);
343
+ parts = splitIntAndDecimalPart(rep);
344
+ rep = "¤" + thousandSeparate(parts.integral) + "." + padRight(parts.decimal, precision, "0");
345
+ if (isNegative) {
346
+ rep = "(" + rep + ")";
347
+ }
348
+ break;
349
+ case "d": case "D":
350
+ if (!isIntegral(rep)) {
351
+ throw new Error("Format specifier was invalid.");
352
+ }
353
+ rep = String(rep);
354
+ if (precision != null) {
355
+ if (rep.startsWith("-")) {
356
+ rep = "-" + padLeft(rep.substring(1), precision, "0");
357
+ } else {
358
+ rep = padLeft(rep, precision, "0");
359
+ }
360
+ }
361
+ break;
362
+ case "e": case "E":
363
+ rep = precision != null ? toExponential(rep, precision) : toExponential(rep);
364
+ break;
316
365
  case "f": case "F":
317
366
  precision = precision != null ? precision : 2;
318
367
  rep = toFixed(rep, precision);
368
+ if (precision > 0) {
369
+ parts = splitIntAndDecimalPart(rep);
370
+ rep = parts.integral + "." + padRight(parts.decimal, precision, "0");
371
+ }
319
372
  break;
320
373
  case "g": case "G":
321
374
  rep = precision != null ? toPrecision(rep, precision) : toPrecision(rep);
375
+ // TODO: Check why some numbers are formatted with decimal part
376
+ rep = trimEnd(trimEnd(rep, "0"), ".");
322
377
  break;
323
- case "e": case "E":
324
- rep = precision != null ? toExponential(rep, precision) : toExponential(rep);
378
+ case "n": case "N":
379
+ precision = precision != null ? precision : 2;
380
+ rep = toFixed(rep, precision);
381
+ parts = splitIntAndDecimalPart(rep);
382
+ rep = thousandSeparate(parts.integral) + "." + padRight(parts.decimal, precision, "0");
325
383
  break;
326
384
  case "p": case "P":
327
385
  precision = precision != null ? precision : 2;
328
- rep = toFixed(multiply(rep, 100), precision) + " %";
329
- break;
330
- case "d": case "D":
331
- rep = precision != null ? padLeft(String(rep), precision, "0") : String(rep);
386
+ rep = toFixed(multiply(rep, 100), precision)
387
+ parts = splitIntAndDecimalPart(rep);
388
+ rep = thousandSeparate(parts.integral) + "." + padRight(parts.decimal, precision, "0") + " %";
332
389
  break;
390
+ case "r": case "R":
391
+ throw new Error("The round-trip format is not supported by Fable");
333
392
  case "x": case "X":
334
- rep = precision != null ? padLeft(toHex(rep), precision, "0") : toHex(rep);
335
- if (format === "X") { rep = rep.toUpperCase(); }
393
+ if (!isIntegral(rep)) {
394
+ throw new Error("Format specifier was invalid.");
395
+ }
396
+ precision = precision != null ? precision : 2;
397
+ rep = padLeft(toHex(rep), precision, "0");
398
+ if (format === "X") {
399
+ rep = rep.toUpperCase();
400
+ }
336
401
  break;
337
402
  default:
403
+ // If we have format and were not able to handle it throw
404
+ // See: https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#standard-format-specifiers
405
+ if (format) {
406
+ throw new Error("Format specifier was invalid.");
407
+ }
408
+
338
409
  if (pattern) {
339
410
  let sign = "";
340
411
  rep = (pattern as string).replace(/([0#,]+)(\.[0#]+)?/, (_, intPart: string, decimalPart: string) => {