Skip to content

remove lodash#250

Open
galisufyan-327 wants to merge 1 commit intomasterfrom
remove-lodash
Open

remove lodash#250
galisufyan-327 wants to merge 1 commit intomasterfrom
remove-lodash

Conversation

@galisufyan-327
Copy link
Collaborator

@galisufyan-327 galisufyan-327 commented Sep 9, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a lightweight, built-in utility library to power sorting, filtering, and cloning operations.
  • Refactor

    • Replaced third-party utility usage with native in-house utilities, improving performance and reliability across data listing, sorting, and spreadsheet interactions.
    • Enhanced stability with safer handling of nested data and debounced searches for smoother UX.
  • Chores

    • Removed an external dependency to reduce bundle size and potential conflicts.
    • Updated asset loading to include the new utility library.

@coderabbitai
Copy link

coderabbitai bot commented Sep 9, 2025

Walkthrough

Introduces a new global utility library FlipletDSUtils and refactors existing modules to replace Lodash/Underscore usage with this library. Updates the widget manifest to drop Lodash as a dependency and include the new utility script. No public API signatures are changed, except for adding the new global.

Changes

Cohort / File(s) Summary of changes
New utility library
js/utils.js
Adds window.FlipletDSUtils with lodash-like helpers (e.g., get, isNil, isEqual, clone/cloneDeep, debounce, sortBy/orderBy, map/filter/find, groupBy, uniq/uniqBy, pick/omitBy, assignIn/extend, etc.).
Refactor: replace Lodash with FlipletDSUtils
js/interface.js, js/history.js, js/spreadsheet.js
Replaces all Lodash/Underscore calls with FlipletDSUtils equivalents. Minor defensive checks added (e.g., array-like validations). Behaviour and control flow retained.
Manifest update
widget.json
Removes lodash from interface.dependencies. Adds js/utils.js to interface.assets.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant UI as interface.js
  participant H as history.js
  participant S as spreadsheet.js
  participant FU as FlipletDSUtils

  U->>UI: Trigger UI actions (load/sort/filter)
  UI->>FU: orderBy/get/map/filter/clone
  UI->>H: Load history data
  H->>FU: map/forEach/cloneDeep
  UI->>S: Render spreadsheet / search
  S->>FU: max/clone/isEqual/compact/isNil/debounce
  Note over FU: Provides lodash-like utilities<br/>via global window.FlipletDSUtils
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description Check ❓ Inconclusive The current pull request description is missing or provides no meaningful context about the specific changes introduced beyond what can be inferred from the diff summaries. Without any description, reviewers lack an overview of the intent and scope of the refactor. Please add a descriptive overview that summarises the migration from Lodash to FlipletDSUtils, explains the rationale for introducing the new utility library, and highlights any notable changes or impacts on public APIs.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title “remove lodash” succinctly and accurately reflects the main change in the pull request, which is the replacement and removal of Lodash utilities in favour of the newly introduced FlipletDSUtils library. It is concise, clear, and directly related to the core refactoring performed.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch remove-lodash

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
js/interface.js (5)

658-668: Incorrect equality comparison inflates updates

You compare the full entry to a picked original. Extra props (e.g. clientId) will force unnecessary updates. Compare the same picked shape.

