@ntnyq/utils 0.6.5 → 0.7.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/package.json +3 -10
- package/dist/index.cjs +0 -1124
- package/dist/index.d.cts +0 -763
package/dist/index.cjs
DELETED
|
@@ -1,1124 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
//#region src/fn/noop.ts
|
|
4
|
-
/**
|
|
5
|
-
* A function that does nothing.
|
|
6
|
-
*/
|
|
7
|
-
function noop() {}
|
|
8
|
-
/**
|
|
9
|
-
* Alias of {@link noop}.
|
|
10
|
-
*/
|
|
11
|
-
const NOOP = noop;
|
|
12
|
-
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/fn/once.ts
|
|
15
|
-
function once(func) {
|
|
16
|
-
let called = false;
|
|
17
|
-
return function(...args) {
|
|
18
|
-
if (called) return false;
|
|
19
|
-
called = true;
|
|
20
|
-
func.apply(this, args);
|
|
21
|
-
return true;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
//#endregion
|
|
26
|
-
//#region src/is/dom.ts
|
|
27
|
-
/**
|
|
28
|
-
* @file is/dom.ts
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* Check if given value is an HTMLElement
|
|
32
|
-
* @param value - The value to check
|
|
33
|
-
* @returns True if the value is an HTMLElement, false otherwise
|
|
34
|
-
*/
|
|
35
|
-
function isHTMLElement(value) {
|
|
36
|
-
return typeof value === "object" && value !== null && "nodeType" in value && value.nodeType === Node.ELEMENT_NODE && value instanceof HTMLElement;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/is/core.ts
|
|
41
|
-
function getObjectType(value) {
|
|
42
|
-
return Object.prototype.toString.call(value).slice(8, -1);
|
|
43
|
-
}
|
|
44
|
-
function isUndefined(value) {
|
|
45
|
-
return value === void 0;
|
|
46
|
-
}
|
|
47
|
-
function isNull(value) {
|
|
48
|
-
return value === null;
|
|
49
|
-
}
|
|
50
|
-
function isNil(value) {
|
|
51
|
-
return isNull(value) || isUndefined(value);
|
|
52
|
-
}
|
|
53
|
-
const isNullOrUndefined = isNil;
|
|
54
|
-
function isString(value) {
|
|
55
|
-
return typeof value === "string";
|
|
56
|
-
}
|
|
57
|
-
function isEmptyString(value) {
|
|
58
|
-
return isString(value) && value.length === 0;
|
|
59
|
-
}
|
|
60
|
-
function isNonEmptyString(value) {
|
|
61
|
-
return isString(value) && value.length > 0;
|
|
62
|
-
}
|
|
63
|
-
function isWhitespaceString(value) {
|
|
64
|
-
return isString(value) && /^\s*$/.test(value);
|
|
65
|
-
}
|
|
66
|
-
function isEmptyStringOrWhitespace(value) {
|
|
67
|
-
return isEmptyString(value) || isWhitespaceString(value);
|
|
68
|
-
}
|
|
69
|
-
function isNumbericString(value) {
|
|
70
|
-
return isString(value) && !isEmptyStringOrWhitespace(value) && !Number.isNaN(Number(value));
|
|
71
|
-
}
|
|
72
|
-
function isNumber(value) {
|
|
73
|
-
return typeof value === "number";
|
|
74
|
-
}
|
|
75
|
-
function isZero(value) {
|
|
76
|
-
return value === 0;
|
|
77
|
-
}
|
|
78
|
-
function isNaN(value) {
|
|
79
|
-
return Number.isNaN(value);
|
|
80
|
-
}
|
|
81
|
-
function isInteger(value) {
|
|
82
|
-
return Number.isInteger(value);
|
|
83
|
-
}
|
|
84
|
-
function isBigInt(value) {
|
|
85
|
-
return typeof value === "bigint";
|
|
86
|
-
}
|
|
87
|
-
function isBoolean(value) {
|
|
88
|
-
return typeof value === "boolean";
|
|
89
|
-
}
|
|
90
|
-
function isTruthy(value) {
|
|
91
|
-
return Boolean(value);
|
|
92
|
-
}
|
|
93
|
-
function isFunction(value) {
|
|
94
|
-
return typeof value === "function";
|
|
95
|
-
}
|
|
96
|
-
function isArray(value) {
|
|
97
|
-
return Array.isArray(value);
|
|
98
|
-
}
|
|
99
|
-
function isEmptyArray(value) {
|
|
100
|
-
return isArray(value) && value.length === 0;
|
|
101
|
-
}
|
|
102
|
-
function isNonEmptyArray(value) {
|
|
103
|
-
return isArray(value) && value.length > 0;
|
|
104
|
-
}
|
|
105
|
-
function isObject(value) {
|
|
106
|
-
return (typeof value === "object" || isFunction(value)) && !isNull(value);
|
|
107
|
-
}
|
|
108
|
-
function isEmptyObject(value) {
|
|
109
|
-
return isObject(value) && !isMap(value) && !isSet(value) && Object.keys(value).length === 0;
|
|
110
|
-
}
|
|
111
|
-
function isMap(value) {
|
|
112
|
-
return getObjectType(value) === "Map";
|
|
113
|
-
}
|
|
114
|
-
function isEmptyMap(value) {
|
|
115
|
-
return isMap(value) && value.size === 0;
|
|
116
|
-
}
|
|
117
|
-
function isSet(value) {
|
|
118
|
-
return getObjectType(value) === "Set";
|
|
119
|
-
}
|
|
120
|
-
function isEmptySet(value) {
|
|
121
|
-
return isSet(value) && value.size === 0;
|
|
122
|
-
}
|
|
123
|
-
function isRegExp(value) {
|
|
124
|
-
return getObjectType(value) === "RegExp";
|
|
125
|
-
}
|
|
126
|
-
function isError(value) {
|
|
127
|
-
return getObjectType(value) === "Error";
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* @internal
|
|
131
|
-
*/
|
|
132
|
-
function hasPromiseApi(value) {
|
|
133
|
-
return isFunction(value?.then) && isFunction(value?.catch);
|
|
134
|
-
}
|
|
135
|
-
function isNativePromise(value) {
|
|
136
|
-
return getObjectType(value) === "Promise";
|
|
137
|
-
}
|
|
138
|
-
function isPromise(value) {
|
|
139
|
-
return isNativePromise(value) || hasPromiseApi(value);
|
|
140
|
-
}
|
|
141
|
-
function isIterable(value) {
|
|
142
|
-
return isFunction(value?.[Symbol.iterator]);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
//#endregion
|
|
146
|
-
//#region src/is/isDeepEqual.ts
|
|
147
|
-
/**
|
|
148
|
-
* check if two values are deeply equal
|
|
149
|
-
*/
|
|
150
|
-
function isDeepEqual(value1, value2) {
|
|
151
|
-
const type1 = getObjectType(value1);
|
|
152
|
-
const type2 = getObjectType(value2);
|
|
153
|
-
if (type1 !== type2) return false;
|
|
154
|
-
if (isArray(value1) && isArray(value2)) {
|
|
155
|
-
if (value1.length !== value2.length) return false;
|
|
156
|
-
return value1.every((item, index) => isDeepEqual(item, value2[index]));
|
|
157
|
-
}
|
|
158
|
-
if (isObject(value1) && isObject(value2)) {
|
|
159
|
-
const keys = Object.keys(value1);
|
|
160
|
-
if (keys.length !== Object.keys(value2).length) return false;
|
|
161
|
-
return keys.every((key) => isDeepEqual(value1[key], value2[key]));
|
|
162
|
-
}
|
|
163
|
-
return Object.is(value1, value2);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
//#endregion
|
|
167
|
-
//#region src/dom/scrollIntoView.ts
|
|
168
|
-
/**
|
|
169
|
-
* Scroll element into view if it is out of view.
|
|
170
|
-
*
|
|
171
|
-
* @param element - element to scroll
|
|
172
|
-
* @param options - scroll options
|
|
173
|
-
*/
|
|
174
|
-
function scrollElementIntoView(element, options = {}) {
|
|
175
|
-
const body = document.body;
|
|
176
|
-
const { parent = body,...scrollIntoViewOptions } = options;
|
|
177
|
-
if (parent === body) {
|
|
178
|
-
parent.scrollIntoView(scrollIntoViewOptions);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const parentRect = parent.getBoundingClientRect();
|
|
182
|
-
const elementRect = element.getBoundingClientRect();
|
|
183
|
-
const isHorizontal = parent.scrollWidth > parent.scrollHeight;
|
|
184
|
-
const isOutOfView = isHorizontal ? elementRect.left < parentRect.left || elementRect.right > parentRect.right : elementRect.top < parentRect.top || elementRect.bottom > parentRect.bottom;
|
|
185
|
-
if (isOutOfView) parent.scrollIntoView(scrollIntoViewOptions);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
//#endregion
|
|
189
|
-
//#region src/dom/openExternalURL.ts
|
|
190
|
-
/**
|
|
191
|
-
* Open external url
|
|
192
|
-
* @param url - URL to open
|
|
193
|
-
* @param options - open options
|
|
194
|
-
* @returns window proxy
|
|
195
|
-
*/
|
|
196
|
-
function openExternalURL(url, options = {}) {
|
|
197
|
-
const { target = "_blank" } = options;
|
|
198
|
-
const proxy = window.open(url, target);
|
|
199
|
-
return proxy;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
//#endregion
|
|
203
|
-
//#region src/dom/isVisibleInViewport.ts
|
|
204
|
-
/**
|
|
205
|
-
* Check if element is in viewport
|
|
206
|
-
* @param element - checked element
|
|
207
|
-
* @param targetWindow - window
|
|
208
|
-
* @returns true if element is in viewport, false otherwise
|
|
209
|
-
*/
|
|
210
|
-
function isElementVisibleInViewport(element, targetWindow = window) {
|
|
211
|
-
const { top, left, bottom, right } = element.getBoundingClientRect();
|
|
212
|
-
const { innerWidth, innerHeight } = targetWindow;
|
|
213
|
-
return (top >= 0 && top <= innerHeight || bottom >= 0 && bottom <= innerHeight) && (left >= 0 && left <= innerWidth || right >= 0 && right <= innerWidth);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
//#endregion
|
|
217
|
-
//#region src/env/isBrowser.ts
|
|
218
|
-
/**
|
|
219
|
-
* @file env.ts
|
|
220
|
-
*/
|
|
221
|
-
/**
|
|
222
|
-
* Checks if the code is running in a browser
|
|
223
|
-
*
|
|
224
|
-
* @returns boolean - true if the code is running in a browser
|
|
225
|
-
*/
|
|
226
|
-
const isBrowser = () => typeof document !== "undefined";
|
|
227
|
-
|
|
228
|
-
//#endregion
|
|
229
|
-
//#region src/html/escape.ts
|
|
230
|
-
const htmlEscapeMap = {
|
|
231
|
-
"&": "&",
|
|
232
|
-
"<": "<",
|
|
233
|
-
">": ">",
|
|
234
|
-
"'": "'",
|
|
235
|
-
"\"": """
|
|
236
|
-
};
|
|
237
|
-
const htmlEscapeRegexp = /[&<>'"]/g;
|
|
238
|
-
/**
|
|
239
|
-
* Escape html chars
|
|
240
|
-
*/
|
|
241
|
-
function escapeHTML(str) {
|
|
242
|
-
return str.replace(htmlEscapeRegexp, (char) => htmlEscapeMap[char]);
|
|
243
|
-
}
|
|
244
|
-
const htmlUnescapeMap = {
|
|
245
|
-
"&": "&",
|
|
246
|
-
"&": "&",
|
|
247
|
-
"<": "<",
|
|
248
|
-
"<": "<",
|
|
249
|
-
">": ">",
|
|
250
|
-
">": ">",
|
|
251
|
-
"'": "'",
|
|
252
|
-
"'": "'",
|
|
253
|
-
""": "\"",
|
|
254
|
-
""": "\""
|
|
255
|
-
};
|
|
256
|
-
const htmlUnescapeRegexp = /&(amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g;
|
|
257
|
-
/**
|
|
258
|
-
* Unescape html chars
|
|
259
|
-
*/
|
|
260
|
-
function unescapeHTML(str) {
|
|
261
|
-
return str.replace(htmlUnescapeRegexp, (char) => htmlUnescapeMap[char]);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
//#endregion
|
|
265
|
-
//#region src/misc/raf.ts
|
|
266
|
-
const root = isBrowser() ? window : globalThis;
|
|
267
|
-
let prev = Date.now();
|
|
268
|
-
function mockRAF(fn) {
|
|
269
|
-
const curr = Date.now();
|
|
270
|
-
const ms = Math.max(0, 16 - (curr - prev));
|
|
271
|
-
const id = setTimeout(fn, ms);
|
|
272
|
-
prev = curr + ms;
|
|
273
|
-
return id;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Request animation frame
|
|
277
|
-
*
|
|
278
|
-
* @param fn - callback
|
|
279
|
-
* @returns id
|
|
280
|
-
*/
|
|
281
|
-
function rAF(fn) {
|
|
282
|
-
const raf = root.requestAnimationFrame || mockRAF;
|
|
283
|
-
return raf.call(root, fn);
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Cancel animation frame
|
|
287
|
-
*
|
|
288
|
-
* @param id - id
|
|
289
|
-
* @returns void
|
|
290
|
-
*/
|
|
291
|
-
function cAF(id) {
|
|
292
|
-
const caf = root.cancelAnimationFrame || root.clearTimeout;
|
|
293
|
-
return caf.call(root, id);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
//#endregion
|
|
297
|
-
//#region src/misc/time.ts
|
|
298
|
-
/**
|
|
299
|
-
* @file time utils
|
|
300
|
-
* @module Time
|
|
301
|
-
*/
|
|
302
|
-
const ONE_SECOND = 1e3;
|
|
303
|
-
const ONE_MINUTE = 60 * ONE_SECOND;
|
|
304
|
-
const ONE_HOUR = 60 * ONE_MINUTE;
|
|
305
|
-
const ONE_DAY = 24 * ONE_HOUR;
|
|
306
|
-
const ONE_WEEK = 7 * ONE_DAY;
|
|
307
|
-
function seconds(count) {
|
|
308
|
-
return count * ONE_SECOND;
|
|
309
|
-
}
|
|
310
|
-
function minutes(count) {
|
|
311
|
-
return count * ONE_MINUTE;
|
|
312
|
-
}
|
|
313
|
-
function hours(count) {
|
|
314
|
-
return count * ONE_HOUR;
|
|
315
|
-
}
|
|
316
|
-
function days(count) {
|
|
317
|
-
return count * ONE_DAY;
|
|
318
|
-
}
|
|
319
|
-
function weeks(count) {
|
|
320
|
-
return count * ONE_WEEK;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
//#endregion
|
|
324
|
-
//#region src/misc/clamp.ts
|
|
325
|
-
/**
|
|
326
|
-
* Clamps a number between a minimum and maximum value
|
|
327
|
-
* @param value - the value to clamp within the given range
|
|
328
|
-
* @param min - the minimum value to clamp
|
|
329
|
-
* @param max - the maximum value to clamp
|
|
330
|
-
* @returns the new value
|
|
331
|
-
*/
|
|
332
|
-
function clamp(value, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY) {
|
|
333
|
-
return Math.min(Math.max(value, min), max);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
//#endregion
|
|
337
|
-
//#region src/misc/waitFor.ts
|
|
338
|
-
/**
|
|
339
|
-
* Wait for a number of milliseconds
|
|
340
|
-
*
|
|
341
|
-
* @param ms - millseconds to wait
|
|
342
|
-
* @returns a promise that resolves after ms milliseconds
|
|
343
|
-
*
|
|
344
|
-
* @example
|
|
345
|
-
* ```
|
|
346
|
-
* import { waitFor } from '@ntnyq/utils'
|
|
347
|
-
* await waitFor(3e3)
|
|
348
|
-
* // do somthing after 3 seconds
|
|
349
|
-
* ```
|
|
350
|
-
*/
|
|
351
|
-
async function waitFor(ms) {
|
|
352
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
//#endregion
|
|
356
|
-
//#region src/misc/throttle.ts
|
|
357
|
-
/**
|
|
358
|
-
* Throttle a function to limit its execution to a maximum of once per a specified time interval.
|
|
359
|
-
*
|
|
360
|
-
* @param delay - Zero or greater delay in milliseconds
|
|
361
|
-
* @param callback - A function to be throttled
|
|
362
|
-
* @param options - throttle options
|
|
363
|
-
* @returns A throttled function
|
|
364
|
-
*/
|
|
365
|
-
function throttle(delay, callback, options = {}) {
|
|
366
|
-
const { isDebounce } = options;
|
|
367
|
-
/**
|
|
368
|
-
* Track the last time `callback` was executed
|
|
369
|
-
*/
|
|
370
|
-
let lastExec = 0;
|
|
371
|
-
let cancelled = false;
|
|
372
|
-
let timeoutId;
|
|
373
|
-
function clearExistingTimeout() {
|
|
374
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
375
|
-
}
|
|
376
|
-
function cancel() {
|
|
377
|
-
clearExistingTimeout();
|
|
378
|
-
cancelled = true;
|
|
379
|
-
}
|
|
380
|
-
function wrapper(...args) {
|
|
381
|
-
if (cancelled) return;
|
|
382
|
-
const _this = this;
|
|
383
|
-
const now = Date.now();
|
|
384
|
-
const elapsed = now - lastExec;
|
|
385
|
-
function clear() {
|
|
386
|
-
timeoutId = void 0;
|
|
387
|
-
}
|
|
388
|
-
function exec(cur) {
|
|
389
|
-
lastExec = cur || Date.now();
|
|
390
|
-
callback.apply(_this, args);
|
|
391
|
-
}
|
|
392
|
-
if (isDebounce && !timeoutId) exec(now);
|
|
393
|
-
clearExistingTimeout();
|
|
394
|
-
if (!isDebounce && elapsed > delay) exec(now);
|
|
395
|
-
else timeoutId = setTimeout(isDebounce ? clear : exec, isDebounce ? delay : delay - elapsed);
|
|
396
|
-
}
|
|
397
|
-
wrapper.cancel = cancel;
|
|
398
|
-
return wrapper;
|
|
399
|
-
}
|
|
400
|
-
function debounce(delay, callback, options = {}) {
|
|
401
|
-
return throttle(delay, callback, {
|
|
402
|
-
...options,
|
|
403
|
-
isDebounce: true
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
//#endregion
|
|
408
|
-
//#region src/misc/warnOnce.ts
|
|
409
|
-
/**
|
|
410
|
-
* Cached warnings
|
|
411
|
-
*/
|
|
412
|
-
const warned = new Set();
|
|
413
|
-
/**
|
|
414
|
-
* Warn message only once
|
|
415
|
-
*
|
|
416
|
-
* @param message - warning message
|
|
417
|
-
*/
|
|
418
|
-
function warnOnce(message) {
|
|
419
|
-
if (warned.has(message)) return;
|
|
420
|
-
warned.add(message);
|
|
421
|
-
console.warn(message);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
//#endregion
|
|
425
|
-
//#region src/array/at.ts
|
|
426
|
-
/**
|
|
427
|
-
* Get array item by index, negative for backward
|
|
428
|
-
* @param array - given array
|
|
429
|
-
* @param index - index of item
|
|
430
|
-
* @returns undefined if not match, otherwise matched item
|
|
431
|
-
*/
|
|
432
|
-
function at(array, index) {
|
|
433
|
-
const length = array.length;
|
|
434
|
-
if (!length) return void 0;
|
|
435
|
-
if (index < 0) index += length;
|
|
436
|
-
return array[index];
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Get the last item of given array
|
|
440
|
-
* @param array - given array
|
|
441
|
-
* @returns undefined if empty array, otherwise last item
|
|
442
|
-
*/
|
|
443
|
-
function last(array) {
|
|
444
|
-
return at(array, -1);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
//#endregion
|
|
448
|
-
//#region src/array/chunk.ts
|
|
449
|
-
/**
|
|
450
|
-
* Splits an array into smaller chunks of a given size.
|
|
451
|
-
* @param array - The array to split
|
|
452
|
-
* @param size - The size of each chunk
|
|
453
|
-
* @returns An array of arrays, where each sub-array has `size` elements from the original array.
|
|
454
|
-
*/
|
|
455
|
-
function chunk(array, size) {
|
|
456
|
-
const result = [];
|
|
457
|
-
for (let i = 0; i < array.length; i += size) result.push(array.slice(i, i + size));
|
|
458
|
-
return result;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
//#endregion
|
|
462
|
-
//#region src/array/unique.ts
|
|
463
|
-
/**
|
|
464
|
-
* Returns a new array with unique values.
|
|
465
|
-
* @param array - The array to process.
|
|
466
|
-
* @returns The new array.
|
|
467
|
-
*/
|
|
468
|
-
function unique(array) {
|
|
469
|
-
return Array.from(new Set(array));
|
|
470
|
-
}
|
|
471
|
-
/**
|
|
472
|
-
* Returns a new array with unique values.
|
|
473
|
-
* @param array - The array to process.
|
|
474
|
-
* @param equalFn - The function to compare values.
|
|
475
|
-
* @returns The new array.
|
|
476
|
-
*/
|
|
477
|
-
function uniqueBy(array, equalFn) {
|
|
478
|
-
return array.reduce((acc, cur) => {
|
|
479
|
-
const idx = acc.findIndex((item) => equalFn(item, cur));
|
|
480
|
-
if (idx === -1) acc.push(cur);
|
|
481
|
-
return acc;
|
|
482
|
-
}, []);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
//#endregion
|
|
486
|
-
//#region src/array/toArray.ts
|
|
487
|
-
/**
|
|
488
|
-
* Converts a value to an array.
|
|
489
|
-
* @param array - The value to convert.
|
|
490
|
-
* @returns The array.
|
|
491
|
-
*/
|
|
492
|
-
function toArray(array) {
|
|
493
|
-
array = array ?? [];
|
|
494
|
-
return Array.isArray(array) ? array : [array];
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
//#endregion
|
|
498
|
-
//#region src/array/arrayable.ts
|
|
499
|
-
/**
|
|
500
|
-
* Convert `Arrayable<T>` to `Array<T>` and flatten the result
|
|
501
|
-
* @param array - given array
|
|
502
|
-
* @returns Array<T>
|
|
503
|
-
*/
|
|
504
|
-
function flattenArrayable(array) {
|
|
505
|
-
return toArray(array).flat();
|
|
506
|
-
}
|
|
507
|
-
/**
|
|
508
|
-
* Use rest arguments to merge arrays
|
|
509
|
-
* @param args - rest arguments
|
|
510
|
-
* @returns Array<T>
|
|
511
|
-
*/
|
|
512
|
-
function mergeArrayable(...args) {
|
|
513
|
-
return args.flatMap((i) => toArray(i));
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
//#endregion
|
|
517
|
-
//#region src/array/intersect.ts
|
|
518
|
-
/**
|
|
519
|
-
* Get intersect items
|
|
520
|
-
*
|
|
521
|
-
* @returns intersect items
|
|
522
|
-
*/
|
|
523
|
-
function intersect(a, b) {
|
|
524
|
-
return a.filter((item) => b.includes(item));
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
//#endregion
|
|
528
|
-
//#region src/array/isArrayEqual.ts
|
|
529
|
-
/**
|
|
530
|
-
* Check if values of two arrays are equal
|
|
531
|
-
* @param array1 - array 1
|
|
532
|
-
* @param array2 - array 2
|
|
533
|
-
* @returns `true` if equal
|
|
534
|
-
*/
|
|
535
|
-
function isArrayEqual(array1, array2) {
|
|
536
|
-
if (array1.length !== array2.length) return false;
|
|
537
|
-
return array1.every((item, idx) => item === array2[idx]);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
//#endregion
|
|
541
|
-
//#region src/string/pad.ts
|
|
542
|
-
function createPadString(options) {
|
|
543
|
-
const { length, char } = options;
|
|
544
|
-
return (value) => (char.repeat(length) + value).slice(-length);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
//#endregion
|
|
548
|
-
//#region src/string/join.ts
|
|
549
|
-
/**
|
|
550
|
-
* Joins an array of strings or numbers into a single string.
|
|
551
|
-
* @param array - An array of strings or numbers.
|
|
552
|
-
* @param options - An object of options.
|
|
553
|
-
* @returns A string.
|
|
554
|
-
*/
|
|
555
|
-
function join(array, options = {}) {
|
|
556
|
-
const { separator = "" } = options;
|
|
557
|
-
if (!Array.isArray(array) || !array.length) return "";
|
|
558
|
-
return array.filter((v) => Boolean(v) || v === 0).join(separator);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
//#endregion
|
|
562
|
-
//#region src/string/slash.ts
|
|
563
|
-
/**
|
|
564
|
-
* Replace backslash to slash
|
|
565
|
-
*/
|
|
566
|
-
function slash(input) {
|
|
567
|
-
return input.replace(/\\/g, "/");
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
//#endregion
|
|
571
|
-
//#region src/number/random.ts
|
|
572
|
-
/**
|
|
573
|
-
* random an integer by given range
|
|
574
|
-
*
|
|
575
|
-
* @param min - min value
|
|
576
|
-
* @param max - max value
|
|
577
|
-
* @returns random integer in range
|
|
578
|
-
*/
|
|
579
|
-
function randomNumber(min, max = 0, options = {}) {
|
|
580
|
-
if (max === 0) {
|
|
581
|
-
max = min;
|
|
582
|
-
min = 0;
|
|
583
|
-
}
|
|
584
|
-
if (min > max) [min, max] = [max, min];
|
|
585
|
-
return Math.trunc(Math.random() * (max - min + (options.includeMax ? 1 : 0)) + min);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
//#endregion
|
|
589
|
-
//#region src/number/toInteger.ts
|
|
590
|
-
/**
|
|
591
|
-
* Transforms a value to an integer.
|
|
592
|
-
* @param value - The value to convert to an integer.
|
|
593
|
-
* @param options - Options for the conversion.
|
|
594
|
-
* @returns The converted integer.
|
|
595
|
-
*/
|
|
596
|
-
function toInteger(value, options = {}) {
|
|
597
|
-
const { defaultValue = 0, allowDecimal = false, allowNaN = false, onError = "useDefault", min, max, outOfRange = "clamp" } = options;
|
|
598
|
-
let numberValue;
|
|
599
|
-
let result;
|
|
600
|
-
if (isNumber(value)) numberValue = value;
|
|
601
|
-
else if (isString(value)) {
|
|
602
|
-
const trimmed = value.trim();
|
|
603
|
-
if (isEmptyString(trimmed)) {
|
|
604
|
-
if (onError === "throwError") throw new TypeError("Cannot convert empty string to an integer");
|
|
605
|
-
return onError === "returnOriginal" ? value : defaultValue;
|
|
606
|
-
}
|
|
607
|
-
numberValue = Number(trimmed);
|
|
608
|
-
} else if (isNullOrUndefined(value)) {
|
|
609
|
-
if (onError === "throwError") throw new TypeError(`Cannot convert ${value} to an integer`);
|
|
610
|
-
return onError === "useDefault" ? value : defaultValue;
|
|
611
|
-
} else numberValue = Number(value);
|
|
612
|
-
if (isNaN(numberValue)) {
|
|
613
|
-
if (allowNaN) return numberValue;
|
|
614
|
-
if (onError === "throwError") throw new TypeError(`Cannot convert NaN to an integer`);
|
|
615
|
-
return onError === "returnOriginal" ? value : defaultValue;
|
|
616
|
-
}
|
|
617
|
-
if (allowDecimal) result = Math.floor(numberValue);
|
|
618
|
-
else {
|
|
619
|
-
if (numberValue % 1 !== 0) {
|
|
620
|
-
if (onError === "throwError") throw new Error("Decimal values are not allowed");
|
|
621
|
-
return onError === "returnOriginal" ? value : defaultValue;
|
|
622
|
-
}
|
|
623
|
-
result = numberValue;
|
|
624
|
-
}
|
|
625
|
-
if (!isUndefined(min) || !isUndefined(max)) {
|
|
626
|
-
const minVal = min ?? -Infinity;
|
|
627
|
-
const maxVal = max ?? Infinity;
|
|
628
|
-
if (result < minVal || result > maxVal) {
|
|
629
|
-
if (outOfRange === "throwError") throw new RangeError(`Value ${result} is out of range [${minVal}, ${maxVal}]`);
|
|
630
|
-
if (outOfRange === "useDefault") return defaultValue;
|
|
631
|
-
if (outOfRange === "clamp") result = Math.max(minVal, Math.min(maxVal, numberValue));
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
return result;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
//#endregion
|
|
638
|
-
//#region src/string/random.ts
|
|
639
|
-
/**
|
|
640
|
-
* randome a string useing given chars
|
|
641
|
-
*
|
|
642
|
-
* @param length - string length
|
|
643
|
-
* @param chars - string chars
|
|
644
|
-
* @returns random string
|
|
645
|
-
*/
|
|
646
|
-
function randomString(length = 16, chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") {
|
|
647
|
-
const result = [];
|
|
648
|
-
for (let i = length; i > 0; --i) result.push(chars[randomNumber(chars.length)]);
|
|
649
|
-
return result.join("");
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
//#endregion
|
|
653
|
-
//#region src/string/slugify.ts
|
|
654
|
-
const rControl = /[\u0000-\u001F]/g;
|
|
655
|
-
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g;
|
|
656
|
-
const rCombining = /[\u0300-\u036F]/g;
|
|
657
|
-
/**
|
|
658
|
-
* Default slugify function
|
|
659
|
-
*/
|
|
660
|
-
function slugify(str) {
|
|
661
|
-
return str.normalize("NFKD").replace(rCombining, "").replace(rControl, "").replace(rSpecial, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").replace(/^(\d)/, "_$1").toLowerCase();
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
//#endregion
|
|
665
|
-
//#region src/string/unindent.ts
|
|
666
|
-
const _RE_FULL_WS = /^\s*$/;
|
|
667
|
-
/**
|
|
668
|
-
* Remove common leading whitespace from a template string
|
|
669
|
-
* Empty lines at the beginning and end of the template string are also removed.
|
|
670
|
-
* @param input - template string
|
|
671
|
-
*
|
|
672
|
-
* @example
|
|
673
|
-
*
|
|
674
|
-
* ```ts
|
|
675
|
-
* const str = unindent`
|
|
676
|
-
* if (foo) {
|
|
677
|
-
* bar()
|
|
678
|
-
* }
|
|
679
|
-
* `
|
|
680
|
-
* ```
|
|
681
|
-
*/
|
|
682
|
-
function unindent(input) {
|
|
683
|
-
const lines = (typeof input === "string" ? input : input[0]).split("\n");
|
|
684
|
-
const whitespaceLines = lines.map((line) => _RE_FULL_WS.test(line));
|
|
685
|
-
const commonIndent = lines.reduce((min, line, idx) => {
|
|
686
|
-
if (whitespaceLines[idx]) return min;
|
|
687
|
-
const indent = line.match(/^\s*/)?.[0].length;
|
|
688
|
-
return indent === void 0 ? min : Math.min(min, indent);
|
|
689
|
-
}, Number.POSITIVE_INFINITY);
|
|
690
|
-
let emptylinesHead = 0;
|
|
691
|
-
while (emptylinesHead < lines.length && whitespaceLines[emptylinesHead]) emptylinesHead++;
|
|
692
|
-
let emptylinesTail = 0;
|
|
693
|
-
while (emptylinesTail < lines.length && whitespaceLines[lines.length - emptylinesTail - 1]) emptylinesTail++;
|
|
694
|
-
return lines.slice(emptylinesHead, lines.length - emptylinesTail).map((line) => line.slice(commonIndent)).join("\n");
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
//#endregion
|
|
698
|
-
//#region src/string/getLength.ts
|
|
699
|
-
let segmenter;
|
|
700
|
-
function isASCII(value) {
|
|
701
|
-
return /^[\u0020-\u007F]*$/u.test(value);
|
|
702
|
-
}
|
|
703
|
-
/**
|
|
704
|
-
* Counts graphemes in a given string
|
|
705
|
-
* @param value - A string to count graphemes.
|
|
706
|
-
* @returns The number of graphemes in `value`.
|
|
707
|
-
*/
|
|
708
|
-
function getStringLength(value) {
|
|
709
|
-
if (isASCII(value)) return value.length;
|
|
710
|
-
segmenter ??= new Intl.Segmenter();
|
|
711
|
-
return [...segmenter.segment(value)].length;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
//#endregion
|
|
715
|
-
//#region src/string/ensurePrefix.ts
|
|
716
|
-
function ensurePrefix(input, prefix) {
|
|
717
|
-
return input.startsWith(prefix) ? input : `${prefix}${input}`;
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
//#endregion
|
|
721
|
-
//#region src/string/ensureSuffix.ts
|
|
722
|
-
function ensureSuffix(input, suffix) {
|
|
723
|
-
return input.endsWith(suffix) ? input : `${input}${suffix}`;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
//#endregion
|
|
727
|
-
//#region src/color/color.ts
|
|
728
|
-
const pad2 = createPadString({
|
|
729
|
-
length: 2,
|
|
730
|
-
char: "0"
|
|
731
|
-
});
|
|
732
|
-
const RE_VALID_HEX_COLOR = /^#(?:[0-9a-f]{6}|[0-9a-f]{3})$/i;
|
|
733
|
-
function validateHexColor(hex) {
|
|
734
|
-
if (hex.length !== 4 && hex.length !== 7) return false;
|
|
735
|
-
if (!hex.startsWith("#")) return false;
|
|
736
|
-
return RE_VALID_HEX_COLOR.test(hex);
|
|
737
|
-
}
|
|
738
|
-
function normalizeHexString(hex) {
|
|
739
|
-
return hex.length === 6 ? hex : hex.replace(/./g, "$&$&");
|
|
740
|
-
}
|
|
741
|
-
var Color = class Color {
|
|
742
|
-
red = 0;
|
|
743
|
-
green = 0;
|
|
744
|
-
blue = 0;
|
|
745
|
-
alpha = 1;
|
|
746
|
-
constructor(red = 0, green = 0, blue = 0, alpha = 1) {
|
|
747
|
-
this.red = red;
|
|
748
|
-
this.green = green;
|
|
749
|
-
this.blue = blue;
|
|
750
|
-
this.alpha = alpha;
|
|
751
|
-
}
|
|
752
|
-
static fromRGB(red, green, blue) {
|
|
753
|
-
return new Color(red, green, blue);
|
|
754
|
-
}
|
|
755
|
-
static fromRGBA(red, green, blue, alpha) {
|
|
756
|
-
return new Color(red, green, blue, alpha);
|
|
757
|
-
}
|
|
758
|
-
static fromHex(hex) {
|
|
759
|
-
if (!validateHexColor(hex)) throw new Error("Invalid hex color");
|
|
760
|
-
const [red, green, blue] = normalizeHexString(hex.slice(1)).match(/.{2}/g)?.map((value) => Number.parseInt(value, 16)) ?? [
|
|
761
|
-
0,
|
|
762
|
-
0,
|
|
763
|
-
0
|
|
764
|
-
];
|
|
765
|
-
return new Color(red, green, blue);
|
|
766
|
-
}
|
|
767
|
-
get brightness() {
|
|
768
|
-
return (this.red * 299 + this.green * 587 + this.blue * 114) / 1e3;
|
|
769
|
-
}
|
|
770
|
-
get isDark() {
|
|
771
|
-
return this.brightness < 128;
|
|
772
|
-
}
|
|
773
|
-
get isLight() {
|
|
774
|
-
return !this.isDark;
|
|
775
|
-
}
|
|
776
|
-
toHexString(isUpperCase = true) {
|
|
777
|
-
const hexString = `#${pad2(this.red.toString(16))}${pad2(this.green.toString(16))}${pad2(this.blue.toString(16))}`;
|
|
778
|
-
return isUpperCase ? hexString.toUpperCase() : hexString;
|
|
779
|
-
}
|
|
780
|
-
toRGBAString() {
|
|
781
|
-
return `rgba(${this.red}, ${this.green}, ${this.blue}, ${this.alpha})`;
|
|
782
|
-
}
|
|
783
|
-
/**
|
|
784
|
-
* add alpha value to {@link Color}
|
|
785
|
-
*
|
|
786
|
-
* @param alpha - alpha value
|
|
787
|
-
* @returns instance of {@link Color}
|
|
788
|
-
*/
|
|
789
|
-
withAlpha(alpha = 1) {
|
|
790
|
-
return new Color(this.red, this.green, this.blue, alpha);
|
|
791
|
-
}
|
|
792
|
-
/**
|
|
793
|
-
* lighten the color by percentage
|
|
794
|
-
*
|
|
795
|
-
* @param percentage - percentage to lighten
|
|
796
|
-
*/
|
|
797
|
-
lighten(percentage = 0) {
|
|
798
|
-
const amount = Math.round(percentage / 100 * 255);
|
|
799
|
-
return new Color(Math.min(this.red + amount, 255), Math.min(this.green + amount, 255), Math.min(this.blue + amount, 255), this.alpha);
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* darken the color by percentage
|
|
803
|
-
*
|
|
804
|
-
* @param percentage - percentage to darken
|
|
805
|
-
*/
|
|
806
|
-
darken(percentage = 0) {
|
|
807
|
-
const amount = Math.round(percentage / 100 * 255);
|
|
808
|
-
return new Color(Math.max(this.red - amount, 0), Math.max(this.green - amount, 0), Math.max(this.blue - amount, 0), this.alpha);
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
|
|
812
|
-
//#endregion
|
|
813
|
-
//#region src/color/random.ts
|
|
814
|
-
/**
|
|
815
|
-
* the maximum value of RGB
|
|
816
|
-
*/
|
|
817
|
-
const MAX_RGB = 255;
|
|
818
|
-
/**
|
|
819
|
-
* get a random RGB color
|
|
820
|
-
* @returns a random RGB color
|
|
821
|
-
*/
|
|
822
|
-
function randomRGBColor() {
|
|
823
|
-
return `rgb(${randomNumber(MAX_RGB)}, ${randomNumber(MAX_RGB)}, ${randomNumber(MAX_RGB)})`;
|
|
824
|
-
}
|
|
825
|
-
/**
|
|
826
|
-
* get a random RGBA color
|
|
827
|
-
* @returns a random RGBA color
|
|
828
|
-
*/
|
|
829
|
-
function randomRGBAColor() {
|
|
830
|
-
return `rgba(${randomNumber(MAX_RGB)}, ${randomNumber(MAX_RGB)}, ${randomNumber(MAX_RGB)}, ${Math.random().toFixed(1)})`;
|
|
831
|
-
}
|
|
832
|
-
/**
|
|
833
|
-
* get a random hex color
|
|
834
|
-
* @returns a random hex color
|
|
835
|
-
*/
|
|
836
|
-
function randomHexColor() {
|
|
837
|
-
return `#${Math.random().toString(16).slice(2, 8)}`;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
//#endregion
|
|
841
|
-
//#region src/proxy/enhance.ts
|
|
842
|
-
/**
|
|
843
|
-
* enhance object
|
|
844
|
-
* @module proxy
|
|
845
|
-
*/
|
|
846
|
-
function enhance(module$1, extra) {
|
|
847
|
-
return new Proxy(module$1, {
|
|
848
|
-
get(target, key, receiver) {
|
|
849
|
-
if (Reflect.has(extra, key)) return Reflect.get(extra, key, receiver);
|
|
850
|
-
return Reflect.get(target, key, receiver);
|
|
851
|
-
},
|
|
852
|
-
has(target, key) {
|
|
853
|
-
return Reflect.has(extra, key) || Reflect.has(target, key);
|
|
854
|
-
}
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
//#endregion
|
|
859
|
-
//#region src/module/interopDefault.ts
|
|
860
|
-
/**
|
|
861
|
-
* Interop default export from a module
|
|
862
|
-
*
|
|
863
|
-
* @param mod - The module
|
|
864
|
-
* @returns The default export
|
|
865
|
-
*
|
|
866
|
-
* @example
|
|
867
|
-
*
|
|
868
|
-
* ```ts
|
|
869
|
-
* import { interopDefault } from '@ntnyq/utils'
|
|
870
|
-
*
|
|
871
|
-
* const { unindent } = await interopDefault(import('@ntnyq/utils'))
|
|
872
|
-
* ```
|
|
873
|
-
*/
|
|
874
|
-
async function interopDefault(mod) {
|
|
875
|
-
const resolved = await mod;
|
|
876
|
-
return resolved.default || resolved;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
//#endregion
|
|
880
|
-
//#region src/module/resolveSubOptions.ts
|
|
881
|
-
/**
|
|
882
|
-
* Resolve sub options `boolean | Options` to `Options`
|
|
883
|
-
* @param options - core options
|
|
884
|
-
* @param key - sub options key
|
|
885
|
-
* @returns resolved sub options
|
|
886
|
-
*
|
|
887
|
-
* @example
|
|
888
|
-
*
|
|
889
|
-
* ```ts
|
|
890
|
-
* import { resolveSubOptions } from '@ntnyq/utils'
|
|
891
|
-
*
|
|
892
|
-
* interface Options {
|
|
893
|
-
* compile?: boolean | {
|
|
894
|
-
* include?: string[]
|
|
895
|
-
* exclude?: string[]
|
|
896
|
-
* }
|
|
897
|
-
* }
|
|
898
|
-
*
|
|
899
|
-
* const options: Options = {
|
|
900
|
-
* compile: true
|
|
901
|
-
* }
|
|
902
|
-
*
|
|
903
|
-
* console.log(resolveSubOptions(options, 'compile'))
|
|
904
|
-
*
|
|
905
|
-
* // => {}
|
|
906
|
-
* ```
|
|
907
|
-
*/
|
|
908
|
-
function resolveSubOptions(options, key) {
|
|
909
|
-
return typeof options[key] === "boolean" ? {} : options[key] || {};
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
//#endregion
|
|
913
|
-
//#region src/object/omit.ts
|
|
914
|
-
function omit(object, ...keys) {
|
|
915
|
-
keys.forEach((key) => delete object[key]);
|
|
916
|
-
return object;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
//#endregion
|
|
920
|
-
//#region src/object/hasOwn.ts
|
|
921
|
-
/**
|
|
922
|
-
* check object has a property with given key
|
|
923
|
-
* @param object - the object to check
|
|
924
|
-
* @param key - the key to check
|
|
925
|
-
* @returns true if object has a property with given key, false otherwise
|
|
926
|
-
*/
|
|
927
|
-
function hasOwn(object, key) {
|
|
928
|
-
if (object === null) return false;
|
|
929
|
-
return Object.prototype.hasOwnProperty.call(object, key);
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
//#endregion
|
|
933
|
-
//#region src/object/pick.ts
|
|
934
|
-
function pick(object, keys) {
|
|
935
|
-
return Object.assign(
|
|
936
|
-
{},
|
|
937
|
-
// eslint-disable-next-line array-callback-return
|
|
938
|
-
...keys.map((key) => {
|
|
939
|
-
if (object && hasOwn(object, key)) return { [key]: object[key] };
|
|
940
|
-
})
|
|
941
|
-
);
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
//#endregion
|
|
945
|
-
//#region src/object/clean.ts
|
|
946
|
-
/**
|
|
947
|
-
* clean undefined, null, zero, empty string, empty array, empty object from object
|
|
948
|
-
* @param obj - object to be cleaned
|
|
949
|
-
* @param options - clean options
|
|
950
|
-
* @returns cleaned object
|
|
951
|
-
*/
|
|
952
|
-
function cleanObject(obj, options = {}) {
|
|
953
|
-
const { cleanUndefined = true, cleanNull = true, cleanNaN = true, cleanZero = false, cleanEmptyString = false, cleanEmptyArray = false, cleanEmptyObject = false, recursive = true } = options;
|
|
954
|
-
Object.keys(obj).forEach((key) => {
|
|
955
|
-
const v = obj[key];
|
|
956
|
-
if (cleanUndefined && isUndefined(v)) delete obj[key];
|
|
957
|
-
if (cleanNull && isNull(v)) delete obj[key];
|
|
958
|
-
if (cleanZero && isZero(v)) delete obj[key];
|
|
959
|
-
if (cleanNaN && isZero(v)) delete obj[key];
|
|
960
|
-
if (cleanEmptyString && isEmptyString(v)) delete obj[key];
|
|
961
|
-
if (cleanEmptyArray && isEmptyArray(v)) delete obj[key];
|
|
962
|
-
if (cleanEmptyObject && isEmptyObject(v)) delete obj[key];
|
|
963
|
-
if (recursive && isObject(v)) cleanObject(v, options);
|
|
964
|
-
});
|
|
965
|
-
return obj;
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
//#endregion
|
|
969
|
-
//#region src/object/isPlainObject.ts
|
|
970
|
-
/**
|
|
971
|
-
* Check if a value is a plain object (not an array, Date, RegExp, Map, Set, etc.)
|
|
972
|
-
*
|
|
973
|
-
* @param value - Checked value
|
|
974
|
-
* @copyright {@link https://github.com/sindresorhus/is/blob/main/source/index.ts}
|
|
975
|
-
*/
|
|
976
|
-
function isPlainObject(value) {
|
|
977
|
-
if (!isObject(value)) return false;
|
|
978
|
-
const prototype = Object.getPrototypeOf(value);
|
|
979
|
-
return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
//#endregion
|
|
983
|
-
//#region src/object/sortObject.ts
|
|
984
|
-
/**
|
|
985
|
-
* Sort object properties
|
|
986
|
-
*/
|
|
987
|
-
function sortObject(obj, options = {}) {
|
|
988
|
-
const { compareFn = (a, b) => a.localeCompare(b) } = options;
|
|
989
|
-
function sortKeys(obj$1) {
|
|
990
|
-
const sortedKeys = Object.keys(obj$1).sort(compareFn);
|
|
991
|
-
const result = {};
|
|
992
|
-
for (const key of sortedKeys) {
|
|
993
|
-
const value = obj$1[key];
|
|
994
|
-
let newValue;
|
|
995
|
-
if (options.deep && isPlainObject(value)) newValue = sortKeys(value);
|
|
996
|
-
else newValue = value;
|
|
997
|
-
Object.defineProperty(result, key, {
|
|
998
|
-
...Object.getOwnPropertyDescriptor(obj$1, key),
|
|
999
|
-
value: newValue
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
return result;
|
|
1003
|
-
}
|
|
1004
|
-
return sortKeys(obj);
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
//#endregion
|
|
1008
|
-
//#region src/constants/char.ts
|
|
1009
|
-
/**
|
|
1010
|
-
* Special chars
|
|
1011
|
-
*/
|
|
1012
|
-
const SPECIAL_CHAR = { newline: "\n" };
|
|
1013
|
-
|
|
1014
|
-
//#endregion
|
|
1015
|
-
//#region src/constants/regexp.ts
|
|
1016
|
-
/**
|
|
1017
|
-
* 注释正则
|
|
1018
|
-
*
|
|
1019
|
-
* 匹配 \<!-- 或 /* 开头的注释,直到 --> 或 *\/ 结尾
|
|
1020
|
-
*/
|
|
1021
|
-
const RE_COMMENTS = /(?:<!--|\/\*)([\s\S]*?)(?:-->|\*\/)/g;
|
|
1022
|
-
/**
|
|
1023
|
-
* JavaScript line comment
|
|
1024
|
-
*/
|
|
1025
|
-
const RE_LINE_COMMENT = /\/\/.*/;
|
|
1026
|
-
/**
|
|
1027
|
-
* JavaScript block comment
|
|
1028
|
-
*/
|
|
1029
|
-
const RE_BLOCK_COMMENT = /\/\*[\s\S]*?\*\//g;
|
|
1030
|
-
|
|
1031
|
-
//#endregion
|
|
1032
|
-
exports.Color = Color
|
|
1033
|
-
exports.NOOP = NOOP
|
|
1034
|
-
exports.RE_BLOCK_COMMENT = RE_BLOCK_COMMENT
|
|
1035
|
-
exports.RE_COMMENTS = RE_COMMENTS
|
|
1036
|
-
exports.RE_LINE_COMMENT = RE_LINE_COMMENT
|
|
1037
|
-
exports.SPECIAL_CHAR = SPECIAL_CHAR
|
|
1038
|
-
exports.at = at
|
|
1039
|
-
exports.cAF = cAF
|
|
1040
|
-
exports.chunk = chunk
|
|
1041
|
-
exports.clamp = clamp
|
|
1042
|
-
exports.cleanObject = cleanObject
|
|
1043
|
-
exports.createPadString = createPadString
|
|
1044
|
-
exports.days = days
|
|
1045
|
-
exports.debounce = debounce
|
|
1046
|
-
exports.enhance = enhance
|
|
1047
|
-
exports.ensurePrefix = ensurePrefix
|
|
1048
|
-
exports.ensureSuffix = ensureSuffix
|
|
1049
|
-
exports.escapeHTML = escapeHTML
|
|
1050
|
-
exports.flattenArrayable = flattenArrayable
|
|
1051
|
-
exports.getObjectType = getObjectType
|
|
1052
|
-
exports.getStringLength = getStringLength
|
|
1053
|
-
exports.hasOwn = hasOwn
|
|
1054
|
-
exports.hours = hours
|
|
1055
|
-
exports.interopDefault = interopDefault
|
|
1056
|
-
exports.intersect = intersect
|
|
1057
|
-
exports.isArray = isArray
|
|
1058
|
-
exports.isArrayEqual = isArrayEqual
|
|
1059
|
-
exports.isBigInt = isBigInt
|
|
1060
|
-
exports.isBoolean = isBoolean
|
|
1061
|
-
exports.isBrowser = isBrowser
|
|
1062
|
-
exports.isDeepEqual = isDeepEqual
|
|
1063
|
-
exports.isElementVisibleInViewport = isElementVisibleInViewport
|
|
1064
|
-
exports.isEmptyArray = isEmptyArray
|
|
1065
|
-
exports.isEmptyMap = isEmptyMap
|
|
1066
|
-
exports.isEmptyObject = isEmptyObject
|
|
1067
|
-
exports.isEmptySet = isEmptySet
|
|
1068
|
-
exports.isEmptyString = isEmptyString
|
|
1069
|
-
exports.isEmptyStringOrWhitespace = isEmptyStringOrWhitespace
|
|
1070
|
-
exports.isError = isError
|
|
1071
|
-
exports.isFunction = isFunction
|
|
1072
|
-
exports.isHTMLElement = isHTMLElement
|
|
1073
|
-
exports.isInteger = isInteger
|
|
1074
|
-
exports.isIterable = isIterable
|
|
1075
|
-
exports.isMap = isMap
|
|
1076
|
-
exports.isNaN = isNaN
|
|
1077
|
-
exports.isNativePromise = isNativePromise
|
|
1078
|
-
exports.isNil = isNil
|
|
1079
|
-
exports.isNonEmptyArray = isNonEmptyArray
|
|
1080
|
-
exports.isNonEmptyString = isNonEmptyString
|
|
1081
|
-
exports.isNull = isNull
|
|
1082
|
-
exports.isNullOrUndefined = isNullOrUndefined
|
|
1083
|
-
exports.isNumber = isNumber
|
|
1084
|
-
exports.isNumbericString = isNumbericString
|
|
1085
|
-
exports.isObject = isObject
|
|
1086
|
-
exports.isPromise = isPromise
|
|
1087
|
-
exports.isRegExp = isRegExp
|
|
1088
|
-
exports.isSet = isSet
|
|
1089
|
-
exports.isString = isString
|
|
1090
|
-
exports.isTruthy = isTruthy
|
|
1091
|
-
exports.isUndefined = isUndefined
|
|
1092
|
-
exports.isWhitespaceString = isWhitespaceString
|
|
1093
|
-
exports.isZero = isZero
|
|
1094
|
-
exports.join = join
|
|
1095
|
-
exports.last = last
|
|
1096
|
-
exports.mergeArrayable = mergeArrayable
|
|
1097
|
-
exports.minutes = minutes
|
|
1098
|
-
exports.noop = noop
|
|
1099
|
-
exports.omit = omit
|
|
1100
|
-
exports.once = once
|
|
1101
|
-
exports.openExternalURL = openExternalURL
|
|
1102
|
-
exports.pick = pick
|
|
1103
|
-
exports.rAF = rAF
|
|
1104
|
-
exports.randomHexColor = randomHexColor
|
|
1105
|
-
exports.randomNumber = randomNumber
|
|
1106
|
-
exports.randomRGBAColor = randomRGBAColor
|
|
1107
|
-
exports.randomRGBColor = randomRGBColor
|
|
1108
|
-
exports.randomString = randomString
|
|
1109
|
-
exports.resolveSubOptions = resolveSubOptions
|
|
1110
|
-
exports.scrollElementIntoView = scrollElementIntoView
|
|
1111
|
-
exports.seconds = seconds
|
|
1112
|
-
exports.slash = slash
|
|
1113
|
-
exports.slugify = slugify
|
|
1114
|
-
exports.sortObject = sortObject
|
|
1115
|
-
exports.throttle = throttle
|
|
1116
|
-
exports.toArray = toArray
|
|
1117
|
-
exports.toInteger = toInteger
|
|
1118
|
-
exports.unescapeHTML = unescapeHTML
|
|
1119
|
-
exports.unindent = unindent
|
|
1120
|
-
exports.unique = unique
|
|
1121
|
-
exports.uniqueBy = uniqueBy
|
|
1122
|
-
exports.waitFor = waitFor
|
|
1123
|
-
exports.warnOnce = warnOnce
|
|
1124
|
-
exports.weeks = weeks
|