@ezez/utils 1.2.0 → 1.4.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 +13 -0
- package/README.md +5 -1
- 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 +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -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/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 +11 -6
- package/docs/functions/capitalize.html +11 -6
- package/docs/functions/coalesce.html +13 -9
- package/docs/functions/ensureArray.html +12 -7
- package/docs/functions/ensureDate.html +11 -6
- package/docs/functions/ensureError.html +21 -6
- package/docs/functions/ensurePrefix.html +11 -6
- package/docs/functions/ensureSuffix.html +11 -6
- package/docs/functions/ensureTimestamp.html +11 -6
- package/docs/functions/escapeRegExp.html +127 -0
- package/docs/functions/get.html +11 -6
- package/docs/functions/getMultiple.html +11 -6
- package/docs/functions/insertSeparator.html +11 -6
- package/docs/functions/isEmpty.html +11 -6
- package/docs/functions/isNumericString.html +11 -6
- package/docs/functions/isPlainObject.html +26 -6
- package/docs/functions/last.html +11 -6
- package/docs/functions/mapAsync.html +11 -6
- package/docs/functions/mapValues.html +11 -6
- package/docs/functions/match.html +11 -6
- package/docs/functions/merge.html +19 -14
- package/docs/functions/mostFrequent.html +11 -6
- package/docs/functions/noop.html +11 -6
- package/docs/functions/occurrences.html +11 -6
- package/docs/functions/omit.html +11 -6
- package/docs/functions/pick.html +11 -6
- package/docs/functions/pull.html +11 -6
- package/docs/functions/remove.html +11 -6
- package/docs/functions/replace.html +133 -0
- package/docs/functions/rethrow.html +11 -6
- package/docs/functions/safe.html +171 -0
- package/docs/functions/scale.html +11 -6
- package/docs/functions/seq.html +11 -6
- package/docs/functions/seqEarlyBreak.html +11 -6
- package/docs/functions/set.html +11 -6
- package/docs/functions/setImmutable.html +11 -6
- package/docs/functions/sortBy.html +11 -6
- package/docs/functions/sortProps.html +136 -0
- package/docs/functions/stripPrefix.html +11 -6
- package/docs/functions/stripSuffix.html +11 -6
- package/docs/functions/throttle.html +11 -6
- package/docs/functions/truthy.html +11 -6
- package/docs/functions/wait.html +11 -6
- package/docs/functions/waitFor.html +11 -6
- package/docs/functions/waitSync.html +11 -6
- package/docs/index.html +16 -7
- package/docs/interfaces/GetMultipleSource.html +11 -6
- package/docs/interfaces/GetSource.html +11 -6
- package/docs/interfaces/IsNumericStringOptions.html +11 -10
- package/docs/interfaces/OccurencesOptions.html +8 -7
- package/docs/interfaces/SetImmutableSource.html +11 -6
- package/docs/interfaces/SetSource.html +11 -6
- package/docs/interfaces/ThrottleOptions.html +9 -8
- package/docs/interfaces/ThrottledFunctionExtras.html +9 -8
- package/docs/modules.html +14 -5
- package/docs/pages/CHANGELOG.html +764 -0
- package/docs/pages/Introduction.html +10 -5
- package/docs/types/MapValuesFn.html +11 -6
- package/docs/types/MatchCallback.html +11 -6
- package/docs/types/SeqEarlyBreaker.html +11 -6
- package/docs/types/SeqFn.html +11 -6
- package/docs/types/SeqFunctions.html +11 -6
- package/docs/types/SetImmutablePath.html +11 -6
- package/docs/types/ThrottledFunction.html +11 -6
- package/docs/variables/mapValuesUNSET.html +11 -6
- package/docs/variables/mergeUNSET.html +11 -6
- 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 +4 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +4 -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/package.json +23 -23
- package/src/coalesce.ts +2 -2
- 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 +4 -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/typedoc.cjs +5 -0
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ezez/utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"repository": "https://github.com/dzek69/bottom-line.git",
|
|
5
5
|
"author": "Jacek Nowacki @dzek69 <git-public@dzek.eu>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "NODE_ENV=test jest",
|
|
9
9
|
"test:lodashImports": "node test/lodash-tree.cjs",
|
|
10
|
-
"docs": "typedoc src/index.ts --skipErrorChecking --out docs --includeVersion
|
|
10
|
+
"docs": "typedoc src/index.ts --skipErrorChecking --out docs --includeVersion",
|
|
11
11
|
"compile": "yarn compile:esm && yarn compile:cjs",
|
|
12
12
|
"compile:esm": "rm -rf esm && tsc --project tsconfig.esm.json && node ./build-scripts/compile.esm.after.mjs",
|
|
13
13
|
"compile:cjs": "rm -rf dist && tsc --project tsconfig.cjs.json && node ./build-scripts/compile.cjs.after.mjs",
|
|
@@ -35,31 +35,31 @@
|
|
|
35
35
|
"module": "./esm/index.js",
|
|
36
36
|
"type": "module",
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@babel/core": "^7.
|
|
39
|
-
"@babel/preset-env": "^7.
|
|
40
|
-
"@babel/preset-typescript": "^7.
|
|
41
|
-
"@dzek69/eslint-config-base": "^2.
|
|
42
|
-
"@dzek69/eslint-config-
|
|
43
|
-
"@
|
|
38
|
+
"@babel/core": "^7.21.4",
|
|
39
|
+
"@babel/preset-env": "^7.21.4",
|
|
40
|
+
"@babel/preset-typescript": "^7.21.4",
|
|
41
|
+
"@dzek69/eslint-config-base": "^2.4.0",
|
|
42
|
+
"@dzek69/eslint-config-import": "^1.2.0",
|
|
43
|
+
"@dzek69/eslint-config-import-typescript": "^1.0.0",
|
|
44
|
+
"@dzek69/eslint-config-typescript": "^1.1.0",
|
|
45
|
+
"@knodes/typedoc-plugin-pages": "^0.23.4",
|
|
46
|
+
"@types/jest": "^29.5.0",
|
|
44
47
|
"@types/lodash": "^4.14.168",
|
|
45
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
46
|
-
"@typescript-eslint/parser": "^5.
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
|
49
|
+
"@typescript-eslint/parser": "^5.58.0",
|
|
47
50
|
"babel-plugin-module-extension": "^0.1.3",
|
|
48
|
-
"eslint": "^8.
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
51
|
+
"eslint": "^8.38.0",
|
|
52
|
+
"eslint-plugin-import": "^2.27.5",
|
|
53
|
+
"fs-extra": "^11.1.1",
|
|
54
|
+
"husky": "^8.0.3",
|
|
55
|
+
"jest": "^29.5.0",
|
|
52
56
|
"lodash": "^4.17.21",
|
|
53
57
|
"must": "^0.13.4",
|
|
54
|
-
"nodemon": "^2.0.
|
|
58
|
+
"nodemon": "^2.0.22",
|
|
59
|
+
"prettier": "^2.8.7",
|
|
55
60
|
"ts-node": "^10.9.1",
|
|
56
|
-
"typedoc": "^0.23.
|
|
57
|
-
"typescript": "^
|
|
58
|
-
"eslint-plugin-import": "^2.26.0",
|
|
59
|
-
"@dzek69/eslint-config-import": "^1.0.0",
|
|
60
|
-
"@dzek69/eslint-config-import-typescript": "^1.0.0",
|
|
61
|
-
"@knodes/typedoc-plugin-pages": "^0.23.1",
|
|
62
|
-
"prettier": "^2.8.3"
|
|
61
|
+
"typedoc": "^0.23.28",
|
|
62
|
+
"typescript": "^5.0.4"
|
|
63
63
|
},
|
|
64
64
|
"husky": {
|
|
65
65
|
"hooks": {
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
69
|
"libraryTemplate": {
|
|
70
|
-
"version": "3.
|
|
70
|
+
"version": "3.9.1",
|
|
71
71
|
"language": "typescript",
|
|
72
72
|
"fixDefaultForCommonJS": false,
|
|
73
73
|
"jsx": false
|
package/src/coalesce.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Returns first non-nil (not undefined, not null) value from given arguments.
|
|
3
3
|
*
|
|
4
4
|
* @param {...*} args - values
|
|
5
|
-
* @example coalesce(null, undefined, void
|
|
6
|
-
* @example coalesce(
|
|
5
|
+
* @example coalesce(null, undefined, void 1, 5); // returns 5 (mind the `void`)
|
|
6
|
+
* @example coalesce(0, null, 6, undefined); // returns 0
|
|
7
7
|
* @example coalesce(undefined); // returns null
|
|
8
8
|
* @example coalesce(); // returns null
|
|
9
9
|
* @returns {*|null} first non-nil value or null
|
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
|
@@ -8,6 +8,7 @@ export * from "./ensureArray.js";
|
|
|
8
8
|
export * from "./ensureDate.js";
|
|
9
9
|
export * from "./ensureError.js";
|
|
10
10
|
export * from "./ensureTimestamp.js";
|
|
11
|
+
export * from "./escapeRegExp.js";
|
|
11
12
|
export * from "./get.js";
|
|
12
13
|
export * from "./getMultiple.js";
|
|
13
14
|
export * from "./insertSeparator.js";
|
|
@@ -26,12 +27,15 @@ export * from "./omit.js";
|
|
|
26
27
|
export * from "./pick.js";
|
|
27
28
|
export * from "./pull.js";
|
|
28
29
|
export * from "./remove.js";
|
|
30
|
+
export * from "./replace.js";
|
|
29
31
|
export * from "./rethrow.js";
|
|
32
|
+
export * from "./safe.js";
|
|
30
33
|
export * from "./scale.js";
|
|
31
34
|
export * from "./seq.js";
|
|
32
35
|
export * from "./set.js";
|
|
33
36
|
export * from "./setImmutable.js";
|
|
34
37
|
export * from "./sortBy.js";
|
|
38
|
+
export * from "./sortProps.js";
|
|
35
39
|
export * from "./stripPrefix.js";
|
|
36
40
|
export * from "./stripSuffix.js";
|
|
37
41
|
export * from "./throttle.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
|
+
};
|