-    if (FlipletDSUtils.isEqual(entry, original)) {
+    if (FlipletDSUtils.isEqual(
+      FlipletDSUtils.pick(entry, ['id', 'data', 'order']),
+      original
+    )) {
       return;
     }

697-702: Behaviour of some(columns) without predicate

Lodash treats _.some(array) as “any truthy”. Confirm FlipletDSUtils.some does the same. If unsure, prefer native: columns.some(Boolean).

-    if (FlipletDSUtils.some(columns)) {
+    if (columns.some(Boolean)) {

1836-1845: Logic bug: using map() discards matches; should filter or find

map returns an array of mostly undefined; caller expects an array of matched rules (or emptiness). Replace with filter.

-function findSecurityRule() {
-  var rule = currentDataSourceRules.map(function(rule) {
-    var tokens = FlipletDSUtils.get(rule, 'allow.tokens');
-
-    if (!tokens || tokens.indexOf(widgetData.tokenId) === -1) {
-      return;
-    }
-  });
-
-  return rule;
-}
+function findSecurityRule() {
+  return FlipletDSUtils.filter(currentDataSourceRules, function(rule) {
+    var tokens = FlipletDSUtils.get(rule, 'allow.tokens', []);
+    return Array.isArray(tokens) && tokens.indexOf(widgetData.tokenId) !== -1;
+  });
+}

2069-2082: forIn over allow.user is fine

Assuming FlipletDSUtils.forIn matches lodash (own + inherited). If you only need own props, prefer Object.keys(...).forEach to avoid prototype pollution surprises.


2403-2409: Another object-shorthand in find

Same concern as above; use function predicate.

-            var app = FlipletDSUtils.find(apps, {
-              id: appId
-            });
+            var app = FlipletDSUtils.find(apps, function(a) { return a.id === appId; });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7efce06 and ae0d9b8.

📒 Files selected for processing (5)
  • js/history.js (2 hunks)
  • js/interface.js (40 hunks)
  • js/spreadsheet.js (4 hunks)
  • js/utils.js (1 hunks)
  • widget.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

Review the JavaScript code against the Google JavaScript style guide and point out any mismatches. Ensure all code is fully documented. Suggest a testing plan to ensure code is adequately tested. Suggest unit tests. Avoid integration tests.

Files:

  • js/history.js
  • js/utils.js
  • js/interface.js
  • js/spreadsheet.js
🧠 Learnings (2)
📓 Common learnings
Learnt from: armine-fliplet
PR: Fliplet/fliplet-widget-data-source#245
File: js/interface.js:737-742
Timestamp: 2025-07-22T06:19:44.810Z
Learning: In the Fliplet Widget Data Source project, the team prefers to maintain consistency with existing codebase style patterns (using 'var' declarations and string concatenation) rather than strictly following external style guides like the Google JavaScript Style Guide, even when static analysis tools suggest otherwise.
📚 Learning: 2025-07-22T06:19:44.810Z
Learnt from: armine-fliplet
PR: Fliplet/fliplet-widget-data-source#245
File: js/interface.js:737-742
Timestamp: 2025-07-22T06:19:44.810Z
Learning: In the Fliplet Widget Data Source project, the team prefers to maintain consistency with existing codebase style patterns (using 'var' declarations and string concatenation) rather than strictly following external style guides like the Google JavaScript Style Guide, even when static analysis tools suggest otherwise.

Applied to files:

  • js/interface.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (13)
widget.json (1)

38-38: Load order check for FlipletDSUtils (approved) and verify Lodash removal & object-shorthand usage

  • Load order looks correct.
  • Re-run Lodash/Underscore search without the -g '!**/dist/**' filter to ensure no stray usages:
    rg -nP '\b(_\.|_\()|lodash|underscore' -tjs -n
  • FlipletDSUtils.find is called with object-shorthand predicates in js/interface.js at lines 1703, 2054 and 2403—verify the utility supports shorthand matching or refactor to explicit predicate functions.
js/history.js (1)

13-23: Switch to FlipletDSUtils.map/forEach looks fine

Parity with lodash is maintained here. Ensure FlipletDSUtils.forEach over arrays doesn’t iterate custom props (e.g. id) — expected behaviour is to iterate indices only.

If unsure, add a quick unit test asserting forEach doesn’t visit non-index properties on arrays.

js/spreadsheet.js (1)

837-847: Parity check: clone/compact/isEqual semantics must match lodash

The row-matching relies on:

  • clone producing a shallow copy of arrays (retaining non-index props like .id).
  • compact removing all falsy (including 0, '') — this may drop legitimate zeroes and change matching.
  • isEqual doing deep equality.

Please verify FlipletDSUtils mirrors lodash here; otherwise, matching could fail or mis-map IDs.

Add unit tests covering: numbers including 0, empty strings, null/undefined, arrays with extra props, and deep-equality of nested objects in cells.

Also applies to: 862-864

js/interface.js (9)

61-64: Apps sorting switch is OK

sortBy by app.name.toLowerCase() maintains previous intent.


129-141: find() predicate refactor looks correct

Using a function predicate avoids depending on object-shorthand semantics.


281-284: some() for live app detection

OK if FlipletDSUtils.some mirrors lodash. Edge case: app.productionAppId = 0 should still be truthy check; current predicate returns the value, which is fine if 0 never occurs. If it can, use Boolean(app.productionAppId).


318-324: Caching originals with pick is good

Good to restrict to id/data/order here.


411-419: Column computation via keys/uniq/concat

OK. Watch for ordering changes; uniq may reorder depending on implementation.


749-756: Robust mapping of clientIds → ids

Guarding against non-array responses is good. zipObject usage is appropriate.


1000-1116: orderBy for sorting data sources

Looks fine. Confirm order stability equals previous behaviour (stable sort) to avoid jittery UI when keys are equal.


2218-2227: Token grouping by app with get/first

Looks good. Ensure DESCRIPTION_APP_UNKNOWN stays last via the 'z' trick; OK.


1703-1704: No change needed—object shorthand is supported. FlipletDSUtils.find handles object predicates by checking each key’s value, so using { id: id } works as intended.

Likely an incorrect or invalid review comment.

js/utils.js (1)

15-31: Safe to implement bracket-notation support and safer null handling

No existing calls use non-numeric bracket syntax or pass empty/null paths, so the proposed diff can be applied as-is.

Comment on lines +76 to +77
// Use FlipletDSUtils.cloneDeep to drop the ID in each row to ensure data is loaded correctly
hot.loadData(FlipletDSUtils.cloneDeep(state.getData()));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Comment is misleading; cloneDeep does not “drop” IDs

cloneDeep won’t remove the row.id property. If IDs must be ignored by Handsontable, that’s already true because it reads numeric indices only. Please fix the comment to avoid confusion.

-// Use FlipletDSUtils.cloneDeep to drop the ID in each row to ensure data is loaded correctly
+// Deep-clone data before loading; Handsontable ignores non-index props (e.g. row.id)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Use FlipletDSUtils.cloneDeep to drop the ID in each row to ensure data is loaded correctly
hot.loadData(FlipletDSUtils.cloneDeep(state.getData()));
// Deep-clone data before loading; Handsontable ignores non-index props (e.g. row.id)
hot.loadData(FlipletDSUtils.cloneDeep(state.getData()));
🤖 Prompt for AI Agents
In js/history.js around lines 76 to 77, the inline comment incorrectly states
that FlipletDSUtils.cloneDeep "drops the ID in each row"; update the comment to
accurately describe what cloneDeep does (creates a deep copy of the state data)
and note that Handsontable ignores row.id by using numeric indices, so no ID
removal occurs. Replace the misleading wording with a concise comment such as
"Use FlipletDSUtils.cloneDeep to create a deep copy of state data before loading
into Handsontable; Handsontable uses numeric indices for rows, so row.id is
ignored."

Comment on lines +521 to 524
version.entriesCount = FlipletDSUtils.get(version, 'data.entries.count', 'Unknown');
version.hasEntries = version.entriesCount > 0;
version.columnsCount = version.data.columns && version.data.columns.length || 'Not defined';

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add focused unit tests for lodash → FlipletDSUtils migration

Minimal Jest tests to de-risk:

  • find: supports function predicate; object shorthand either supported or refactor as above.
  • orderBy/sortBy: stable ordering, case-insensitive paths used here.
  • some: behaviour with no predicate.
  • isEqual + pick: ensure unchanged entries aren’t marked updated.
  • compact: confirm 0 is removed (lodash behaviour) and OK for your dataset.
  • debounce: trailing-only call after typing.

I can scaffold these tests quickly if helpful.

Also applies to: 564-567, 578-608, 659-673, 697-702, 749-756, 1703-1704, 1836-1845, 2051-2056, 2403-2409

🤖 Prompt for AI Agents
In js/interface.js around lines 521-524 (and also review the other referenced
ranges: 564-567, 578-608, 659-673, 697-702, 749-756, 1703-1704, 1836-1845,
2051-2056, 2403-2409), add focused Jest unit tests to validate the
lodash→FlipletDSUtils migration: create tests that (1) verify find supports a
function predicate and the expected object-shorthand behavior (or refactor usage
if unsupported), (2) validate orderBy/sortBy stability and case-insensitive path
sorting used in this file, (3) test some behavior when no predicate is provided,
(4) ensure isEqual combined with pick does not mark unchanged entries as
updated, (5) confirm compact removes 0 as lodash does (or adapt code if compact
semantics differ), and (6) test debounce for trailing-only invocation after
typing; implement mocks/data fixtures that reproduce current dataset shapes and
assert identical behavior to previous lodash results, or adjust the
implementation where behavior diverges.

Comment on lines 2051 to 2056

if (selectedTokenId) {
// Add token when not found in the list
if (!_.find(integrationTokenList, { id: selectedTokenId })) {
if (!FlipletDSUtils.find(integrationTokenList, { id: selectedTokenId })) {
integrationTokenList.push({ id: selectedTokenId, fullName: 'API Token' });
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Object-shorthand in find; prefer function to avoid dependency on shorthand support

Align with earlier suggestion.

-        if (!FlipletDSUtils.find(integrationTokenList, { id: selectedTokenId })) {
+        if (!FlipletDSUtils.find(integrationTokenList, function(t) { return t.id === selectedTokenId; })) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (selectedTokenId) {
// Add token when not found in the list
if (!_.find(integrationTokenList, { id: selectedTokenId })) {
if (!FlipletDSUtils.find(integrationTokenList, { id: selectedTokenId })) {
integrationTokenList.push({ id: selectedTokenId, fullName: 'API Token' });
}
if (selectedTokenId) {
// Add token when not found in the list
if (!FlipletDSUtils.find(integrationTokenList, function(t) { return t.id === selectedTokenId; })) {
integrationTokenList.push({ id: selectedTokenId, fullName: 'API Token' });
}
🤖 Prompt for AI Agents
In js/interface.js around lines 2051 to 2056, the call to FlipletDSUtils.find
uses object-shorthand ({ id: selectedTokenId }) which relies on shorthand
support; change it to an explicit predicate function (e.g., function(item) {
return item.id === selectedTokenId; } or an arrow equivalent) so the find
invocation does not depend on object shorthand, keeping behavior identical and
matching earlier suggestions.

Comment on lines +2625 to 2626
var exclude = FlipletDSUtils.compact($('input[name="exclude"]').val().split(',').map(column => column.trim()));

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Style consistency: avoid arrow here

Team preference is var/functions and string concatenation; replace the arrow for consistency across browser targets.

-    var exclude = FlipletDSUtils.compact($('input[name="exclude"]').val().split(',').map(column => column.trim()));
+    var exclude = FlipletDSUtils.compact($('input[name="exclude"]').val().split(',').map(function(column) { return column.trim(); }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var exclude = FlipletDSUtils.compact($('input[name="exclude"]').val().split(',').map(column => column.trim()));
var exclude = FlipletDSUtils.compact($('input[name="exclude"]').val().split(',').map(function(column) { return column.trim(); }));
🤖 Prompt for AI Agents
In js/interface.js around lines 2625-2626, replace the arrow function inside the
map call for style consistency: change .map(column => column.trim()) to a
traditional function expression such as .map(function(column) { return
column.trim(); }) so it uses var/functions and avoids arrow syntax while leaving
the surrounding logic (split, trim, compact) unchanged.

Comment on lines +194 to 195
lastRow = FlipletDSUtils.max([lastRow - 1, 0]);
} else if (moveTo.right) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: lastRow can become NaN or 0 when no border was found

If the scan never finds an empty cell, lastRow is undefined. Computing max([lastRow - 1, 0]) yields NaN or 0, collapsing the selection. Set lastRow to the table bottom when not found.

-      lastRow = FlipletDSUtils.max([lastRow - 1, 0]);
+      if (typeof lastRow === 'number') {
+        lastRow = Math.max(lastRow - 1, 0);
+      } else {
+        // No empty border found; span to last row
+        lastRow = allData.length - 1;
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
lastRow = FlipletDSUtils.max([lastRow - 1, 0]);
} else if (moveTo.right) {
if (typeof lastRow === 'number') {
lastRow = Math.max(lastRow - 1, 0);
} else {
// No empty border found; span to last row
lastRow = allData.length - 1;
}
} else if (moveTo.right) {
🤖 Prompt for AI Agents
In js/spreadsheet.js around lines 194-195, the code computing lastRow uses
lastRow - 1 without handling the case where lastRow is undefined (scan found no
empty cell), causing NaN/0 and collapsing the selection; update the logic so if
lastRow is falsy/undefined after the scan you set lastRow to the table's bottom
index (e.g., last row index from the table/rows length minus one) before
applying FlipletDSUtils.max, then proceed with lastRow =
FlipletDSUtils.max([lastRow - 1, 0]) so the selection extends to the table
bottom instead of becoming NaN.

Comment on lines +327 to +341
pick: function(obj, paths) {
const result = {};
if (Array.isArray(paths)) {
paths.forEach(path => {
if (obj.hasOwnProperty(path)) {
result[path] = obj[path];
}
});
} else {
if (obj.hasOwnProperty(paths)) {
result[paths] = obj[paths];
}
}
return result;
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

pick(): null guard and robust hasOwnProperty

Calling hasOwnProperty directly on objects with null prototypes will throw. Also guard null source.

-  pick: function(obj, paths) {
-    const result = {};
+  pick: function(obj, paths) {
+    const result = {};
+    if (obj == null) return result;
     if (Array.isArray(paths)) {
       paths.forEach(path => {
-        if (obj.hasOwnProperty(path)) {
+        if (Object.prototype.hasOwnProperty.call(obj, path)) {
           result[path] = obj[path];
         }
       });
     } else {
-      if (obj.hasOwnProperty(paths)) {
+      if (Object.prototype.hasOwnProperty.call(obj, paths)) {
         result[paths] = obj[paths];
       }
     }
     return result;
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pick: function(obj, paths) {
const result = {};
if (Array.isArray(paths)) {
paths.forEach(path => {
if (obj.hasOwnProperty(path)) {
result[path] = obj[path];
}
});
} else {
if (obj.hasOwnProperty(paths)) {
result[paths] = obj[paths];
}
}
return result;
},
pick: function(obj, paths) {
const result = {};
if (obj == null) return result;
if (Array.isArray(paths)) {
paths.forEach(path => {
if (Object.prototype.hasOwnProperty.call(obj, path)) {
result[path] = obj[path];
}
});
} else {
if (Object.prototype.hasOwnProperty.call(obj, paths)) {
result[paths] = obj[paths];
}
}
return result;
},
🤖 Prompt for AI Agents
In js/utils.js around lines 327 to 341, the pick function doesn't guard against
a null/undefined source object and calls obj.hasOwnProperty directly (which
throws for null prototypes); update it to first return an empty object if obj is
null/undefined, and replace direct hasOwnProperty calls with
Object.prototype.hasOwnProperty.call(obj, key) (apply for both array and
single-path branches) so it safely handles null-prototype objects and
null/undefined sources.

Comment on lines +350 to +358
omitBy: function(obj, predicate) {
const result = {};
for (let key in obj) {
if (obj.hasOwnProperty(key) && !predicate(obj[key], key)) {
result[key] = obj[key];
}
}
return result;
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

omitBy(): null guard and robust hasOwnProperty

Mirror lodash behaviour on null input; avoid direct hasOwnProperty usage.

-  omitBy: function(obj, predicate) {
-    const result = {};
-    for (let key in obj) {
-      if (obj.hasOwnProperty(key) && !predicate(obj[key], key)) {
-        result[key] = obj[key];
-      }
-    }
-    return result;
-  },
+  omitBy: function(obj, predicate) {
+    const result = {};
+    if (obj == null) return result;
+    for (let key in obj) {
+      if (Object.prototype.hasOwnProperty.call(obj, key) && !predicate(obj[key], key)) {
+        result[key] = obj[key];
+      }
+    }
+    return result;
+  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
omitBy: function(obj, predicate) {
const result = {};
for (let key in obj) {
if (obj.hasOwnProperty(key) && !predicate(obj[key], key)) {
result[key] = obj[key];
}
}
return result;
},
omitBy: function(obj, predicate) {
const result = {};
if (obj == null) return result;
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key) && !predicate(obj[key], key)) {
result[key] = obj[key];
}
}
return result;
},
🤖 Prompt for AI Agents
In js/utils.js around lines 350 to 358, the omitBy implementation lacks a null
guard and uses obj.hasOwnProperty directly; update it to first return an empty
object if obj is null or undefined (mirror lodash behavior) and replace
obj.hasOwnProperty(key) with Object.prototype.hasOwnProperty.call(obj, key) to
avoid relying on the object's own prototype.

Comment on lines +442 to +450
mapValues: function(obj, iteratee) {
const result = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = iteratee(obj[key], key);
}
}
return result;
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

mapValues(): null guard and robust hasOwnProperty

Prevents TypeError and matches common expectations for missing objects.

-  mapValues: function(obj, iteratee) {
-    const result = {};
-    for (let key in obj) {
-      if (obj.hasOwnProperty(key)) {
-        result[key] = iteratee(obj[key], key);
-      }
-    }
-    return result;
-  },
+  mapValues: function(obj, iteratee) {
+    const result = {};
+    if (obj == null) return result;
+    for (let key in obj) {
+      if (Object.prototype.hasOwnProperty.call(obj, key)) {
+        result[key] = iteratee(obj[key], key);
+      }
+    }
+    return result;
+  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mapValues: function(obj, iteratee) {
const result = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = iteratee(obj[key], key);
}
}
return result;
},
mapValues: function(obj, iteratee) {
const result = {};
if (obj == null) return result;
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = iteratee(obj[key], key);
}
}
return result;
},
🤖 Prompt for AI Agents
In js/utils.js around lines 442 to 450, mapValues should guard against
null/undefined inputs and use a robust hasOwnProperty check; update the function
to return an empty object immediately if obj is null or undefined, and replace
obj.hasOwnProperty(key) with Object.prototype.hasOwnProperty.call(obj, key) so
it won't throw for non-plain objects or when hasOwnProperty is shadowed, then
continue assigning result[key] = iteratee(obj[key], key) for own properties
only.

Comment on lines +458 to +464
forIn: function(obj, iteratee) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
iteratee(obj[key], key);
}
}
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

forIn(): include inherited enumerable properties

lodash’s forIn iterates own and inherited enumerable properties. Dropping the hasOwnProperty guard restores parity.

-  forIn: function(obj, iteratee) {
-    for (let key in obj) {
-      if (obj.hasOwnProperty(key)) {
-        iteratee(obj[key], key);
-      }
-    }
-  },
+  forIn: function(obj, iteratee) {
+    for (let key in obj) {
+      iteratee(obj[key], key);
+    }
+  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
forIn: function(obj, iteratee) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
iteratee(obj[key], key);
}
}
},
forIn: function(obj, iteratee) {
for (let key in obj) {
iteratee(obj[key], key);
}
},
🤖 Prompt for AI Agents
In js/utils.js around lines 458 to 464, the forIn implementation currently skips
inherited enumerable properties by checking obj.hasOwnProperty(key); remove that
hasOwnProperty guard so the iteratee is invoked for both own and inherited
enumerable properties (i.e., iteratee(obj[key], key) should run for every key
encountered by for...in), restoring parity with lodash.forIn behavior.

Comment on lines +515 to +526
assignIn: function(target) {
var sources = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < sources.length; i++) {
var source = sources[i];
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
return target;
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

assignIn()/extend(): copy inherited enumerable props like lodash

Current implementation only copies own props due to hasOwnProperty. assignIn/extend in lodash include inherited enumerable properties.

   assignIn: function(target) {
     var sources = Array.prototype.slice.call(arguments, 1);
     for (var i = 0; i < sources.length; i++) {
       var source = sources[i];
-      for (var key in source) {
-        if (source.hasOwnProperty(key)) {
-          target[key] = source[key];
-        }
-      }
+      if (source == null) continue;
+      for (var key in source) {
+        target[key] = source[key];
+      }
     }
     return target;
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assignIn: function(target) {
var sources = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < sources.length; i++) {
var source = sources[i];
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
return target;
},
assignIn: function(target) {
var sources = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < sources.length; i++) {
var source = sources[i];
if (source == null) continue;
for (var key in source) {
- if (source.hasOwnProperty(key)) {
- target[key] = source[key];
target[key] = source[key];
}
}
return target;
},
🤖 Prompt for AI Agents
In js/utils.js around lines 515 to 526, assignIn currently uses
source.hasOwnProperty(key) which prevents copying inherited enumerable
properties; change the logic to copy all enumerable properties (own and
inherited) by removing the hasOwnProperty check, and also guard against
null/undefined sources (skip if a source is null/undefined) so you iterate
sources with for..in and assign target[key] = source[key] for each enumerable
key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant