@ditojs/utils 2.100.0 → 3.1.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 +5 -9
- package/src/array/shuffle.js +1 -1
- package/src/string/formatPlainDate.js +14 -0
- package/src/string/index.js +2 -0
- package/src/string/isDomain.js +10 -3
- package/src/string/parsePlainDate.js +18 -0
- package/types/index.d.ts +13 -0
- package/src/__snapshots__/index.test.js.snap +0 -88
- package/src/array/flattten.test.js +0 -29
- package/src/array/shuffle.test.js +0 -25
- package/src/base/base.test.js +0 -590
- package/src/class/mixin.test.js +0 -70
- package/src/dataPath/getEntriesAtDataPath.test.js +0 -204
- package/src/dataPath/getValueAtDataPath.test.js +0 -140
- package/src/dataPath/normalizeDataPath.test.js +0 -36
- package/src/dataPath/parseDataPath.test.js +0 -67
- package/src/dataPath/setDataPathEntries.test.js +0 -36
- package/src/dataPath/setValueAtDataPath.test.js +0 -34
- package/src/function/toAsync.test.js +0 -31
- package/src/function/toCallback.test.js +0 -29
- package/src/function/toPromiseCallback.test.js +0 -24
- package/src/html/escapeHtml.test.js +0 -19
- package/src/html/stripHtml.test.js +0 -37
- package/src/index.test.js +0 -7
- package/src/object/clone.test.js +0 -131
- package/src/object/equals.test.js +0 -278
- package/src/object/groupBy.test.js +0 -70
- package/src/object/mapKeys.test.js +0 -16
- package/src/object/mapValues.test.js +0 -38
- package/src/object/mergeDeeply.test.js +0 -152
- package/src/object/pick.test.js +0 -23
- package/src/object/pickBy.test.js +0 -48
- package/src/string/camelize.test.js +0 -37
- package/src/string/capitalize.test.js +0 -33
- package/src/string/decamelize.test.js +0 -83
- package/src/string/deindent.test.js +0 -181
- package/src/string/format.test.js +0 -196
- package/src/string/formatDate.test.js +0 -28
- package/src/string/getCommonPrefix.test.js +0 -23
- package/src/string/isAbsoluteUrl.test.js +0 -15
- package/src/string/isCreditCard.test.js +0 -50
- package/src/string/isDomain.test.js +0 -15
- package/src/string/isEmail.test.js +0 -37
- package/src/string/isHostname.test.js +0 -12
- package/src/string/isUrl.test.js +0 -1595
- package/src/string/labelize.test.js +0 -39
- package/src/timer/debounce.test.js +0 -101
- package/src/timer/debounceAsync.test.js +0 -143
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dito.js Utility Functions – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/main/packages/utils",
|
|
@@ -17,11 +17,10 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"src/",
|
|
20
|
-
"types/"
|
|
20
|
+
"types/",
|
|
21
|
+
"!src/**/*.test.js",
|
|
22
|
+
"!src/**/__snapshots__/"
|
|
21
23
|
],
|
|
22
|
-
"scripts": {
|
|
23
|
-
"types": "tsc --noEmit types/index.d.ts"
|
|
24
|
-
},
|
|
25
24
|
"engines": {
|
|
26
25
|
"node": ">= 24.0.0"
|
|
27
26
|
},
|
|
@@ -32,11 +31,8 @@
|
|
|
32
31
|
"ie >= 11",
|
|
33
32
|
"ie_mob >= 11"
|
|
34
33
|
],
|
|
35
|
-
"dependencies": {
|
|
36
|
-
"punycode": "^2.3.1"
|
|
37
|
-
},
|
|
38
34
|
"devDependencies": {
|
|
39
35
|
"typescript": "^6.0.3"
|
|
40
36
|
},
|
|
41
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "97314265c5cbac6a248d7350f43fea8ea92b995f"
|
|
42
38
|
}
|
package/src/array/shuffle.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a date as its local plain date, e.g. `'2026-05-14'`, without the
|
|
3
|
+
* time and time zone of `toISOString()`, which can shift it to another day.
|
|
4
|
+
*/
|
|
5
|
+
export function formatPlainDate(date) {
|
|
6
|
+
// Use the date parts, as shifting by `getTimezoneOffset()` misses offsets
|
|
7
|
+
// with seconds, e.g. local mean time before 1894 in Europe/Zurich.
|
|
8
|
+
const pad = (number, length = 2) => String(number).padStart(length, '0')
|
|
9
|
+
return [
|
|
10
|
+
pad(date.getFullYear(), 4),
|
|
11
|
+
pad(date.getMonth() + 1),
|
|
12
|
+
pad(date.getDate())
|
|
13
|
+
].join('-')
|
|
14
|
+
}
|
package/src/string/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export * from './decamelize.js'
|
|
|
4
4
|
export * from './deindent.js'
|
|
5
5
|
export * from './escapeRegexp.js'
|
|
6
6
|
export * from './format.js'
|
|
7
|
+
export * from './formatPlainDate.js'
|
|
7
8
|
export * from './formatDate.js'
|
|
8
9
|
export * from './getCommonPrefix.js'
|
|
9
10
|
export * from './isAbsoluteUrl.js'
|
|
@@ -13,3 +14,4 @@ export * from './isEmail.js'
|
|
|
13
14
|
export * from './isHostname.js'
|
|
14
15
|
export * from './isUrl.js'
|
|
15
16
|
export * from './labelize.js'
|
|
17
|
+
export * from './parsePlainDate.js'
|
package/src/string/isDomain.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import punycode from 'punycode/punycode.js'
|
|
2
|
-
|
|
3
1
|
// Best effort approach, allowing Internationalized domain name (with punycode)
|
|
4
2
|
const domainRegExp =
|
|
5
3
|
/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i
|
|
6
4
|
|
|
7
5
|
export function isDomain(str) {
|
|
8
|
-
|
|
6
|
+
// Reject characters that `URL` would treat as delimiters or decode, so that
|
|
7
|
+
// only the domain itself is converted to its ASCII (punycode) form.
|
|
8
|
+
if (!str || /[\s/\\?#@:%]/.test(str)) {
|
|
9
|
+
return false
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
return domainRegExp.test(new URL(`http://${str}`).hostname)
|
|
13
|
+
} catch {
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
9
16
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { formatPlainDate } from './formatPlainDate.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parses a plain date like `'2026-05-14'` as local midnight, unlike
|
|
5
|
+
* `new Date()`, which parses it as UTC midnight. Returns `null` for anything
|
|
6
|
+
* else, including invalid dates like `'2026-02-30'`.
|
|
7
|
+
*/
|
|
8
|
+
export function parsePlainDate(string) {
|
|
9
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(string)) {
|
|
10
|
+
// Date-time strings without an offset are parsed as local time.
|
|
11
|
+
const date = new Date(`${string}T00:00`)
|
|
12
|
+
// Reject invalid days, which `Date` rolls over to the next month.
|
|
13
|
+
if (formatPlainDate(date) === string) {
|
|
14
|
+
return date
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return null
|
|
18
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -518,6 +518,19 @@ export function format(
|
|
|
518
518
|
}
|
|
519
519
|
}
|
|
520
520
|
): string | null | undefined
|
|
521
|
+
/**
|
|
522
|
+
* Formats a date as its local plain date, e.g. `'2026-05-14'`, without the
|
|
523
|
+
* time and time zone of `toISOString()`, which can shift it to another day.
|
|
524
|
+
*/
|
|
525
|
+
export function formatPlainDate(date: Date): string
|
|
526
|
+
/**
|
|
527
|
+
* Parses a plain date like `'2026-05-14'` as local midnight, unlike
|
|
528
|
+
* `new Date()`, which parses it as UTC midnight. Returns `null` for anything
|
|
529
|
+
* else, including invalid dates like `'2026-02-30'`.
|
|
530
|
+
*/
|
|
531
|
+
export function parsePlainDate(
|
|
532
|
+
string: string | null | undefined
|
|
533
|
+
): Date | null
|
|
521
534
|
/**
|
|
522
535
|
* Formats a date value as a string. If the value is not a Date,
|
|
523
536
|
* attempts to convert it to a Date first.
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`index > exports all symbols 1`] = `
|
|
4
|
-
{
|
|
5
|
-
"asArray": [Function],
|
|
6
|
-
"asFunction": [Function],
|
|
7
|
-
"asObject": [Function],
|
|
8
|
-
"assignDeeply": [Function],
|
|
9
|
-
"camelize": [Function],
|
|
10
|
-
"capitalize": [Function],
|
|
11
|
-
"clone": [Function],
|
|
12
|
-
"debounce": [Function],
|
|
13
|
-
"debounceAsync": [Function],
|
|
14
|
-
"decamelize": [Function],
|
|
15
|
-
"defaultFormats": {
|
|
16
|
-
"date": {
|
|
17
|
-
"day": "numeric",
|
|
18
|
-
"month": "long",
|
|
19
|
-
"year": "numeric",
|
|
20
|
-
},
|
|
21
|
-
"number": {
|
|
22
|
-
"style": "decimal",
|
|
23
|
-
},
|
|
24
|
-
"time": {
|
|
25
|
-
"hour": "2-digit",
|
|
26
|
-
"minute": "2-digit",
|
|
27
|
-
"second": "2-digit",
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
"deindent": [Function],
|
|
31
|
-
"deprecate": [Function],
|
|
32
|
-
"equals": [Function],
|
|
33
|
-
"escapeHtml": [Function],
|
|
34
|
-
"escapeRegexp": [Function],
|
|
35
|
-
"flatten": [Function],
|
|
36
|
-
"format": [Function],
|
|
37
|
-
"formatDate": [Function],
|
|
38
|
-
"getCommonOffset": [Function],
|
|
39
|
-
"getCommonPrefix": [Function],
|
|
40
|
-
"getEntriesAtDataPath": [Function],
|
|
41
|
-
"getValueAtDataPath": [Function],
|
|
42
|
-
"groupBy": [Function],
|
|
43
|
-
"hyphenate": [Function],
|
|
44
|
-
"is": [Function],
|
|
45
|
-
"isAbsoluteUrl": [Function],
|
|
46
|
-
"isArray": [Function],
|
|
47
|
-
"isArrayLike": [Function],
|
|
48
|
-
"isAsync": [Function],
|
|
49
|
-
"isBoolean": [Function],
|
|
50
|
-
"isCreditCard": [Function],
|
|
51
|
-
"isDate": [Function],
|
|
52
|
-
"isDomain": [Function],
|
|
53
|
-
"isEmail": [Function],
|
|
54
|
-
"isEmpty": [Function],
|
|
55
|
-
"isFunction": [Function],
|
|
56
|
-
"isHostname": [Function],
|
|
57
|
-
"isInteger": [Function],
|
|
58
|
-
"isModule": [Function],
|
|
59
|
-
"isNumber": [Function],
|
|
60
|
-
"isObject": [Function],
|
|
61
|
-
"isPlainArray": [Function],
|
|
62
|
-
"isPlainObject": [Function],
|
|
63
|
-
"isPromise": [Function],
|
|
64
|
-
"isRegExp": [Function],
|
|
65
|
-
"isString": [Function],
|
|
66
|
-
"isUrl": [Function],
|
|
67
|
-
"labelize": [Function],
|
|
68
|
-
"mapConcurrently": [Function],
|
|
69
|
-
"mapKeys": [Function],
|
|
70
|
-
"mapSequentially": [Function],
|
|
71
|
-
"mapValues": [Function],
|
|
72
|
-
"mergeDeeply": [Function],
|
|
73
|
-
"mixin": [Function],
|
|
74
|
-
"normalizeDataPath": [Function],
|
|
75
|
-
"parseDataPath": [Function],
|
|
76
|
-
"pick": [Function],
|
|
77
|
-
"pickBy": [Function],
|
|
78
|
-
"setDataPathEntries": [Function],
|
|
79
|
-
"setValueAtDataPath": [Function],
|
|
80
|
-
"shuffle": [Function],
|
|
81
|
-
"stripHtml": [Function],
|
|
82
|
-
"stripTags": [Function],
|
|
83
|
-
"toAsync": [Function],
|
|
84
|
-
"toCallback": [Function],
|
|
85
|
-
"toPromiseCallback": [Function],
|
|
86
|
-
"underscore": [Function],
|
|
87
|
-
}
|
|
88
|
-
`;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { flatten } from './flatten.js'
|
|
2
|
-
|
|
3
|
-
describe('flatten()', () => {
|
|
4
|
-
it('should support flattening of nested arrays', () => {
|
|
5
|
-
const array = [1, [2, [3, [4]], 5]]
|
|
6
|
-
expect(flatten(array)).toStrictEqual([1, 2, 3, 4, 5])
|
|
7
|
-
expect(flatten(array, 1)).toStrictEqual([1, 2, [3, [4]], 5])
|
|
8
|
-
expect(flatten(array, 2)).toStrictEqual([1, 2, 3, [4], 5])
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
it('should work with empty arrays', () => {
|
|
12
|
-
const array = [[], [[]], [[], [[[]]]]]
|
|
13
|
-
|
|
14
|
-
expect(flatten(array)).toStrictEqual([])
|
|
15
|
-
expect(flatten(array, 1)).toStrictEqual([[], [], [[[]]]])
|
|
16
|
-
expect(flatten(array, 2)).toStrictEqual([[[]]])
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('should treat sparse arrays as dense', () => {
|
|
20
|
-
const array = [[1, 2, 3], Array(3)]
|
|
21
|
-
const expected = [1, 2, 3]
|
|
22
|
-
expected.push(undefined, undefined, undefined)
|
|
23
|
-
expect(flatten(array)).toStrictEqual(expected)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('should return an empty array for non array-like objects', () => {
|
|
27
|
-
expect(flatten({ 0: 'a' })).toStrictEqual([])
|
|
28
|
-
})
|
|
29
|
-
})
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { shuffle } from './shuffle.js'
|
|
2
|
-
|
|
3
|
-
describe('shuffle()', () => {
|
|
4
|
-
const array = [1, 2, 3]
|
|
5
|
-
|
|
6
|
-
it('should return a new array', () => {
|
|
7
|
-
expect(shuffle(array)).not.toBe(array)
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
it(`should contain the same elements after a collection is shuffled and sorted again`, () => {
|
|
11
|
-
expect(shuffle(array).sort()).toStrictEqual(array)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('should shuffle small collections', () => {
|
|
15
|
-
const results = {}
|
|
16
|
-
for (let i = 0; i < 1000; i++) {
|
|
17
|
-
const res = shuffle([1, 2])
|
|
18
|
-
results[res] = res
|
|
19
|
-
}
|
|
20
|
-
const sorted = Object.entries(results)
|
|
21
|
-
.sort()
|
|
22
|
-
.map(([, value]) => value)
|
|
23
|
-
expect(sorted).toStrictEqual([[1, 2], [2, 1]])
|
|
24
|
-
})
|
|
25
|
-
})
|