@naman_deep_singh/js-extensions 1.0.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/README.md +89 -0
- package/dist/array-extensions.d.ts +1 -0
- package/dist/array-extensions.js +65 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +45 -0
- package/dist/number-extensions.d.ts +1 -0
- package/dist/number-extensions.js +72 -0
- package/dist/object-extensions.d.ts +1 -0
- package/dist/object-extensions.js +53 -0
- package/dist/string-extensions.d.ts +1 -0
- package/dist/string-extensions.js +54 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.js +2 -0
- package/package.json +27 -0
- package/src/array-extensions.ts +73 -0
- package/src/index.ts +55 -0
- package/src/number-extensions.ts +73 -0
- package/src/object-extensions.ts +53 -0
- package/src/string-extensions.ts +63 -0
- package/src/types.ts +56 -0
- package/tsconfig.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# @naman_deep_singh/js-extensions
|
|
2
|
+
|
|
3
|
+
Universal JavaScript prototype extensions for common development utilities. Works in both Node.js and browser environments.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @naman_deep_singh/js-extensions
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @naman_deep_singh/js-extensions
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Initialize All Extensions
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { initExtensions } from '@naman_deep_singh/js-extensions';
|
|
19
|
+
|
|
20
|
+
// Initialize all extensions
|
|
21
|
+
initExtensions();
|
|
22
|
+
|
|
23
|
+
// Now use the extensions
|
|
24
|
+
"hello world".toCapitalize(); // "Hello world"
|
|
25
|
+
[1, 2, 2, 3].unique(); // [1, 2, 3]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Selective Extensions
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { extend } from '@naman_deep_singh/js-extensions';
|
|
32
|
+
|
|
33
|
+
// Only extend strings
|
|
34
|
+
extend.string();
|
|
35
|
+
|
|
36
|
+
// Only extend arrays and objects
|
|
37
|
+
initExtensions({ array: true, object: true, string: false, number: false });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## String Extensions
|
|
41
|
+
|
|
42
|
+
- `toCapitalize()` - Capitalize first letter
|
|
43
|
+
- `toCamelCase()` - Convert to camelCase
|
|
44
|
+
- `toKebabCase()` - Convert to kebab-case
|
|
45
|
+
- `toSnakeCase()` - Convert to snake_case
|
|
46
|
+
- `truncate(length, suffix?)` - Truncate with optional suffix
|
|
47
|
+
- `isEmail()` - Check if valid email
|
|
48
|
+
- `isUrl()` - Check if valid URL
|
|
49
|
+
- `removeWhitespace()` - Remove all whitespace
|
|
50
|
+
- `reverse()` - Reverse string
|
|
51
|
+
|
|
52
|
+
## Array Extensions
|
|
53
|
+
|
|
54
|
+
- `unique()` - Remove duplicates
|
|
55
|
+
- `shuffle()` - Randomly shuffle array
|
|
56
|
+
- `chunk(size)` - Split into chunks
|
|
57
|
+
- `groupBy(keyFn)` - Group by key function
|
|
58
|
+
- `sum()` - Sum numeric values
|
|
59
|
+
- `average()` - Calculate average
|
|
60
|
+
- `compact()` - Remove falsy values
|
|
61
|
+
- `pluck(key)` - Extract property values
|
|
62
|
+
|
|
63
|
+
## Object Extensions
|
|
64
|
+
|
|
65
|
+
- `isEmpty()` - Check if object is empty
|
|
66
|
+
- `pick(keys)` - Pick specific keys
|
|
67
|
+
- `omit(keys)` - Omit specific keys
|
|
68
|
+
- `deepClone()` - Deep clone object
|
|
69
|
+
- `merge(other)` - Merge with another object
|
|
70
|
+
|
|
71
|
+
## Number Extensions
|
|
72
|
+
|
|
73
|
+
- `toPercent(decimals?)` - Convert to percentage string
|
|
74
|
+
- `toCurrency(currency?, locale?)` - Format as currency
|
|
75
|
+
- `clamp(min, max)` - Clamp between min/max
|
|
76
|
+
- `isEven()` - Check if even
|
|
77
|
+
- `isOdd()` - Check if odd
|
|
78
|
+
- `isPrime()` - Check if prime number
|
|
79
|
+
- `factorial()` - Calculate factorial
|
|
80
|
+
|
|
81
|
+
## Browser Usage
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<script src="path/to/js-extensions.js"></script>
|
|
85
|
+
<script>
|
|
86
|
+
// Extensions are automatically initialized
|
|
87
|
+
console.log("hello".toCapitalize()); // "Hello"
|
|
88
|
+
</script>
|
|
89
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function extendArray(): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extendArray = extendArray;
|
|
4
|
+
// Array prototype extensions
|
|
5
|
+
function extendArray() {
|
|
6
|
+
Array.prototype.unique = function () {
|
|
7
|
+
return [...new Set(this)];
|
|
8
|
+
};
|
|
9
|
+
Array.prototype.shuffle = function () {
|
|
10
|
+
const arr = [...this];
|
|
11
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
12
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
13
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
14
|
+
}
|
|
15
|
+
return arr;
|
|
16
|
+
};
|
|
17
|
+
Array.prototype.chunk = function (size) {
|
|
18
|
+
const chunks = [];
|
|
19
|
+
for (let i = 0; i < this.length; i += size) {
|
|
20
|
+
chunks.push(this.slice(i, i + size));
|
|
21
|
+
}
|
|
22
|
+
return chunks;
|
|
23
|
+
};
|
|
24
|
+
Array.prototype.groupBy = function (keyFn) {
|
|
25
|
+
return this.reduce((groups, item) => {
|
|
26
|
+
const key = keyFn(item);
|
|
27
|
+
if (!groups[key])
|
|
28
|
+
groups[key] = [];
|
|
29
|
+
groups[key].push(item);
|
|
30
|
+
return groups;
|
|
31
|
+
}, {});
|
|
32
|
+
};
|
|
33
|
+
Array.prototype.sum = function () {
|
|
34
|
+
return this.reduce((sum, num) => sum + (typeof num === 'number' ? num : 0), 0);
|
|
35
|
+
};
|
|
36
|
+
Array.prototype.average = function () {
|
|
37
|
+
const numbers = this.filter(item => typeof item === 'number');
|
|
38
|
+
return numbers.length > 0 ? numbers.reduce((sum, num) => sum + num, 0) / numbers.length : 0;
|
|
39
|
+
};
|
|
40
|
+
Array.prototype.compact = function () {
|
|
41
|
+
return this.filter(item => item != null && item !== '' && item !== false);
|
|
42
|
+
};
|
|
43
|
+
Array.prototype.pluck = function (key) {
|
|
44
|
+
return this.map(item => item && typeof item === 'object' ? item[key] : undefined).filter(val => val !== undefined);
|
|
45
|
+
};
|
|
46
|
+
Array.prototype.findLast = function (predicate) {
|
|
47
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
48
|
+
if (predicate(this[i]))
|
|
49
|
+
return this[i];
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
};
|
|
53
|
+
Array.prototype.partition = function (predicate) {
|
|
54
|
+
const truthy = [];
|
|
55
|
+
const falsy = [];
|
|
56
|
+
this.forEach(item => predicate(item) ? truthy.push(item) : falsy.push(item));
|
|
57
|
+
return [truthy, falsy];
|
|
58
|
+
};
|
|
59
|
+
Array.prototype.flatten = function (depth = 1) {
|
|
60
|
+
return depth > 0 ? this.reduce((acc, val) => acc.concat(Array.isArray(val) ? val.flatten(depth - 1) : val), []) : this.slice();
|
|
61
|
+
};
|
|
62
|
+
Array.prototype.deepFlatten = function () {
|
|
63
|
+
return this.reduce((acc, val) => acc.concat(Array.isArray(val) ? val.deepFlatten() : val), []);
|
|
64
|
+
};
|
|
65
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { extendString } from './string-extensions';
|
|
2
|
+
import { extendArray } from './array-extensions';
|
|
3
|
+
import { extendObject } from './object-extensions';
|
|
4
|
+
import { extendNumber } from './number-extensions';
|
|
5
|
+
import './types';
|
|
6
|
+
export interface ExtensionOptions {
|
|
7
|
+
string?: boolean;
|
|
8
|
+
array?: boolean;
|
|
9
|
+
object?: boolean;
|
|
10
|
+
number?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Initialize JavaScript prototype extensions
|
|
14
|
+
* @param options - Configure which extensions to enable (default: all enabled)
|
|
15
|
+
*/
|
|
16
|
+
export declare function initExtensions(options?: ExtensionOptions): void;
|
|
17
|
+
/**
|
|
18
|
+
* Initialize all extensions (convenience function)
|
|
19
|
+
*/
|
|
20
|
+
export declare function extendAll(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Initialize only specific extensions
|
|
23
|
+
*/
|
|
24
|
+
export declare const extend: {
|
|
25
|
+
string: typeof extendString;
|
|
26
|
+
array: typeof extendArray;
|
|
27
|
+
object: typeof extendObject;
|
|
28
|
+
number: typeof extendNumber;
|
|
29
|
+
};
|
|
30
|
+
declare const _default: {
|
|
31
|
+
initExtensions: typeof initExtensions;
|
|
32
|
+
extendAll: typeof extendAll;
|
|
33
|
+
extend: {
|
|
34
|
+
string: typeof extendString;
|
|
35
|
+
array: typeof extendArray;
|
|
36
|
+
object: typeof extendObject;
|
|
37
|
+
number: typeof extendNumber;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export default _default;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extend = void 0;
|
|
4
|
+
exports.initExtensions = initExtensions;
|
|
5
|
+
exports.extendAll = extendAll;
|
|
6
|
+
const string_extensions_1 = require("./string-extensions");
|
|
7
|
+
const array_extensions_1 = require("./array-extensions");
|
|
8
|
+
const object_extensions_1 = require("./object-extensions");
|
|
9
|
+
const number_extensions_1 = require("./number-extensions");
|
|
10
|
+
require("./types");
|
|
11
|
+
/**
|
|
12
|
+
* Initialize JavaScript prototype extensions
|
|
13
|
+
* @param options - Configure which extensions to enable (default: all enabled)
|
|
14
|
+
*/
|
|
15
|
+
function initExtensions(options = {}) {
|
|
16
|
+
const { string = true, array = true, object = true, number = true } = options;
|
|
17
|
+
if (string)
|
|
18
|
+
(0, string_extensions_1.extendString)();
|
|
19
|
+
if (array)
|
|
20
|
+
(0, array_extensions_1.extendArray)();
|
|
21
|
+
if (object)
|
|
22
|
+
(0, object_extensions_1.extendObject)();
|
|
23
|
+
if (number)
|
|
24
|
+
(0, number_extensions_1.extendNumber)();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Initialize all extensions (convenience function)
|
|
28
|
+
*/
|
|
29
|
+
function extendAll() {
|
|
30
|
+
initExtensions();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Initialize only specific extensions
|
|
34
|
+
*/
|
|
35
|
+
exports.extend = {
|
|
36
|
+
string: string_extensions_1.extendString,
|
|
37
|
+
array: array_extensions_1.extendArray,
|
|
38
|
+
object: object_extensions_1.extendObject,
|
|
39
|
+
number: number_extensions_1.extendNumber
|
|
40
|
+
};
|
|
41
|
+
exports.default = {
|
|
42
|
+
initExtensions,
|
|
43
|
+
extendAll,
|
|
44
|
+
extend: exports.extend
|
|
45
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function extendNumber(): void;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extendNumber = extendNumber;
|
|
4
|
+
// Number prototype extensions
|
|
5
|
+
function extendNumber() {
|
|
6
|
+
Number.prototype.toPercent = function (decimals = 2) {
|
|
7
|
+
return (this.valueOf() * 100).toFixed(decimals) + '%';
|
|
8
|
+
};
|
|
9
|
+
Number.prototype.toCurrency = function (currency = 'USD', locale = 'en-US') {
|
|
10
|
+
return new Intl.NumberFormat(locale, {
|
|
11
|
+
style: 'currency',
|
|
12
|
+
currency: currency
|
|
13
|
+
}).format(this.valueOf());
|
|
14
|
+
};
|
|
15
|
+
Number.prototype.clamp = function (min, max) {
|
|
16
|
+
return Math.min(Math.max(this.valueOf(), min), max);
|
|
17
|
+
};
|
|
18
|
+
Number.prototype.isEven = function () {
|
|
19
|
+
return this.valueOf() % 2 === 0;
|
|
20
|
+
};
|
|
21
|
+
Number.prototype.isOdd = function () {
|
|
22
|
+
return this.valueOf() % 2 !== 0;
|
|
23
|
+
};
|
|
24
|
+
Number.prototype.isPrime = function () {
|
|
25
|
+
const num = this.valueOf();
|
|
26
|
+
if (num < 2)
|
|
27
|
+
return false;
|
|
28
|
+
for (let i = 2; i <= Math.sqrt(num); i++) {
|
|
29
|
+
if (num % i === 0)
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
};
|
|
34
|
+
Number.prototype.factorial = function () {
|
|
35
|
+
const num = Math.floor(this.valueOf());
|
|
36
|
+
if (num < 0)
|
|
37
|
+
return NaN;
|
|
38
|
+
if (num === 0 || num === 1)
|
|
39
|
+
return 1;
|
|
40
|
+
let result = 1;
|
|
41
|
+
for (let i = 2; i <= num; i++) {
|
|
42
|
+
result *= i;
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
Number.prototype.toOrdinal = function () {
|
|
47
|
+
const num = Math.floor(this.valueOf());
|
|
48
|
+
const suffix = ['th', 'st', 'nd', 'rd'];
|
|
49
|
+
const v = num % 100;
|
|
50
|
+
return num + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
|
|
51
|
+
};
|
|
52
|
+
Number.prototype.toRoman = function () {
|
|
53
|
+
const num = Math.floor(this.valueOf());
|
|
54
|
+
if (num <= 0 || num >= 4000)
|
|
55
|
+
return num.toString();
|
|
56
|
+
const values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
|
|
57
|
+
const symbols = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
|
|
58
|
+
let result = '';
|
|
59
|
+
let n = num;
|
|
60
|
+
for (let i = 0; i < values.length; i++) {
|
|
61
|
+
while (n >= values[i]) {
|
|
62
|
+
result += symbols[i];
|
|
63
|
+
n -= values[i];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
68
|
+
Number.prototype.inRange = function (min, max) {
|
|
69
|
+
const num = this.valueOf();
|
|
70
|
+
return num >= min && num <= max;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function extendObject(): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extendObject = extendObject;
|
|
4
|
+
// Object prototype extensions
|
|
5
|
+
function extendObject() {
|
|
6
|
+
Object.prototype.isEmpty = function () {
|
|
7
|
+
return Object.keys(this).length === 0;
|
|
8
|
+
};
|
|
9
|
+
Object.prototype.pick = function (keys) {
|
|
10
|
+
const result = {};
|
|
11
|
+
const obj = this;
|
|
12
|
+
keys.forEach(key => {
|
|
13
|
+
if (key in obj) {
|
|
14
|
+
result[key] = obj[key];
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return result;
|
|
18
|
+
};
|
|
19
|
+
Object.prototype.omit = function (keys) {
|
|
20
|
+
const result = { ...this };
|
|
21
|
+
keys.forEach(key => {
|
|
22
|
+
delete result[key];
|
|
23
|
+
});
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
Object.prototype.deepClone = function () {
|
|
27
|
+
if (this === null || typeof this !== 'object')
|
|
28
|
+
return this;
|
|
29
|
+
if (this instanceof Date)
|
|
30
|
+
return new Date(this.getTime());
|
|
31
|
+
if (this instanceof Array)
|
|
32
|
+
return this.map(item => item?.deepClone?.() || item);
|
|
33
|
+
const cloned = {};
|
|
34
|
+
Object.keys(this).forEach(key => {
|
|
35
|
+
const value = this[key];
|
|
36
|
+
cloned[key] = value?.deepClone?.() || value;
|
|
37
|
+
});
|
|
38
|
+
return cloned;
|
|
39
|
+
};
|
|
40
|
+
Object.prototype.merge = function (other) {
|
|
41
|
+
return { ...this, ...other };
|
|
42
|
+
};
|
|
43
|
+
Object.prototype.deepFreeze = function () {
|
|
44
|
+
const propNames = Object.getOwnPropertyNames(this);
|
|
45
|
+
for (const name of propNames) {
|
|
46
|
+
const value = this[name];
|
|
47
|
+
if (value && typeof value === 'object') {
|
|
48
|
+
value.deepFreeze();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return Object.freeze(this);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function extendString(): void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extendString = extendString;
|
|
4
|
+
// String prototype extensions
|
|
5
|
+
function extendString() {
|
|
6
|
+
String.prototype.toCapitalize = function () {
|
|
7
|
+
return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
|
|
8
|
+
};
|
|
9
|
+
String.prototype.toCamelCase = function () {
|
|
10
|
+
return this.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '');
|
|
11
|
+
};
|
|
12
|
+
String.prototype.toKebabCase = function () {
|
|
13
|
+
return this.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
14
|
+
.replace(/[\s_]+/g, '-')
|
|
15
|
+
.toLowerCase();
|
|
16
|
+
};
|
|
17
|
+
String.prototype.toSnakeCase = function () {
|
|
18
|
+
return this.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
19
|
+
.replace(/[\s-]+/g, '_')
|
|
20
|
+
.toLowerCase();
|
|
21
|
+
};
|
|
22
|
+
String.prototype.truncate = function (length, suffix = '...') {
|
|
23
|
+
return this.length > length ? this.substring(0, length) + suffix : this.toString();
|
|
24
|
+
};
|
|
25
|
+
String.prototype.isEmail = function () {
|
|
26
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
27
|
+
return emailRegex.test(this.toString());
|
|
28
|
+
};
|
|
29
|
+
String.prototype.isUrl = function () {
|
|
30
|
+
try {
|
|
31
|
+
new URL(this.toString());
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
String.prototype.removeWhitespace = function () {
|
|
39
|
+
return this.replace(/\s+/g, '');
|
|
40
|
+
};
|
|
41
|
+
String.prototype.reverse = function () {
|
|
42
|
+
return this.split('').reverse().join('');
|
|
43
|
+
};
|
|
44
|
+
String.prototype.isPalindrome = function () {
|
|
45
|
+
const cleaned = this.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
46
|
+
return cleaned === cleaned.split('').reverse().join('');
|
|
47
|
+
};
|
|
48
|
+
String.prototype.toTitleCase = function () {
|
|
49
|
+
return this.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
|
50
|
+
};
|
|
51
|
+
String.prototype.stripHtml = function () {
|
|
52
|
+
return this.replace(/<[^>]*>/g, '');
|
|
53
|
+
};
|
|
54
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface String {
|
|
3
|
+
toCapitalize(): string;
|
|
4
|
+
toCamelCase(): string;
|
|
5
|
+
toKebabCase(): string;
|
|
6
|
+
toSnakeCase(): string;
|
|
7
|
+
truncate(length: number, suffix?: string): string;
|
|
8
|
+
isEmail(): boolean;
|
|
9
|
+
isUrl(): boolean;
|
|
10
|
+
removeWhitespace(): string;
|
|
11
|
+
reverse(): string;
|
|
12
|
+
isPalindrome(): boolean;
|
|
13
|
+
toTitleCase(): string;
|
|
14
|
+
stripHtml(): string;
|
|
15
|
+
}
|
|
16
|
+
interface Array<T> {
|
|
17
|
+
unique(): T[];
|
|
18
|
+
shuffle(): T[];
|
|
19
|
+
chunk(size: number): T[][];
|
|
20
|
+
groupBy<K extends string | number>(keyFn: (item: T) => K): Record<K, T[]>;
|
|
21
|
+
sum(): number;
|
|
22
|
+
average(): number;
|
|
23
|
+
compact(): T[];
|
|
24
|
+
pluck<K extends keyof T>(key: K): T[K][];
|
|
25
|
+
findLast(predicate: (item: T) => boolean): T | undefined;
|
|
26
|
+
partition(predicate: (item: T) => boolean): [T[], T[]];
|
|
27
|
+
flatten(depth?: number): any[];
|
|
28
|
+
deepFlatten(): any[];
|
|
29
|
+
}
|
|
30
|
+
interface Object {
|
|
31
|
+
isEmpty(): boolean;
|
|
32
|
+
pick<T extends Record<string, any>, K extends keyof T>(keys: K[]): Pick<T, K>;
|
|
33
|
+
omit<T extends Record<string, any>, K extends keyof T>(keys: K[]): Omit<T, K>;
|
|
34
|
+
deepClone<T>(): T;
|
|
35
|
+
merge(other: Record<string, any>): Record<string, any>;
|
|
36
|
+
deepFreeze<T>(): T;
|
|
37
|
+
}
|
|
38
|
+
interface Number {
|
|
39
|
+
toPercent(decimals?: number): string;
|
|
40
|
+
toCurrency(currency?: string, locale?: string): string;
|
|
41
|
+
clamp(min: number, max: number): number;
|
|
42
|
+
isEven(): boolean;
|
|
43
|
+
isOdd(): boolean;
|
|
44
|
+
isPrime(): boolean;
|
|
45
|
+
factorial(): number;
|
|
46
|
+
toOrdinal(): string;
|
|
47
|
+
toRoman(): string;
|
|
48
|
+
inRange(min: number, max: number): boolean;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export {};
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@naman_deep_singh/js-extensions",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Universal JavaScript prototype extensions for common development utilities",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"browser": "dist/index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"javascript",
|
|
13
|
+
"utilities",
|
|
14
|
+
"extensions",
|
|
15
|
+
"prototype",
|
|
16
|
+
"string",
|
|
17
|
+
"array",
|
|
18
|
+
"object",
|
|
19
|
+
"universal"
|
|
20
|
+
],
|
|
21
|
+
"author": "Naman Deep Singh",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"packageManager": "pnpm@10.20.0",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"typescript": "^5.9.3"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Array prototype extensions
|
|
2
|
+
export function extendArray() {
|
|
3
|
+
Array.prototype.unique = function<T>(): T[] {
|
|
4
|
+
return [...new Set(this)];
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
Array.prototype.shuffle = function<T>(): T[] {
|
|
8
|
+
const arr = [...this];
|
|
9
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
10
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
11
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
12
|
+
}
|
|
13
|
+
return arr;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
Array.prototype.chunk = function<T>(size: number): T[][] {
|
|
17
|
+
const chunks: T[][] = [];
|
|
18
|
+
for (let i = 0; i < this.length; i += size) {
|
|
19
|
+
chunks.push(this.slice(i, i + size));
|
|
20
|
+
}
|
|
21
|
+
return chunks;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
Array.prototype.groupBy = function<T>(keyFn: (item: T) => string | number): Record<string | number, T[]> {
|
|
25
|
+
return this.reduce((groups, item) => {
|
|
26
|
+
const key = keyFn(item);
|
|
27
|
+
if (!groups[key]) groups[key] = [];
|
|
28
|
+
groups[key].push(item);
|
|
29
|
+
return groups;
|
|
30
|
+
}, {} as Record<string | number, T[]>);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
Array.prototype.sum = function(): number {
|
|
34
|
+
return this.reduce((sum, num) => sum + (typeof num === 'number' ? num : 0), 0);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
Array.prototype.average = function(): number {
|
|
38
|
+
const numbers = this.filter(item => typeof item === 'number');
|
|
39
|
+
return numbers.length > 0 ? numbers.reduce((sum, num) => sum + num, 0) / numbers.length : 0;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
Array.prototype.compact = function<T>(): T[] {
|
|
43
|
+
return this.filter(item => item != null && item !== '' && item !== false);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
Array.prototype.pluck = function<T, K extends keyof T>(key: K): T[K][] {
|
|
47
|
+
return this.map(item => item && typeof item === 'object' ? item[key] : undefined).filter(val => val !== undefined);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
Array.prototype.findLast = function<T>(predicate: (item: T) => boolean): T | undefined {
|
|
51
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
52
|
+
if (predicate(this[i])) return this[i];
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
Array.prototype.partition = function<T>(predicate: (item: T) => boolean): [T[], T[]] {
|
|
58
|
+
const truthy: T[] = [];
|
|
59
|
+
const falsy: T[] = [];
|
|
60
|
+
this.forEach(item => predicate(item) ? truthy.push(item) : falsy.push(item));
|
|
61
|
+
return [truthy, falsy];
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
Array.prototype.flatten = function(depth: number = 1): any[] {
|
|
65
|
+
return depth > 0 ? this.reduce((acc, val) =>
|
|
66
|
+
acc.concat(Array.isArray(val) ? val.flatten(depth - 1) : val), []) : this.slice();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
Array.prototype.deepFlatten = function(): any[] {
|
|
70
|
+
return this.reduce((acc, val) =>
|
|
71
|
+
acc.concat(Array.isArray(val) ? val.deepFlatten() : val), []);
|
|
72
|
+
};
|
|
73
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { extendString } from './string-extensions';
|
|
2
|
+
import { extendArray } from './array-extensions';
|
|
3
|
+
import { extendObject } from './object-extensions';
|
|
4
|
+
import { extendNumber } from './number-extensions';
|
|
5
|
+
import './types';
|
|
6
|
+
|
|
7
|
+
export interface ExtensionOptions {
|
|
8
|
+
string?: boolean;
|
|
9
|
+
array?: boolean;
|
|
10
|
+
object?: boolean;
|
|
11
|
+
number?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initialize JavaScript prototype extensions
|
|
16
|
+
* @param options - Configure which extensions to enable (default: all enabled)
|
|
17
|
+
*/
|
|
18
|
+
export function initExtensions(options: ExtensionOptions = {}): void {
|
|
19
|
+
const {
|
|
20
|
+
string = true,
|
|
21
|
+
array = true,
|
|
22
|
+
object = true,
|
|
23
|
+
number = true
|
|
24
|
+
} = options;
|
|
25
|
+
|
|
26
|
+
if (string) extendString();
|
|
27
|
+
if (array) extendArray();
|
|
28
|
+
if (object) extendObject();
|
|
29
|
+
if (number) extendNumber();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Initialize all extensions (convenience function)
|
|
34
|
+
*/
|
|
35
|
+
export function extendAll(): void {
|
|
36
|
+
initExtensions();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Initialize only specific extensions
|
|
41
|
+
*/
|
|
42
|
+
export const extend = {
|
|
43
|
+
string: extendString,
|
|
44
|
+
array: extendArray,
|
|
45
|
+
object: extendObject,
|
|
46
|
+
number: extendNumber
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export default {
|
|
52
|
+
initExtensions,
|
|
53
|
+
extendAll,
|
|
54
|
+
extend
|
|
55
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Number prototype extensions
|
|
2
|
+
export function extendNumber() {
|
|
3
|
+
Number.prototype.toPercent = function(decimals: number = 2): string {
|
|
4
|
+
return (this.valueOf() * 100).toFixed(decimals) + '%';
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
Number.prototype.toCurrency = function(currency: string = 'USD', locale: string = 'en-US'): string {
|
|
8
|
+
return new Intl.NumberFormat(locale, {
|
|
9
|
+
style: 'currency',
|
|
10
|
+
currency: currency
|
|
11
|
+
}).format(this.valueOf());
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
Number.prototype.clamp = function(min: number, max: number): number {
|
|
15
|
+
return Math.min(Math.max(this.valueOf(), min), max);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
Number.prototype.isEven = function(): boolean {
|
|
19
|
+
return this.valueOf() % 2 === 0;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
Number.prototype.isOdd = function(): boolean {
|
|
23
|
+
return this.valueOf() % 2 !== 0;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
Number.prototype.isPrime = function(): boolean {
|
|
27
|
+
const num = this.valueOf();
|
|
28
|
+
if (num < 2) return false;
|
|
29
|
+
for (let i = 2; i <= Math.sqrt(num); i++) {
|
|
30
|
+
if (num % i === 0) return false;
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
Number.prototype.factorial = function(): number {
|
|
36
|
+
const num = Math.floor(this.valueOf());
|
|
37
|
+
if (num < 0) return NaN;
|
|
38
|
+
if (num === 0 || num === 1) return 1;
|
|
39
|
+
let result = 1;
|
|
40
|
+
for (let i = 2; i <= num; i++) {
|
|
41
|
+
result *= i;
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
Number.prototype.toOrdinal = function(): string {
|
|
47
|
+
const num = Math.floor(this.valueOf());
|
|
48
|
+
const suffix = ['th', 'st', 'nd', 'rd'];
|
|
49
|
+
const v = num % 100;
|
|
50
|
+
return num + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
Number.prototype.toRoman = function(): string {
|
|
54
|
+
const num = Math.floor(this.valueOf());
|
|
55
|
+
if (num <= 0 || num >= 4000) return num.toString();
|
|
56
|
+
const values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
|
|
57
|
+
const symbols = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
|
|
58
|
+
let result = '';
|
|
59
|
+
let n = num;
|
|
60
|
+
for (let i = 0; i < values.length; i++) {
|
|
61
|
+
while (n >= values[i]) {
|
|
62
|
+
result += symbols[i];
|
|
63
|
+
n -= values[i];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
Number.prototype.inRange = function(min: number, max: number): boolean {
|
|
70
|
+
const num = this.valueOf();
|
|
71
|
+
return num >= min && num <= max;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Object prototype extensions
|
|
2
|
+
export function extendObject() {
|
|
3
|
+
Object.prototype.isEmpty = function(): boolean {
|
|
4
|
+
return Object.keys(this).length === 0;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
Object.prototype.pick = function<T extends Record<string, any>, K extends keyof T>(keys: K[]): Pick<T, K> {
|
|
8
|
+
const result = {} as Pick<T, K>;
|
|
9
|
+
const obj = this as T;
|
|
10
|
+
keys.forEach(key => {
|
|
11
|
+
if (key in obj) {
|
|
12
|
+
result[key] = obj[key];
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
Object.prototype.omit = function<T extends Record<string, any>, K extends keyof T>(keys: K[]): Omit<T, K> {
|
|
19
|
+
const result = { ...this } as T;
|
|
20
|
+
keys.forEach(key => {
|
|
21
|
+
delete result[key];
|
|
22
|
+
});
|
|
23
|
+
return result as Omit<T, K>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
Object.prototype.deepClone = function<T>(): T {
|
|
27
|
+
if (this === null || typeof this !== 'object') return this;
|
|
28
|
+
if (this instanceof Date) return new Date(this.getTime()) as any;
|
|
29
|
+
if (this instanceof Array) return this.map(item => item?.deepClone?.() || item) as any;
|
|
30
|
+
|
|
31
|
+
const cloned = {} as T;
|
|
32
|
+
Object.keys(this).forEach(key => {
|
|
33
|
+
const value = (this as any)[key];
|
|
34
|
+
(cloned as any)[key] = value?.deepClone?.() || value;
|
|
35
|
+
});
|
|
36
|
+
return cloned;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
Object.prototype.merge = function(other: Record<string, any>): Record<string, any> {
|
|
40
|
+
return { ...this, ...other };
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
Object.prototype.deepFreeze = function<T>(): T {
|
|
44
|
+
const propNames = Object.getOwnPropertyNames(this);
|
|
45
|
+
for (const name of propNames) {
|
|
46
|
+
const value = (this as any)[name];
|
|
47
|
+
if (value && typeof value === 'object') {
|
|
48
|
+
value.deepFreeze();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return Object.freeze(this) as T;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// String prototype extensions
|
|
2
|
+
export function extendString() {
|
|
3
|
+
String.prototype.toCapitalize = function(): string {
|
|
4
|
+
return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
String.prototype.toCamelCase = function(): string {
|
|
8
|
+
return this.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '');
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
String.prototype.toKebabCase = function(): string {
|
|
12
|
+
return this.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
13
|
+
.replace(/[\s_]+/g, '-')
|
|
14
|
+
.toLowerCase();
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
String.prototype.toSnakeCase = function(): string {
|
|
18
|
+
return this.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
19
|
+
.replace(/[\s-]+/g, '_')
|
|
20
|
+
.toLowerCase();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
String.prototype.truncate = function(length: number, suffix: string = '...'): string {
|
|
24
|
+
return this.length > length ? this.substring(0, length) + suffix : this.toString();
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
String.prototype.isEmail = function(): boolean {
|
|
28
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
29
|
+
return emailRegex.test(this.toString());
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
String.prototype.isUrl = function(): boolean {
|
|
33
|
+
try {
|
|
34
|
+
new URL(this.toString());
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
String.prototype.removeWhitespace = function(): string {
|
|
42
|
+
return this.replace(/\s+/g, '');
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
String.prototype.reverse = function(): string {
|
|
46
|
+
return this.split('').reverse().join('');
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
String.prototype.isPalindrome = function(): boolean {
|
|
50
|
+
const cleaned = this.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
51
|
+
return cleaned === cleaned.split('').reverse().join('');
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
String.prototype.toTitleCase = function(): string {
|
|
55
|
+
return this.replace(/\w\S*/g, (txt) =>
|
|
56
|
+
txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
String.prototype.stripHtml = function(): string {
|
|
61
|
+
return this.replace(/<[^>]*>/g, '');
|
|
62
|
+
};
|
|
63
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// TypeScript declarations for all extensions
|
|
2
|
+
declare global {
|
|
3
|
+
interface String {
|
|
4
|
+
toCapitalize(): string;
|
|
5
|
+
toCamelCase(): string;
|
|
6
|
+
toKebabCase(): string;
|
|
7
|
+
toSnakeCase(): string;
|
|
8
|
+
truncate(length: number, suffix?: string): string;
|
|
9
|
+
isEmail(): boolean;
|
|
10
|
+
isUrl(): boolean;
|
|
11
|
+
removeWhitespace(): string;
|
|
12
|
+
reverse(): string;
|
|
13
|
+
isPalindrome(): boolean;
|
|
14
|
+
toTitleCase(): string;
|
|
15
|
+
stripHtml(): string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Array<T> {
|
|
19
|
+
unique(): T[];
|
|
20
|
+
shuffle(): T[];
|
|
21
|
+
chunk(size: number): T[][];
|
|
22
|
+
groupBy<K extends string | number>(keyFn: (item: T) => K): Record<K, T[]>;
|
|
23
|
+
sum(): number;
|
|
24
|
+
average(): number;
|
|
25
|
+
compact(): T[];
|
|
26
|
+
pluck<K extends keyof T>(key: K): T[K][];
|
|
27
|
+
findLast(predicate: (item: T) => boolean): T | undefined;
|
|
28
|
+
partition(predicate: (item: T) => boolean): [T[], T[]];
|
|
29
|
+
flatten(depth?: number): any[];
|
|
30
|
+
deepFlatten(): any[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface Object {
|
|
34
|
+
isEmpty(): boolean;
|
|
35
|
+
pick<T extends Record<string, any>, K extends keyof T>(keys: K[]): Pick<T, K>;
|
|
36
|
+
omit<T extends Record<string, any>, K extends keyof T>(keys: K[]): Omit<T, K>;
|
|
37
|
+
deepClone<T>(): T;
|
|
38
|
+
merge(other: Record<string, any>): Record<string, any>;
|
|
39
|
+
deepFreeze<T>(): T;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface Number {
|
|
43
|
+
toPercent(decimals?: number): string;
|
|
44
|
+
toCurrency(currency?: string, locale?: string): string;
|
|
45
|
+
clamp(min: number, max: number): number;
|
|
46
|
+
isEven(): boolean;
|
|
47
|
+
isOdd(): boolean;
|
|
48
|
+
isPrime(): boolean;
|
|
49
|
+
factorial(): number;
|
|
50
|
+
toOrdinal(): string;
|
|
51
|
+
toRoman(): string;
|
|
52
|
+
inRange(min: number, max: number): boolean;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export {};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"rootDir": "./src",
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"allowSyntheticDefaultImports": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"baseUrl": ".",
|
|
15
|
+
"paths": {
|
|
16
|
+
"*": ["*", "*.ts", "*.js"]
|
|
17
|
+
},
|
|
18
|
+
"lib": ["ES2020", "DOM"]
|
|
19
|
+
},
|
|
20
|
+
"include": ["src/**/*"],
|
|
21
|
+
"exclude": ["node_modules", "dist"]
|
|
22
|
+
}
|