@ditojs/utils 2.0.1 → 2.1.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/utils",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
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/master/packages/utils",
@@ -29,10 +29,10 @@
29
29
  "punycode": "^2.3.0"
30
30
  },
31
31
  "devDependencies": {
32
- "typescript": "^5.0.3"
32
+ "typescript": "^5.0.4"
33
33
  },
34
34
  "types": "types",
35
- "gitHead": "80c209774fe4ee5243eb25d77ac9a97bfb694d8c",
35
+ "gitHead": "b4ff87b7ffa812092a27b6c28a8ab3c5d7d7094d",
36
36
  "scripts": {
37
37
  "types": "tsc --noEmit types/index.d.ts"
38
38
  },
@@ -1,7 +1,7 @@
1
1
  export function shuffle(array) {
2
2
  // Do the Fisher-Yates (aka Knuth) Shuffle:
3
3
  const res = array.slice()
4
- for (let i = array.length; i;) {
4
+ for (let i = array.length; i; ) {
5
5
  const r = Math.floor(Math.random() * i)
6
6
  const t = res[--i]
7
7
  res[i] = res[r]
@@ -17,7 +17,9 @@ describe('shuffle()', () => {
17
17
  const res = shuffle([1, 2])
18
18
  results[res] = res
19
19
  }
20
- const sorted = Object.entries(results).sort().map(([, value]) => value)
20
+ const sorted = Object.entries(results)
21
+ .sort()
22
+ .map(([, value]) => value)
21
23
  expect(sorted).toStrictEqual([[1, 2], [2, 1]])
22
24
  })
23
25
  })
package/src/base/base.js CHANGED
@@ -1,11 +1,12 @@
1
1
  export const { isArray } = Array
2
2
  const { toString } = Object.prototype
3
3
 
