@yuhere/js-strings 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) @yuhere
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # String Utilities
2
+
3
+ A utility library aimed at providing common language-level helper functions (strings, dates, vars, etc.) in JavaScript/TypeScript projects.
4
+
5
+ ## Installation
6
+
7
+ Install the package via npm:
8
+
9
+ ```bash
10
+ npm install @yuhere/js-strings
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Import the functions you need from the library:
16
+
17
+ ```javascript
18
+ import {
19
+ s_pad,
20
+ s_left,
21
+ s_right,
22
+ s_ellipsis,
23
+ s_ellipsis_o,
24
+ s_xprint_extract_fmt,
25
+ s_xprint_fmts,
26
+ s_xprint,
27
+ s_glob
28
+ } from '@yuhere/js-strings';
29
+ ```
30
+
31
+ ### String Utilities
32
+
33
+ #### s_pad
34
+
35
+ Pad a string to a fixed width.
36
+
37
+ - `which`: padding direction, supports `'l'`, `'r'`, `'c'`
38
+ - `padchar`: padding character, default is space
39
+ - `is_trunc`: when `true`, truncates the string if it exceeds `width`
40
+
41
+ ```javascript
42
+ import { s_pad } from '@yuhere/js-strings';
43
+
44
+ console.log(s_pad('hello', 10, 'l', '-')); // '-----hello'
45
+ console.log(s_pad('hello', 10, 'r', '-')); // 'hello-----'
46
+ console.log(s_pad('hello', 10, 'c')); // ' hello '
47
+ console.log(s_pad('hello world', 5, 'r', ' ', true)); // 'hello'
48
+ ```
49
+
50
+ #### s_left
51
+
52
+ Get the leftmost characters of a string.
53
+
54
+ ```javascript
55
+ import { s_left } from '@yuhere/js-strings';
56
+
57
+ console.log(s_left('hello world', 5)); // 'hello'
58
+ console.log(s_left('hello', 0)); // ''
59
+ ```
60
+
61
+ #### s_right
62
+
63
+ Get the rightmost characters of a string.
64
+
65
+ ```javascript
66
+ import { s_right } from '@yuhere/js-strings';
67
+
68
+ console.log(s_right('hello world', 5)); // 'world'
69
+ console.log(s_right('hello', 0)); // ''
70
+ ```
71
+
72
+ #### s_ellipsis
73
+
74
+ Pad or truncate a string to a fixed width. When truncation happens, `...` is inserted according to the alignment.
75
+
76
+ - `'l'`: keep the left part and put `...` at the end
77
+ - `'r'`: keep the right part and put `...` at the beginning
78
+ - `'c'`: keep both ends and put `...` in the middle
79
+
80
+ ```javascript
81
+ import { s_ellipsis } from '@yuhere/js-strings';
82
+
83
+ console.log(s_ellipsis('hello', 8)); // 'hello '
84
+ console.log(s_ellipsis('abcdefghijklmnopqrstuvwxyz', 10, 'l')); // 'abcdefg...'
85
+ console.log(s_ellipsis('abcdefghijklmnopqrstuvwxyz', 10, 'r')); // '...tuvwxyz'
86
+ console.log(s_ellipsis('abcdefghijklmnopqrstuvwxyz', 10, 'c')); // 'abcd...xyz'
87
+ ```
88
+
89
+ #### s_ellipsis_o
90
+
91
+ Options-object form of `s_ellipsis`, suitable when parameters come from configuration.
92
+
93
+ ```javascript
94
+ import { s_ellipsis_o } from '@yuhere/js-strings';
95
+
96
+ console.log(s_ellipsis_o('hello', { width: 8 })); // 'hello '
97
+ console.log(s_ellipsis_o('abcdefghijklmnopqrstuvwxyz', { width: 10, align: 'r' })); // '...tuvwxyz'
98
+ ```
99
+
100
+ #### s_xprint_extract_fmt
101
+
102
+ Parse a column format definition used by `s_xprint`.
103
+
104
+ - string form: `"<width>[<align><padchar>]"`
105
+ - object form: `{ width, align, padchar }`
106
+
107
+ ```javascript
108
+ import { s_xprint_extract_fmt } from '@yuhere/js-strings';
109
+
110
+ console.log(s_xprint_extract_fmt('8')); // { width: 8, align: 'l', padchar: undefined }
111
+ console.log(s_xprint_extract_fmt('8r0')); // { width: 8, align: 'r', padchar: '0' }
112
+ console.log(s_xprint_extract_fmt({ width: 12, align: 'l', padchar: '_' }));
113
+ // { width: 12, align: 'l', padchar: '_' }
114
+ ```
115
+
116
+ #### s_xprint_fmts
117
+
118
+ Batch-convert multiple format definitions into normalized format objects.
119
+
120
+ ```javascript
121
+ import { s_xprint_fmts } from '@yuhere/js-strings';
122
+
123
+ console.log(s_xprint_fmts(['8', '6r0', { width: 10, align: 'l', padchar: '_' }]));
124
+ // [
125
+ // { width: 8, align: 'l', padchar: undefined },
126
+ // { width: 6, align: 'r', padchar: '0' },
127
+ // { width: 10, align: 'l', padchar: '_' }
128
+ // ]
129
+ ```
130
+
131
+ #### s_xprint
132
+
133
+ Format multiple columns into one line of fixed-width text. This is useful for CLI table rows, aligned logs, and report output.
134
+
135
+ - `columns`: values to output
136
+ - `formats`: per-column format definitions
137
+ - `sep`: separator inserted between columns
138
+ - `ellipsisEnd`: whether the last column should also be padded/truncated
139
+
140
+ ```javascript
141
+ import { s_xprint } from '@yuhere/js-strings';
142
+
143
+ console.log(
144
+ s_xprint({
145
+ columns: ['id', 7, 'abcdefghijklmnopqrstuvwxyz'],
146
+ formats: ['6l ', '4r0', '10l '],
147
+ sep: ' | '
148
+ })
149
+ );
150
+ // 'id | 0007 | abcdefg...'
151
+
152
+ console.log(
153
+ s_xprint({
154
+ columns: ['INFO', 'startup ok', 'this part is not truncated'],
155
+ formats: ['6l ', '12l ', '8l '],
156
+ sep: ' ',
157
+ ellipsisEnd: false
158
+ })
159
+ );
160
+ // 'INFO startup ok this part is not truncated'
161
+ ```
162
+
163
+ #### s_glob
164
+
165
+ Match a string against a simple glob pattern.
166
+
167
+ Current public wrapper behavior uses the default glob conversion rules, which are suitable for common `*` pattern matching.
168
+
169
+ ```javascript
170
+ import { s_glob } from '@yuhere/js-strings';
171
+
172
+ console.log(s_glob('*.ts', 'index.ts')); // true
173
+ console.log(s_glob('*.ts', 'index.js')); // false
174
+ console.log(s_glob('src/*', 'src/strings.ts')); // true
175
+ ```
176
+
177
+ ## License
178
+
179
+ MIT
@@ -0,0 +1,228 @@
1
+ type SAlign = "l" | "r" | "c";
2
+ type SXPrintAlign = "l" | "r";
3
+ interface SEllipsisOptions {
4
+ width: number;
5
+ align?: SAlign;
6
+ padchar?: string;
7
+ }
8
+ interface SXPrintFormat {
9
+ width: number;
10
+ align?: SXPrintAlign;
11
+ padchar?: string;
12
+ }
13
+ interface SXPrintParams {
14
+ columns: Array<string | number | null | undefined>;
15
+ formats?: Array<string | SXPrintFormat>;
16
+ sep?: string;
17
+ ellipsisEnd?: boolean;
18
+ }
19
+ /**
20
+ * String padding.
21
+ *
22
+ * @param text - Input text.
23
+ * @param width - Target width. If `undefined`, defaults to `text.length`.
24
+ * @param which - Padding side:
25
+ * - `"r"`: pad on the right (left-justified)
26
+ * - `"l"`: pad on the left (right-justified)
27
+ * - `"c"`: pad both sides (center)
28
+ * @param padchar - Padding character (single-column). Defaults to `" "`.
29
+ * @param is_trunc - If `true`, truncate `text` when it exceeds `width`.
30
+ * @returns Padded (or truncated) string with length `width`.
31
+ */
32
+ export declare function s_pad(text: string, width?: number, which?: SAlign, padchar?: string, is_trunc?: boolean): string;
33
+ /**
34
+ * Returns a string containing the specified number of characters from the left side of `str`.
35
+ *
36
+ * @param str - The source string.
37
+ * @param length - Number of characters to return.
38
+ * - If `length <= 0`, returns an empty string (`""`).
39
+ * - If `length >= str.length`, returns the entire `str`.
40
+ * @returns The leftmost `length` characters of `str`.
41
+ */
42
+ export declare function s_left(str: string, length: number): string;
43
+ /**
44
+ * Returns a string containing the specified number of characters from the right side of `str`.
45
+ *
46
+ * @param str - The source string.
47
+ * @param length - Number of characters to return.
48
+ * - If `length <= 0`, returns an empty string (`""`).
49
+ * - If `length >= str.length`, returns the entire `str`.
50
+ * @returns The rightmost `length` characters of `str`.
51
+ */
52
+ export declare function s_right(str: string, length: number): string;
53
+ /**
54
+ * Truncate or pad a string to a fixed width, using an ellipsis (`"..."`) when truncation happens.
55
+ *
56
+ * - If `str.length < width`, pads with `padchar` according to `align`.
57
+ * - If `str.length > width`, truncates and inserts `"..."` based on `align`:
58
+ * - `"l"`: keep left part + `"..."` at end
59
+ * - `"r"`: `"..."` at start + keep right part
60
+ * - `"c"`: keep head and tail with `"..."` in the middle
61
+ *
62
+ * @param str - Input value to format. Non-string values are coerced to string.
63
+ * @param width - Target width (in characters).
64
+ * @param align - Alignment mode: `"l"` (left), `"r"` (right), `"c"` (center). Defaults to `"l"`.
65
+ * @param padchar - Padding character used when `str` is shorter than `width`. Defaults to `" "`.
66
+ * @returns The formatted string with exact length `width` (unless `width` is too small to hold content).
67
+ */
68
+ export declare function s_ellipsis(str: string, width: number, align?: SAlign, padchar?: string): string;
69
+ /**
70
+ * Convenience wrapper around {@link s_ellipsis} that accepts an options object.
71
+ *
72
+ * @param str - Input value to format. Non-string values are coerced to string by {@link s_ellipsis}.
73
+ * @param opts - Formatting options.
74
+ * @param opts.width - Target width (in characters).
75
+ * @param opts.align - Alignment mode: `"l"` (left), `"r"` (right), `"c"` (center).
76
+ * @param opts.padchar - Padding character used when `str` is shorter than `width`.
77
+ * @returns The formatted string as produced by {@link s_ellipsis}.
78
+ *
79
+ * @example
80
+ * // Pad (left aligned) to width 8 with spaces
81
+ * s_ellipsis_o("abc", { width: 8, align: "l", padchar: " " })
82
+ * // => "abc "
83
+ *
84
+ * @example
85
+ * // Pad (right aligned) to width 8 with '0'
86
+ * s_ellipsis_o("abc", { width: 8, align: "r", padchar: "0" })
87
+ * // => "00000abc"
88
+ *
89
+ * @example
90
+ * // Truncate with ellipsis at end (left align behavior for truncation)
91
+ * s_ellipsis_o("abcdefghijklmnopqrstuvwxyz", { width: 10, align: "l", padchar: " " })
92
+ * // => "abcdefg..."
93
+ *
94
+ * @example
95
+ * // Truncate with ellipsis at start (right align behavior for truncation)
96
+ * s_ellipsis_o("abcdefghijklmnopqrstuvwxyz", { width: 10, align: "r", padchar: " " })
97
+ * // => "...tuvwxyz"
98
+ *
99
+ * @example
100
+ * // Truncate with ellipsis in the middle (center)
101
+ * s_ellipsis_o("abcdefghijklmnopqrstuvwxyz", { width: 10, align: "c", padchar: " " })
102
+ * // => "abcd...xyz"
103
+ */
104
+ export declare function s_ellipsis_o(str: string, opts: SEllipsisOptions): string;
105
+ /**
106
+ * Parse an `s_xprint` format specifier into a normalized format object.
107
+ *
108
+ * Supports either:
109
+ * - A compact string spec: `"<width>[<align><padchar>]"`, where:
110
+ * - `width` is a positive integer (required)
111
+ * - `align` is optional: `"l"` (left) or `"r"` (right)
112
+ * - `padchar` is optional: a single character used for padding
113
+ * - An already-constructed format object, which is returned as-is.
114
+ *
115
+ * The string form is matched by: `/^(\\d+)(?:([lr])(.?))?$/`
116
+ *
117
+ * @param fmt - Format specifier as a string (e.g. `"10l_"`) or a format object
118
+ * (e.g. `{ width: 10, align: "l", padchar: "_" }`).
119
+ * @returns A format object `{ width, align, padchar }`.
120
+ * @throws {Error} If `fmt` is a string but does not match the expected format.
121
+ *
122
+ * @example
123
+ * // String form: width only
124
+ * s_xprint_extract_fmt("8")
125
+ * // => { width: 8, align: undefined, padchar: undefined }
126
+ *
127
+ * @example
128
+ * // String form: width + align
129
+ * s_xprint_extract_fmt("8l")
130
+ * // => { width: 8, align: "l", padchar: "" }
131
+ *
132
+ * @example
133
+ * // String form: width + align + padchar
134
+ * s_xprint_extract_fmt("8r0")
135
+ * // => { width: 8, align: "r", padchar: "0" }
136
+ *
137
+ * @example
138
+ * // Object form: returned unchanged
139
+ * s_xprint_extract_fmt({ width: 12, align: "l", padchar: " " })
140
+ * // => { width: 12, align: "l", padchar: " " }
141
+ */
142
+ export declare function s_xprint_extract_fmt(fmt: string | SXPrintFormat): SXPrintFormat;
143
+ /**
144
+ * Normalize a list of `s_xprint` format specifiers into format objects.
145
+ *
146
+ * This is a convenience helper that applies {@link s_xprint_extract_fmt} to each
147
+ * element in `fmts`.
148
+ *
149
+ * Supported input items:
150
+ * - String form: `"<width>[<align><padchar>]"`, e.g. `"8"`, `"10l"`, `"6r0"`
151
+ * - Object form: `{ width, align, padchar }`
152
+ *
153
+ * @param fmts - An array of format specifiers (strings or format objects).
154
+ * @returns An array of normalized format objects: `{ width, align, padchar }`.
155
+ * @throws {Error} If any string format does not match the expected pattern.
156
+ *
157
+ * @example
158
+ * // String formats
159
+ * s_xprint_fmts(["8", "10l_", "6r0"])
160
+ * // => [
161
+ * // { width: 8, align: undefined, padchar: undefined },
162
+ * // { width: 10, align: "l", padchar: "_" },
163
+ * // { width: 6, align: "r", padchar: "0" }
164
+ * // ]
165
+ *
166
+ * @example
167
+ * // Mixed formats (object formats are returned as-is)
168
+ * s_xprint_fmts([{ width: 4, align: "l", padchar: " " }, "3r0"])
169
+ * // => [
170
+ * // { width: 4, align: "l", padchar: " " },
171
+ * // { width: 3, align: "r", padchar: "0" }
172
+ * // ]
173
+ */
174
+ export declare function s_xprint_fmts(fmts: Array<string | SXPrintFormat>): SXPrintFormat[];
175
+ /**
176
+ * Format and join a list of columns into a single fixed-width string (table-like output).
177
+ *
178
+ * Each column is formatted using {@link s_ellipsis} with the corresponding `formats` entry.
179
+ * Columns are then joined by `sep`.
180
+ *
181
+ * - `formats` entries accept the same specifiers as {@link s_xprint_extract_fmt}:
182
+ * - String form: `"<width>[<align><padchar>]"`, e.g. `"10"`, `"8l_"`, `"6r0"`
183
+ * - Object form: `{ width, align, padchar }`
184
+ * - The last column can optionally skip ellipsis formatting when `ellipsisEnd === false`.
185
+ *
186
+ * @param params - Options object.
187
+ * @param params.columns - Column values to print. `null`/`undefined` become `""`.
188
+ * @param params.formats - Per-column format specifiers (same length as `columns` is recommended).
189
+ * @param params.sep - Separator string inserted between formatted columns. Defaults to `" "`.
190
+ * @param params.ellipsisEnd - Whether to apply ellipsis formatting to the last column.
191
+ * Defaults to `true`. When `false`, the last column is returned as-is (no padding/truncation).
192
+ * @returns A single formatted line string.
193
+ *
194
+ * @example
195
+ * // Basic usage with string formats (width + align + padchar)
196
+ * // - 1st: width 6, left align, pad with space
197
+ * // - 2nd: width 4, right align, pad with '0'
198
+ * // - 3rd: width 10, left align (will ellipsis if too long)
199
+ * s_xprint({
200
+ * columns: ["id", 7, "abcdefghijklmnopqrstuvwxyz"],
201
+ * formats: ["6l ", "4r0", "10l "],
202
+ * sep: " | "
203
+ * })
204
+ * // => "id | 0007 | abcdefg..."
205
+ *
206
+ * @example
207
+ * // Skip formatting for the last column (useful for "message" / "tail" fields)
208
+ * s_xprint({
209
+ * columns: ["INFO", "startup ok", "this part is not truncated"],
210
+ * formats: ["6l ", "12l ", "8l "],
211
+ * sep: " ",
212
+ * ellipsisEnd: false
213
+ * })
214
+ * // => "INFO startup ok this part is not truncated"
215
+ */
216
+ export declare function s_xprint({ columns, formats, sep, ellipsisEnd }?: SXPrintParams): string;
217
+ /**
218
+ * Test whether a string matches a glob pattern.
219
+ *
220
+ * This is a small wrapper around {@link glob_to_regexp} that converts the given
221
+ * glob `pattern` to a {@link RegExp} and then checks it against `str`.
222
+ *
223
+ * @param pattern - Glob pattern e.g. `"*.ts"`.
224
+ * @param str - The input string to test.
225
+ * @returns `true` if `str` matches `pattern`; otherwise `false`.
226
+ */
227
+ export declare function s_glob(pattern: string, str: string): boolean;
228
+ export {};
package/lib/strings.js ADDED
@@ -0,0 +1,200 @@
1
+ function s_pad(text, width, which = "l", padchar = " ", is_trunc = false) {
2
+ if (typeof width !== "number") width = text.length;
3
+ let w = text.length;
4
+ if (is_trunc && w > width) {
5
+ text = text.substring(0, width);
6
+ w = width;
7
+ } else {
8
+ var r = Math.max(0, width - w);
9
+ if (which === "l") {
10
+ text = padchar.repeat(r) + text;
11
+ } else if (which === "c") {
12
+ let n = Math.floor(r / 2);
13
+ text = padchar.repeat(n) + text + padchar.repeat(r - n);
14
+ } else {
15
+ text = text + padchar.repeat(r);
16
+ }
17
+ }
18
+ return text;
19
+ }
20
+ function s_left(str, length) {
21
+ if (length <= 0) return "";
22
+ let str_length = str.length;
23
+ if (length > str_length) {
24
+ return str;
25
+ }
26
+ return str.substring(0, length);
27
+ }
28
+ function s_right(str, length) {
29
+ if (length <= 0) return "";
30
+ let str_length = str.length;
31
+ if (length > str_length) {
32
+ return str;
33
+ }
34
+ return str.substring(str_length - length);
35
+ }
36
+ function s_ellipsis(str, width, align = "l", padchar = " ") {
37
+ str = typeof str === "string" ? str : str + "";
38
+ width = Number.isFinite(width) ? Math.max(0, Math.trunc(width)) : 0;
39
+ if (width === 0) return "";
40
+ align = align != void 0 ? align : "l";
41
+ const _align = align.substring(0, 1);
42
+ align = _align === "l" || _align === "r" || _align === "c" ? _align : "l";
43
+ padchar = typeof padchar === "string" ? padchar ? padchar : " " : " ";
44
+ str = str.replace(/\r ?\n/g, "");
45
+ let length = str.length;
46
+ if (length === width) {
47
+ return str;
48
+ } else if (length < width) {
49
+ if (align === "r") {
50
+ str = s_pad(str, width, "l", padchar);
51
+ } else if (align === "l") {
52
+ str = s_pad(str, width, "r", padchar);
53
+ } else {
54
+ str = s_pad(str, width, "c", padchar);
55
+ }
56
+ } else {
57
+ if (width <= 3) {
58
+ return ".".repeat(width);
59
+ }
60
+ if (align === "r") {
61
+ str = "..." + s_right(str, width - 3);
62
+ } else if (align === "c") {
63
+ let m_odd = width % 2;
64
+ let m_mid_len = Math.floor(width / 2);
65
+ let head_str = s_left(str, m_mid_len - 1);
66
+ let tail_str = s_right(str, m_mid_len - (m_odd ? 1 : 2));
67
+ str = head_str + "..." + tail_str;
68
+ } else {
69
+ str = s_left(str, width - 3) + "...";
70
+ }
71
+ }
72
+ return str;
73
+ }
74
+ function s_ellipsis_o(str, opts) {
75
+ const { width, align, padchar } = opts;
76
+ return s_ellipsis(str, width, align, padchar);
77
+ }
78
+ function s_xprint_extract_fmt(fmt) {
79
+ if (typeof fmt === "string") {
80
+ const mmm = /^(\d+)(?:([lr])(.?))?$/.exec(fmt);
81
+ if (mmm == null) {
82
+ throw Error("wrong foramt of s_xprint [" + fmt + "]");
83
+ }
84
+ const _align = mmm[2] === "l" || mmm[2] === "r" ? mmm[2] : "l";
85
+ return {
86
+ width: parseInt(mmm[1], 10),
87
+ align: _align,
88
+ padchar: mmm[3]
89
+ };
90
+ } else {
91
+ return fmt;
92
+ }
93
+ }
94
+ function s_xprint_fmts(fmts) {
95
+ return fmts.map(s_xprint_extract_fmt);
96
+ }
97
+ function s_xprint({ columns, formats = [], sep = " ", ellipsisEnd = true } = { columns: [], formats: [], sep: " ", ellipsisEnd: true }) {
98
+ const fmts = formats.map(s_xprint_extract_fmt);
99
+ return columns.map((column, idx) => {
100
+ const column_str = column == null ? "" : String(column);
101
+ const fmt = fmts[idx] ?? { width: column_str.length, align: "l", padchar: " " };
102
+ const { width, align, padchar } = fmt;
103
+ if (!ellipsisEnd && idx === columns.length - 1) {
104
+ return column_str;
105
+ } else {
106
+ return s_ellipsis(column_str, width, align, padchar);
107
+ }
108
+ }).join(sep);
109
+ }
110
+ function glob_to_regexp(glob, opts = {}) {
111
+ if (typeof glob !== "string") {
112
+ throw new TypeError("Expected a string");
113
+ }
114
+ var str = String(glob);
115
+ var reStr = "";
116
+ var extended = opts ? !!opts.extended : false;
117
+ var globstar = opts ? !!opts.globstar : false;
118
+ var inGroup = false;
119
+ var flags = opts && typeof opts.flags === "string" ? opts.flags : "";
120
+ var c;
121
+ for (var i = 0, len = str.length; i < len; i++) {
122
+ c = str[i];
123
+ switch (c) {
124
+ case "/":
125
+ case "$":
126
+ case "^":
127
+ case "+":
128
+ case ".":
129
+ case "(":
130
+ case ")":
131
+ case "=":
132
+ case "!":
133
+ case "|":
134
+ reStr += "\\" + c;
135
+ break;
136
+ case "?":
137
+ if (extended) {
138
+ reStr += ".";
139
+ break;
140
+ }
141
+ case "[":
142
+ case "]":
143
+ if (extended) {
144
+ reStr += c;
145
+ break;
146
+ }
147
+ case "{":
148
+ if (extended) {
149
+ inGroup = true;
150
+ reStr += "(";
151
+ break;
152
+ }
153
+ case "}":
154
+ if (extended) {
155
+ inGroup = false;
156
+ reStr += ")";
157
+ break;
158
+ }
159
+ case ",":
160
+ if (inGroup) {
161
+ reStr += "|";
162
+ break;
163
+ }
164
+ reStr += "\\" + c;
165
+ break;
166
+ case "*":
167
+ var prevChar = str[i - 1];
168
+ var starCount = 1;
169
+ while (str[i + 1] === "*") {
170
+ starCount++;
171
+ i++;
172
+ }
173
+ var nextChar = str[i + 1];
174
+ if (!globstar) {
175
+ reStr += ".*";
176
+ } else {
177
+ var isGlobstar = starCount > 1 && (prevChar === "/" || prevChar === void 0) && (nextChar === "/" || nextChar === void 0);
178
+ if (isGlobstar) {
179
+ reStr += "((?:[^/]*(?:/|$))*)";
180
+ i++;
181
+ } else {
182
+ reStr += "([^/]*)";
183
+ }
184
+ }
185
+ break;
186
+ default:
187
+ reStr += c;
188
+ }
189
+ }
190
+ if (!flags || !~flags.indexOf("g")) {
191
+ reStr = "^" + reStr + "$";
192
+ }
193
+ return new RegExp(reStr, flags);
194
+ }
195
+ function s_glob(pattern, str) {
196
+ const regex = glob_to_regexp(pattern, {});
197
+ return regex.test(str);
198
+ }
199
+
200
+ export { s_ellipsis, s_ellipsis_o, s_glob, s_left, s_pad, s_right, s_xprint, s_xprint_extract_fmt, s_xprint_fmts };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@yuhere/js-strings",
3
+ "type": "module",
4
+ "description": "utility library aimed at providing common helper functions for strings in JavaScript/TypeScript projects.",
5
+ "version": "1.0.2",
6
+ "publisher": "yuhere",
7
+ "repository": "https://github.com/yuhere/js-strings.git",
8
+ "license": "MIT",
9
+ "main": "lib/strings.js",
10
+ "module": "lib/strings.js",
11
+ "types": "lib/strings.d.ts",
12
+ "files": [
13
+ "bin/",
14
+ "lib/",
15
+ "src/",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "build": "npm-run-all -p build:*",
21
+ "build:rollup": "rollup --config rollup.config.js",
22
+ "build:types": "tsc -p tsconfig.types.json",
23
+ "lint": "eslint src",
24
+ "test": "npm run build && npx c8 mocha"
25
+ },
26
+ "devDependencies": {
27
+ "@types/chai": "^5.2.3",
28
+ "@types/mocha": "^10.0.10",
29
+ "@types/node": "^24.6.1",
30
+ "@typescript-eslint/eslint-plugin": "^7.14.1",
31
+ "@typescript-eslint/parser": "^7.11.0",
32
+ "eslint": "^8.57.0",
33
+ "npm-run-all": "^4.1.5",
34
+ "rollup": "^4.53.3",
35
+ "c8": "^10.1.3",
36
+ "chai": "^4.3.7",
37
+ "mocha": "^10.2.0",
38
+ "typescript": "^5.9.3",
39
+ "tsx": "^4.21.0",
40
+ "rollup-plugin-esbuild": "^6.2.1"
41
+ }
42
+ }
package/src/strings.ts ADDED
@@ -0,0 +1,513 @@
1
+
2
+ // ########################################
3
+ // # STRINGS
4
+ // ########################################
5
+
6
+ type SAlign = "l" | "r" | "c"
7
+ type SXPrintAlign = "l" | "r"
8
+
9
+ interface SEllipsisOptions {
10
+ width: number
11
+ align?: SAlign
12
+ padchar?: string
13
+ }
14
+
15
+ interface SXPrintFormat {
16
+ width: number
17
+ align?: SXPrintAlign
18
+ padchar?: string
19
+ }
20
+
21
+ interface SXPrintParams {
22
+ columns: Array<string | number | null | undefined>
23
+ formats?: Array<string | SXPrintFormat>
24
+ sep?: string
25
+ ellipsisEnd?: boolean
26
+ }
27
+
28
+ interface GlobToRegExpOptions {
29
+ extended?: boolean
30
+ globstar?: boolean
31
+ flags?: string
32
+ }
33
+
34
+ /**
35
+ * String padding.
36
+ *
37
+ * @param text - Input text.
38
+ * @param width - Target width. If `undefined`, defaults to `text.length`.
39
+ * @param which - Padding side:
40
+ * - `"r"`: pad on the right (left-justified)
41
+ * - `"l"`: pad on the left (right-justified)
42
+ * - `"c"`: pad both sides (center)
43
+ * @param padchar - Padding character (single-column). Defaults to `" "`.
44
+ * @param is_trunc - If `true`, truncate `text` when it exceeds `width`.
45
+ * @returns Padded (or truncated) string with length `width`.
46
+ */
47
+ export function s_pad(text: string, width?: number, which: SAlign = "l", padchar: string = " ", is_trunc: boolean = false): string {
48
+ if (typeof (width) !== "number") width = text.length
49
+ // ####
50
+ let w = text.length;
51
+ if (is_trunc && w > width) {
52
+ text = text.substring(0, width)
53
+ w = width;
54
+ } else {
55
+ var r = Math.max(0, width - w)
56
+ if (which === 'l') {
57
+ text = padchar.repeat(r) + text
58
+ } else if (which === 'c') {
59
+ let n = Math.floor(r / 2)
60
+ text = padchar.repeat(n) + text + padchar.repeat(r - n)
61
+ } else {
62
+ text = text + (padchar.repeat(r));
63
+ }
64
+ }
65
+ // ####
66
+ return text
67
+ }
68
+
69
+ /**
70
+ * Returns a string containing the specified number of characters from the left side of `str`.
71
+ *
72
+ * @param str - The source string.
73
+ * @param length - Number of characters to return.
74
+ * - If `length <= 0`, returns an empty string (`""`).
75
+ * - If `length >= str.length`, returns the entire `str`.
76
+ * @returns The leftmost `length` characters of `str`.
77
+ */
78
+ export function s_left(str: string, length: number): string {
79
+ if (length <= 0) return ""
80
+ let str_length = str.length
81
+ if (length > str_length) {
82
+ return str;
83
+ }
84
+ return str.substring(0, length);
85
+ }
86
+
87
+ /**
88
+ * Returns a string containing the specified number of characters from the right side of `str`.
89
+ *
90
+ * @param str - The source string.
91
+ * @param length - Number of characters to return.
92
+ * - If `length <= 0`, returns an empty string (`""`).
93
+ * - If `length >= str.length`, returns the entire `str`.
94
+ * @returns The rightmost `length` characters of `str`.
95
+ */
96
+ export function s_right(str: string, length: number): string {
97
+ if (length <= 0) return "";
98
+ let str_length = str.length;
99
+ if (length > str_length) {
100
+ return str;
101
+ }
102
+ return str.substring(str_length - length);
103
+ }
104
+
105
+ /**
106
+ * Truncate or pad a string to a fixed width, using an ellipsis (`"..."`) when truncation happens.
107
+ *
108
+ * - If `str.length < width`, pads with `padchar` according to `align`.
109
+ * - If `str.length > width`, truncates and inserts `"..."` based on `align`:
110
+ * - `"l"`: keep left part + `"..."` at end
111
+ * - `"r"`: `"..."` at start + keep right part
112
+ * - `"c"`: keep head and tail with `"..."` in the middle
113
+ *
114
+ * @param str - Input value to format. Non-string values are coerced to string.
115
+ * @param width - Target width (in characters).
116
+ * @param align - Alignment mode: `"l"` (left), `"r"` (right), `"c"` (center). Defaults to `"l"`.
117
+ * @param padchar - Padding character used when `str` is shorter than `width`. Defaults to `" "`.
118
+ * @returns The formatted string with exact length `width` (unless `width` is too small to hold content).
119
+ */
120
+ export function s_ellipsis(str: string, width: number, align: SAlign = "l", padchar: string = " "): string {
121
+ str = typeof (str) === "string" ? str : str + ""
122
+ width = Number.isFinite(width) ? Math.max(0, Math.trunc(width)) : 0
123
+ if (width === 0) return ""
124
+ align = align != undefined ? align : "l";
125
+ const _align = align.substring(0, 1);
126
+ align = _align === "l" || _align === "r" || _align === "c" ? _align : "l";
127
+ // ##
128
+ padchar = typeof (padchar) === "string" ? (padchar ? padchar : " ") : " ";
129
+ // ##
130
+ // $str = ~s /\r ?\n//g;
131
+ str = str.replace(/\r ?\n/g, "")
132
+ // ##
133
+ let length = str.length
134
+ // #
135
+ if (length === width) {
136
+ return str;
137
+ } else if (length < width) {
138
+ if (align === "r") {
139
+ str = s_pad(str, width, "l", padchar)
140
+ } else if (align === "l") {
141
+ str = s_pad(str, width, "r", padchar)
142
+ } else { // "c"
143
+ str = s_pad(str, width, "c", padchar);
144
+ }
145
+ } else {
146
+ if (width <= 3) {
147
+ return ".".repeat(width)
148
+ }
149
+ if (align === "r") {
150
+ str = "..." + s_right(str, width - 3);
151
+ } else if (align === "c") {
152
+ let m_odd = width % 2;
153
+ let m_mid_len = Math.floor(width / 2);
154
+ let head_str = s_left(str, m_mid_len - 1);
155
+ let tail_str = s_right(str, m_mid_len - (m_odd ? 1 : 2));
156
+ str = head_str + "..." + tail_str;
157
+ } else {
158
+ str = s_left(str, width - 3) + "...";
159
+ }
160
+ }
161
+ return str;
162
+ }
163
+
164
+ /**
165
+ * Convenience wrapper around {@link s_ellipsis} that accepts an options object.
166
+ *
167
+ * @param str - Input value to format. Non-string values are coerced to string by {@link s_ellipsis}.
168
+ * @param opts - Formatting options.
169
+ * @param opts.width - Target width (in characters).
170
+ * @param opts.align - Alignment mode: `"l"` (left), `"r"` (right), `"c"` (center).
171
+ * @param opts.padchar - Padding character used when `str` is shorter than `width`.
172
+ * @returns The formatted string as produced by {@link s_ellipsis}.
173
+ *
174
+ * @example
175
+ * // Pad (left aligned) to width 8 with spaces
176
+ * s_ellipsis_o("abc", { width: 8, align: "l", padchar: " " })
177
+ * // => "abc "
178
+ *
179
+ * @example
180
+ * // Pad (right aligned) to width 8 with '0'
181
+ * s_ellipsis_o("abc", { width: 8, align: "r", padchar: "0" })
182
+ * // => "00000abc"
183
+ *
184
+ * @example
185
+ * // Truncate with ellipsis at end (left align behavior for truncation)
186
+ * s_ellipsis_o("abcdefghijklmnopqrstuvwxyz", { width: 10, align: "l", padchar: " " })
187
+ * // => "abcdefg..."
188
+ *
189
+ * @example
190
+ * // Truncate with ellipsis at start (right align behavior for truncation)
191
+ * s_ellipsis_o("abcdefghijklmnopqrstuvwxyz", { width: 10, align: "r", padchar: " " })
192
+ * // => "...tuvwxyz"
193
+ *
194
+ * @example
195
+ * // Truncate with ellipsis in the middle (center)
196
+ * s_ellipsis_o("abcdefghijklmnopqrstuvwxyz", { width: 10, align: "c", padchar: " " })
197
+ * // => "abcd...xyz"
198
+ */
199
+ export function s_ellipsis_o(str: string, opts: SEllipsisOptions): string {
200
+ const { width, align, padchar } = opts
201
+ return s_ellipsis(str, width, align, padchar)
202
+ }
203
+
204
+ /**
205
+ * Parse an `s_xprint` format specifier into a normalized format object.
206
+ *
207
+ * Supports either:
208
+ * - A compact string spec: `"<width>[<align><padchar>]"`, where:
209
+ * - `width` is a positive integer (required)
210
+ * - `align` is optional: `"l"` (left) or `"r"` (right)
211
+ * - `padchar` is optional: a single character used for padding
212
+ * - An already-constructed format object, which is returned as-is.
213
+ *
214
+ * The string form is matched by: `/^(\\d+)(?:([lr])(.?))?$/`
215
+ *
216
+ * @param fmt - Format specifier as a string (e.g. `"10l_"`) or a format object
217
+ * (e.g. `{ width: 10, align: "l", padchar: "_" }`).
218
+ * @returns A format object `{ width, align, padchar }`.
219
+ * @throws {Error} If `fmt` is a string but does not match the expected format.
220
+ *
221
+ * @example
222
+ * // String form: width only
223
+ * s_xprint_extract_fmt("8")
224
+ * // => { width: 8, align: undefined, padchar: undefined }
225
+ *
226
+ * @example
227
+ * // String form: width + align
228
+ * s_xprint_extract_fmt("8l")
229
+ * // => { width: 8, align: "l", padchar: "" }
230
+ *
231
+ * @example
232
+ * // String form: width + align + padchar
233
+ * s_xprint_extract_fmt("8r0")
234
+ * // => { width: 8, align: "r", padchar: "0" }
235
+ *
236
+ * @example
237
+ * // Object form: returned unchanged
238
+ * s_xprint_extract_fmt({ width: 12, align: "l", padchar: " " })
239
+ * // => { width: 12, align: "l", padchar: " " }
240
+ */
241
+ export function s_xprint_extract_fmt(fmt: string | SXPrintFormat): SXPrintFormat {
242
+ if (typeof (fmt) === "string") {
243
+ const mmm = /^(\d+)(?:([lr])(.?))?$/.exec(fmt)
244
+ if (mmm == null) {
245
+ throw Error("wrong foramt of s_xprint [" + fmt + "]")
246
+ }
247
+ const _align = mmm[2] === "l" || mmm[2] === "r" ? mmm[2] : "l";
248
+ return {
249
+ width: parseInt(mmm[1], 10),
250
+ align: _align,
251
+ padchar: mmm[3]
252
+ }
253
+ } else {
254
+ return fmt
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Normalize a list of `s_xprint` format specifiers into format objects.
260
+ *
261
+ * This is a convenience helper that applies {@link s_xprint_extract_fmt} to each
262
+ * element in `fmts`.
263
+ *
264
+ * Supported input items:
265
+ * - String form: `"<width>[<align><padchar>]"`, e.g. `"8"`, `"10l"`, `"6r0"`
266
+ * - Object form: `{ width, align, padchar }`
267
+ *
268
+ * @param fmts - An array of format specifiers (strings or format objects).
269
+ * @returns An array of normalized format objects: `{ width, align, padchar }`.
270
+ * @throws {Error} If any string format does not match the expected pattern.
271
+ *
272
+ * @example
273
+ * // String formats
274
+ * s_xprint_fmts(["8", "10l_", "6r0"])
275
+ * // => [
276
+ * // { width: 8, align: undefined, padchar: undefined },
277
+ * // { width: 10, align: "l", padchar: "_" },
278
+ * // { width: 6, align: "r", padchar: "0" }
279
+ * // ]
280
+ *
281
+ * @example
282
+ * // Mixed formats (object formats are returned as-is)
283
+ * s_xprint_fmts([{ width: 4, align: "l", padchar: " " }, "3r0"])
284
+ * // => [
285
+ * // { width: 4, align: "l", padchar: " " },
286
+ * // { width: 3, align: "r", padchar: "0" }
287
+ * // ]
288
+ */
289
+ export function s_xprint_fmts(fmts: Array<string | SXPrintFormat>): SXPrintFormat[] {
290
+ return fmts.map(s_xprint_extract_fmt)
291
+ }
292
+
293
+ /**
294
+ * Format and join a list of columns into a single fixed-width string (table-like output).
295
+ *
296
+ * Each column is formatted using {@link s_ellipsis} with the corresponding `formats` entry.
297
+ * Columns are then joined by `sep`.
298
+ *
299
+ * - `formats` entries accept the same specifiers as {@link s_xprint_extract_fmt}:
300
+ * - String form: `"<width>[<align><padchar>]"`, e.g. `"10"`, `"8l_"`, `"6r0"`
301
+ * - Object form: `{ width, align, padchar }`
302
+ * - The last column can optionally skip ellipsis formatting when `ellipsisEnd === false`.
303
+ *
304
+ * @param params - Options object.
305
+ * @param params.columns - Column values to print. `null`/`undefined` become `""`.
306
+ * @param params.formats - Per-column format specifiers (same length as `columns` is recommended).
307
+ * @param params.sep - Separator string inserted between formatted columns. Defaults to `" "`.
308
+ * @param params.ellipsisEnd - Whether to apply ellipsis formatting to the last column.
309
+ * Defaults to `true`. When `false`, the last column is returned as-is (no padding/truncation).
310
+ * @returns A single formatted line string.
311
+ *
312
+ * @example
313
+ * // Basic usage with string formats (width + align + padchar)
314
+ * // - 1st: width 6, left align, pad with space
315
+ * // - 2nd: width 4, right align, pad with '0'
316
+ * // - 3rd: width 10, left align (will ellipsis if too long)
317
+ * s_xprint({
318
+ * columns: ["id", 7, "abcdefghijklmnopqrstuvwxyz"],
319
+ * formats: ["6l ", "4r0", "10l "],
320
+ * sep: " | "
321
+ * })
322
+ * // => "id | 0007 | abcdefg..."
323
+ *
324
+ * @example
325
+ * // Skip formatting for the last column (useful for "message" / "tail" fields)
326
+ * s_xprint({
327
+ * columns: ["INFO", "startup ok", "this part is not truncated"],
328
+ * formats: ["6l ", "12l ", "8l "],
329
+ * sep: " ",
330
+ * ellipsisEnd: false
331
+ * })
332
+ * // => "INFO startup ok this part is not truncated"
333
+ */
334
+ export function s_xprint({ columns, formats = [], sep = " ", ellipsisEnd = true }: SXPrintParams = { columns: [], formats: [], sep: " ", ellipsisEnd: true }): string {
335
+ const fmts = formats.map(s_xprint_extract_fmt)
336
+ return columns.map((column, idx) => {
337
+ const column_str = column == null ? "" : String(column);
338
+ const fmt = fmts[idx] ?? { width: column_str.length, align: "l" as SXPrintAlign, padchar: " " }
339
+ const { width, align, padchar } = fmt
340
+ if (!ellipsisEnd && idx === columns.length - 1) {
341
+ return column_str
342
+ } else {
343
+ return s_ellipsis(column_str, width, align, padchar)
344
+ }
345
+ }).join(sep)
346
+ }
347
+
348
+ /**
349
+ * Convert a glob pattern string to a {@link RegExp}.
350
+ *
351
+ * Supports basic glob syntax (`*`) by default, and optionally "extended" glob
352
+ * syntax (e.g. `?`, `[]`, `{a,b}`) and "globstar" (`**`) via {@link opts}.
353
+ *
354
+ * Notes:
355
+ * - When `opts.flags` does not include `g`, the generated regexp is wrapped with
356
+ * `^` and `$` to match the whole input.
357
+ * - When `opts.globstar` is enabled, `**` matches zero or more path segments.
358
+ *
359
+ * @param glob - Glob pattern to convert (must be a string).
360
+ * @param opts - Options controlling how the glob is translated.
361
+ * @param opts.extended - Enable extended glob features: `?`, character classes
362
+ * (`[a-z]`), and groups (`{a,b}`).
363
+ * @param opts.globstar - Enable globstar behavior for `**` (path segment aware).
364
+ * @param opts.flags - RegExp flags passed to the {@link RegExp} constructor
365
+ * (e.g. `"i"`, `"m"`, `"g"`).
366
+ * @returns A {@link RegExp} equivalent to the provided glob.
367
+ * @throws {TypeError} If `glob` is not a string.
368
+ */
369
+ function glob_to_regexp(glob: string, opts: GlobToRegExpOptions = {}): RegExp {
370
+ if (typeof glob !== 'string') {
371
+ throw new TypeError('Expected a string');
372
+ }
373
+
374
+ var str = String(glob);
375
+
376
+ // The regexp we are building, as a string.
377
+ var reStr = "";
378
+
379
+ // Whether we are matching so called "extended" globs (like bash) and should
380
+ // support single character matching, matching ranges of characters, group
381
+ // matching, etc.
382
+ var extended = opts ? !!opts.extended : false;
383
+
384
+ // When globstar is _false_ (default), '/foo/*' is translated a regexp like
385
+ // '^\/foo\/.*$' which will match any string beginning with '/foo/'
386
+ // When globstar is _true_, '/foo/*' is translated to regexp like
387
+ // '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT
388
+ // which does not have a '/' to the right of it.
389
+ // E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but
390
+ // these will not '/foo/bar/baz', '/foo/bar/baz.txt'
391
+ // Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when
392
+ // globstar is _false_
393
+ var globstar = opts ? !!opts.globstar : false;
394
+
395
+ // If we are doing extended matching, this boolean is true when we are inside
396
+ // a group (eg {*.html,*.js}), and false otherwise.
397
+ var inGroup = false;
398
+
399
+ // RegExp flags (eg "i" ) to pass in to RegExp constructor.
400
+ var flags = opts && typeof (opts.flags) === "string" ? opts.flags : "";
401
+
402
+ var c;
403
+ for (var i = 0, len = str.length; i < len; i++) {
404
+ c = str[i];
405
+
406
+ switch (c) {
407
+ case "/":
408
+ case "$":
409
+ case "^":
410
+ case "+":
411
+ case ".":
412
+ case "(":
413
+ case ")":
414
+ case "=":
415
+ case "!":
416
+ case "|":
417
+ reStr += "\\" + c;
418
+ break;
419
+
420
+ case "?":
421
+ if (extended) {
422
+ reStr += ".";
423
+ break;
424
+ }
425
+
426
+ case "[":
427
+ case "]":
428
+ if (extended) {
429
+ reStr += c;
430
+ break;
431
+ }
432
+
433
+ case "{":
434
+ if (extended) {
435
+ inGroup = true;
436
+ reStr += "(";
437
+ break;
438
+ }
439
+
440
+ case "}":
441
+ if (extended) {
442
+ inGroup = false;
443
+ reStr += ")";
444
+ break;
445
+ }
446
+
447
+ case ",":
448
+ if (inGroup) {
449
+ reStr += "|";
450
+ break;
451
+ }
452
+ reStr += "\\" + c;
453
+ break;
454
+
455
+ case "*":
456
+ // Move over all consecutive "*"'s.
457
+ // Also store the previous and next characters
458
+ var prevChar = str[i - 1];
459
+ var starCount = 1;
460
+ while (str[i + 1] === "*") {
461
+ starCount++;
462
+ i++;
463
+ }
464
+ var nextChar = str[i + 1];
465
+
466
+ if (!globstar) {
467
+ // globstar is disabled, so treat any number of "*" as one
468
+ reStr += ".*";
469
+ } else {
470
+ // globstar is enabled, so determine if this is a globstar segment
471
+ var isGlobstar = starCount > 1 // multiple "*"'s
472
+ && (prevChar === "/" || prevChar === undefined) // from the start of the segment
473
+ && (nextChar === "/" || nextChar === undefined) // to the end of the segment
474
+
475
+ if (isGlobstar) {
476
+ // it's a globstar, so match zero or more path segments
477
+ reStr += "((?:[^/]*(?:\/|$))*)";
478
+ i++; // move over the "/"
479
+ } else {
480
+ // it's not a globstar, so only match one path segment
481
+ reStr += "([^/]*)";
482
+ }
483
+ }
484
+ break;
485
+
486
+ default:
487
+ reStr += c;
488
+ }
489
+ }
490
+
491
+ // When regexp 'g' flag is specified don't
492
+ // constrain the regular expression with ^ & $
493
+ if (!flags || !~flags.indexOf('g')) {
494
+ reStr = "^" + reStr + "$";
495
+ }
496
+
497
+ return new RegExp(reStr, flags);
498
+ }
499
+
500
+ /**
501
+ * Test whether a string matches a glob pattern.
502
+ *
503
+ * This is a small wrapper around {@link glob_to_regexp} that converts the given
504
+ * glob `pattern` to a {@link RegExp} and then checks it against `str`.
505
+ *
506
+ * @param pattern - Glob pattern e.g. `"*.ts"`.
507
+ * @param str - The input string to test.
508
+ * @returns `true` if `str` matches `pattern`; otherwise `false`.
509
+ */
510
+ export function s_glob(pattern: string, str: string): boolean {
511
+ const regex = glob_to_regexp(pattern, {})
512
+ return regex.test(str)
513
+ }