rsformat 1.3.0 → 1.4.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/README.md +76 -22
- package/docs.md +42 -12
- package/lib/format.d.ts +3 -0
- package/lib/format.js +81 -51
- package/lib/index.d.ts +6 -1
- package/lib/index.js +1 -0
- package/package.json +10 -5
package/README.md
CHANGED
|
@@ -12,6 +12,21 @@ println(rs`${'a'}:^5`);
|
|
|
12
12
|
// Prints ' a '
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Motivation](#motivation)
|
|
18
|
+
- [Usage](#usage)
|
|
19
|
+
- [Basic formatting and printing to console](#basic-formatting-and-printing-to-console)
|
|
20
|
+
- [Decorating terminal output](#decorating-terminal-output)
|
|
21
|
+
- [Format specifiers](#format-specifiers)
|
|
22
|
+
- [Different formatting types](#different-formatting-types)
|
|
23
|
+
- [Padding, Alignment](#padding-alignment)
|
|
24
|
+
- [Pretty printing with `#`](#pretty-printing-with-)
|
|
25
|
+
- [Specific number formatting](#specific-number-formatting)
|
|
26
|
+
- [Specific string formatting](#specific-string-formatting)
|
|
27
|
+
- [Formatting without `rs`](#formatting-without-rs)
|
|
28
|
+
- [Older versions of RSFormat](#older-versions-of-rsformat)
|
|
29
|
+
|
|
15
30
|
## Motivation
|
|
16
31
|
|
|
17
32
|
`console.log` is an odd method: its output can be affected by functions called before/after it (such as `console.group`), or their order affected by what parameters there are. For example, when calling `console.log(string, number)`, number can come either after or inside `string` depending on the value of `string`.
|
|
@@ -43,7 +58,7 @@ let number = 14;
|
|
|
43
58
|
let info = rs`${number+1} is ${rs.ref(0)}:x in hex`; // info == '15 is f in hex'
|
|
44
59
|
```
|
|
45
60
|
|
|
46
|
-
> NB: templates tagged with `rs` are instances of a special class `RsString` that extends `String`, rather than a primitive value. This is to enable
|
|
61
|
+
> NB: templates tagged with `rs` are instances of a special class `RsString` that extends `String`, rather than a primitive value. This is to enable colours for debug formatting inside the printing functions. This difference should not affect normal usage, but `rs.raw` can be used as an alternative tag to get a primitive `string`.
|
|
47
62
|
|
|
48
63
|
The printing functions can be called with plain strings, instances of `String` or templates formatted with `rs`:
|
|
49
64
|
|
|
@@ -53,23 +68,24 @@ println(`This template did ${'Not'} need fancy formatting`);
|
|
|
53
68
|
println(rs`...`);
|
|
54
69
|
```
|
|
55
70
|
|
|
56
|
-
###
|
|
71
|
+
### Decorating terminal output
|
|
72
|
+
|
|
73
|
+
If you want to decorate text for terminal output, you can use `rs.style`, which will format a string using one (or more with an array) of [the modifiers provided by node's `util` module](https://nodejs.org/docs/latest-v22.x/api/util.html#modifiers).
|
|
57
74
|
|
|
58
|
-
|
|
75
|
+
This is a re-export of node's `util.styleText`, and is thus aware of whether the current stdout will support the provided styles.
|
|
59
76
|
|
|
60
|
-
|
|
77
|
+
```ts
|
|
78
|
+
println(rs.style("red", "I am angry"));
|
|
79
|
+
println(rs.style(["red", "bold", "underline"], "I am very angry"));
|
|
80
|
+
```
|
|
61
81
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- Specifying precision dynamically with `*` is unsupported. Instead, precision and width can both be specified dynamically by using a separate number parameter in place of the number.
|
|
70
|
-
- New format types have been added:
|
|
71
|
-
- `N` for uppercase ordinal suffixing of numbers (rounded to integers)
|
|
72
|
-
- `n` for lowercase ordinal suffixing of numbers (rounded to integers)
|
|
82
|
+
This also works if passed inside `rs` tagged templates.
|
|
83
|
+
|
|
84
|
+
### Format Specifiers
|
|
85
|
+
|
|
86
|
+
Format specifiers can be used by adding a `:` after the format argument, and will format the value differently inside the string.
|
|
87
|
+
|
|
88
|
+
See [docs.md](./docs.md) for a detailed yet quick reference for format specifiers.
|
|
73
89
|
|
|
74
90
|
#### Different formatting types
|
|
75
91
|
|
|
@@ -81,7 +97,7 @@ println(rs`${obj}`); // prints '[object Object]'
|
|
|
81
97
|
println(rs`${obj}:?`); // prints '{ a: 1 }'
|
|
82
98
|
```
|
|
83
99
|
|
|
84
|
-
The provided printing functions will display
|
|
100
|
+
The provided printing functions will display colours in the output of `util.inspect`, but otherwise it will be formatted without colour.
|
|
85
101
|
|
|
86
102
|
The specifiers `b`,`o`,`x`,`X`,`e`,`E`,`n`,`N` will convert a `number` or `bigint` parameter to:
|
|
87
103
|
- `b`: binary
|
|
@@ -115,13 +131,20 @@ let pyramidLevels = ['a', 'aaa', 'aaaaa'];
|
|
|
115
131
|
for(let value of pyramidLevels) {
|
|
116
132
|
println(rs`${value}:^5`);
|
|
117
133
|
}
|
|
134
|
+
|
|
135
|
+
// More powerful equivalent:
|
|
136
|
+
const character = 'a';
|
|
137
|
+
const baseWidth = 5;
|
|
138
|
+
for(let width = 1; width <= baseWidth; width += 2) {
|
|
139
|
+
println(rs`${character.repeat(width)}:^${baseWidth}`);
|
|
140
|
+
}
|
|
118
141
|
```
|
|
119
142
|
|
|
120
143
|
```js
|
|
121
|
-
rs`${[1,2]}
|
|
144
|
+
rs`${[1,2]}:.>7` // '....1,2'
|
|
122
145
|
```
|
|
123
146
|
|
|
124
|
-
#### Pretty
|
|
147
|
+
#### Pretty printing with `#`
|
|
125
148
|
|
|
126
149
|
In some instances (namely debug, binary, octal and hexadecimal formatting), adding a `#` before the format specifier will use an alternative 'pretty' printing style. This amounts to using multiline `util.inspect` for debug printing (spanning multiple lines), and adding `0b`/`0o`/`0x` as a prefix for the numbers in the respective bases.
|
|
127
150
|
|
|
@@ -129,18 +152,18 @@ In some instances (namely debug, binary, octal and hexadecimal formatting), addi
|
|
|
129
152
|
rs`${255}:#X` // '0xFF'
|
|
130
153
|
```
|
|
131
154
|
|
|
132
|
-
#### Specific
|
|
155
|
+
#### Specific number formatting
|
|
133
156
|
|
|
134
157
|
Specifically for `number` and `bigint` values, a `0` can be placed before the width to pad the number with zeroes instead. This will account for signs and possible formatting differences.
|
|
135
158
|
|
|
136
159
|
```js
|
|
137
|
-
rs`${15}:#07x` // '
|
|
160
|
+
rs`${15}:#07x` // '0x0000f'
|
|
138
161
|
```
|
|
139
162
|
|
|
140
163
|
Decimal precision can be specified for numbers by adding a `.` and specifying an integer for precision. An additional parameter can also be provided to do this dynamically.
|
|
141
164
|
|
|
142
165
|
```js
|
|
143
|
-
rs`${1.23456789}:.3` // '1.
|
|
166
|
+
rs`${1.23456789}:.3` // '1.235'
|
|
144
167
|
rs`${-1}:.${3}` // '-1.000'
|
|
145
168
|
```
|
|
146
169
|
|
|
@@ -152,7 +175,7 @@ rs`${1}:+` // '+1'
|
|
|
152
175
|
rs`${1}:-` // ' 1'
|
|
153
176
|
```
|
|
154
177
|
|
|
155
|
-
####
|
|
178
|
+
#### Specific string formatting
|
|
156
179
|
|
|
157
180
|
Adding a `+` or `-` to a formatting specifier of a string will instead convert it to uppercase or lowercase respectively.
|
|
158
181
|
|
|
@@ -162,6 +185,37 @@ let str_upper = rs`${str}:+` // 'HELLO!'
|
|
|
162
185
|
let str_lower = rs`${str}:-` // 'hello!'
|
|
163
186
|
```
|
|
164
187
|
|
|
188
|
+
Specifying precision will truncate the string to the given length.
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
let str = "Hello!"
|
|
192
|
+
let str_truncated = rs`${str}:.3` // 'Hel'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Formatting without `rs`
|
|
196
|
+
|
|
197
|
+
If you want to format a single value without using an `rs` template, you can use the `formatParam` function. It provides a more explicit, object‑based API and avoids parsing format specifiers.
|
|
198
|
+
|
|
199
|
+
> NB: formatParam returns an array with the raw and debug-colored string at indices `0` and `1` respectively.
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { formatParam } from "rsformat/format";
|
|
203
|
+
|
|
204
|
+
// Equivalent to `${255}:+#09X` or `${255}:<+#09.0X`
|
|
205
|
+
let [ pretty255 ] = formatParam(255, {
|
|
206
|
+
fill: '',
|
|
207
|
+
align: '<',
|
|
208
|
+
force_sign: '+',
|
|
209
|
+
pretty: true,
|
|
210
|
+
pad_zeroes: true,
|
|
211
|
+
width: 9,
|
|
212
|
+
precision: 0,
|
|
213
|
+
type: "X"
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// pretty255 == '+0x0000FF'
|
|
217
|
+
```
|
|
218
|
+
|
|
165
219
|
## Older versions of RSFormat
|
|
166
220
|
|
|
167
221
|
Versions of RSFormat on npm prior to `1.0.0` provide formatting and printing functions that are more similar in syntax to Rust, using plain strings instead of tagged templates:
|
package/docs.md
CHANGED
|
@@ -6,24 +6,54 @@ A rsformat format specifier is parsed as follows:
|
|
|
6
6
|
|
|
7
7
|
- `${value}:[fill][align][sign][#][0][width][.(precision)][format_type][:]` is an unescaped format specifier and will parse the string as follows:
|
|
8
8
|
|
|
9
|
-
| Name | Syntax
|
|
10
|
-
| ------------- |
|
|
11
|
-
| `fill` | any single
|
|
12
|
-
| `align` | `<`, `^` or `>`
|
|
13
|
-
| `sign` | `+` or `-`
|
|
14
|
-
| `#` | `#`
|
|
15
|
-
| `0` | `0`
|
|
16
|
-
| `width` | any positive integer | Minimum width for fill/alignment | 0 |
|
|
17
|
-
| `precision` | `.` + any positive integer | Minimum precision for non-integer numbers | 0 |
|
|
18
|
-
| `format_type` | `?`,`o`,`x`,`X`,`b`,`e`,`E`,`n` or `N`
|
|
19
|
-
| `:` | `:`
|
|
9
|
+
| Name | Syntax | Purpose | Default value |
|
|
10
|
+
| ------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
11
|
+
| `fill` | any single character | The fill character for alignment | space (` `) |
|
|
12
|
+
| `align` | `<`, `^` or `>` | The alignment direction (left, center, right) | right (`>`) |
|
|
13
|
+
| `sign` | `+` or `-` | Pad a positive number with `+` or a space ` ` (or convert a string to uppercase/lowercase) | None |
|
|
14
|
+
| `#` | `#` | Pretty printing (Add prefixes `0b`, `0o` and `0x` to binary, octal and hex-formatted numbers, and use non-compact debug formatting) | None |
|
|
15
|
+
| `0` | `0` | Pads numbers with `0` characters instead of using fill/align | None |
|
|
16
|
+
| `width` | any positive integer (in string or as ${} parameter) | Minimum width for fill/alignment | 0 |
|
|
17
|
+
| `precision` | `.` + any positive integer (in string or as ${} parameter) | Minimum precision for non-integer numbers | 0 |
|
|
18
|
+
| `format_type` | `?`,`o`,`x`,`X`,`b`,`e`,`E`,`n` or `N` | Format type (see [Format types](#format-types)) | None |
|
|
19
|
+
| `:` | `:` | Add to the end of a format specifier to not have to insert a space after it | None |
|
|
20
20
|
|
|
21
21
|
Every single one of the above values is optional, but must be included in that order.
|
|
22
22
|
|
|
23
|
+
## Format types
|
|
24
|
+
|
|
25
|
+
| Character | Purpose | Example |
|
|
26
|
+
| :-------: | ------------------------------------------------------------ | -------------------------- |
|
|
27
|
+
| `?` | Debug formatting (`util.inspect` rather than `toString`) | `[10]` -> `[10]` |
|
|
28
|
+
| `o` | Octal number formatting | `10` -> `12` or `0o12` |
|
|
29
|
+
| `x` | Hexadecimal number formatting | `10` -> `a` or `0xa` |
|
|
30
|
+
| `X` | Uppercase hexadecimal number formatting | `10` -> `A` or `0xA` |
|
|
31
|
+
| `b` | Binary number formatting | `10` -> `1010` or `0b1010` |
|
|
32
|
+
| `e` | Scientific notation number formatting | `10` -> `1e1` |
|
|
33
|
+
| `E` | Uppercase scientific notation number formatting | `10` -> `1E1` |
|
|
34
|
+
| `n` | Ordinal-suffixed number formatting | `10` -> `10th` |
|
|
35
|
+
| `N` | Uppercase ordinal-suffixed number formatting | `10` -> `10TH` |
|
|
36
|
+
|
|
23
37
|
## Examples
|
|
24
38
|
|
|
25
39
|
`${"abc"}:+:!` Will capitalise `"abc"` with an exclamation mark right after it, ie. `ABC!`
|
|
26
40
|
|
|
27
41
|
`${15}:#08x` Will convert `15` to hexadecimal, add `0x` and pad it with 0s until it is 8 characters wide, ie. `0x00000F`
|
|
28
42
|
|
|
29
|
-
`${1.2345678}:,^10.${3}` will round `1.2345678` to 3 decimal places and center align it with `,`
|
|
43
|
+
`${1.2345678}:,^10.${3}` will round `1.2345678` to 3 decimal places and center align it with `,` until it is 10 characters wide, ie. `,,1.234,,,`
|
|
44
|
+
|
|
45
|
+
## rsformat specifiers for those familiar with Rust formatting
|
|
46
|
+
|
|
47
|
+
This implementation parses formatting very similarly to Rust, but differing in a few key ways:
|
|
48
|
+
|
|
49
|
+
- Rather than escaping the braces using `{{` or `}}`, the formatting colon can be escaped using `::`.
|
|
50
|
+
- Different parameters are referenced using `rs.ref(n)` rather than the number literal `n`.
|
|
51
|
+
- To separate a formatting specifier from the rest of the string without adding a space, an extra closing colon must be added (eg. `:#?:foo` - specifier gets parsed as `:#?`)
|
|
52
|
+
- The `-` sign (unused in Rust) will add a space if the number is positive to align it with negative numbers without showing a `+`.
|
|
53
|
+
- When used on strings, `+` and `-` sign specifiers will conver them to uppercase and lowercase respectively
|
|
54
|
+
- Pointer format type `p` is unsupported.
|
|
55
|
+
- Hexadecimal debug types `x?` and `X?` are unsupported.
|
|
56
|
+
- Specifying precision dynamically with `*` is unsupported. Instead, precision and width can both be specified dynamically by using a separate number parameter in place of the number.
|
|
57
|
+
- New format types have been added:
|
|
58
|
+
- `N` for uppercase ordinal suffixing of numbers (rounded to integers)
|
|
59
|
+
- `n` for lowercase ordinal suffixing of numbers (rounded to integers)
|
package/lib/format.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import util from 'node:util';
|
|
1
2
|
/**
|
|
2
3
|
* Type representing a string formatted by `rs`.
|
|
3
4
|
* An extension of `String`.
|
|
@@ -43,4 +44,6 @@ type FormatSpecifier = {
|
|
|
43
44
|
* @returns `param` as a debug-colored and raw formatted string
|
|
44
45
|
*/
|
|
45
46
|
export declare function formatParam(param: any, format: FormatSpecifier): [string, string];
|
|
47
|
+
/** Re-export of node's `util.styleText`. */
|
|
48
|
+
export declare const style: typeof util.styleText;
|
|
46
49
|
export {};
|
package/lib/format.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.RsString = void 0;
|
|
6
|
+
exports.style = exports.RsString = void 0;
|
|
7
7
|
exports.buildString = buildString;
|
|
8
8
|
exports.formatParam = formatParam;
|
|
9
9
|
const node_util_1 = __importDefault(require("node:util"));
|
|
@@ -103,7 +103,7 @@ function buildString(strings, params) {
|
|
|
103
103
|
idx++;
|
|
104
104
|
width = Number(string.substring(width_substring_start, idx));
|
|
105
105
|
}
|
|
106
|
-
else if (idx == string.length) {
|
|
106
|
+
else if (idx == string.length && i < params.length) {
|
|
107
107
|
// Grab the next parameter and fuse the string with the next one
|
|
108
108
|
width = params[i];
|
|
109
109
|
if (typeof width != 'number')
|
|
@@ -116,7 +116,7 @@ function buildString(strings, params) {
|
|
|
116
116
|
// Grab the next parameter and fuse the string with the next one
|
|
117
117
|
precision = params[i];
|
|
118
118
|
if (typeof precision != 'number')
|
|
119
|
-
throw error(i - 1, idx, `Expected a number or number parameter for precision specifier after . (found ${string[idx] ? "'" + string[idx] + "'" : typeof
|
|
119
|
+
throw error(i - 1, idx, `Expected a number or number parameter for precision specifier after . (found ${string[idx] ? "'" + string[idx] + "'" : typeof precision + ' parameter'}).\nIf the next parameter was not meant to be a precision number, add a : to the end of the formatting specifier.`);
|
|
120
120
|
string += strings[++i];
|
|
121
121
|
}
|
|
122
122
|
else {
|
|
@@ -176,69 +176,80 @@ function buildString(strings, params) {
|
|
|
176
176
|
*/
|
|
177
177
|
function formatParam(param, format) {
|
|
178
178
|
let param_type = typeof param;
|
|
179
|
+
let base = 10;
|
|
180
|
+
switch (format.type) {
|
|
181
|
+
case 'o':
|
|
182
|
+
base = 8;
|
|
183
|
+
break;
|
|
184
|
+
case 'x':
|
|
185
|
+
case 'X':
|
|
186
|
+
base = 16;
|
|
187
|
+
break;
|
|
188
|
+
case 'b':
|
|
189
|
+
base = 2;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
let param_raw;
|
|
179
193
|
let param_colored = "";
|
|
180
|
-
// embed
|
|
194
|
+
// embed Strings directly
|
|
181
195
|
if (param instanceof String && format.type != '?') {
|
|
196
|
+
param_raw = param.toString(false);
|
|
182
197
|
param_colored = param.toString(true);
|
|
183
|
-
param = param.toString(false);
|
|
184
198
|
}
|
|
185
199
|
else
|
|
186
200
|
switch (format.type) {
|
|
187
201
|
// Process format type
|
|
188
202
|
case 'o':
|
|
189
|
-
param = param.toString(8);
|
|
190
|
-
break;
|
|
191
203
|
case 'x':
|
|
192
|
-
param = param.toString(16);
|
|
193
|
-
break;
|
|
194
204
|
case 'X':
|
|
195
|
-
param = param.toString(16).toUpperCase();
|
|
196
|
-
break;
|
|
197
205
|
case 'b':
|
|
198
|
-
|
|
206
|
+
param_raw = roundInBase(param, base, format.precision);
|
|
207
|
+
if (format.type == "X")
|
|
208
|
+
param_raw = param_raw.toUpperCase();
|
|
199
209
|
break;
|
|
200
210
|
case 'e':
|
|
201
211
|
case 'E':
|
|
202
212
|
if (param_type != 'number' && param_type != 'bigint') {
|
|
203
|
-
|
|
213
|
+
param_raw = param.toString();
|
|
204
214
|
break;
|
|
205
215
|
}
|
|
206
|
-
|
|
216
|
+
param_raw = param.toLocaleString('en-US', { notation: 'scientific', maximumFractionDigits: 20 });
|
|
207
217
|
if (format.type == 'e')
|
|
208
|
-
|
|
218
|
+
param_raw = param_raw.toLowerCase();
|
|
219
|
+
// Do not pad with zeroes when using scientific formatting
|
|
220
|
+
format.pad_zeroes = false;
|
|
209
221
|
break;
|
|
210
222
|
case 'n':
|
|
211
223
|
case 'N':
|
|
212
224
|
if (param_type != 'number' && param_type != 'bigint') {
|
|
213
|
-
|
|
225
|
+
param_raw = param.toString();
|
|
214
226
|
break;
|
|
215
227
|
}
|
|
216
228
|
// Round and add suffix
|
|
217
229
|
if (param_type == 'number')
|
|
218
230
|
param = Math.round(param);
|
|
219
|
-
|
|
220
|
-
let last_2_digits =
|
|
231
|
+
param_raw = param.toString();
|
|
232
|
+
let last_2_digits = param_raw.substring(param_raw.length - 2);
|
|
221
233
|
if (last_2_digits == '11' || last_2_digits == '12' || last_2_digits == '13') {
|
|
222
|
-
|
|
234
|
+
param_raw += 'th';
|
|
223
235
|
}
|
|
224
236
|
else
|
|
225
237
|
switch (last_2_digits[last_2_digits.length - 1]) {
|
|
226
238
|
case '1':
|
|
227
|
-
|
|
239
|
+
param_raw += 'st';
|
|
228
240
|
break;
|
|
229
241
|
case '2':
|
|
230
|
-
|
|
242
|
+
param_raw += 'nd';
|
|
231
243
|
break;
|
|
232
244
|
case '3':
|
|
233
|
-
|
|
245
|
+
param_raw += 'rd';
|
|
234
246
|
break;
|
|
235
|
-
default:
|
|
247
|
+
default: param_raw += 'th';
|
|
236
248
|
}
|
|
237
249
|
if (format.type == 'N')
|
|
238
|
-
|
|
239
|
-
// Do not pad with zeroes
|
|
250
|
+
param_raw = param_raw.toUpperCase();
|
|
251
|
+
// Do not pad with zeroes when using ordinal formatting
|
|
240
252
|
format.pad_zeroes = false;
|
|
241
|
-
format.precision = -1;
|
|
242
253
|
break;
|
|
243
254
|
case '?':
|
|
244
255
|
param_colored = node_util_1.default.inspect(param, {
|
|
@@ -246,36 +257,25 @@ function formatParam(param, format) {
|
|
|
246
257
|
colors: true,
|
|
247
258
|
compact: !format.pretty
|
|
248
259
|
});
|
|
249
|
-
|
|
260
|
+
param_raw = node_util_1.default.stripVTControlCharacters(param_colored);
|
|
250
261
|
// Do not force sign, pad with zeroes or align to precision when using debug formatting
|
|
251
262
|
param_type = 'string';
|
|
252
|
-
format.force_sign = '';
|
|
263
|
+
// format.force_sign = '';
|
|
253
264
|
break;
|
|
254
265
|
default:
|
|
255
|
-
|
|
266
|
+
param_raw = roundInBase(param, base, format.precision);
|
|
256
267
|
break;
|
|
257
268
|
}
|
|
258
269
|
;
|
|
259
270
|
if (param_type == 'string' && format.force_sign != '') {
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
// Compute radix-point precision on numbers
|
|
263
|
-
if (param_type == 'number' && format.precision != -1) {
|
|
264
|
-
let [pre, post] = param.split('.');
|
|
265
|
-
if (!format.precision) { // precision = 0, do not include radix point
|
|
266
|
-
param = pre;
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
post = ((post || '') + '0'.repeat(format.precision)).slice(0, format.precision);
|
|
270
|
-
param = pre + '.' + post;
|
|
271
|
-
}
|
|
271
|
+
param_raw = format.force_sign == '+' ? param_raw.toUpperCase() : param_raw.toLowerCase();
|
|
272
272
|
}
|
|
273
273
|
// let filled = false;
|
|
274
274
|
if ((param_type == 'number') || (param_type == 'bigint')) {
|
|
275
275
|
// Compute parameter sign
|
|
276
|
-
let maybe_sign =
|
|
276
|
+
let maybe_sign = param_raw.substring(0, 1);
|
|
277
277
|
if (maybe_sign === '-') {
|
|
278
|
-
|
|
278
|
+
param_raw = param_raw.substring(1, param_raw.length);
|
|
279
279
|
}
|
|
280
280
|
else if (format.force_sign == '+') {
|
|
281
281
|
maybe_sign = '+';
|
|
@@ -304,19 +304,20 @@ function formatParam(param, format) {
|
|
|
304
304
|
//pad with zeroes if specified
|
|
305
305
|
if (format.pad_zeroes) {
|
|
306
306
|
// filled = true;
|
|
307
|
-
while (
|
|
308
|
-
|
|
307
|
+
while (param_raw.length < format.width - maybe_sign.length) {
|
|
308
|
+
param_raw = '0' + param_raw;
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
|
-
|
|
311
|
+
param_raw = maybe_sign + param_raw;
|
|
312
312
|
}
|
|
313
313
|
if (param_colored == "")
|
|
314
|
-
param_colored =
|
|
315
|
-
|
|
314
|
+
param_colored = param_raw;
|
|
315
|
+
let visible_length = [...param_raw].length;
|
|
316
|
+
if (format.width > visible_length) {
|
|
316
317
|
// Compute fill/align
|
|
317
318
|
let left = '';
|
|
318
319
|
let right = '';
|
|
319
|
-
let diff = format.width -
|
|
320
|
+
let diff = format.width - visible_length;
|
|
320
321
|
switch (format.align) {
|
|
321
322
|
case '>':
|
|
322
323
|
left = format.fill.repeat(diff);
|
|
@@ -330,8 +331,37 @@ function formatParam(param, format) {
|
|
|
330
331
|
right = format.fill.repeat(diff / 2 + diff % 2);
|
|
331
332
|
break;
|
|
332
333
|
}
|
|
333
|
-
|
|
334
|
+
param_raw = left + param_raw + right;
|
|
334
335
|
param_colored = left + param_colored + right;
|
|
335
336
|
}
|
|
336
|
-
return [param_colored,
|
|
337
|
+
return [param_colored, param_raw];
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Helper function to round a number to a given precision in a given base.
|
|
341
|
+
* Will also truncate strings to the desired length.
|
|
342
|
+
*/
|
|
343
|
+
function roundInBase(n, base, precision) {
|
|
344
|
+
if (typeof n == "string") {
|
|
345
|
+
if (precision == -1)
|
|
346
|
+
return n;
|
|
347
|
+
return n.slice(0, precision);
|
|
348
|
+
}
|
|
349
|
+
if (typeof n != "number" && typeof n != "bigint") {
|
|
350
|
+
return n.toString();
|
|
351
|
+
}
|
|
352
|
+
if (precision < 0) {
|
|
353
|
+
return n.toString(base);
|
|
354
|
+
}
|
|
355
|
+
if (precision == 0) {
|
|
356
|
+
return (typeof n == "bigint" ? n : Math.round(n)).toString(base);
|
|
357
|
+
}
|
|
358
|
+
const factor = base ** precision;
|
|
359
|
+
const rounded = typeof n == "bigint" ? n * BigInt(factor) : Math.round((n + Number.EPSILON) * factor);
|
|
360
|
+
const str = rounded.toString(base);
|
|
361
|
+
// Insert radix point from the right
|
|
362
|
+
const intPart = str.slice(0, -precision) || "0";
|
|
363
|
+
const fracPart = str.slice(-precision).padStart(precision, "0");
|
|
364
|
+
return intPart + "." + fracPart;
|
|
337
365
|
}
|
|
366
|
+
/** Re-export of node's `util.styleText`. */
|
|
367
|
+
exports.style = node_util_1.default.styleText;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RsString } from './format';
|
|
1
|
+
import { RsString, style } from './format';
|
|
2
2
|
export { print, println, eprint, eprintln, dbg } from './print';
|
|
3
3
|
type ParameterReference = {
|
|
4
4
|
__rs_param_ref: number;
|
|
@@ -25,6 +25,11 @@ type rs = {
|
|
|
25
25
|
* @returns A reference to the `n`th parameter
|
|
26
26
|
*/
|
|
27
27
|
ref: (n: number) => ParameterReference;
|
|
28
|
+
/**
|
|
29
|
+
* Format a string using one or more of [the modifiers provided by node's `util` module](https://nodejs.org/docs/latest-v22.x/api/util.html#modifiers).
|
|
30
|
+
* This is a re-export of `util.styleText`.
|
|
31
|
+
*/
|
|
32
|
+
style: typeof style;
|
|
28
33
|
};
|
|
29
34
|
/**
|
|
30
35
|
* Tag to use Rust-style formatting in a template literal.
|
package/lib/index.js
CHANGED
|
@@ -23,3 +23,4 @@ Object.defineProperty(exports, "dbg", { enumerable: true, get: function () { ret
|
|
|
23
23
|
exports.rs = ((strings, ...params) => new format_1.RsString(strings, params));
|
|
24
24
|
exports.rs.raw = (strings, ...params) => (0, format_1.buildString)(strings, params).raw;
|
|
25
25
|
exports.rs.ref = (n) => ({ __rs_param_ref: n });
|
|
26
|
+
exports.rs.style = format_1.style;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rsformat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Formatting/printing library for JavaScript that takes after rust's string formatting ",
|
|
5
5
|
"files": [
|
|
6
6
|
"lib",
|
|
@@ -31,11 +31,16 @@
|
|
|
31
31
|
"author": "Alfio (https://github.com/p2js)",
|
|
32
32
|
"license": "ISC",
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@types/
|
|
34
|
+
"@types/jest": "^30.0.0",
|
|
35
|
+
"@types/node": "^22.8.6",
|
|
36
|
+
"jest": "^30.2.0",
|
|
37
|
+
"typescript": "^5.9.3"
|
|
35
38
|
},
|
|
36
39
|
"scripts": {
|
|
37
|
-
"build": "tsc",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
+
"build": "pnpm exec tsc",
|
|
41
|
+
"build:test": "pnpm exec tsc -p tsconfig.test.json",
|
|
42
|
+
"pretest": "pnpm build && pnpm build:test",
|
|
43
|
+
"test": "pnpm exec jest test-build",
|
|
44
|
+
"posttest": "rm -r test-build"
|
|
40
45
|
}
|
|
41
46
|
}
|