@ezez/utils 1.2.0 → 1.5.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/.github/workflows/docs.yml +50 -0
- package/CHANGELOG.md +18 -0
- package/README.md +7 -1
- package/dist/compareArrays.d.ts +7 -0
- package/dist/compareArrays.d.ts.map +1 -0
- package/dist/compareArrays.js +12 -0
- package/dist/compareArrays.js.map +1 -0
- package/dist/ensureError.d.ts.map +1 -1
- package/dist/ensureError.js.map +1 -1
- package/dist/escapeRegExp.d.ts +3 -0
- package/dist/escapeRegExp.d.ts.map +1 -0
- package/dist/escapeRegExp.js +8 -0
- package/dist/escapeRegExp.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/isPlainObject.d.ts.map +1 -1
- package/dist/isPlainObject.js.map +1 -1
- package/dist/replace.d.ts +3 -0
- package/dist/replace.d.ts.map +1 -0
- package/dist/replace.js +10 -0
- package/dist/replace.js.map +1 -0
- package/dist/safe.d.ts +4 -0
- package/dist/safe.d.ts.map +1 -0
- package/dist/safe.js +13 -0
- package/dist/safe.js.map +1 -0
- package/dist/sortProps.d.ts +3 -0
- package/dist/sortProps.d.ts.map +1 -0
- package/dist/sortProps.js +19 -0
- package/dist/sortProps.js.map +1 -0
- package/dist/unique.d.ts +3 -0
- package/dist/unique.d.ts.map +1 -0
- package/dist/unique.js +8 -0
- package/dist/unique.js.map +1 -0
- package/docs/assets/highlight.css +14 -0
- package/docs/assets/main.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/assets/style.css +4 -5
- package/docs/functions/cap.html +13 -6
- package/docs/functions/capitalize.html +13 -6
- package/docs/functions/coalesce.html +15 -9
- package/docs/functions/compareArrays.html +146 -0
- package/docs/functions/ensureArray.html +14 -7
- package/docs/functions/ensureDate.html +13 -6
- package/docs/functions/ensureError.html +23 -6
- package/docs/functions/ensurePrefix.html +13 -6
- package/docs/functions/ensureSuffix.html +13 -6
- package/docs/functions/ensureTimestamp.html +13 -6
- package/docs/functions/escapeRegExp.html +129 -0
- package/docs/functions/get.html +13 -6
- package/docs/functions/getMultiple.html +13 -6
- package/docs/functions/insertSeparator.html +13 -6
- package/docs/functions/isEmpty.html +13 -6
- package/docs/functions/isNumericString.html +13 -6
- package/docs/functions/isPlainObject.html +28 -6
- package/docs/functions/last.html +13 -6
- package/docs/functions/mapAsync.html +13 -6
- package/docs/functions/mapValues.html +13 -6
- package/docs/functions/match.html +13 -6
- package/docs/functions/merge.html +21 -14
- package/docs/functions/mostFrequent.html +13 -6
- package/docs/functions/noop.html +13 -6
- package/docs/functions/occurrences.html +13 -6
- package/docs/functions/omit.html +13 -6
- package/docs/functions/pick.html +13 -6
- package/docs/functions/pull.html +13 -6
- package/docs/functions/remove.html +13 -6
- package/docs/functions/replace.html +135 -0
- package/docs/functions/rethrow.html +13 -6
- package/docs/functions/safe.html +173 -0
- package/docs/functions/scale.html +13 -6
- package/docs/functions/seq.html +13 -6
- package/docs/functions/seqEarlyBreak.html +13 -6
- package/docs/functions/set.html +13 -6
- package/docs/functions/setImmutable.html +13 -6
- package/docs/functions/sortBy.html +13 -6
- package/docs/functions/sortProps.html +138 -0
- package/docs/functions/stripPrefix.html +13 -6
- package/docs/functions/stripSuffix.html +13 -6
- package/docs/functions/throttle.html +13 -6
- package/docs/functions/truthy.html +13 -6
- package/docs/functions/unique.html +133 -0
- package/docs/functions/wait.html +13 -6
- package/docs/functions/waitFor.html +13 -6
- package/docs/functions/waitSync.html +13 -6
- package/docs/index.html +20 -7
- package/docs/interfaces/GetMultipleSource.html +13 -6
- package/docs/interfaces/GetSource.html +13 -6
- package/docs/interfaces/IsNumericStringOptions.html +11 -10
- package/docs/interfaces/OccurencesOptions.html +8 -7
- package/docs/interfaces/SetImmutableSource.html +13 -6
- package/docs/interfaces/SetSource.html +13 -6
- package/docs/interfaces/ThrottleOptions.html +9 -8
- package/docs/interfaces/ThrottledFunctionExtras.html +9 -8
- package/docs/modules.html +18 -5
- package/docs/pages/CHANGELOG.html +778 -0
- package/docs/pages/Introduction.html +12 -5
- package/docs/types/MapValuesFn.html +13 -6
- package/docs/types/MatchCallback.html +13 -6
- package/docs/types/SeqEarlyBreaker.html +13 -6
- package/docs/types/SeqFn.html +13 -6
- package/docs/types/SeqFunctions.html +13 -6
- package/docs/types/SetImmutablePath.html +13 -6
- package/docs/types/ThrottledFunction.html +13 -6
- package/docs/variables/mapValuesUNSET.html +13 -6
- package/docs/variables/mergeUNSET.html +13 -6
- package/esm/compareArrays.d.ts +7 -0
- package/esm/compareArrays.d.ts.map +1 -0
- package/esm/compareArrays.js +9 -0
- package/esm/compareArrays.js.map +1 -0
- package/esm/ensureError.d.ts.map +1 -1
- package/esm/ensureError.js.map +1 -1
- package/esm/escapeRegExp.d.ts +3 -0
- package/esm/escapeRegExp.d.ts.map +1 -0
- package/esm/escapeRegExp.js +5 -0
- package/esm/escapeRegExp.js.map +1 -0
- package/esm/index.d.ts +6 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +6 -0
- package/esm/index.js.map +1 -1
- package/esm/isPlainObject.d.ts.map +1 -1
- package/esm/isPlainObject.js.map +1 -1
- package/esm/replace.d.ts +3 -0
- package/esm/replace.d.ts.map +1 -0
- package/esm/replace.js +7 -0
- package/esm/replace.js.map +1 -0
- package/esm/safe.d.ts +4 -0
- package/esm/safe.d.ts.map +1 -0
- package/esm/safe.js +10 -0
- package/esm/safe.js.map +1 -0
- package/esm/sortProps.d.ts +3 -0
- package/esm/sortProps.d.ts.map +1 -0
- package/esm/sortProps.js +16 -0
- package/esm/sortProps.js.map +1 -0
- package/esm/unique.d.ts +3 -0
- package/esm/unique.d.ts.map +1 -0
- package/esm/unique.js +5 -0
- package/esm/unique.js.map +1 -0
- package/package.json +23 -23
- package/src/coalesce.ts +2 -2
- package/src/compareArrays.spec.ts +38 -0
- package/src/compareArrays.ts +22 -0
- package/src/ensureArray.ts +1 -1
- package/src/ensureError.ts +7 -0
- package/src/escapeRegExp.spec.ts +31 -0
- package/src/escapeRegExp.ts +17 -0
- package/src/index.ts +6 -0
- package/src/isPlainObject.spec.ts +0 -7
- package/src/isPlainObject.ts +5 -0
- package/src/replace.spec.ts +22 -0
- package/src/replace.ts +19 -0
- package/src/safe.spec.ts +52 -0
- package/src/safe.ts +24 -0
- package/src/sortProps.spec.ts +20 -0
- package/src/sortProps.ts +21 -0
- package/src/unique.spec.ts +10 -0
- package/src/unique.ts +12 -0
- package/typedoc.cjs +5 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { compareArrays } from "./compareArrays.js";
|
|
2
|
+
|
|
3
|
+
describe("compareArrays", () => {
|
|
4
|
+
it("should mark items that are only in a or b", async () => {
|
|
5
|
+
compareArrays([1, 2, 3], [4, 5, 6]).must.eql({
|
|
6
|
+
onlyA: [1, 2, 3],
|
|
7
|
+
onlyB: [4, 5, 6],
|
|
8
|
+
both: [],
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("should mark items that exists in both arrays", async () => {
|
|
13
|
+
compareArrays([1, 2, 3], [3, 4, 5]).must.eql({
|
|
14
|
+
onlyA: [1, 2],
|
|
15
|
+
onlyB: [4, 5],
|
|
16
|
+
both: [3],
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should not return duplicates if given", async () => {
|
|
21
|
+
compareArrays([1, 1, 2, 3, 3], [3, 4, 5]).must.eql({
|
|
22
|
+
onlyA: [1, 2],
|
|
23
|
+
onlyB: [4, 5],
|
|
24
|
+
both: [3],
|
|
25
|
+
});
|
|
26
|
+
compareArrays([1, 2, 3, 3], [3, 3, 4, 5]).must.eql({
|
|
27
|
+
onlyA: [1, 2],
|
|
28
|
+
onlyB: [4, 5],
|
|
29
|
+
both: [3],
|
|
30
|
+
});
|
|
31
|
+
compareArrays([1, 2, 3, 3], [3, 3, 3, 4, 5]).must.eql({
|
|
32
|
+
onlyA: [1, 2],
|
|
33
|
+
onlyB: [4, 5],
|
|
34
|
+
both: [3],
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { unique } from "./unique.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Compares two arrays and returns the items that are only in arrayA, only in arrayB, and in both arrays.
|
|
5
|
+
* @param arrayA - first array
|
|
6
|
+
* @param arrayB - second array
|
|
7
|
+
* @returns an object with three properties:
|
|
8
|
+
* onlyA - items that exists only in first array,
|
|
9
|
+
* onlyB - items that exists only in second array,
|
|
10
|
+
* both - items that exists in both arrays
|
|
11
|
+
*/
|
|
12
|
+
const compareArrays = <T extends unknown[]>(arrayA: T, arrayB: T): { onlyA: T; onlyB: T; both: T } => {
|
|
13
|
+
const onlyA = unique(arrayA.filter((item) => !arrayB.includes(item)));
|
|
14
|
+
const onlyB = unique(arrayB.filter((item) => !arrayA.includes(item)));
|
|
15
|
+
const both = unique(arrayA.filter((item) => arrayB.includes(item)));
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
17
|
+
return { onlyA, onlyB, both } as { onlyA: T; onlyB: T; both: T };
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
compareArrays,
|
|
22
|
+
};
|
package/src/ensureArray.ts
CHANGED
package/src/ensureError.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ensures given value is an instance of Error.
|
|
3
|
+
*
|
|
4
|
+
* This is for simple use cases only, for maximum flexibility use `@ezez/errors` package.
|
|
5
|
+
* @example ensureError(new Error("test")); // returns given Error instance (not modified)
|
|
6
|
+
* @example ensureError("test");
|
|
7
|
+
* // ^ returns new Error instance with error message: "Expected error instance, got something else: test"
|
|
8
|
+
* @example ensureError({});
|
|
9
|
+
* // ^ returns new Error instance with error message: "Expected error instance, got something else: [object Object]"
|
|
3
10
|
* @param {*} e - value to check
|
|
4
11
|
* @returns Error - original error or new Error instance
|
|
5
12
|
*/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { escapeRegExp } from "./escapeRegExp.js";
|
|
2
|
+
|
|
3
|
+
describe("escapeRegExp", () => {
|
|
4
|
+
it("it should escape dot", () => {
|
|
5
|
+
const userName = ".";
|
|
6
|
+
const regex = new RegExp(`^(maciek|${escapeRegExp(userName)})$`);
|
|
7
|
+
const result = regex.test("maciek");
|
|
8
|
+
result.must.be.true();
|
|
9
|
+
|
|
10
|
+
const result2 = regex.test("stefan");
|
|
11
|
+
result2.must.be.false();
|
|
12
|
+
|
|
13
|
+
const result3 = regex.test(",");
|
|
14
|
+
result3.must.be.false();
|
|
15
|
+
|
|
16
|
+
const result4 = regex.test(".");
|
|
17
|
+
result4.must.be.true();
|
|
18
|
+
|
|
19
|
+
const result5 = regex.test("...");
|
|
20
|
+
result5.must.be.false();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should escape complex string", () => {
|
|
24
|
+
const userName = "([{^|";
|
|
25
|
+
const fn = () => new RegExp(`^(maciek|${escapeRegExp(userName)})$`);
|
|
26
|
+
fn.must.not.throw();
|
|
27
|
+
|
|
28
|
+
const fnNoEscape = () => new RegExp(`^(maciek|${userName})$`);
|
|
29
|
+
fnNoEscape.must.throw();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escapes a string to be used in a regular expression.
|
|
3
|
+
* From: https://stackoverflow.com/a/3561711
|
|
4
|
+
* @param string - string to escape
|
|
5
|
+
* @example ```typescript
|
|
6
|
+
* const badName = "([{^|";
|
|
7
|
+
* const regex = new RegExp(`^(maciek|${escapeRegExp(badName)})$`); // won't crash
|
|
8
|
+
* regex.test(badName); // true
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
const escapeRegExp = (string: string) => {
|
|
12
|
+
return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
escapeRegExp,
|
|
17
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -4,10 +4,12 @@ export * from "./ensureSuffix.js";
|
|
|
4
4
|
export * from "./cap.js";
|
|
5
5
|
export * from "./capitalize.js";
|
|
6
6
|
export * from "./coalesce.js";
|
|
7
|
+
export * from "./compareArrays.js";
|
|
7
8
|
export * from "./ensureArray.js";
|
|
8
9
|
export * from "./ensureDate.js";
|
|
9
10
|
export * from "./ensureError.js";
|
|
10
11
|
export * from "./ensureTimestamp.js";
|
|
12
|
+
export * from "./escapeRegExp.js";
|
|
11
13
|
export * from "./get.js";
|
|
12
14
|
export * from "./getMultiple.js";
|
|
13
15
|
export * from "./insertSeparator.js";
|
|
@@ -26,16 +28,20 @@ export * from "./omit.js";
|
|
|
26
28
|
export * from "./pick.js";
|
|
27
29
|
export * from "./pull.js";
|
|
28
30
|
export * from "./remove.js";
|
|
31
|
+
export * from "./replace.js";
|
|
29
32
|
export * from "./rethrow.js";
|
|
33
|
+
export * from "./safe.js";
|
|
30
34
|
export * from "./scale.js";
|
|
31
35
|
export * from "./seq.js";
|
|
32
36
|
export * from "./set.js";
|
|
33
37
|
export * from "./setImmutable.js";
|
|
34
38
|
export * from "./sortBy.js";
|
|
39
|
+
export * from "./sortProps.js";
|
|
35
40
|
export * from "./stripPrefix.js";
|
|
36
41
|
export * from "./stripSuffix.js";
|
|
37
42
|
export * from "./throttle.js";
|
|
38
43
|
export * from "./truthy.js";
|
|
44
|
+
export * from "./unique.js";
|
|
39
45
|
export * from "./wait.js";
|
|
40
46
|
export * from "./waitFor.js";
|
|
41
47
|
export * from "./waitSync.js";
|
|
@@ -22,13 +22,6 @@ describe("isPlainObject", function() {
|
|
|
22
22
|
isPlainObject({ constructor: () => null }).must.be.true();
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
it("accepts plain objects", function() {
|
|
26
|
-
isPlainObject({}).must.be.true();
|
|
27
|
-
isPlainObject({ some: "data" }).must.be.true();
|
|
28
|
-
isPlainObject({ some: function fn() {} }).must.be.true();
|
|
29
|
-
isPlainObject(Object.create(null)).must.be.true();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
25
|
it("shouldn't accept any instances", function() {
|
|
33
26
|
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
|
34
27
|
class X {}
|
package/src/isPlainObject.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Checks if given value is a plain object. Plain object should be an object that's not an instance of anything.
|
|
3
|
+
* @example isPlainObject({}); // returns true
|
|
4
|
+
* @example isPlainObject(Object.create(null)); // returns true
|
|
5
|
+
* @example isPlainObject(new URL("https://ezez.dev")); // returns false
|
|
6
|
+
* @example isPlainObject([]); // returns false
|
|
7
|
+
* @example isPlainObject(5); // returns false
|
|
3
8
|
* @param value - value to test
|
|
4
9
|
*/
|
|
5
10
|
const isPlainObject = (value: unknown) => Boolean(
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { replace } from "./replace.js";
|
|
2
|
+
|
|
3
|
+
describe("replace", () => {
|
|
4
|
+
it("does a basic replace", () => {
|
|
5
|
+
replace("Hello, %name%!", { "%name%": "John" }).must.equal("Hello, John!");
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it("replaces multiple occurrences", () => {
|
|
9
|
+
replace("Hello, %name%! Nice to meet you %name%!", { "%name%": "Jane" })
|
|
10
|
+
.must.equal("Hello, Jane! Nice to meet you Jane!");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("replaces multiple occurences of multiple variables", () => {
|
|
14
|
+
replace("Hello, %name%, your age is %age%! Nice to meet you %age% yo %name%!",
|
|
15
|
+
{ "%name%": "Jane", "%age%": "30" },
|
|
16
|
+
).must.equal("Hello, Jane, your age is 30! Nice to meet you 30 yo Jane!");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("doesn't break on regexp characters", () => {
|
|
20
|
+
replace("Hello.", { ".": "!" }).must.equal("Hello!");
|
|
21
|
+
});
|
|
22
|
+
});
|
package/src/replace.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
import { escapeRegExp } from "./escapeRegExp.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Replaces all occurrences of the keys in the replaceMap with the values.
|
|
6
|
+
* @param source - source string
|
|
7
|
+
* @param replaceMap - keys from this object will be replaced with values in source string
|
|
8
|
+
* @example replace("Hello, %name%!", { "%name%: "John" }) // "Hello, John!"
|
|
9
|
+
* @example replace("Hello, %name%! Nice to meet you %name%!", { "%name%": "Jane" }) // "Hello, Jane! Nice to meet you Jane!"
|
|
10
|
+
*/
|
|
11
|
+
const replace = (source: string, replaceMap: Record<string, string>) => {
|
|
12
|
+
/* eslint-enable max-len */
|
|
13
|
+
const regex = new RegExp(Object.keys(replaceMap).map(escapeRegExp).join("|"), "g");
|
|
14
|
+
return source.replace(regex, (matched) => replaceMap[matched]);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
replace,
|
|
19
|
+
};
|
package/src/safe.spec.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { safe } from "./safe.js";
|
|
2
|
+
|
|
3
|
+
describe("safe", () => {
|
|
4
|
+
it("returns value from a function if it doesn't throw", () => {
|
|
5
|
+
const result = safe(() => 5);
|
|
6
|
+
result.must.equal(5);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("returns undefined if function throws and no default value is given", () => {
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
11
|
+
const result = safe(() => {
|
|
12
|
+
throw new Error("Boo");
|
|
13
|
+
});
|
|
14
|
+
(result === undefined).must.be.true();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("returns given default value if function throws", () => {
|
|
18
|
+
const result = safe(() => {
|
|
19
|
+
throw new Error("Boo");
|
|
20
|
+
}, 15);
|
|
21
|
+
result.must.equal(15);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("returns undefined if function throws and default value is undefined", () => {
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
26
|
+
const result = safe(() => {
|
|
27
|
+
throw new Error("Boo");
|
|
28
|
+
}, undefined);
|
|
29
|
+
(result === undefined).must.be.true();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("returns value from function if it doesn't throw and default is given", () => {
|
|
33
|
+
const result = safe(() => 5, 15);
|
|
34
|
+
result.must.equal(5);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("doesn't unwrap promises", () => {
|
|
38
|
+
const result = safe(() => Promise.resolve(5));
|
|
39
|
+
result.must.be.instanceof(Promise);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("doesn't 'work' with promises", () => {
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
44
|
+
const result = safe(async () => {
|
|
45
|
+
throw new Error("Boo!!!");
|
|
46
|
+
});
|
|
47
|
+
result.must.be.instanceof(Promise);
|
|
48
|
+
|
|
49
|
+
// prevent unhandled rejection warning
|
|
50
|
+
result.catch(() => null);
|
|
51
|
+
});
|
|
52
|
+
});
|
package/src/safe.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function safe<T>(fn: () => T): T | undefined;
|
|
2
|
+
function safe<T, Y>(fn: () => T, def: Y): T | Y;
|
|
3
|
+
|
|
4
|
+
/* eslint-disable max-len */
|
|
5
|
+
/**
|
|
6
|
+
* Safely execute a function, return its return value or default value if the function throws.
|
|
7
|
+
* @param fn - function to run
|
|
8
|
+
* @param def - default value
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* safe(() => JSON.parse(unknownString), null); // if unknownString is not a valid JSON, null will be returned
|
|
12
|
+
* safe(() => trySomethingComplicated(), defaultValue); // if trySomethingComplicated throws, defaultValue will be returned
|
|
13
|
+
*/
|
|
14
|
+
function safe<T, Y>(fn: () => T, def?: Y) { // eslint-disable-line func-style
|
|
15
|
+
/* eslint-enable max-len */
|
|
16
|
+
try {
|
|
17
|
+
return fn();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return def;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { safe };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { sortProps } from "./sortProps.js";
|
|
2
|
+
|
|
3
|
+
describe("sort props", () => {
|
|
4
|
+
it("can sort props ascending", () => {
|
|
5
|
+
const sorted = sortProps({ b: 2, a: 1, z: 26 });
|
|
6
|
+
Object.keys(sorted).must.eql(["a", "b", "z"]);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("can sort props descending", () => {
|
|
10
|
+
const sorted = sortProps({ b: 2, a: 1, z: 26 }, false);
|
|
11
|
+
Object.keys(sorted).must.eql(["z", "b", "a"]);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("doesn't modify original object", () => {
|
|
15
|
+
const source = { b: 2, a: 1, z: 26 };
|
|
16
|
+
const sorted = sortProps(source);
|
|
17
|
+
Object.keys(source).must.eql(["b", "a", "z"]);
|
|
18
|
+
Object.keys(sorted).must.eql(["a", "b", "z"]);
|
|
19
|
+
});
|
|
20
|
+
});
|
package/src/sortProps.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sorts the properties of an object alphabetically, ascending or descending.
|
|
3
|
+
* REMEMBER: In theory JS engines do not guarantee the order of object properties. In practice most popular engines do.
|
|
4
|
+
* @param object - source object
|
|
5
|
+
* @param asc - sort ascending?
|
|
6
|
+
* @example sortProps({ b: 2, a: 1, z: 26 }) // { a: 1, b: 2, z: 26 }
|
|
7
|
+
*/
|
|
8
|
+
const sortProps = <T extends Record<string, unknown>>(object: T, asc = true): T => {
|
|
9
|
+
const sorted: Record<string, unknown> = {};
|
|
10
|
+
const keys = Object.keys(object);
|
|
11
|
+
if (asc) { keys.sort(); }
|
|
12
|
+
else { keys.sort().reverse(); }
|
|
13
|
+
for (const key of keys) {
|
|
14
|
+
sorted[key] = object[key];
|
|
15
|
+
}
|
|
16
|
+
return sorted as T;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
sortProps,
|
|
21
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { unique } from "./unique.js";
|
|
2
|
+
|
|
3
|
+
describe("unique", () => {
|
|
4
|
+
it("returns unique values in array", async () => {
|
|
5
|
+
unique([1, 2, 3, 2, 1]).must.eql([1, 2, 3]);
|
|
6
|
+
unique([{ a: true }, { a: true }]).must.eql([{ a: true }, { a: true }]);
|
|
7
|
+
const a = { a: true };
|
|
8
|
+
unique([a, a]).must.eql([a]);
|
|
9
|
+
});
|
|
10
|
+
});
|
package/src/unique.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a new array with unique values of given array.
|
|
3
|
+
* @param arr - source array
|
|
4
|
+
* @example unique([1, 2, 3, 2, 1]) // [1, 2, 3]
|
|
5
|
+
*/
|
|
6
|
+
const unique = <T extends unknown[]>(arr: T): T => {
|
|
7
|
+
return [...new Set(arr)] as T;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
unique,
|
|
12
|
+
};
|