@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.
Files changed (137) hide show
  1. package/.github/workflows/docs.yml +50 -0
  2. package/CHANGELOG.md +13 -0
  3. package/README.md +5 -1
  4. package/dist/ensureError.d.ts.map +1 -1
  5. package/dist/ensureError.js.map +1 -1
  6. package/dist/escapeRegExp.d.ts +3 -0
  7. package/dist/escapeRegExp.d.ts.map +1 -0
  8. package/dist/escapeRegExp.js +8 -0
  9. package/dist/escapeRegExp.js.map +1 -0
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/isPlainObject.d.ts.map +1 -1
  15. package/dist/isPlainObject.js.map +1 -1
  16. package/dist/replace.d.ts +3 -0
  17. package/dist/replace.d.ts.map +1 -0
  18. package/dist/replace.js +10 -0
  19. package/dist/replace.js.map +1 -0
  20. package/dist/safe.d.ts +4 -0
  21. package/dist/safe.d.ts.map +1 -0
  22. package/dist/safe.js +13 -0
  23. package/dist/safe.js.map +1 -0
  24. package/dist/sortProps.d.ts +3 -0
  25. package/dist/sortProps.d.ts.map +1 -0
  26. package/dist/sortProps.js +19 -0
  27. package/dist/sortProps.js.map +1 -0
  28. package/docs/assets/highlight.css +14 -0
  29. package/docs/assets/main.js +1 -1
  30. package/docs/assets/search.js +1 -1
  31. package/docs/assets/style.css +4 -5
  32. package/docs/functions/cap.html +11 -6
  33. package/docs/functions/capitalize.html +11 -6
  34. package/docs/functions/coalesce.html +13 -9
  35. package/docs/functions/ensureArray.html +12 -7
  36. package/docs/functions/ensureDate.html +11 -6
  37. package/docs/functions/ensureError.html +21 -6
  38. package/docs/functions/ensurePrefix.html +11 -6
  39. package/docs/functions/ensureSuffix.html +11 -6
  40. package/docs/functions/ensureTimestamp.html +11 -6
  41. package/docs/functions/escapeRegExp.html +127 -0
  42. package/docs/functions/get.html +11 -6
  43. package/docs/functions/getMultiple.html +11 -6
  44. package/docs/functions/insertSeparator.html +11 -6
  45. package/docs/functions/isEmpty.html +11 -6
  46. package/docs/functions/isNumericString.html +11 -6
  47. package/docs/functions/isPlainObject.html +26 -6
  48. package/docs/functions/last.html +11 -6
  49. package/docs/functions/mapAsync.html +11 -6
  50. package/docs/functions/mapValues.html +11 -6
  51. package/docs/functions/match.html +11 -6
  52. package/docs/functions/merge.html +19 -14
  53. package/docs/functions/mostFrequent.html +11 -6
  54. package/docs/functions/noop.html +11 -6
  55. package/docs/functions/occurrences.html +11 -6
  56. package/docs/functions/omit.html +11 -6
  57. package/docs/functions/pick.html +11 -6
  58. package/docs/functions/pull.html +11 -6
  59. package/docs/functions/remove.html +11 -6
  60. package/docs/functions/replace.html +133 -0
  61. package/docs/functions/rethrow.html +11 -6
  62. package/docs/functions/safe.html +171 -0
  63. package/docs/functions/scale.html +11 -6
  64. package/docs/functions/seq.html +11 -6
  65. package/docs/functions/seqEarlyBreak.html +11 -6
  66. package/docs/functions/set.html +11 -6
  67. package/docs/functions/setImmutable.html +11 -6
  68. package/docs/functions/sortBy.html +11 -6
  69. package/docs/functions/sortProps.html +136 -0
  70. package/docs/functions/stripPrefix.html +11 -6
  71. package/docs/functions/stripSuffix.html +11 -6
  72. package/docs/functions/throttle.html +11 -6
  73. package/docs/functions/truthy.html +11 -6
  74. package/docs/functions/wait.html +11 -6
  75. package/docs/functions/waitFor.html +11 -6
  76. package/docs/functions/waitSync.html +11 -6
  77. package/docs/index.html +16 -7
  78. package/docs/interfaces/GetMultipleSource.html +11 -6
  79. package/docs/interfaces/GetSource.html +11 -6
  80. package/docs/interfaces/IsNumericStringOptions.html +11 -10
  81. package/docs/interfaces/OccurencesOptions.html +8 -7
  82. package/docs/interfaces/SetImmutableSource.html +11 -6
  83. package/docs/interfaces/SetSource.html +11 -6
  84. package/docs/interfaces/ThrottleOptions.html +9 -8
  85. package/docs/interfaces/ThrottledFunctionExtras.html +9 -8
  86. package/docs/modules.html +14 -5
  87. package/docs/pages/CHANGELOG.html +764 -0
  88. package/docs/pages/Introduction.html +10 -5
  89. package/docs/types/MapValuesFn.html +11 -6
  90. package/docs/types/MatchCallback.html +11 -6
  91. package/docs/types/SeqEarlyBreaker.html +11 -6
  92. package/docs/types/SeqFn.html +11 -6
  93. package/docs/types/SeqFunctions.html +11 -6
  94. package/docs/types/SetImmutablePath.html +11 -6
  95. package/docs/types/ThrottledFunction.html +11 -6
  96. package/docs/variables/mapValuesUNSET.html +11 -6
  97. package/docs/variables/mergeUNSET.html +11 -6
  98. package/esm/ensureError.d.ts.map +1 -1
  99. package/esm/ensureError.js.map +1 -1
  100. package/esm/escapeRegExp.d.ts +3 -0
  101. package/esm/escapeRegExp.d.ts.map +1 -0
  102. package/esm/escapeRegExp.js +5 -0
  103. package/esm/escapeRegExp.js.map +1 -0
  104. package/esm/index.d.ts +4 -0
  105. package/esm/index.d.ts.map +1 -1
  106. package/esm/index.js +4 -0
  107. package/esm/index.js.map +1 -1
  108. package/esm/isPlainObject.d.ts.map +1 -1
  109. package/esm/isPlainObject.js.map +1 -1
  110. package/esm/replace.d.ts +3 -0
  111. package/esm/replace.d.ts.map +1 -0
  112. package/esm/replace.js +7 -0
  113. package/esm/replace.js.map +1 -0
  114. package/esm/safe.d.ts +4 -0
  115. package/esm/safe.d.ts.map +1 -0
  116. package/esm/safe.js +10 -0
  117. package/esm/safe.js.map +1 -0
  118. package/esm/sortProps.d.ts +3 -0
  119. package/esm/sortProps.d.ts.map +1 -0
  120. package/esm/sortProps.js +16 -0
  121. package/esm/sortProps.js.map +1 -0
  122. package/package.json +23 -23
  123. package/src/coalesce.ts +2 -2
  124. package/src/ensureArray.ts +1 -1
  125. package/src/ensureError.ts +7 -0
  126. package/src/escapeRegExp.spec.ts +31 -0
  127. package/src/escapeRegExp.ts +17 -0
  128. package/src/index.ts +4 -0
  129. package/src/isPlainObject.spec.ts +0 -7
  130. package/src/isPlainObject.ts +5 -0
  131. package/src/replace.spec.ts +22 -0
  132. package/src/replace.ts +19 -0
  133. package/src/safe.spec.ts +52 -0
  134. package/src/safe.ts +24 -0
  135. package/src/sortProps.spec.ts +20 -0
  136. package/src/sortProps.ts +21 -0
  137. package/typedoc.cjs +5 -0
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@ezez/utils",
3
- "version": "1.2.0",
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 --pluginPages ./pagesconfig.json",
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.19.3",
39
- "@babel/preset-env": "^7.19.3",
40
- "@babel/preset-typescript": "^7.18.6",
41
- "@dzek69/eslint-config-base": "^2.3.0",
42
- "@dzek69/eslint-config-typescript": "^1.0.2",
43
- "@types/jest": "^29.0.3",
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.38.1",
46
- "@typescript-eslint/parser": "^5.38.1",
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.24.0",
49
- "fs-extra": "^10.1.0",
50
- "husky": "^8.0.1",
51
- "jest": "^29.1.1",
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.20",
58
+ "nodemon": "^2.0.22",
59
+ "prettier": "^2.8.7",
55
60
  "ts-node": "^10.9.1",
56
- "typedoc": "^0.23.15",
57
- "typescript": "^4.9.5",
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.8.0",
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 0, 5); // returns 5
6
- * @example coalesce(4, null, 6, undefined); // returns 4
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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Wraps value in an array until the value is already an array
2
+ * Wraps value in an array unless the value is already an array
3
3
  * @param {*} value - value to be wrapped
4
4
  * @returns {Array}
5
5
  */
@@ -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 {}
@@ -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
+ };
@@ -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
+ });
@@ -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
+ };
package/typedoc.cjs ADDED
@@ -0,0 +1,5 @@
1
+ const json = require("./pagesconfig.json");
2
+
3
+ module.exports = {
4
+ pluginPages: json,
5
+ };