4
- export const is = Object.is || (
4
+ export const is = (
5
+ Object.is ||
5
6
  // SameValue algorithm:
6
7
  // istanbul ignore next
7
8
  // eslint-disable-next-line no-self-compare
8
- (x, y) => x === y ? x !== 0 || 1 / x === 1 / y : x !== x && y !== y
9
+ ((x, y) => (x === y ? x !== 0 || 1 / x === 1 / y : x !== x && y !== y))
9
10
  )
10
11
 
11
12
  export function isPlainObject(arg) {
@@ -13,11 +14,14 @@ export function isPlainObject(arg) {
13
14
  // We also need to check for ctor.name === 'Object', in case this is an object
14
15
  // from another global scope (e.g. another vm context in Node.js).
15
16
  // When an value has no constructor, it was created with `Object.create(null)`
16
- return !!arg && (
17
- ctor && (
18
- ctor === Object ||
19
- ctor.name === 'Object'
20
- ) || !ctor && !isModule(arg)
17
+ return (
18
+ !!arg && (
19
+ ctor && (
20
+ ctor === Object ||
21
+ ctor.name === 'Object'
22
+ ) ||
23
+ !ctor && !isModule(arg)
24
+ )
21
25
  )
22
26
  }
23
27
 
@@ -35,7 +39,7 @@ function getPrimitiveCheck(name) {
35
39
  // here because `new Date().valueOf()` also returns a number.
36
40
  const typeName = name.toLowerCase()
37
41
  const toStringName = `[object ${name}]`
38
- return function(arg) {
42
+ return function (arg) {
39
43
  const type = typeof arg
40
44
  return (
41
45
  type === typeName ||
@@ -64,13 +68,16 @@ export function isPromise(arg) {
64
68
  return !!arg && isFunction(arg.then) && isFunction(arg.catch)
65
69
  }
66
70
 
67
- export const isInteger = Number.isInteger || function isInteger(arg) {
68
- return (
69
- isNumber(arg) &&
70
- isFinite(arg) &&
71
- Math.floor(arg) === arg
72
- )
73
- }
71
+ export const isInteger = (
72
+ Number.isInteger ||
73
+ function isInteger(arg) {
74
+ return (
75
+ isNumber(arg) &&
76
+ isFinite(arg) &&
77
+ Math.floor(arg) === arg
78
+ )
79
+ }
80
+ )
74
81
 
75
82
  export function isAsync(arg) {
76
83
  return arg?.[Symbol.toStringTag] === 'AsyncFunction'
@@ -88,9 +95,11 @@ export function isArrayLike(arg) {
88
95
  }
89
96
 
90
97
  export function isEmpty(arg) {
91
- return arg == null ||
98
+ return (
99
+ arg == null ||
92
100
  isArrayLike(arg) && arg.length === 0 ||
93
101
  isObject(arg) && Object.keys(arg).length === 0
102
+ )
94
103
  }
95
104
 
96
105
  export function asObject(arg) {
@@ -1,7 +1,22 @@
1
1
  import {
2
- isPlainObject, isObject, isArray, isModule, isFunction, isString, isBoolean,
3
- isNumber, isDate, isRegExp, isPromise, isAsync, isInteger, isArrayLike,
4
- isEmpty, asObject, asArray, asFunction
2
+ isPlainObject,
3
+ isObject,
4
+ isArray,
5
+ isModule,
6
+ isFunction,
7
+ isString,
8
+ isBoolean,
9
+ isNumber,
10
+ isDate,
11
+ isRegExp,
12
+ isPromise,
13
+ isAsync,
14
+ isInteger,
15
+ isArrayLike,
16
+ isEmpty,
17
+ asObject,
18
+ asArray,
19
+ asFunction
5
20
  } from './base.js'
6
21
 
7
22
  import * as module from './index.js'
@@ -14,12 +29,12 @@ const boolean = true
14
29
  const date = new Date()
15
30
  const regexp = /x/
16
31
  const symbol = Symbol('a')
17
- const instance = new class {}()
32
+ const instance = new (class {})()
18
33
  const promise = new Promise(() => {})
19
- const func = function() {}
34
+ const func = function () {}
20
35
  const lambda = () => {}
21
- const asyncFunc = async function() {}
22
- const asyncLambda = async function() {}
36
+ const asyncFunc = async function () {}
37
+ const asyncLambda = async function () {}
23
38
 
24
39
  describe('isPlainObject()', () => {
25
40
  describe.each([
@@ -3,7 +3,7 @@
3
3
  const mixinsMap = new WeakMap()
4
4
 
5
5
  export function mixin(mixinFunction) {
6
- return function(targetClass) {
6
+ return function (targetClass) {
7
7
  // Prevent multiple application of the same mixins in nested inheritance
8
8
  // through keeping track of what was already applied in weak-maps:
9
9
  if (targetClass && !hasMixin(targetClass, mixinFunction)) {
@@ -5,13 +5,19 @@ describe('mixin()', () => {
5
5
  function() {}
6
6
  }
7
7
 
8
- const SomeMixin1 = mixin(Class => class extends Class {
9
- function1() {}
10
- })
8
+ const SomeMixin1 = mixin(
9
+ Class =>
10
+ class extends Class {
11
+ function1() {}
12
+ }
13
+ )
11
14
 
12
- const SomeMixin2 = mixin(Class => class extends Class {
13
- function2() {}
14
- })
15
+ const SomeMixin2 = mixin(
16
+ Class =>
17
+ class extends Class {
18
+ function2() {}
19
+ }
20
+ )
15
21
 
16
22
  it('should apply mixin to class', () => {
17
23
  const NewClass = SomeMixin1(SomeClass)
@@ -57,8 +63,7 @@ describe('mixin()', () => {
57
63
 
58
64
  it(`should not apply mixin more than once, even if it was applied higher up`, () => {
59
65
  const NewClass1 = SomeMixin1(SomeClass)
60
- class NewClass2 extends NewClass1 {
61
- }
66
+ class NewClass2 extends NewClass1 {}
62
67
  const NewClass3 = SomeMixin1(NewClass2)
63
68
  expect(NewClass3).toBe(NewClass2)
64
69
  })
@@ -6,7 +6,9 @@ const NOT_FOUND = Symbol('NOT_FOUND')
6
6
  export function getEntriesAtDataPath(
7
7
  obj,
8
8
  path,
9
- handleError = () => { throw new Error(`Invalid path: ${path}`) }
9
+ handleError = () => {
10
+ throw new Error(`Invalid path: ${path}`)
11
+ }
10
12
  ) {
11
13
  const parsedPath = parseDataPath(path)
12
14
 
@@ -6,7 +6,9 @@ const NOT_FOUND = Symbol('NOT_FOUND')
6
6
  export function getValueAtDataPath(
7
7
  obj,
8
8
  path,
9
- handleError = () => { throw new Error(`Invalid path: ${path}`) }
9
+ handleError = () => {
10
+ throw new Error(`Invalid path: ${path}`)
11
+ }
10
12
  ) {
11
13
  const parsedPath = parseDataPath(path)
12
14
  let index = 0
@@ -20,18 +22,21 @@ export function getValueAtDataPath(
20
22
  // Support wildcards on arrays and objects
21
23
  const pathNested = parsedPath.slice(index + 1)
22
24
  const values = isArray(obj) ? obj : Object.values(obj)
23
- return values.map(
24
- value => getValueAtDataPath(value, pathNested, handleError)
25
- ).flat(1)
25
+ return values
26
+ .map(value => getValueAtDataPath(value, pathNested, handleError))
27
+ .flat(1)
26
28
  } else if (part === '**') {
27
29
  // Support deep wildcards on arrays and objects
28
30
  const pathNested = parsedPath.slice(index + 1)
29
31
  const pathSelf = parsedPath.slice(index)
30
32
  const values = isArray(obj) ? obj : Object.values(obj)
31
- return values.map(value => [
32
- ...asArray(getValueAtDataPath(value, pathNested, () => NOT_FOUND)),
33
- ...asArray(getValueAtDataPath(value, pathSelf, () => NOT_FOUND))
34
- ]).flat(1).filter(value => value !== NOT_FOUND)
33
+ return values
34
+ .map(value => [
35
+ ...asArray(getValueAtDataPath(value, pathNested, () => NOT_FOUND)),
36
+ ...asArray(getValueAtDataPath(value, pathSelf, () => NOT_FOUND))
37
+ ])
38
+ .flat(1)
39
+ .filter(value => value !== NOT_FOUND)
35
40
  }
36
41
  }
37
42
  return handleError?.(obj, part, index)
@@ -13,10 +13,12 @@ describe('setDataPathEntries()', () => {
13
13
  const add = { prop: 'new' }
14
14
 
15
15
  it('should add data at a path to a given object and array', () => {
16
- expect(() => setDataPathEntries(data, {
17
- 'object.array[0].added': add,
18
- 'object.array[1]': add
19
- }))
16
+ expect(() =>
17
+ setDataPathEntries(data, {
18
+ 'object.array[0].added': add,
19
+ 'object.array[1]': add
20
+ })
21
+ )
20
22
  .not.toThrow()
21
23
  expect(data.object.array[0].added).toStrictEqual(add)
22
24
  expect(data.object.array[1]).toStrictEqual(add)
@@ -3,7 +3,7 @@ import { toAsync } from './toAsync.js'
3
3
  describe('toAsync()', () => {
4
4
  it('should convert callback functions to async', async () => {
5
5
  expect.assertions(1)
6
- const asyncFunc = toAsync(function(toResolve, callback) {
6
+ const asyncFunc = toAsync(function (toResolve, callback) {
7
7
  process.nextTick(() => {
8
8
  callback(null, toResolve)
9
9
  })
@@ -16,7 +16,7 @@ describe('toAsync()', () => {
16
16
  it('should convert callback errors to exceptions', async () => {
17
17
  expect.assertions(1)
18
18
  const error = new Error('This error is intentional')
19
- const throwError = toAsync(function(toReject, callback) {
19
+ const throwError = toAsync(function (toReject, callback) {
20
20
  process.nextTick(() => {
21
21
  callback(toReject)
22
22
  })
@@ -1,6 +1,11 @@
1
1
  export function escapeHtml(html) {
2
2
  return html != null
3
- ? html.toString().replace(/["&<>]/g,
4
- chr => ({ '"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;' })[chr])
3
+ ? html
4
+ .toString()
5
+ .replace(
6
+ /["&<>]/g,
7
+ chr =>
8
+ ({ '"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;' }[chr])
9
+ )
5
10
  : ''
6
11
  }
@@ -1,5 +1,10 @@
1
1
  import {
2
- isArray, isObject, isDate, isRegExp, isFunction, isPromise
2
+ isArray,
3
+ isObject,
4
+ isDate,
5
+ isRegExp,
6
+ isFunction,
7
+ isPromise
3
8
  } from '../base/index.js'
4
9
  import { pick } from './pick.js'
5
10
 
@@ -21,11 +26,12 @@ export function clone(value, options) {
21
26
  return copy
22
27
  }
23
28
 
24
- const handleValue = value => {
25
- return shallow || transferables?.includes(value) ? value
26
- : clones.has(value) ? clones.get(value)
27
- : cloneValue(value)
28
- }
29
+ const handleValue = value =>
30
+ shallow || transferables?.includes(value)
31
+ ? value
32
+ : clones.has(value)
33
+ ? clones.get(value)
34
+ : cloneValue(value)
29
35
 
30
36
  const cloneValue = value => {
31
37
  let copy = value
@@ -39,15 +45,15 @@ export function clone(value, options) {
39
45
  copy[i] = handleValue(value[i])
40
46
  }
41
47
  } else if (isObject(value)) {
42
- // Rely on arg.clone() if it exists and assume it creates an actual clone.
48
+ // Rely on arg.clone() if it exists and assume it creates an actual clone.
43
49
  if (isFunction(value.clone)) {
44
50
  copy = storeClone(value, value.clone(options))
45
51
  } else if (isPromise(value)) {
46
- // https://stackoverflow.com/questions/37063293/can-i-clone-a-promise
52
+ // https://stackoverflow.com/questions/37063293/can-i-clone-a-promise
47
53
  copy = storeClone(value, value.then())
48
54
  } else {
49
- // Prevent calling the actual constructor since it is not guaranteed to
50
- // work as intended here, and only clone the non-inherited own properties.
55
+ // Prevent calling the actual constructor since it is not guaranteed to
56
+ // work as intended here, and only clone non-inherited own properties.
51
57
  copy = storeClone(value, Object.create(Object.getPrototypeOf(value)))
52
58
  clones.set(value, copy)
53
59
  const keys = enumerable ? Object.keys(value) : Reflect.ownKeys(value)
@@ -4,27 +4,52 @@ describe('equals()', () => {
4
4
  const symbol1 = Symbol('a')
5
5
  const symbol2 = Symbol('b')
6
6
  describe.each([
7
- [1, 1, true], [1, Object(1), true], [1, '1', false], [1, 2, false],
8
- [-0, -0, true], [0, 0, true], [0, Object(0), true],
9
- [Object(0), Object(0), true], [-0, 0, true], [0, '0', false],
7
+ [1, 1, true],
8
+ [1, Object(1), true],
9
+ [1, '1', false],
10
+ [1, 2, false],
11
+ [-0, -0, true],
12
+ [0, 0, true],
13
+ [0, Object(0), true],
14
+ [Object(0), Object(0), true],
15
+ [-0, 0, true],
16
+ [0, '0', false],
10
17
  [0, null, false],
11
18
 
12
- [NaN, NaN, true], [NaN, Object(NaN), true],
13
- [Object(NaN), Object(NaN), true], [NaN, 'a', false], [NaN, Infinity, false],
14
-
15
- ['a', 'a', true], ['a', Object('a'), true],
16
- [Object('a'), Object('a'), true], ['a', 'b', false], ['a', ['a'], false],
17
-
18
- [true, true, true], [true, Object(true), true],
19
- [Object(true), Object(true), true], [true, 1, false], [true, 'a', false],
20
- [false, false, true], [false, Object(false), true],
21
- [Object(false), Object(false), true], [false, 0, false], [false, '', false],
22
-
23
- [symbol1, symbol1, true], [symbol1, Object(symbol1), true],
24
- [Object(symbol1), Object(symbol1), true], [symbol1, symbol2, false],
25
-
26
- [null, null, true], [null, undefined, false], [null, {}, false],
27
- [null, '', false], [undefined, undefined, true], [undefined, null, false],
19
+ [NaN, NaN, true],
20
+ [NaN, Object(NaN), true],
21
+ [Object(NaN), Object(NaN), true],
22
+ [NaN, 'a', false],
23
+ [NaN, Infinity, false],
24
+
25
+ ['a', 'a', true],
26
+ ['a', Object('a'), true],
27
+ [Object('a'), Object('a'), true],
28
+ ['a', 'b', false],
29
+ ['a', ['a'], false],
30
+
31
+ [true, true, true],
32
+ [true, Object(true), true],
33
+ [Object(true), Object(true), true],
34
+ [true, 1, false],
35
+ [true, 'a', false],
36
+ [false, false, true],
37
+ [false, Object(false), true],
38
+ [Object(false), Object(false), true],
39
+ [false, 0, false],
40
+ [false, '', false],
41
+
42
+ [symbol1, symbol1, true],
43
+ [symbol1, Object(symbol1), true],
44
+ [Object(symbol1), Object(symbol1), true],
45
+ [symbol1, symbol2, false],
46
+
47
+ [null, null, true],
48
+ [null, undefined, false],
49
+ [null, {}, false],
50
+ [null, '', false],
51
+ [undefined, undefined, true],
52
+ [undefined, null, false],
28
53
  [undefined, '', false]
29
54
  ])(
30
55
  'equals(%o, %o) (should compare primitives)',
@@ -91,13 +116,27 @@ describe('equals()', () => {
91
116
  let array1 = [1, 2, 3]
92
117
  let array2 = [1, 2, 3]
93
118
 
94
- array1.every = array1.filter = array1.forEach =
95
- array1.indexOf = array1.lastIndexOf = array1.map =
96
- array1.some = array1.reduce = array1.reduceRight = null
97
-
98
- array2.concat = array2.join = array2.pop =
99
- array2.reverse = array2.shift = array2.slice =
100
- array2.sort = array2.splice = array2.unshift = null
119
+ array1.every =
120
+ array1.filter =
121
+ array1.forEach =
122
+ array1.indexOf =
123
+ array1.lastIndexOf =
124
+ array1.map =
125
+ array1.some =
126
+ array1.reduce =
127
+ array1.reduceRight =
128
+ null
129
+
130
+ array2.concat =
131
+ array2.join =
132
+ array2.pop =
133
+ array2.reverse =
134
+ array2.shift =
135
+ array2.slice =
136
+ array2.sort =
137
+ array2.splice =
138
+ array2.unshift =
139
+ null
101
140
 
102
141
  expect(equals(array1, array2)).toBe(true)
103
142
 
@@ -217,8 +256,12 @@ describe('equals()', () => {
217
256
  })
218
257
 
219
258
  it('should compare functions', () => {
220
- function a() { return 1 + 2 }
221
- function b() { return 1 + 2 }
259
+ function a() {
260
+ return 1 + 2
261
+ }
262
+ function b() {
263
+ return 1 + 2
264
+ }
222
265
 
223
266
  expect(equals(a, a)).toBe(true)
224
267
  expect(equals(a, b)).toBe(false)
@@ -17,12 +17,14 @@ describe('groupBy()', () => {
17
17
  [2, 'a'],
18
18
  [2, 'b']
19
19
  ]
20
- expect(groupBy(array, value => value[0])).toStrictEqual(
21
- { 1: [[1, 'a']], 2: [[2, 'a'], [2, 'b']] }
22
- )
23
- expect(groupBy(array, value => value[1])).toStrictEqual(
24
- { a: [[1, 'a'], [2, 'a']], b: [[2, 'b']] }
25
- )
20
+ expect(groupBy(array, value => value[0])).toStrictEqual({
21
+ 1: [[1, 'a']],
22
+ 2: [[2, 'a'], [2, 'b']]
23
+ })
24
+ expect(groupBy(array, value => value[1])).toStrictEqual({
25
+ a: [[1, 'a'], [2, 'a']],
26
+ b: [[2, 'b']]
27
+ })
26
28
  })
27
29
 
28
30
  it('should work with an object for `collection`', () => {
@@ -1,4 +1,3 @@
1
-
2
1
  export function mapKeys(object, callback) {
3
2
  return Object.keys(object).reduce((mapped, key) => {
4
3
  const value = object[key]
@@ -1,4 +1,3 @@
1
-
2
1
  export function mapValues(object, callback) {
3
2
  return Object.keys(object).reduce((mapped, key) => {
4
3
  mapped[key] = callback(object[key], key, object)
@@ -3,10 +3,14 @@ import { clone } from './clone.js'
3
3
 
4
4
  export function merge(target, ...sources) {
5
5
  const _merge = (target, source, cloneTarget) => {
6
- if (target && source && target !== source && (
7
- isArray(target) && isArray(source) ||
8
- isPlainObject(target) && isPlainObject(source)
9
- )) {
6
+ if (
7
+ target &&
8
+ source &&
9
+ target !== source && (
10
+ isArray(target) && isArray(source) ||
11
+ isPlainObject(target) && isPlainObject(source)
12
+ )
13
+ ) {
10
14
  const result = cloneTarget
11
15
  ? clone(target)
12
16
  : target
@@ -1,13 +1,14 @@
1
1
  export function camelize(str, pascalCase = false) {
2
2
  return str
3
3
  ? str
4
- // Trim beginnings and ends
5
- .replace(/^[-_\s]+|[-_\s]+$/g, '')
6
- .replace(
7
- /(^|[-_\s]+)(\w)([A-Z]*)([a-z]*)/g,
8
- (all, sep, first, upperRest, lowerRest) => `${
9
- pascalCase || sep ? first.toUpperCase() : first.toLowerCase()
10
- }${upperRest.toLowerCase()}${lowerRest}`
11
- )
4
+ // Trim beginnings and ends
5
+ .replace(/^[-_\s]+|[-_\s]+$/g, '')
6
+ .replace(
7
+ /(^|[-_\s]+)(\w)([A-Z]*)([a-z]*)/g,
8
+ (all, sep, first, upperRest, lowerRest) =>
9
+ `${
10
+ pascalCase || sep ? first.toUpperCase() : first.toLowerCase()
11
+ }${upperRest.toLowerCase()}${lowerRest}`
12
+ )
12
13
  : ''
13
14
  }
@@ -14,7 +14,10 @@ describe('camelize()', () => {
14
14
  (pascalCase, expected) => {
15
15
  describe.each([
16
16
  ...strings,
17
- 'foo-bar', 'foo_bar', '--foo-bar--', '__foo_bar__'
17
+ 'foo-bar',
18
+ 'foo_bar',
19
+ '--foo-bar--',
20
+ '__foo_bar__'
18
21
  ])(
19
22
  `camelize(%o, ${pascalCase})`,
20
23
  value => {
@@ -1,6 +1,7 @@
1
1
  export function capitalize(str) {
2
2
  return str
3
- ? str.replace(/(^|[^a-zA-Z\u00C0-\u017F'])([a-zA-Z\u00C0-\u017F])/g,
4
- chr => chr.toUpperCase())
3
+ ? str.replace(/(^|[^a-zA-Z\u00C0-\u017F'])([a-zA-Z\u00C0-\u017F])/g, chr =>
4
+ chr.toUpperCase()
5
+ )
5
6
  : ''
6
7
  }
@@ -1,16 +1,18 @@
1
1
  export function decamelize(str, sep = ' ') {
2
2
  return str
3
3
  ? str
4
- .replace(
5
- // TODO: Once JavaScript supports Unicode properties in regexps, switch
6
- // to better parsing that matches non-ASCII uppercase letters:
7
- // /([\p{Ll}\d])(\p{Lu})/gu
8
- /([a-z\d])([A-Z])|(\S)(?:\s+)(\S)/g,
9
- (all, lower, upper, beforeSpace, afterSpace) => upper
10
- ? `${lower}${sep}${upper}`
11
- : `${beforeSpace}${sep}${afterSpace}`
12
- )
13
- .toLowerCase()
4
+ .replace(
5
+ // TODO: Once JavaScript supports Unicode property escapes in regexps,
6
+ // switch to better parsing that matches non-ASCII uppercase letters:
7
+ // /([\p{Ll}\d])(\p{Lu})/gu
8
+ // See: https://caniuse.com/?search=Unicode%20property%20escape
9
+ /([a-z\d])([A-Z])|(\S)(?:\s+)(\S)/g,
10
+ (all, lower, upper, beforeSpace, afterSpace) =>
11
+ upper
12
+ ? `${lower}${sep}${upper}`
13
+ : `${beforeSpace}${sep}${afterSpace}`
14
+ )
15
+ .toLowerCase()
14
16
  : ''
15
17
  }
16
18
 
@@ -11,11 +11,13 @@ export function deindent(strings, ...values) {
11
11
  // First, perform interpolation
12
12
  let parts = []
13
13
  for (let i = 0; i < strings.length; i++) {
14
- parts.push(strings[i]
15
- // Join lines when there is a line-break suppressed by a preceding '\\'
16
- .replace(/\\\n\s*/g, '')
17
- // Handle escaped back-ticks
18
- .replace(/\\`/g, '`'))
14
+ parts.push(
15
+ strings[i]
16
+ // Join lines when there is a line-break suppressed by a preceding '\\'
17
+ .replace(/\\\n\s*/g, '')
18
+ // Handle escaped back-ticks
19
+ .replace(/\\`/g, '`')
20
+ )
19
21
  if (i < values.length) {
20
22
  // See if the value itself contains multiple lines, and if so, indent
21
23
  // each of them by the whitespace that precedes it except the first.
@@ -32,7 +34,9 @@ export function deindent(strings, ...values) {
32
34
  // # line, by making sure no line breaks follow
33
35
  // # until the end.
34
36
  const str = parts.join('')
35
- const match = str.match(/((?:^[\n\r]*|[\n\r])(?:[\n\r]*))(\s+)(?:[^\n\r]*)$/)
37
+ const match = str.match(
38
+ /((?:^[\n\r]*|[\n\r])(?:[\n\r]*))(\s+)(?:[^\n\r]*)$/
39
+ )
36
40
  const [, lineBreaks = '', indent = ''] = match || []
37
41
  parts = [str]
38
42
  parts.push(lines.join(`${getLineBreak(lineBreaks)}${indent}`))
@@ -8,7 +8,7 @@ describe('deindent()', () => {
8
8
  indented
9
9
  text`
10
10
  ).toBe(
11
- `some
11
+ `some
12
12
  indented
13
13
  text`
14
14
  )
@@ -22,7 +22,7 @@ describe('deindent()', () => {
22
22
  text
23
23
  `
24
24
  ).toBe(
25
- `some
25
+ `some
26
26
  indented
27
27
  text
28
28
  `
@@ -39,7 +39,7 @@ describe('deindent()', () => {
39
39
  text
40
40
  `
41
41
  ).toBe(
42
- `
42
+ `
43
43
 
44
44
  some
45
45
  indented
@@ -58,7 +58,7 @@ some
58
58
 
59
59
  `
60
60
  ).toBe(
61
- `some
61
+ `some
62
62
  indented
63
63
  text
64
64
 
@@ -76,7 +76,7 @@ some
76
76
  indented
77
77
  text`
78
78
  ).toBe(
79
- `some
79
+ `some
80
80
  indented
81
81
  text
82
82
  some
@@ -91,7 +91,7 @@ some
91
91
  indented
92
92
  text`
93
93
  ).toBe(
94
- `some
94
+ `some
95
95
  indented
96
96
  text`
97
97
  )
@@ -106,7 +106,7 @@ some
106
106
  ${singleLineText}
107
107
  `
108
108
  ).toBe(
109
- `some
109
+ `some
110
110
  indented
111
111
  single-line text
112
112
  `
@@ -122,7 +122,7 @@ indented
122
122
  ${multiLineText}
123
123
  `
124
124
  ).toBe(
125
- `some
125
+ `some
126
126
  indented
127
127
  multi-
128
128
  line
@@ -140,7 +140,7 @@ text
140
140
  ${multiLineText}
141
141
  `
142
142
  ).toBe(
143
- `some
143
+ `some
144
144
  indented
145
145
  multi-
146
146
  line
@@ -158,7 +158,7 @@ text
158
158
  content: ${multiLineText}
159
159
  `
160
160
  ).toBe(
161
- `some
161
+ `some
162
162
  indented
163
163
  content: multi-
164
164
  line
@@ -173,7 +173,7 @@ text
173
173
  indented
174
174
  text`
175
175
  ).toBe(
176
- `some
176
+ `some
177
177
  indented
178
178
  text`
179
179
  )
@@ -32,14 +32,14 @@ export function format(value, {
32
32
  ? defaultOption
33
33
  : isObject(option)
34
34
  ? Object.entries({
35
- ...defaultOption,
36
- ...option
37
- }).reduce((opt, [key, value]) => {
38
- if (value !== false) {
39
- opt[key] = value
40
- }
41
- return opt
42
- }, {})
35
+ ...defaultOption,
36
+ ...option
37
+ }).reduce((opt, [key, value]) => {
38
+ if (value !== false) {
39
+ opt[key] = value
40
+ }
41
+ return opt
42
+ }, {})
43
43
  : {}
44
44
  }
45
45
 
@@ -62,9 +62,11 @@ export function format(value, {
62
62
  // e.g. to replace decimals and such:
63
63
  const parts = new Intl.NumberFormat(locale, options)
64
64
  .formatToParts(value)
65
- return parts.map(({ type, value }) =>
66
- options.format(value, type, options) ?? value
67
- ).join('')
65
+ return parts
66
+ .map(
67
+ ({ type, value }) => options.format(value, type, options) ?? value
68
+ )
69
+ .join('')
68
70
  }
69
71
  }
70
72
  } else if (isDate(value)) {
@@ -86,16 +88,18 @@ export function format(value, {
86
88
  const parts = new Intl.DateTimeFormat(locale, options)
87
89
  .formatToParts(value)
88
90
  let modeOpts = null
89
- return parts.map(({ type, value }) => {
90
- if (type !== 'literal') {
91
- // Switch mode between date & time to pick the right format methdod:
92
- const mode = ['weekday', 'day', 'month', 'year'].includes(type)
93
- ? 'date'
94
- : 'time'
95
- modeOpts = opts[mode]
96
- }
97
- return modeOpts?.format?.(value, type, modeOpts) ?? value
98
- }).join('')
91
+ return parts
92
+ .map(({ type, value }) => {
93
+ if (type !== 'literal') {
94
+ // Switch mode between date/time to pick the right format method:
95
+ const mode = ['weekday', 'day', 'month', 'year'].includes(type)
96
+ ? 'date'
97
+ : 'time'
98
+ modeOpts = opts[mode]
99
+ }
100
+ return modeOpts?.format?.(value, type, modeOpts) ?? value
101
+ })
102
+ .join('')
99
103
  }
100
104
  }
101
105
  }
@@ -148,11 +148,12 @@ describe('format()', () => {
148
148
  number: {
149
149
  style: 'currency',
150
150
  currency: 'GBP',
151
- format: (value, type) => type === 'group'
152
- ? `’`
153
- : type === 'decimal'
154
- ? ','
155
- : undefined
151
+ format: (value, type) =>
152
+ type === 'group'
153
+ ? `’`
154
+ : type === 'decimal'
155
+ ? ','
156
+ : undefined
156
157
  }
157
158
  })
158
159
  ).toBe(`£123’456’789,00`)
@@ -166,11 +167,10 @@ describe('format()', () => {
166
167
  day: 'numeric',
167
168
  month: 'numeric',
168
169
  year: 'numeric',
169
- format: (value, type, options) => (
170
+ format: (value, type, options) =>
170
171
  type === 'literal' && options.month === 'numeric'
171
172
  ? value.replace(/\//, '.').replace(/,/, '')
172
173
  : undefined
173
- )
174
174
  },
175
175
  time: {
176
176
  hour: '2-digit',
@@ -1,4 +1,5 @@
1
- const creditCardRegExp = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})|62[0-9]{14}$/
1
+ const creditCardRegExp =
2
+ /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})|62[0-9]{14}$/
2
3
 
3
4
  export function isCreditCard(str) {
4
5
  str = str && str.replace(/[\s-]+/g, '')
@@ -2,7 +2,8 @@
2
2
  import punycode from 'punycode'
3
3
 
4
4
  // Best effort approach, allowing Internationalized domain name (with punycode)
5
- const domainRegExp = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i
5
+ const domainRegExp =
6
+ /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i
6
7
 
7
8
  export function isDomain(str) {
8
9
  return !!(str && domainRegExp.test(punycode.toASCII(str)))
@@ -1,4 +1,5 @@
1
- const emailRegExp = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i
1
+ const emailRegExp =
2
+ /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i
2
3
 
3
4
  export function isEmail(str) {
4
5
  return !!(str && emailRegExp.test(str))
@@ -1,6 +1,7 @@
1
1
  // https://tools.ietf.org/html/rfc1034#section-3.5
2
2
  // https://tools.ietf.org/html/rfc1123#section-2
3
- const hostnameRegExp = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*$/i
3
+ const hostnameRegExp =
4
+ /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*$/i
4
5
 
5
6
  export function isHostname(str) {
6
7
  return !!(str && hostnameRegExp.test(str))
@@ -2,19 +2,19 @@
2
2
  // isUrl() returns `true` for 'google.com', and isAbsoluteUrl() returns `false`!
3
3
 
4
4
  const isUrlRegExp = new RegExp(
5
- '^((https?|ftps?|mailto|rtsp|mms)?://)?' +
6
- '(([0-9a-z_!~*\'().&=+$%-]+:)?[0-9a-z_!~*\'().&=+$%-]*@)?' + // user:pass@
5
+ '^((https?|ftps?|mailto|rtsp|mms)?://)?' + // user:pass@
6
+ "(([0-9a-z_!~*'().&=+$%-]+:)?[0-9a-z_!~*'().&=+$%-]*@)?" +
7
7
  '(' +
8
- '(\\d{1,3}\\.){3}\\d{1,3}' + // ip
9
- '|' +
10
- '([0-9a-z_-]+\\.)*' + // sub-domain: www.
11
- '[0-9a-z][0-9a-z_-]{0,61}[0-9a-z]\\.' + // domain-name
12
- // tld - https://tools.ietf.org/id/draft-liman-tld-names-00.html#rfc.section.2
13
- '([a-z]{2,61}|xn--[0-9a-z][0-9a-z-]{0,61}[0-9a-z])' +
8
+ '(\\d{1,3}\\.){3}\\d{1,3}' + // ip
9
+ '|' +
10
+ '([0-9a-z_-]+\\.)*' + // sub-domain: www.
11
+ '[0-9a-z][0-9a-z_-]{0,61}[0-9a-z]\\.' + // domain-name
12
+ // tld - https://tools.ietf.org/id/draft-liman-tld-names-00.html#rfc.section.2
13
+ '([a-z]{2,61}|xn--[0-9a-z][0-9a-z-]{0,61}[0-9a-z])' +
14
14
  ')' + // top level domain.
15
15
  '(:[0-9]{1,5})?' + // port
16
16
  '((/?)|' + // allow ending in a slash
17
- '([/?#][0-9a-z_!~*\'().;:@&=+$,%/?#-]+)+/?)$', // path
17
+ "([/?#][0-9a-z_!~*'().;:@&=+$,%/?#-]+)+/?)$", // path
18
18
  'i'
19
19
  )
20
20
 
@@ -24,7 +24,7 @@ describe('isUrl()', () => {
24
24
  'http://10.0.0.0/',
25
25
  'http://189.123.14.13/',
26
26
  'http://duckduckgo.com/?q=%2F',
27
- 'http://foobar.com/t$-_.+!*\'(),',
27
+ "http://foobar.com/t$-_.+!*'(),",
28
28
  'http://foobar.com/?foo=bar#baz=qux',
29
29
  'http://foobar.com?foo=bar',
30
30
  'http://foobar.com#baz=qux',
@@ -76,7 +76,7 @@ describe('isUrl()', () => {
76
76
  'http://localhost:61500this is an invalid url!!!!',
77
77
  '////foobar.com',
78
78
  'http:////foobar.com',
79
- 'https://example.com/foo/<script>alert(\'XSS\')</script>/'
79
+ "https://example.com/foo/<script>alert('XSS')</script>/"
80
80
  // TODO: block this: 'mailto:foo@bar.com'
81
81
  ])(
82
82
  'isUrl(%o)',
@@ -2,13 +2,15 @@ export function labelize(str) {
2
2
  // Handle hyphenated, underscored and camel-cased property names and
3
3
  // expand them to title cased labels.
4
4
  return str
5
- ? str.replace(/([-_ ]|^)(\w)|([a-z])(?=[A-Z0-9])|(\d)([a-zA-Z])/g,
6
- function(all, hyphen, hyphenated, camel, decimal, decimalNext) {
7
- return hyphenated
8
- ? `${hyphen ? ' ' : ''}${hyphenated.toUpperCase()}`
9
- : camel
10
- ? `${camel} `
11
- : `${decimal} ${decimalNext.toUpperCase()}`
12
- })
5
+ ? str.replace(
6
+ /([-_ ]|^)(\w)|([a-z])(?=[A-Z0-9])|(\d)([a-zA-Z])/g,
7
+ function (all, hyphen, hyphenated, camel, decimal, decimalNext) {
8
+ return hyphenated
9
+ ? `${hyphen ? ' ' : ''}${hyphenated.toUpperCase()}`
10
+ : camel
11
+ ? `${camel} `
12
+ : `${decimal} ${decimalNext.toUpperCase()}`
13
+ }
14
+ )
13
15
  : ''
14
16
  }
@@ -6,7 +6,7 @@ export function debounce(func, options) {
6
6
  let timer = null
7
7
  let result
8
8
 
9
- const debounced = function(...args) {
9
+ const debounced = function (...args) {
10
10
  const callNow = immediate && !timer
11
11
  clearTimeout(timer)
12
12
  timer = setTimeout(async () => {
@@ -21,7 +21,7 @@ export function debounce(func, options) {
21
21
  return result
22
22
  }
23
23
 
24
- debounced.cancel = function() {
24
+ debounced.cancel = function () {
25
25
  const pending = timer !== null
26
26
  if (pending) {
27
27
  clearTimeout(timer)
@@ -26,7 +26,7 @@ export function debounceAsync(func, options) {
26
26
  callbacks = []
27
27
  }
28
28
 
29
- const debounced = function(...args) {
29
+ const debounced = function (...args) {
30
30
  return new Promise((resolve, reject) => {
31
31
  const callNow = immediate && !timer
32
32
  clearTimeout(timer)
@@ -45,7 +45,7 @@ export function debounceAsync(func, options) {
45
45
  })
46
46
  }
47
47
 
48
- debounced.cancel = function() {
48
+ debounced.cancel = function () {
49
49
  const pending = timer !== null
50
50
  if (pending) {
51
51
  resolve(undefined)
@@ -53,7 +53,9 @@ describe('debounceAsync()', () => {
53
53
 
54
54
  it('should pass through `this`', async () => {
55
55
  expect.assertions(2)
56
- const func = vi.fn(async function() { return this })
56
+ const func = vi.fn(async function () {
57
+ return this
58
+ })
57
59
  const debounced = debounceAsync(func, 1000)
58
60
  const obj = {}
59
61
  const promises = []
@@ -82,69 +84,60 @@ describe('debounceAsync()', () => {
82
84
  expect(func).toBeCalledTimes(1)
83
85
  })
84
86
 
85
- it(
86
- 'should execute once immediately if intervals are less than wait',
87
- async () => {
88
- expect.assertions(2)
89
- const func = vi.fn()
90
- const debounced = debounceAsync(func, { delay: 1000, immediate: true })
91
- const promises = []
87
+ it('should execute once immediately if intervals are less than wait', async () => {
88
+ expect.assertions(2)
89
+ const func = vi.fn()
90
+ const debounced = debounceAsync(func, { delay: 1000, immediate: true })
91
+ const promises = []
92
+ promises.push(debounced())
93
+ expect(func).toBeCalledTimes(1)
94
+ for (let i = 0; i < 10; i++) {
95
+ vi.advanceTimersByTime(500)
92
96
  promises.push(debounced())
93
- expect(func).toBeCalledTimes(1)
94
- for (let i = 0; i < 10; i++) {
95
- vi.advanceTimersByTime(500)
96
- promises.push(debounced())
97
- }
98
- await Promise.all(promises)
99
- expect(func).toBeCalledTimes(1)
100
97
  }
101
- )
98
+ await Promise.all(promises)
99
+ expect(func).toBeCalledTimes(1)
100
+ })
102
101
 
103
- it(
104
- 'should execute twice immediately with long enough intervals',
105
- async () => {
106
- expect.assertions(3)
107
- const func = vi.fn()
108
- const debounced = debounceAsync(func, { delay: 1000, immediate: true })
109
- const promises = []
110
- promises.push(debounced())
111
- expect(func).toBeCalledTimes(1)
112
- for (let i = 0; i < 10; i++) {
113
- vi.advanceTimersByTime(500)
114
- promises.push(debounced())
115
- }
116
- expect(func).toBeCalledTimes(1)
117
- vi.advanceTimersByTime(1000)
102
+ it('should execute twice immediately with long enough intervals', async () => {
103
+ expect.assertions(3)
104
+ const func = vi.fn()
105
+ const debounced = debounceAsync(func, { delay: 1000, immediate: true })
106
+ const promises = []
107
+ promises.push(debounced())
108
+ expect(func).toBeCalledTimes(1)
109
+ for (let i = 0; i < 10; i++) {
110
+ vi.advanceTimersByTime(500)
118
111
  promises.push(debounced())
119
- await Promise.all(promises)
120
- expect(func).toBeCalledTimes(2)
121
112
  }
122
- )
113
+ expect(func).toBeCalledTimes(1)
114
+ vi.advanceTimersByTime(1000)
115
+ promises.push(debounced())
116
+ await Promise.all(promises)
117
+ expect(func).toBeCalledTimes(2)
118
+ })
123
119
 
124
- it(
125
- 'should only reject the last waiting promise',
126
- async () => {
127
- expect.assertions(5)
128
- let count = 0
129
- const func = vi.fn(() => {
130
- if (++count > 1) {
131
- throw new Error('boom')
132
- }
133
- })
134
- const debounced = debounceAsync(func, { delay: 1000, immediate: true })
135
- const promises = []
136
- promises.push(debounced())
137
- expect(func).toBeCalledTimes(1)
138
- for (let i = 0; i < 10; i++) {
139
- vi.advanceTimersByTime(500)
140
- promises.push(debounced())
120
+ it('should only reject the last waiting promise', async () => {
121
+ expect.assertions(5)
122
+ let count = 0
123
+ const func = vi.fn(() => {
124
+ if (++count > 1) {
125
+ throw new Error('boom')
141
126
  }
142
- expect(func).toBeCalledTimes(1)
143
- vi.advanceTimersByTime(1000)
144
- await expect(debounced()).rejects.toThrow('boom')
145
- const results = await Promise.all(promises)
146
- expect(results).toStrictEqual(new Array(11).fill(undefined))
147
- expect(func).toBeCalledTimes(2)
127
+ })
128
+ const debounced = debounceAsync(func, { delay: 1000, immediate: true })
129
+ const promises = []
130
+ promises.push(debounced())
131
+ expect(func).toBeCalledTimes(1)
132
+ for (let i = 0; i < 10; i++) {
133
+ vi.advanceTimersByTime(500)
134
+ promises.push(debounced())
148
135
  }
149
- )
136
+ expect(func).toBeCalledTimes(1)
137
+ vi.advanceTimersByTime(1000)
138
+ await expect(debounced()).rejects.toThrow('boom')
139
+ const results = await Promise.all(promises)
140
+ expect(results).toStrictEqual(new Array(11).fill(undefined))
141
+ expect(func).toBeCalledTimes(2)
142
+ })
150
143
  })
package/types/index.d.ts CHANGED
@@ -116,23 +116,23 @@ export function clone<T>(value: T, options?: {
116
116
  *
117
117
  * @default false
118
118
  */
119
- shallow?: boolean,
119
+ shallow?: boolean
120
120
  /**
121
121
  * Whether to clone all object properties, or only those that are defined
122
122
  * directly on that object, and are not inherited from the object's prototype.
123
123
  *
124
124
  * @default true
125
125
  */
126
- enumerable?: boolean,
126
+ enumerable?: boolean
127
127
  /**
128
128
  * @default `true` if `options.enumerable` is true, `false` otherwise
129
129
  */
130
- descriptors?: boolean,
131
- transferables?: any[],
130
+ descriptors?: boolean
131
+ transferables?: any[]
132
132
  /**
133
133
  * Optional callback to process the cloned value.
134
134
  */
135
- processValue?: <S>(value: T) => S,
135
+ processValue?: <S>(value: T) => S
136
136
  }): T
137
137
 
138
138
  /**
@@ -220,14 +220,14 @@ export function mapConcurrently<T, R>(
220
220
  input: Promise<T[]> | T[],
221
221
  callback: (value: T, index: number, array: T[]) => Promise<R> | R,
222
222
  options?: {
223
- concurrency?: number;
223
+ concurrency?: number
224
224
  }
225
- ): Promise<R[]>;
225
+ ): Promise<R[]>
226
226
 
227
227
  export function mapSequentially<T, R>(
228
228
  input: Promise<T[]> | T[],
229
229
  callback: (value: T, index: number) => Promise<R> | R
230
- ): Promise<R[]>;
230
+ ): Promise<R[]>
231
231
 
232
232
  /* --------------------------------- string --------------------------------- */
233
233
 
@@ -458,8 +458,7 @@ export function debounceAsync<T extends (...args: any[]) => Promise<any>>(
458
458
  // overloaded node function types), excluding the custom promisified function
459
459
  // functionality, which toAsync does not support:
460
460
  // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/fa723dee727e433add7e700420523f0f90e87a01/types/node/util.d.ts#L86
461
- interface CustomPromisifyLegacy<TCustom extends Function>
462
- extends Function {
461
+ interface CustomPromisifyLegacy<TCustom extends Function> extends Function {
463
462
  __promisify__: TCustom
464
463
  }
465
464
  type CustomToAsync<TCustom extends Function> = CustomPromisifyLegacy<TCustom>