@tannin/sprintf 1.1.0 → 1.3.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 +16 -4
- package/build/index.js +95 -44
- package/dist/sprintf.js +97 -46
- package/dist/sprintf.min.js +1 -1
- package/package.json +39 -30
- package/src/index.d.ts +46 -0
- package/{index.js → src/index.js} +95 -44
- package/types/index.d.ts +87 -0
- package/CHANGELOG.md +0 -25
- package/LICENSE.md +0 -7
package/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
`@tannin/sprintf`
|
|
2
|
-
=================
|
|
1
|
+
# `@tannin/sprintf`
|
|
3
2
|
|
|
4
3
|
Given a format string, returns string with arguments interpolatation. Arguments can either be provided directly via function arguments spread, or with an array as the second argument.
|
|
5
4
|
|
|
@@ -32,16 +31,28 @@ Otherwise, download a pre-built copy from unpkg:
|
|
|
32
31
|
|
|
33
32
|
## Usage
|
|
34
33
|
|
|
34
|
+
Using positional arguments:
|
|
35
|
+
|
|
35
36
|
```js
|
|
36
37
|
import sprintf from '@tannin/sprintf';
|
|
37
38
|
|
|
38
|
-
sprintf(
|
|
39
|
+
sprintf('Hello %s!', 'world');
|
|
39
40
|
// ⇒ 'Hello world!'
|
|
40
41
|
```
|
|
41
42
|
|
|
43
|
+
Using named arguments:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import sprintf from '@tannin/sprintf';
|
|
47
|
+
|
|
48
|
+
sprintf('Hello %(place)s! From %(name)s.', { place: 'world', name: 'Andrew' });
|
|
49
|
+
// ⇒ 'Hello world! From Andrew.'
|
|
50
|
+
```
|
|
51
|
+
|
|
42
52
|
## Type coercions and default values
|
|
43
53
|
|
|
44
54
|
When replacing numeric types (`%d` and `%f`), values will be coerced to numeric values, and default to 0:
|
|
55
|
+
|
|
45
56
|
- `sprintf( '%d', 123 )` returns `'123'`
|
|
46
57
|
- `sprintf( '%d', '123' )` returns `'123'`
|
|
47
58
|
- `sprintf( '%d', 'string' )` returns `'0'`
|
|
@@ -50,6 +61,7 @@ When replacing numeric types (`%d` and `%f`), values will be coerced to numeric
|
|
|
50
61
|
- `sprintf( '%d', undefined )` returns `'0'`
|
|
51
62
|
|
|
52
63
|
When replacing string types (`%s`), values will be coerced to strings, and nullish values will be replaced with `''`:
|
|
64
|
+
|
|
53
65
|
- `sprintf( '%s', 'string' )` returns `'string'`
|
|
54
66
|
- `sprintf( '%s', 0 )` returns `'0'`
|
|
55
67
|
- `sprintf( '%s', false )` returns `'false'`
|
|
@@ -58,6 +70,6 @@ When replacing string types (`%s`), values will be coerced to strings, and nulli
|
|
|
58
70
|
|
|
59
71
|
## License
|
|
60
72
|
|
|
61
|
-
Copyright 2019 Andrew Duthie
|
|
73
|
+
Copyright 2019-2025 Andrew Duthie
|
|
62
74
|
|
|
63
75
|
Released under the [MIT License](https://opensource.org/licenses/MIT).
|
package/build/index.js
CHANGED
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
*
|
|
32
32
|
* @type {RegExp}
|
|
33
33
|
*/
|
|
34
|
-
var PATTERN =
|
|
34
|
+
var PATTERN =
|
|
35
|
+
/%(((\d+)\$)|(\(([$_a-zA-Z][$_a-zA-Z0-9]*)\)))?[ +0#-]*\d*(\.(\d+|\*))?(ll|[lhqL])?([cduxXefgsp%])/g;
|
|
35
36
|
// ▲ ▲ ▲ ▲ ▲ ▲ ▲ type
|
|
36
37
|
// │ │ │ │ │ └ Length (unsupported)
|
|
37
38
|
// │ │ │ │ └ Precision / max width
|
|
38
39
|
// │ │ │ └ Min width (unsupported)
|
|
39
40
|
// │ │ └ Flags (unsupported)
|
|
40
41
|
// └ Index └ Name (for named arguments)
|
|
41
|
-
|
|
42
42
|
/**
|
|
43
43
|
* Given a format string, returns string with arguments interpolatation.
|
|
44
44
|
* Arguments can either be provided directly via function arguments spread, or
|
|
@@ -54,84 +54,135 @@ var PATTERN = /%(((\d+)\$)|(\(([$_a-zA-Z][$_a-zA-Z0-9]*)\)))?[ +0#-]*\d*(\.(\d+|
|
|
|
54
54
|
* sprintf( 'Hello %s!', 'world' );
|
|
55
55
|
* // ⇒ 'Hello world!'
|
|
56
56
|
* ```
|
|
57
|
+
* @template {string} T
|
|
58
|
+
* @overload
|
|
59
|
+
* @param {T} string - string printf format string
|
|
60
|
+
* @param {...import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
57
61
|
*
|
|
58
|
-
* @
|
|
59
|
-
|
|
62
|
+
* @return {string} Formatted string.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Given a format string, returns string with arguments interpolatation.
|
|
67
|
+
* Arguments can either be provided directly via function arguments spread, or
|
|
68
|
+
* with an array as the second argument.
|
|
69
|
+
*
|
|
70
|
+
* @see https://en.wikipedia.org/wiki/Printf_format_string
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
*
|
|
74
|
+
* ```js
|
|
75
|
+
* import sprintf from '@tannin/sprintf';
|
|
76
|
+
*
|
|
77
|
+
* sprintf( 'Hello %s!', 'world' );
|
|
78
|
+
* // ⇒ 'Hello world!'
|
|
79
|
+
* ```
|
|
80
|
+
* @template {string} T
|
|
81
|
+
* @overload
|
|
82
|
+
* @param {T} string - string printf format string
|
|
83
|
+
* @param {import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
60
84
|
*
|
|
61
85
|
* @return {string} Formatted string.
|
|
62
86
|
*/
|
|
63
|
-
function sprintf( string, args ) {
|
|
64
|
-
var i;
|
|
65
87
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Given a format string, returns string with arguments interpolatation.
|
|
90
|
+
* Arguments can either be provided directly via function arguments spread, or
|
|
91
|
+
* with an array as the second argument.
|
|
92
|
+
*
|
|
93
|
+
* @see https://en.wikipedia.org/wiki/Printf_format_string
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
*
|
|
97
|
+
* ```js
|
|
98
|
+
* import sprintf from '@tannin/sprintf';
|
|
99
|
+
*
|
|
100
|
+
* sprintf( 'Hello %s!', 'world' );
|
|
101
|
+
* // ⇒ 'Hello world!'
|
|
102
|
+
* ```
|
|
103
|
+
* @template {string} T
|
|
104
|
+
* @param {T} string - string printf format string
|
|
105
|
+
* @param {...import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
106
|
+
*
|
|
107
|
+
* @return {string} Formatted string.
|
|
108
|
+
*/
|
|
109
|
+
function sprintf(string, ...args) {
|
|
110
|
+
var i = 0;
|
|
111
|
+
if (Array.isArray(args[0])) {
|
|
112
|
+
args = /** @type {import('../types').SprintfArgs<T>[]} */ (
|
|
113
|
+
/** @type {unknown} */ args[0]
|
|
114
|
+
);
|
|
73
115
|
}
|
|
74
116
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
117
|
+
return string.replace(PATTERN, function () {
|
|
118
|
+
var index,
|
|
119
|
+
// name needs to be documented as `string | undefined` else value will have tpye unknown.
|
|
120
|
+
/**
|
|
121
|
+
* Name of the argument to substitute, if any.
|
|
122
|
+
*
|
|
123
|
+
* @type {string | undefined}
|
|
124
|
+
*/
|
|
125
|
+
name,
|
|
126
|
+
precision,
|
|
127
|
+
type,
|
|
128
|
+
value;
|
|
79
129
|
|
|
80
|
-
index = arguments[
|
|
81
|
-
name = arguments[
|
|
82
|
-
precision = arguments[
|
|
83
|
-
type = arguments[
|
|
130
|
+
index = arguments[3];
|
|
131
|
+
name = arguments[5];
|
|
132
|
+
precision = arguments[7];
|
|
133
|
+
type = arguments[9];
|
|
84
134
|
|
|
85
135
|
// There's no placeholder substitution in the explicit "%", meaning it
|
|
86
136
|
// is not necessary to increment argument index.
|
|
87
|
-
if (
|
|
137
|
+
if (type === '%') {
|
|
88
138
|
return '%';
|
|
89
139
|
}
|
|
90
140
|
|
|
91
141
|
// Asterisk precision determined by peeking / shifting next argument.
|
|
92
|
-
if (
|
|
93
|
-
precision = args[
|
|
142
|
+
if (precision === '*') {
|
|
143
|
+
precision = args[i];
|
|
94
144
|
i++;
|
|
95
145
|
}
|
|
96
146
|
|
|
97
|
-
if (
|
|
98
|
-
// If it's a named argument, use name.
|
|
99
|
-
if ( args[ 0 ] && typeof args[ 0 ] === 'object' &&
|
|
100
|
-
args[ 0 ].hasOwnProperty( name ) ) {
|
|
101
|
-
value = args[ 0 ][ name ];
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
147
|
+
if (name === undefined) {
|
|
104
148
|
// If not a positional argument, use counter value.
|
|
105
|
-
if (
|
|
106
|
-
index = i;
|
|
149
|
+
if (index === undefined) {
|
|
150
|
+
index = i + 1;
|
|
107
151
|
}
|
|
108
152
|
|
|
109
153
|
i++;
|
|
110
154
|
|
|
111
155
|
// Positional argument.
|
|
112
|
-
value = args[
|
|
156
|
+
value = args[index - 1];
|
|
157
|
+
} else if (
|
|
158
|
+
args[0] &&
|
|
159
|
+
typeof args[0] === 'object' &&
|
|
160
|
+
args[0].hasOwnProperty(name)
|
|
161
|
+
) {
|
|
162
|
+
// If it's a named argument, use name.
|
|
163
|
+
value = args[0][name];
|
|
113
164
|
}
|
|
114
165
|
|
|
115
166
|
// Parse as type.
|
|
116
|
-
if (
|
|
117
|
-
value = parseFloat(
|
|
118
|
-
} else if (
|
|
119
|
-
value = parseInt(
|
|
167
|
+
if (type === 'f') {
|
|
168
|
+
value = parseFloat(value) || 0;
|
|
169
|
+
} else if (type === 'd') {
|
|
170
|
+
value = parseInt(value) || 0;
|
|
120
171
|
}
|
|
121
172
|
|
|
122
173
|
// Apply precision.
|
|
123
|
-
if (
|
|
124
|
-
if (
|
|
125
|
-
value = value.toFixed(
|
|
126
|
-
} else if (
|
|
127
|
-
value = value.substr(
|
|
174
|
+
if (precision !== undefined) {
|
|
175
|
+
if (type === 'f') {
|
|
176
|
+
value = value.toFixed(precision);
|
|
177
|
+
} else if (type === 's') {
|
|
178
|
+
value = value.substr(0, precision);
|
|
128
179
|
}
|
|
129
180
|
}
|
|
130
181
|
|
|
131
182
|
// To avoid "undefined" concatenation, return empty string if no
|
|
132
183
|
// placeholder substitution can be performed.
|
|
133
184
|
return value !== undefined && value !== null ? value : '';
|
|
134
|
-
}
|
|
185
|
+
});
|
|
135
186
|
}
|
|
136
187
|
|
|
137
188
|
module.exports = sprintf;
|
package/dist/sprintf.js
CHANGED
|
@@ -32,14 +32,14 @@ var sprintf = (function () {
|
|
|
32
32
|
*
|
|
33
33
|
* @type {RegExp}
|
|
34
34
|
*/
|
|
35
|
-
var PATTERN =
|
|
35
|
+
var PATTERN =
|
|
36
|
+
/%(((\d+)\$)|(\(([$_a-zA-Z][$_a-zA-Z0-9]*)\)))?[ +0#-]*\d*(\.(\d+|\*))?(ll|[lhqL])?([cduxXefgsp%])/g;
|
|
36
37
|
// ▲ ▲ ▲ ▲ ▲ ▲ ▲ type
|
|
37
38
|
// │ │ │ │ │ └ Length (unsupported)
|
|
38
39
|
// │ │ │ │ └ Precision / max width
|
|
39
40
|
// │ │ │ └ Min width (unsupported)
|
|
40
41
|
// │ │ └ Flags (unsupported)
|
|
41
42
|
// └ Index └ Name (for named arguments)
|
|
42
|
-
|
|
43
43
|
/**
|
|
44
44
|
* Given a format string, returns string with arguments interpolatation.
|
|
45
45
|
* Arguments can either be provided directly via function arguments spread, or
|
|
@@ -55,86 +55,137 @@ var sprintf = (function () {
|
|
|
55
55
|
* sprintf( 'Hello %s!', 'world' );
|
|
56
56
|
* // ⇒ 'Hello world!'
|
|
57
57
|
* ```
|
|
58
|
+
* @template {string} T
|
|
59
|
+
* @overload
|
|
60
|
+
* @param {T} string - string printf format string
|
|
61
|
+
* @param {...import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
62
|
+
*
|
|
63
|
+
* @return {string} Formatted string.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Given a format string, returns string with arguments interpolatation.
|
|
68
|
+
* Arguments can either be provided directly via function arguments spread, or
|
|
69
|
+
* with an array as the second argument.
|
|
70
|
+
*
|
|
71
|
+
* @see https://en.wikipedia.org/wiki/Printf_format_string
|
|
58
72
|
*
|
|
59
|
-
* @
|
|
60
|
-
*
|
|
73
|
+
* @example
|
|
74
|
+
*
|
|
75
|
+
* ```js
|
|
76
|
+
* import sprintf from '@tannin/sprintf';
|
|
77
|
+
*
|
|
78
|
+
* sprintf( 'Hello %s!', 'world' );
|
|
79
|
+
* // ⇒ 'Hello world!'
|
|
80
|
+
* ```
|
|
81
|
+
* @template {string} T
|
|
82
|
+
* @overload
|
|
83
|
+
* @param {T} string - string printf format string
|
|
84
|
+
* @param {import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
61
85
|
*
|
|
62
86
|
* @return {string} Formatted string.
|
|
63
87
|
*/
|
|
64
|
-
function sprintf( string, args ) {
|
|
65
|
-
var i;
|
|
66
88
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Given a format string, returns string with arguments interpolatation.
|
|
91
|
+
* Arguments can either be provided directly via function arguments spread, or
|
|
92
|
+
* with an array as the second argument.
|
|
93
|
+
*
|
|
94
|
+
* @see https://en.wikipedia.org/wiki/Printf_format_string
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
*
|
|
98
|
+
* ```js
|
|
99
|
+
* import sprintf from '@tannin/sprintf';
|
|
100
|
+
*
|
|
101
|
+
* sprintf( 'Hello %s!', 'world' );
|
|
102
|
+
* // ⇒ 'Hello world!'
|
|
103
|
+
* ```
|
|
104
|
+
* @template {string} T
|
|
105
|
+
* @param {T} string - string printf format string
|
|
106
|
+
* @param {...import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
107
|
+
*
|
|
108
|
+
* @return {string} Formatted string.
|
|
109
|
+
*/
|
|
110
|
+
function sprintf(string, ...args) {
|
|
111
|
+
var i = 0;
|
|
112
|
+
if (Array.isArray(args[0])) {
|
|
113
|
+
args = /** @type {import('../types').SprintfArgs<T>[]} */ (
|
|
114
|
+
/** @type {unknown} */ args[0]
|
|
115
|
+
);
|
|
74
116
|
}
|
|
75
117
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
118
|
+
return string.replace(PATTERN, function () {
|
|
119
|
+
var index,
|
|
120
|
+
// name needs to be documented as `string | undefined` else value will have tpye unknown.
|
|
121
|
+
/**
|
|
122
|
+
* Name of the argument to substitute, if any.
|
|
123
|
+
*
|
|
124
|
+
* @type {string | undefined}
|
|
125
|
+
*/
|
|
126
|
+
name,
|
|
127
|
+
precision,
|
|
128
|
+
type,
|
|
129
|
+
value;
|
|
130
|
+
|
|
131
|
+
index = arguments[3];
|
|
132
|
+
name = arguments[5];
|
|
133
|
+
precision = arguments[7];
|
|
134
|
+
type = arguments[9];
|
|
85
135
|
|
|
86
136
|
// There's no placeholder substitution in the explicit "%", meaning it
|
|
87
137
|
// is not necessary to increment argument index.
|
|
88
|
-
if (
|
|
138
|
+
if (type === '%') {
|
|
89
139
|
return '%';
|
|
90
140
|
}
|
|
91
141
|
|
|
92
142
|
// Asterisk precision determined by peeking / shifting next argument.
|
|
93
|
-
if (
|
|
94
|
-
precision = args[
|
|
143
|
+
if (precision === '*') {
|
|
144
|
+
precision = args[i];
|
|
95
145
|
i++;
|
|
96
146
|
}
|
|
97
147
|
|
|
98
|
-
if (
|
|
99
|
-
// If it's a named argument, use name.
|
|
100
|
-
if ( args[ 0 ] && typeof args[ 0 ] === 'object' &&
|
|
101
|
-
args[ 0 ].hasOwnProperty( name ) ) {
|
|
102
|
-
value = args[ 0 ][ name ];
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
148
|
+
if (name === undefined) {
|
|
105
149
|
// If not a positional argument, use counter value.
|
|
106
|
-
if (
|
|
107
|
-
index = i;
|
|
150
|
+
if (index === undefined) {
|
|
151
|
+
index = i + 1;
|
|
108
152
|
}
|
|
109
153
|
|
|
110
154
|
i++;
|
|
111
155
|
|
|
112
156
|
// Positional argument.
|
|
113
|
-
value = args[
|
|
157
|
+
value = args[index - 1];
|
|
158
|
+
} else if (
|
|
159
|
+
args[0] &&
|
|
160
|
+
typeof args[0] === 'object' &&
|
|
161
|
+
args[0].hasOwnProperty(name)
|
|
162
|
+
) {
|
|
163
|
+
// If it's a named argument, use name.
|
|
164
|
+
value = args[0][name];
|
|
114
165
|
}
|
|
115
166
|
|
|
116
167
|
// Parse as type.
|
|
117
|
-
if (
|
|
118
|
-
value = parseFloat(
|
|
119
|
-
} else if (
|
|
120
|
-
value = parseInt(
|
|
168
|
+
if (type === 'f') {
|
|
169
|
+
value = parseFloat(value) || 0;
|
|
170
|
+
} else if (type === 'd') {
|
|
171
|
+
value = parseInt(value) || 0;
|
|
121
172
|
}
|
|
122
173
|
|
|
123
174
|
// Apply precision.
|
|
124
|
-
if (
|
|
125
|
-
if (
|
|
126
|
-
value = value.toFixed(
|
|
127
|
-
} else if (
|
|
128
|
-
value = value.substr(
|
|
175
|
+
if (precision !== undefined) {
|
|
176
|
+
if (type === 'f') {
|
|
177
|
+
value = value.toFixed(precision);
|
|
178
|
+
} else if (type === 's') {
|
|
179
|
+
value = value.substr(0, precision);
|
|
129
180
|
}
|
|
130
181
|
}
|
|
131
182
|
|
|
132
183
|
// To avoid "undefined" concatenation, return empty string if no
|
|
133
184
|
// placeholder substitution can be performed.
|
|
134
185
|
return value !== undefined && value !== null ? value : '';
|
|
135
|
-
}
|
|
186
|
+
});
|
|
136
187
|
}
|
|
137
188
|
|
|
138
189
|
return sprintf;
|
|
139
190
|
|
|
140
|
-
}()
|
|
191
|
+
})();
|
package/dist/sprintf.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var sprintf=function(){"use strict";var
|
|
1
|
+
var sprintf=function(){"use strict";var r=/%(((\d+)\$)|(\(([$_a-zA-Z][$_a-zA-Z0-9]*)\)))?[ +0#-]*\d*(\.(\d+|\*))?(ll|[lhqL])?([cduxXefgsp%])/g;return function(t,...a){var e=0;return Array.isArray(a[0])&&(a=a[0]),t.replace(r,(function(){var r,t,n,o,s;return r=arguments[3],t=arguments[5],"%"===(o=arguments[9])?"%":("*"===(n=arguments[7])&&(n=a[e],e++),void 0===t?(void 0===r&&(r=e+1),e++,s=a[r-1]):a[0]&&"object"==typeof a[0]&&a[0].hasOwnProperty(t)&&(s=a[0][t]),"f"===o?s=parseFloat(s)||0:"d"===o&&(s=parseInt(s)||0),void 0!==n&&("f"===o?s=s.toFixed(n):"s"===o&&(s=s.substr(0,n))),null!=s?s:"")}))}}();
|
package/package.json
CHANGED
|
@@ -1,32 +1,41 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
2
|
+
"name": "@tannin/sprintf",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "printf string formatter",
|
|
6
|
+
"main": "build/index.js",
|
|
7
|
+
"module": "src/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./src/index.js",
|
|
11
|
+
"require": "./build/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"types": "src/index.d.ts",
|
|
15
|
+
"keywords": [],
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Andrew Duthie",
|
|
18
|
+
"email": "andrew@andrewduthie.com",
|
|
19
|
+
"url": "https://andrewduthie.com"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/aduth/tannin",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/aduth/tannin.git",
|
|
25
|
+
"directory": "packages/sprintf"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/aduth/tannin/issues"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"files": [
|
|
32
|
+
"src/index.js",
|
|
33
|
+
"src/index.d.ts",
|
|
34
|
+
"types/index.d.ts",
|
|
35
|
+
"build",
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
}
|
|
32
41
|
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Given a format string, returns string with arguments interpolatation.
|
|
3
|
+
* Arguments can either be provided directly via function arguments spread, or
|
|
4
|
+
* with an array as the second argument.
|
|
5
|
+
*
|
|
6
|
+
* @see https://en.wikipedia.org/wiki/Printf_format_string
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
*
|
|
10
|
+
* ```js
|
|
11
|
+
* import sprintf from '@tannin/sprintf';
|
|
12
|
+
*
|
|
13
|
+
* sprintf( 'Hello %s!', 'world' );
|
|
14
|
+
* // ⇒ 'Hello world!'
|
|
15
|
+
* ```
|
|
16
|
+
* @template {string} T
|
|
17
|
+
* @overload
|
|
18
|
+
* @param {T} string - string printf format string
|
|
19
|
+
* @param {...import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
20
|
+
*
|
|
21
|
+
* @return {string} Formatted string.
|
|
22
|
+
*/
|
|
23
|
+
export default function sprintf<T extends string>(string: T, ...args: import("../types").SprintfArgs<T>): string;
|
|
24
|
+
/**
|
|
25
|
+
* Given a format string, returns string with arguments interpolatation.
|
|
26
|
+
* Arguments can either be provided directly via function arguments spread, or
|
|
27
|
+
* with an array as the second argument.
|
|
28
|
+
*
|
|
29
|
+
* @see https://en.wikipedia.org/wiki/Printf_format_string
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
*
|
|
33
|
+
* ```js
|
|
34
|
+
* import sprintf from '@tannin/sprintf';
|
|
35
|
+
*
|
|
36
|
+
* sprintf( 'Hello %s!', 'world' );
|
|
37
|
+
* // ⇒ 'Hello world!'
|
|
38
|
+
* ```
|
|
39
|
+
* @template {string} T
|
|
40
|
+
* @overload
|
|
41
|
+
* @param {T} string - string printf format string
|
|
42
|
+
* @param {import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
43
|
+
*
|
|
44
|
+
* @return {string} Formatted string.
|
|
45
|
+
*/
|
|
46
|
+
export default function sprintf<T extends string>(string: T, args: import("../types").SprintfArgs<T>): string;
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
*
|
|
30
30
|
* @type {RegExp}
|
|
31
31
|
*/
|
|
32
|
-
var PATTERN =
|
|
32
|
+
var PATTERN =
|
|
33
|
+
/%(((\d+)\$)|(\(([$_a-zA-Z][$_a-zA-Z0-9]*)\)))?[ +0#-]*\d*(\.(\d+|\*))?(ll|[lhqL])?([cduxXefgsp%])/g;
|
|
33
34
|
// ▲ ▲ ▲ ▲ ▲ ▲ ▲ type
|
|
34
35
|
// │ │ │ │ │ └ Length (unsupported)
|
|
35
36
|
// │ │ │ │ └ Precision / max width
|
|
36
37
|
// │ │ │ └ Min width (unsupported)
|
|
37
38
|
// │ │ └ Flags (unsupported)
|
|
38
39
|
// └ Index └ Name (for named arguments)
|
|
39
|
-
|
|
40
40
|
/**
|
|
41
41
|
* Given a format string, returns string with arguments interpolatation.
|
|
42
42
|
* Arguments can either be provided directly via function arguments spread, or
|
|
@@ -52,82 +52,133 @@ var PATTERN = /%(((\d+)\$)|(\(([$_a-zA-Z][$_a-zA-Z0-9]*)\)))?[ +0#-]*\d*(\.(\d+|
|
|
|
52
52
|
* sprintf( 'Hello %s!', 'world' );
|
|
53
53
|
* // ⇒ 'Hello world!'
|
|
54
54
|
* ```
|
|
55
|
+
* @template {string} T
|
|
56
|
+
* @overload
|
|
57
|
+
* @param {T} string - string printf format string
|
|
58
|
+
* @param {...import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
55
59
|
*
|
|
56
|
-
* @
|
|
57
|
-
|
|
60
|
+
* @return {string} Formatted string.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Given a format string, returns string with arguments interpolatation.
|
|
65
|
+
* Arguments can either be provided directly via function arguments spread, or
|
|
66
|
+
* with an array as the second argument.
|
|
67
|
+
*
|
|
68
|
+
* @see https://en.wikipedia.org/wiki/Printf_format_string
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
*
|
|
72
|
+
* ```js
|
|
73
|
+
* import sprintf from '@tannin/sprintf';
|
|
74
|
+
*
|
|
75
|
+
* sprintf( 'Hello %s!', 'world' );
|
|
76
|
+
* // ⇒ 'Hello world!'
|
|
77
|
+
* ```
|
|
78
|
+
* @template {string} T
|
|
79
|
+
* @overload
|
|
80
|
+
* @param {T} string - string printf format string
|
|
81
|
+
* @param {import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
58
82
|
*
|
|
59
83
|
* @return {string} Formatted string.
|
|
60
84
|
*/
|
|
61
|
-
export default function sprintf( string, args ) {
|
|
62
|
-
var i;
|
|
63
85
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Given a format string, returns string with arguments interpolatation.
|
|
88
|
+
* Arguments can either be provided directly via function arguments spread, or
|
|
89
|
+
* with an array as the second argument.
|
|
90
|
+
*
|
|
91
|
+
* @see https://en.wikipedia.org/wiki/Printf_format_string
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
*
|
|
95
|
+
* ```js
|
|
96
|
+
* import sprintf from '@tannin/sprintf';
|
|
97
|
+
*
|
|
98
|
+
* sprintf( 'Hello %s!', 'world' );
|
|
99
|
+
* // ⇒ 'Hello world!'
|
|
100
|
+
* ```
|
|
101
|
+
* @template {string} T
|
|
102
|
+
* @param {T} string - string printf format string
|
|
103
|
+
* @param {...import('../types').SprintfArgs<T>} args - arguments to interpolate
|
|
104
|
+
*
|
|
105
|
+
* @return {string} Formatted string.
|
|
106
|
+
*/
|
|
107
|
+
export default function sprintf(string, ...args) {
|
|
108
|
+
var i = 0;
|
|
109
|
+
if (Array.isArray(args[0])) {
|
|
110
|
+
args = /** @type {import('../types').SprintfArgs<T>[]} */ (
|
|
111
|
+
/** @type {unknown} */ args[0]
|
|
112
|
+
);
|
|
71
113
|
}
|
|
72
114
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
115
|
+
return string.replace(PATTERN, function () {
|
|
116
|
+
var index,
|
|
117
|
+
// name needs to be documented as `string | undefined` else value will have tpye unknown.
|
|
118
|
+
/**
|
|
119
|
+
* Name of the argument to substitute, if any.
|
|
120
|
+
*
|
|
121
|
+
* @type {string | undefined}
|
|
122
|
+
*/
|
|
123
|
+
name,
|
|
124
|
+
precision,
|
|
125
|
+
type,
|
|
126
|
+
value;
|
|
77
127
|
|
|
78
|
-
index = arguments[
|
|
79
|
-
name = arguments[
|
|
80
|
-
precision = arguments[
|
|
81
|
-
type = arguments[
|
|
128
|
+
index = arguments[3];
|
|
129
|
+
name = arguments[5];
|
|
130
|
+
precision = arguments[7];
|
|
131
|
+
type = arguments[9];
|
|
82
132
|
|
|
83
133
|
// There's no placeholder substitution in the explicit "%", meaning it
|
|
84
134
|
// is not necessary to increment argument index.
|
|
85
|
-
if (
|
|
135
|
+
if (type === '%') {
|
|
86
136
|
return '%';
|
|
87
137
|
}
|
|
88
138
|
|
|
89
139
|
// Asterisk precision determined by peeking / shifting next argument.
|
|
90
|
-
if (
|
|
91
|
-
precision = args[
|
|
140
|
+
if (precision === '*') {
|
|
141
|
+
precision = args[i];
|
|
92
142
|
i++;
|
|
93
143
|
}
|
|
94
144
|
|
|
95
|
-
if (
|
|
96
|
-
// If it's a named argument, use name.
|
|
97
|
-
if ( args[ 0 ] && typeof args[ 0 ] === 'object' &&
|
|
98
|
-
args[ 0 ].hasOwnProperty( name ) ) {
|
|
99
|
-
value = args[ 0 ][ name ];
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
145
|
+
if (name === undefined) {
|
|
102
146
|
// If not a positional argument, use counter value.
|
|
103
|
-
if (
|
|
104
|
-
index = i;
|
|
147
|
+
if (index === undefined) {
|
|
148
|
+
index = i + 1;
|
|
105
149
|
}
|
|
106
150
|
|
|
107
151
|
i++;
|
|
108
152
|
|
|
109
153
|
// Positional argument.
|
|
110
|
-
value = args[
|
|
154
|
+
value = args[index - 1];
|
|
155
|
+
} else if (
|
|
156
|
+
args[0] &&
|
|
157
|
+
typeof args[0] === 'object' &&
|
|
158
|
+
args[0].hasOwnProperty(name)
|
|
159
|
+
) {
|
|
160
|
+
// If it's a named argument, use name.
|
|
161
|
+
value = args[0][name];
|
|
111
162
|
}
|
|
112
163
|
|
|
113
164
|
// Parse as type.
|
|
114
|
-
if (
|
|
115
|
-
value = parseFloat(
|
|
116
|
-
} else if (
|
|
117
|
-
value = parseInt(
|
|
165
|
+
if (type === 'f') {
|
|
166
|
+
value = parseFloat(value) || 0;
|
|
167
|
+
} else if (type === 'd') {
|
|
168
|
+
value = parseInt(value) || 0;
|
|
118
169
|
}
|
|
119
170
|
|
|
120
171
|
// Apply precision.
|
|
121
|
-
if (
|
|
122
|
-
if (
|
|
123
|
-
value = value.toFixed(
|
|
124
|
-
} else if (
|
|
125
|
-
value = value.substr(
|
|
172
|
+
if (precision !== undefined) {
|
|
173
|
+
if (type === 'f') {
|
|
174
|
+
value = value.toFixed(precision);
|
|
175
|
+
} else if (type === 's') {
|
|
176
|
+
value = value.substr(0, precision);
|
|
126
177
|
}
|
|
127
178
|
}
|
|
128
179
|
|
|
129
180
|
// To avoid "undefined" concatenation, return empty string if no
|
|
130
181
|
// placeholder substitution can be performed.
|
|
131
182
|
return value !== undefined && value !== null ? value : '';
|
|
132
|
-
}
|
|
183
|
+
});
|
|
133
184
|
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
type Specifiers = {
|
|
2
|
+
s: string;
|
|
3
|
+
d: number;
|
|
4
|
+
f: number;
|
|
5
|
+
};
|
|
6
|
+
type S = keyof Specifiers;
|
|
7
|
+
|
|
8
|
+
type BuildTuple<L extends number, T extends any[] = []> = T['length'] extends L
|
|
9
|
+
? T
|
|
10
|
+
: BuildTuple<L, [any, ...T]>;
|
|
11
|
+
|
|
12
|
+
type Subtract1<N extends number> = BuildTuple<N> extends [any, ...infer Rest]
|
|
13
|
+
? Rest['length']
|
|
14
|
+
: never;
|
|
15
|
+
|
|
16
|
+
type StripEscapedPercents<T extends string> =
|
|
17
|
+
T extends `${infer Head}%%${infer Tail}`
|
|
18
|
+
? `${Head}${StripEscapedPercents<Tail>}`
|
|
19
|
+
: T;
|
|
20
|
+
|
|
21
|
+
type HasNamedPlaceholders<T extends string> =
|
|
22
|
+
StripEscapedPercents<T> extends `${any}%(${string})${S}${string}`
|
|
23
|
+
? true
|
|
24
|
+
: false;
|
|
25
|
+
|
|
26
|
+
type HasPositionalPlaceholders<T extends string> =
|
|
27
|
+
StripEscapedPercents<T> extends `${any}%${number}$${S}${string}`
|
|
28
|
+
? true
|
|
29
|
+
: false;
|
|
30
|
+
|
|
31
|
+
type HasUnnamedPlaceholders<T extends string> =
|
|
32
|
+
StripEscapedPercents<T> extends `${any}%${S}${string}` ? true : false;
|
|
33
|
+
|
|
34
|
+
type HasDynamicPrecisionPlaceholders<T extends string> =
|
|
35
|
+
StripEscapedPercents<T> extends `${any}%.*${S}${any}` ? true : false;
|
|
36
|
+
|
|
37
|
+
type HasStaticPrecisionPlaceholders<T extends string> =
|
|
38
|
+
StripEscapedPercents<T> extends `${any}%.${number}${S}${any}` ? true : false;
|
|
39
|
+
|
|
40
|
+
type ExtractNamedPlaceholders<T extends string> =
|
|
41
|
+
StripEscapedPercents<T> extends `${any}%(${infer Key})${infer Spec}${infer Rest}`
|
|
42
|
+
? Spec extends S
|
|
43
|
+
? { [K in Key]: Specifiers[Spec] } & ExtractNamedPlaceholders<Rest>
|
|
44
|
+
: never
|
|
45
|
+
: {};
|
|
46
|
+
|
|
47
|
+
type ExtractPositionalPlaceholders<T extends string> =
|
|
48
|
+
StripEscapedPercents<T> extends `${any}%${infer Index extends number}$${infer Spec}${infer Rest}`
|
|
49
|
+
? Spec extends S
|
|
50
|
+
? {
|
|
51
|
+
[K in Subtract1<Index>]: Specifiers[Spec];
|
|
52
|
+
} & ExtractPositionalPlaceholders<Rest>
|
|
53
|
+
: ExtractPositionalPlaceholders<Rest>
|
|
54
|
+
: unknown[];
|
|
55
|
+
|
|
56
|
+
type ExtractStaticPrecisionPlaceholders<T extends string> =
|
|
57
|
+
StripEscapedPercents<T> extends `${any}%.${infer Precision extends number}${infer Spec}${infer Rest}`
|
|
58
|
+
? Spec extends S
|
|
59
|
+
? [Specifiers[Spec], ...ExtractStaticPrecisionPlaceholders<Rest>]
|
|
60
|
+
: never
|
|
61
|
+
: [];
|
|
62
|
+
|
|
63
|
+
type ExtractDynamicPrecisionPlaceholder<T extends string> =
|
|
64
|
+
StripEscapedPercents<T> extends `${any}%.*${infer Spec}${infer Rest}`
|
|
65
|
+
? Spec extends S
|
|
66
|
+
? [number, Specifiers[Spec], ...ExtractDynamicPrecisionPlaceholder<Rest>]
|
|
67
|
+
: never
|
|
68
|
+
: [];
|
|
69
|
+
|
|
70
|
+
type ExtractUnnamedPlaceholders<T extends string> =
|
|
71
|
+
StripEscapedPercents<T> extends `${any}%${infer Spec}${infer Rest}`
|
|
72
|
+
? Spec extends S
|
|
73
|
+
? [Specifiers[Spec], ...ExtractUnnamedPlaceholders<Rest>]
|
|
74
|
+
: never
|
|
75
|
+
: [];
|
|
76
|
+
|
|
77
|
+
export type SprintfArgs<T extends string> = HasNamedPlaceholders<T> extends true
|
|
78
|
+
? [values: ExtractNamedPlaceholders<T>]
|
|
79
|
+
: HasDynamicPrecisionPlaceholders<T> extends true
|
|
80
|
+
? ExtractDynamicPrecisionPlaceholder<T>
|
|
81
|
+
: HasStaticPrecisionPlaceholders<T> extends true
|
|
82
|
+
? ExtractStaticPrecisionPlaceholders<T>
|
|
83
|
+
: HasPositionalPlaceholders<T> extends true
|
|
84
|
+
? ExtractPositionalPlaceholders<T>
|
|
85
|
+
: HasUnnamedPlaceholders<T> extends true
|
|
86
|
+
? ExtractUnnamedPlaceholders<T>
|
|
87
|
+
: [];
|
package/CHANGELOG.md
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
## 1.1.0 (2019-11-27)
|
|
2
|
-
|
|
3
|
-
New Features
|
|
4
|
-
|
|
5
|
-
- Add support for named arguments
|
|
6
|
-
|
|
7
|
-
Bugfix
|
|
8
|
-
|
|
9
|
-
- Fix handling of falsy values, notably 0
|
|
10
|
-
|
|
11
|
-
## 1.0.2 (2019-03-07)
|
|
12
|
-
|
|
13
|
-
Internal
|
|
14
|
-
|
|
15
|
-
- Add `repository.directory` to `package.json`
|
|
16
|
-
|
|
17
|
-
## 1.0.1 (2019-01-19)
|
|
18
|
-
|
|
19
|
-
Documentation
|
|
20
|
-
|
|
21
|
-
- Correct documentation unpkg links
|
|
22
|
-
|
|
23
|
-
Internal
|
|
24
|
-
|
|
25
|
-
- Use Lerna for managing mono-repo
|
package/LICENSE.md
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
[The MIT License (MIT)](https://opensource.org/licenses/MIT)
|
|
2
|
-
|
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
-
|
|
5
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
-
|
|
7
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|