Skip to content

Commit 377e727

Browse files
authored
feat: Remove lodash dependency (#220)
Replace lodash with lean custom utility functions, reducing bundle size by 39.8% (71,929 bytes). Implemented eight purpose-specific utilities in src/_/ directory to handle null checks, type validation, object manipulation, and collection operations. Bundle Size Improvement: - Before: 180,599 bytes (176 KiB) - After: 108,670 bytes (106 KiB) - Reduction: 71,929 bytes (39.8%) Custom Utilities Implemented: - isNil(value) - Null/undefined check - isEmpty(value) - Empty value check - has(object, key) - Own property check - hasIn(object, key) - Inherited property check - isString(value) - String type check - isObject(value) - Object type check - head(array) - Array first element - omit(object, keys) - Object key exclusion Implementation Details: - Updated 93 lodash imports across src/ directory - Replaced lodash calls with _.functionName() pattern - Moved lodash from dependencies to devDependencies - Maintained for test files only - All 477 tests pass with 100% coverage - No functionality regressions - TypeScript definitions remain valid
1 parent 374bf56 commit 377e727

File tree

95 files changed

+692
-282
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+692
-282
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
"node": ">=20.0.0"
3232
},
3333
"dependencies": {
34-
"lodash": "^4.17.21"
3534
},
3635
"devDependencies": {
36+
"lodash": "^4.17.21",
3737
"@babel/cli": "^7.25.9",
3838
"@babel/core": "^7.25.9",
3939
"@babel/eslint-parser": "^7.25.9",

src/_/index.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use strict';
2+
3+
/**
4+
* Checks if value is null or undefined.
5+
*
6+
* @param {*} value The value to check.
7+
* @returns {boolean} Returns true if value is null or undefined, else false.
8+
*/
9+
exports.isNil = function isNil(value) {
10+
return value === null || value === undefined;
11+
};
12+
13+
/**
14+
* Checks if value is a string.
15+
*
16+
* @param {*} value The value to check.
17+
* @returns {boolean} Returns true if value is a string, else false.
18+
*/
19+
exports.isString = function isString(value) {
20+
return typeof value === 'string';
21+
};
22+
23+
/**
24+
* Checks if value is an object type (not null, not an array).
25+
*
26+
* @param {*} value The value to check.
27+
* @returns {boolean} Returns true if value is an object, else false.
28+
*/
29+
exports.isObject = function isObject(value) {
30+
return value !== null && typeof value === 'object' && !Array.isArray(value);
31+
};
32+
33+
/**
34+
* Checks if object has a direct property (own property).
35+
*
36+
* @param {Object} object The object to check.
37+
* @param {string} key The property name to check.
38+
* @returns {boolean} Returns true if object has the property, else false.
39+
*/
40+
exports.has = function has(object, key) {
41+
return object != null && Object.prototype.hasOwnProperty.call(object, key);
42+
};
43+
44+
/**
45+
* Checks if object has a property (including inherited properties).
46+
*
47+
* @param {Object} object The object to check.
48+
* @param {string} key The property name to check.
49+
* @returns {boolean} Returns true if object has the property, else false.
50+
*/
51+
exports.hasIn = function hasIn(object, key) {
52+
return object != null && key in object;
53+
};
54+
55+
/**
56+
* Creates a new object excluding specified keys.
57+
*
58+
* @param {Object} object The source object.
59+
* @param {Array<string>} keys The keys to exclude.
60+
* @returns {Object} Returns the new object with specified keys omitted.
61+
*/
62+
exports.omit = function omit(object, keys) {
63+
if (object == null) return {};
64+
const result = {};
65+
const keysToOmit = new Set(keys);
66+
for (const key in object) {
67+
if (
68+
Object.prototype.hasOwnProperty.call(object, key) &&
69+
!keysToOmit.has(key)
70+
) {
71+
result[key] = object[key];
72+
}
73+
}
74+
return result;
75+
};
76+
77+
/**
78+
* Checks if value is empty (null, undefined, empty string, empty array, or empty object).
79+
*
80+
* @param {*} value The value to check.
81+
* @returns {boolean} Returns true if value is empty, else false.
82+
*/
83+
exports.isEmpty = function isEmpty(value) {
84+
if (value == null) return true;
85+
if (typeof value === 'string' || Array.isArray(value)) {
86+
return value.length === 0;
87+
}
88+
if (typeof value === 'object') {
89+
return Object.keys(value).length === 0;
90+
}
91+
return false;
92+
};
93+
94+
/**
95+
* Gets the first element of an array.
96+
*
97+
* @param {Array} array The array to query.
98+
* @returns {*} Returns the first element of array.
99+
*/
100+
exports.head = function head(array) {
101+
return array != null ? array[0] : undefined;
102+
};

src/aggregations/bucket-aggregations/auto-date-histogram-aggregation.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { isNil } = require('lodash');
3+
const _ = require('../../_');
44

55
const BucketAggregationBase = require('./bucket-aggregation-base');
66

@@ -26,7 +26,7 @@ class AutoDateHistogramAggregation extends BucketAggregationBase {
2626
// eslint-disable-next-line require-jsdoc
2727
constructor(name, field, buckets) {
2828
super(name, 'auto_date_histogram', field);
29-
if (!isNil(buckets)) this._aggsDef.buckets = buckets;
29+
if (!_.isNil(buckets)) this._aggsDef.buckets = buckets;
3030
}
3131

3232
/**

src/aggregations/bucket-aggregations/bucket-aggregation-base.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { isNil } = require('lodash');
3+
const _ = require('../../_');
44

55
const {
66
Aggregation,
@@ -27,7 +27,7 @@ class BucketAggregationBase extends Aggregation {
2727
constructor(name, aggType, field) {
2828
super(name, aggType);
2929

30-
if (!isNil(field)) this._aggsDef.field = field;
30+
if (!_.isNil(field)) this._aggsDef.field = field;
3131
}
3232

3333
/**

src/aggregations/bucket-aggregations/composite-agg-values-sources/date-histogram-values-source.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { isNil } = require('lodash');
3+
const _ = require('../../../_');
44

55
const ValuesSourceBase = require('./values-source-base');
66

@@ -32,7 +32,7 @@ class DateHistogramValuesSource extends ValuesSourceBase {
3232
constructor(name, field, interval) {
3333
super('date_histogram', REF_URL, name, field);
3434

35-
if (!isNil(interval)) this._opts.interval = interval;
35+
if (!_.isNil(interval)) this._opts.interval = interval;
3636
}
3737

3838
/**

src/aggregations/bucket-aggregations/composite-agg-values-sources/histogram-values-source.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { isNil } = require('lodash');
3+
const _ = require('../../../_');
44

55
const ValuesSourceBase = require('./values-source-base');
66

@@ -32,7 +32,7 @@ class HistogramValuesSource extends ValuesSourceBase {
3232
constructor(name, field, interval) {
3333
super('histogram', REF_URL, name, field);
3434

35-
if (!isNil(interval)) this._opts.interval = interval;
35+
if (!_.isNil(interval)) this._opts.interval = interval;
3636
}
3737

3838
/**

src/aggregations/bucket-aggregations/composite-agg-values-sources/values-source-base.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { isEmpty, isNil } = require('lodash');
3+
const _ = require('../../../_');
44

55
const {
66
util: { invalidParam, recursiveToJSON }
@@ -24,7 +24,7 @@ const invalidOrderParam = invalidParam('', 'order', "'asc' or 'desc'");
2424
class ValuesSourceBase {
2525
// eslint-disable-next-line require-jsdoc
2626
constructor(valueSrcType, refUrl, name, field) {
27-
if (isEmpty(valueSrcType))
27+
if (_.isEmpty(valueSrcType))
2828
throw new Error('ValuesSourceBase `valueSrcType` cannot be empty');
2929

3030
this._name = name;
@@ -34,7 +34,7 @@ class ValuesSourceBase {
3434
this._body = {};
3535
this._opts = this._body[valueSrcType] = {};
3636

37-
if (!isNil(field)) this._opts.field = field;
37+
if (!_.isNil(field)) this._opts.field = field;
3838
}
3939

4040
/**
@@ -81,7 +81,7 @@ class ValuesSourceBase {
8181
* @returns {ValuesSourceBase} returns `this` so that calls can be chained.
8282
*/
8383
order(order) {
84-
if (isNil(order)) invalidOrderParam(order, this._refUrl);
84+
if (_.isNil(order)) invalidOrderParam(order, this._refUrl);
8585

8686
const orderLower = order.toLowerCase();
8787
if (orderLower !== 'asc' && orderLower !== 'desc') {

src/aggregations/bucket-aggregations/diversified-sampler-aggregation.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { isNil } = require('lodash');
3+
const _ = require('../../_');
44

55
const {
66
util: { invalidParam },
@@ -106,7 +106,7 @@ class DiversifiedSamplerAggregation extends BucketAggregationBase {
106106
* @throws {Error} If Execution Hint is outside the accepted set.
107107
*/
108108
executionHint(hint) {
109-
if (isNil(hint)) invalidExecutionHintParam(hint);
109+
if (_.isNil(hint)) invalidExecutionHintParam(hint);
110110

111111
const hintLower = hint.toLowerCase();
112112
if (!EXECUTION_HINT_SET.has(hintLower)) {

src/aggregations/bucket-aggregations/filter-aggregation.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { isNil } = require('lodash');
3+
const _ = require('../../_');
44

55
const {
66
Query,
@@ -39,7 +39,7 @@ class FilterAggregation extends BucketAggregationBase {
3939
constructor(name, filterQuery) {
4040
super(name, 'filter');
4141

42-
if (!isNil(filterQuery)) this.filter(filterQuery);
42+
if (!_.isNil(filterQuery)) this.filter(filterQuery);
4343
}
4444

4545
/**

src/aggregations/bucket-aggregations/filters-aggregation.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { isEmpty } = require('lodash');
3+
const _ = require('../../_');
44

55
const {
66
Query,
@@ -198,7 +198,7 @@ class FiltersAggregation extends BucketAggregationBase {
198198
otherBucket(enable, otherBucketKey) {
199199
this._aggsDef.other_bucket = enable;
200200

201-
!isEmpty(otherBucketKey) && this.otherBucketKey(otherBucketKey);
201+
!_.isEmpty(otherBucketKey) && this.otherBucketKey(otherBucketKey);
202202

203203
return this;
204204
}

0 commit comments

Comments
 (0)