Kaynağa Gözat

Merge pull request #16346 from overleaf/msm-remove-underscore

Remove `underscore` in monorepo

GitOrigin-RevId: fd3b92e3e8a940a7a086669232102320c34a89e2
Miguel Serrano 2 yıl önce
ebeveyn
işleme
51de84664d
43 değiştirilmiş dosya ile 77 ekleme ve 98 silme
  1. 8 24
      package-lock.json
  2. 1 1
      services/real-time/app/js/DocumentUpdaterManager.js
  3. 1 2
      services/real-time/package.json
  4. 8 8
      services/real-time/test/unit/js/DocumentUpdaterManagerTests.js
  5. 1 1
      services/web/app/src/Features/Compile/ClsiManager.js
  6. 3 3
      services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js
  7. 1 1
      services/web/app/src/Features/FileStore/FileHashManager.js
  8. 1 1
      services/web/app/src/Features/FileStore/FileStoreHandler.js
  9. 1 1
      services/web/app/src/Features/InactiveData/InactiveProjectManager.js
  10. 1 1
      services/web/app/src/Features/LinkedFiles/LinkedFilesController.js
  11. 1 1
      services/web/app/src/Features/LinkedFiles/LinkedFilesHandler.js
  12. 1 1
      services/web/app/src/Features/LinkedFiles/ProjectFileAgent.js
  13. 1 1
      services/web/app/src/Features/LinkedFiles/ProjectOutputFileAgent.js
  14. 1 1
      services/web/app/src/Features/Notifications/NotificationsController.js
  15. 1 1
      services/web/app/src/Features/Project/DocLinesComparitor.js
  16. 1 1
      services/web/app/src/Features/Project/ProjectCreationHandler.js
  17. 4 4
      services/web/app/src/Features/Project/ProjectDetailsHandler.js
  18. 1 1
      services/web/app/src/Features/Project/ProjectEditorHandler.js
  19. 1 3
      services/web/app/src/Features/Project/ProjectEntityMongoUpdateHandler.js
  20. 2 2
      services/web/app/src/Features/Project/ProjectLocator.js
  21. 1 1
      services/web/app/src/Features/Project/ProjectRootDocManager.js
  22. 2 2
      services/web/app/src/Features/Referal/ReferalFeatures.js
  23. 7 7
      services/web/app/src/Features/References/ReferencesHandler.js
  24. 1 1
      services/web/app/src/Features/ServerAdmin/AdminController.js
  25. 1 1
      services/web/app/src/Features/StaticPages/StaticPageHelpers.js
  26. 2 2
      services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js
  27. 1 1
      services/web/app/src/Features/Templates/TemplatesManager.js
  28. 1 1
      services/web/app/src/Features/ThirdPartyDataStore/UpdateMerger.js
  29. 1 1
      services/web/app/src/Features/Uploads/ArchiveManager.js
  30. 2 2
      services/web/app/src/Features/User/UserSessionsManager.js
  31. 1 1
      services/web/app/src/infrastructure/FileWriter.js
  32. 2 2
      services/web/app/src/infrastructure/GeoIpLookup.js
  33. 3 3
      services/web/app/src/models/Project.js
  34. 1 1
      services/web/app/src/router.js
  35. 1 1
      services/web/modules/history-v1/test/acceptance/src/LabelsTests.js
  36. 5 5
      services/web/modules/history-v1/test/acceptance/src/ProjectStructureTests.js
  37. 1 1
      services/web/modules/history-v1/test/acceptance/src/RestoringFilesTest.js
  38. 1 1
      services/web/modules/launchpad/app/src/LaunchpadController.js
  39. 0 1
      services/web/package.json
  40. 1 1
      services/web/test/acceptance/src/LinkedFilesTests.js
  41. 1 1
      services/web/test/acceptance/src/ProjectDuplicateNameTests.js
  42. 0 1
      services/web/test/unit/bootstrap.js
  43. 1 1
      services/web/test/unit/src/Notifications/NotificationsHandlerTests.js

+ 8 - 24
package-lock.json

@@ -45918,8 +45918,7 @@
         "proxy-addr": "^2.0.7",
         "request": "^2.88.2",
         "socket.io": "github:overleaf/socket.io#0.9.19-overleaf-10",
-        "socket.io-client": "github:overleaf/socket.io-client#0.9.17-overleaf-5",
-        "underscore": "1.13.1"
+        "socket.io-client": "github:overleaf/socket.io-client#0.9.17-overleaf-5"
       },
       "devDependencies": {
         "chai": "^4.3.6",
@@ -45957,11 +45956,6 @@
       "integrity": "sha1-kNt58X2Ni1NiFUOJSSuXJ2LP0nY=",
       "dev": true
     },
-    "services/real-time/node_modules/underscore": {
-      "version": "1.13.1",
-      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
-      "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
-    },
     "services/references": {
       "name": "@overleaf/references",
       "dependencies": {
@@ -45976,8 +45970,7 @@
         "express": "^4.18.2",
         "ioredis": "^4.16.1",
         "lodash": "^4.17.19",
-        "request": "^2.88.2",
-        "underscore": "^1.10.2"
+        "request": "^2.88.2"
       },
       "devDependencies": {
         "bson": "^1.1.5",
@@ -46029,11 +46022,11 @@
         "bunyan": "^1.8.15",
         "express": "^4.18.2",
         "install": "^0.13.0",
+        "lodash": "^4.17.21",
         "marked": "^4.1.0",
         "method-override": "^3.0.0",
         "mongoose": "^5.13.20",
-        "request": "^2.88.2",
-        "underscore": "^1.13.6"
+        "request": "^2.88.2"
       },
       "devDependencies": {
         "chai": "^4.3.6",
@@ -46466,7 +46459,6 @@
         "sanitize-html": "^2.8.1",
         "tough-cookie": "^4.0.0",
         "tsscmp": "^1.0.6",
-        "underscore": "^1.13.1",
         "utf-8-validate": "^5.0.2",
         "uuid": "^3.0.1",
         "valid-data-url": "^2.0.0",
@@ -54814,8 +54806,7 @@
         "socket.io": "github:overleaf/socket.io#0.9.19-overleaf-10",
         "socket.io-client": "github:overleaf/socket.io-client#0.9.17-overleaf-5",
         "timekeeper": "0.0.4",
-        "uid-safe": "^2.1.5",
-        "underscore": "1.13.1"
+        "uid-safe": "^2.1.5"
       },
       "dependencies": {
         "sandboxed-module": {
@@ -54839,11 +54830,6 @@
           "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz",
           "integrity": "sha1-kNt58X2Ni1NiFUOJSSuXJ2LP0nY=",
           "dev": true
-        },
-        "underscore": {
-          "version": "1.13.1",
-          "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
-          "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
         }
       }
     },
@@ -54880,8 +54866,7 @@
         "mocha": "^10.2.0",
         "request": "^2.88.2",
         "sandboxed-module": "^2.0.4",
-        "sinon": "^9.2.4",
-        "underscore": "^1.10.2"
+        "sinon": "^9.2.4"
       }
     },
     "@overleaf/settings": {
@@ -54937,14 +54922,14 @@
         "chai-as-promised": "^7.1.1",
         "express": "^4.18.2",
         "install": "^0.13.0",
+        "lodash": "^4.17.21",
         "marked": "^4.1.0",
         "method-override": "^3.0.0",
         "mocha": "^10.2.0",
         "mongoose": "^5.13.20",
         "request": "^2.88.2",
         "sandboxed-module": "^2.0.4",
-        "sinon": "^9.2.4",
-        "underscore": "^1.13.6"
+        "sinon": "^9.2.4"
       }
     },
     "@overleaf/third-party-datastore": {
@@ -55431,7 +55416,6 @@
         "tough-cookie": "^4.0.0",
         "tsscmp": "^1.0.6",
         "typescript": "^5.0.4",
-        "underscore": "^1.13.1",
         "utf-8-validate": "^5.0.2",
         "uuid": "^3.0.1",
         "valid-data-url": "^2.0.0",

+ 1 - 1
services/real-time/app/js/DocumentUpdaterManager.js

@@ -1,5 +1,5 @@
 const request = require('request')
-const _ = require('underscore')
+const _ = require('lodash')
 const OError = require('@overleaf/o-error')
 const logger = require('@overleaf/logger')
 const settings = require('@overleaf/settings')

+ 1 - 2
services/real-time/package.json

@@ -33,8 +33,7 @@
     "proxy-addr": "^2.0.7",
     "request": "^2.88.2",
     "socket.io": "github:overleaf/socket.io#0.9.19-overleaf-10",
-    "socket.io-client": "github:overleaf/socket.io-client#0.9.17-overleaf-5",
-    "underscore": "1.13.1"
+    "socket.io-client": "github:overleaf/socket.io-client#0.9.17-overleaf-5"
   },
   "devDependencies": {
     "chai": "^4.3.6",

+ 8 - 8
services/real-time/test/unit/js/DocumentUpdaterManagerTests.js

@@ -13,7 +13,7 @@ const sinon = require('sinon')
 const SandboxedModule = require('sandboxed-module')
 const path = require('path')
 const modulePath = '../../../app/js/DocumentUpdaterManager'
-const _ = require('underscore')
+const _ = require('lodash')
 
 describe('DocumentUpdaterManager', function () {
   beforeEach(function () {
@@ -400,24 +400,24 @@ describe('DocumentUpdaterManager', function () {
         10000,
         this.DocumentUpdaterManager._getPendingUpdateListKey
       )
-      this.keys = _.unique(keys)
+      this.keys = _.uniq(keys)
     })
     it('should return normal pending updates key', function () {
-      _.contains(this.keys, 'pending-updates-list').should.equal(true)
+      _.includes(this.keys, 'pending-updates-list').should.equal(true)
     })
 
     it('should return pending-updates-list-n keys', function () {
-      _.contains(this.keys, 'pending-updates-list-1').should.equal(true)
-      _.contains(this.keys, 'pending-updates-list-3').should.equal(true)
-      _.contains(this.keys, 'pending-updates-list-9').should.equal(true)
+      _.includes(this.keys, 'pending-updates-list-1').should.equal(true)
+      _.includes(this.keys, 'pending-updates-list-3').should.equal(true)
+      _.includes(this.keys, 'pending-updates-list-9').should.equal(true)
     })
 
     it('should not include pending-updates-list-0 key', function () {
-      _.contains(this.keys, 'pending-updates-list-0').should.equal(false)
+      _.includes(this.keys, 'pending-updates-list-0').should.equal(false)
     })
 
     it('should not include maximum as pendingUpdateListShardCount value', function () {
-      _.contains(this.keys, 'pending-updates-list-10').should.equal(false)
+      _.includes(this.keys, 'pending-updates-list-10').should.equal(false)
     })
   })
 })

+ 1 - 1
services/web/app/src/Features/Compile/ClsiManager.js

@@ -19,7 +19,7 @@ const NewBackendCloudClsiCookieManager = require('./ClsiCookieManager')(
   Settings.apis.clsi_new?.backendGroupName
 )
 const ClsiStateManager = require('./ClsiStateManager')
-const _ = require('underscore')
+const _ = require('lodash')
 const ClsiFormatChecker = require('./ClsiFormatChecker')
 const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
 const Metrics = require('@overleaf/metrics')

+ 3 - 3
services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js

@@ -1,7 +1,7 @@
 const request = require('request').defaults({ timeout: 30 * 1000 })
 const OError = require('@overleaf/o-error')
 const settings = require('@overleaf/settings')
-const _ = require('underscore')
+const _ = require('lodash')
 const async = require('async')
 const logger = require('@overleaf/logger')
 const metrics = require('@overleaf/metrics')
@@ -348,10 +348,10 @@ function _getUpdates(entityType, oldEntities, newEntities) {
   const adds = []
   const renames = []
 
-  const oldEntitiesHash = _.indexBy(oldEntities, entity =>
+  const oldEntitiesHash = _.keyBy(oldEntities, entity =>
     entity[entityType]._id.toString()
   )
-  const newEntitiesHash = _.indexBy(newEntities, entity =>
+  const newEntitiesHash = _.keyBy(newEntities, entity =>
     entity[entityType]._id.toString()
   )
 

+ 1 - 1
services/web/app/src/Features/FileStore/FileHashManager.js

@@ -15,7 +15,7 @@ let FileHashManager
 const crypto = require('crypto')
 const logger = require('@overleaf/logger')
 const fs = require('fs')
-const _ = require('underscore')
+const _ = require('lodash')
 
 module.exports = FileHashManager = {
   computeHash(filePath, callback) {

+ 1 - 1
services/web/app/src/Features/FileStore/FileStoreHandler.js

@@ -1,4 +1,4 @@
-const _ = require('underscore')
+const _ = require('lodash')
 const logger = require('@overleaf/logger')
 const fs = require('fs')
 const request = require('request')

+ 1 - 1
services/web/app/src/Features/InactiveData/InactiveProjectManager.js

@@ -11,7 +11,7 @@
 let InactiveProjectManager
 const OError = require('@overleaf/o-error')
 const async = require('async')
-const _ = require('underscore')
+const _ = require('lodash')
 const logger = require('@overleaf/logger')
 const DocstoreManager = require('../Docstore/DocstoreManager')
 const ProjectGetter = require('../Project/ProjectGetter')

+ 1 - 1
services/web/app/src/Features/LinkedFiles/LinkedFilesController.js

@@ -17,7 +17,7 @@ const EditorController = require('../Editor/EditorController')
 const ProjectLocator = require('../Project/ProjectLocator')
 const Settings = require('@overleaf/settings')
 const logger = require('@overleaf/logger')
-const _ = require('underscore')
+const _ = require('lodash')
 const LinkedFilesHandler = require('./LinkedFilesHandler')
 const {
   CompileFailedError,

+ 1 - 1
services/web/app/src/Features/LinkedFiles/LinkedFilesHandler.js

@@ -15,7 +15,7 @@ const EditorController = require('../Editor/EditorController')
 const ProjectLocator = require('../Project/ProjectLocator')
 const { Project } = require('../../models/Project')
 const ProjectGetter = require('../Project/ProjectGetter')
-const _ = require('underscore')
+const _ = require('lodash')
 const {
   ProjectNotFoundError,
   V1ProjectNotFoundError,

+ 1 - 1
services/web/app/src/Features/LinkedFiles/ProjectFileAgent.js

@@ -18,7 +18,7 @@ const ProjectGetter = require('../Project/ProjectGetter')
 const DocstoreManager = require('../Docstore/DocstoreManager')
 const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
 const FileStoreHandler = require('../FileStore/FileStoreHandler')
-const _ = require('underscore')
+const _ = require('lodash')
 const Settings = require('@overleaf/settings')
 const LinkedFilesHandler = require('./LinkedFilesHandler')
 const {

+ 1 - 1
services/web/app/src/Features/LinkedFiles/ProjectOutputFileAgent.js

@@ -2,7 +2,7 @@ const AuthorizationManager = require('../Authorization/AuthorizationManager')
 const CompileManager = require('../Compile/CompileManager')
 const ClsiManager = require('../Compile/ClsiManager')
 const ProjectFileAgent = require('./ProjectFileAgent')
-const _ = require('underscore')
+const _ = require('lodash')
 const {
   CompileFailedError,
   BadDataError,

+ 1 - 1
services/web/app/src/Features/Notifications/NotificationsController.js

@@ -1,6 +1,6 @@
 const NotificationsHandler = require('./NotificationsHandler')
 const SessionManager = require('../Authentication/SessionManager')
-const _ = require('underscore')
+const _ = require('lodash')
 
 module.exports = {
   getAllUnreadNotifications(req, res, next) {

+ 1 - 1
services/web/app/src/Features/Project/DocLinesComparitor.js

@@ -1,6 +1,6 @@
 // TODO: This file was created by bulk-decaffeinate.
 // Sanity-check the conversion and remove this comment.
-const _ = require('underscore')
+const _ = require('lodash')
 
 module.exports = {
   areSame(lines1, lines2) {

+ 1 - 1
services/web/app/src/Features/Project/ProjectCreationHandler.js

@@ -13,7 +13,7 @@ const { User } = require('../../models/User')
 const fs = require('fs')
 const path = require('path')
 const { callbackify } = require('util')
-const _ = require('underscore')
+const _ = require('lodash')
 const AnalyticsManager = require('../Analytics/AnalyticsManager')
 const TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')
 

+ 4 - 4
services/web/app/src/Features/Project/ProjectDetailsHandler.js

@@ -1,4 +1,4 @@
-const _ = require('underscore')
+const _ = require('lodash')
 const ProjectGetter = require('./ProjectGetter')
 const UserGetter = require('../User/UserGetter')
 const { Project } = require('../../models/Project')
@@ -154,8 +154,8 @@ async function generateUniqueName(userId, name, suffixes = []) {
     await ProjectGetter.promises.findAllUsersProjects(userId, { name: 1 })
   // allUsersProjectNames is returned as a hash {owned: [name1, name2, ...], readOnly: [....]}
   // collect all of the names and flatten them into a single array
-  const projectNameList = _.pluck(
-    _.flatten(_.values(allUsersProjectNames)),
+  const projectNameList = _.map(
+    _.flattenDeep(_.values(allUsersProjectNames)),
     'name'
   )
   const uniqueName = await ProjectHelper.promises.ensureNameIsUnique(
@@ -194,7 +194,7 @@ async function setPublicAccessLevel(projectId, newAccessLevel) {
   if (
     projectId != null &&
     newAccessLevel != null &&
-    _.include(
+    _.includes(
       [PublicAccessLevels.PRIVATE, PublicAccessLevels.TOKEN_BASED],
       newAccessLevel
     )

+ 1 - 1
services/web/app/src/Features/Project/ProjectEditorHandler.js

@@ -1,5 +1,5 @@
 let ProjectEditorHandler
-const _ = require('underscore')
+const _ = require('lodash')
 const Path = require('path')
 
 function mergeDeletedDocs(a, b) {

+ 1 - 3
services/web/app/src/Features/Project/ProjectEntityMongoUpdateHandler.js

@@ -1,6 +1,5 @@
 const { callbackify } = require('util')
 const { callbackifyMultiResult } = require('@overleaf/promise-utils')
-const _ = require('underscore')
 const logger = require('@overleaf/logger')
 const path = require('path')
 const { ObjectId } = require('mongodb')
@@ -264,8 +263,7 @@ async function replaceFileWithDoc(projectId, fileId, newDoc) {
 async function mkdirp(projectId, path, options = {}) {
   // defaults to case insensitive paths, use options {exactCaseMatch:true}
   // to make matching case-sensitive
-  let folders = path.split('/')
-  folders = _.select(folders, folder => folder.length !== 0)
+  const folders = path.split('/').filter(folder => folder.length !== 0)
 
   const project = await ProjectGetter.promises.getProjectWithOnlyFolders(
     projectId

+ 2 - 2
services/web/app/src/Features/Project/ProjectLocator.js

@@ -1,4 +1,4 @@
-const _ = require('underscore')
+const _ = require('lodash')
 const logger = require('@overleaf/logger')
 const OError = require('@overleaf/o-error')
 const async = require('async')
@@ -43,7 +43,7 @@ function findElement(options, _callback) {
       searchFolder.folders != null &&
       searchFolder.folders.length !== 0
     ) {
-      _.each(searchFolder.folders, (folder, index) => {
+      _.forEach(searchFolder.folders, (folder, index) => {
         if (folder == null) {
           return
         }

+ 1 - 1
services/web/app/src/Features/Project/ProjectRootDocManager.js

@@ -22,7 +22,7 @@ const fs = require('fs')
 const { promisify } = require('util')
 const async = require('async')
 const globby = require('globby')
-const _ = require('underscore')
+const _ = require('lodash')
 const { promisifyAll } = require('@overleaf/promise-utils')
 
 module.exports = ProjectRootDocManager = {

+ 2 - 2
services/web/app/src/Features/Referal/ReferalFeatures.js

@@ -1,4 +1,4 @@
-const _ = require('underscore')
+const _ = require('lodash')
 const { promisify } = require('util')
 const { User } = require('../../models/User')
 const Settings = require('@overleaf/settings')
@@ -38,7 +38,7 @@ module.exports = ReferalFeatures = {
 
   _getBonusLevel(user) {
     let highestBonusLevel = 0
-    _.each(_.keys(Settings.bonus_features), function (level) {
+    _.forEach(_.keys(Settings.bonus_features), function (level) {
       const levelIsLessThanUser = level <= user.refered_user_count
       const levelIsMoreThanCurrentHighest = level >= highestBonusLevel
       if (levelIsLessThanUser && levelIsMoreThanCurrentHighest) {

+ 7 - 7
services/web/app/src/Features/References/ReferencesHandler.js

@@ -21,7 +21,7 @@ const Features = require('../../infrastructure/Features')
 const ProjectGetter = require('../Project/ProjectGetter')
 const UserGetter = require('../User/UserGetter')
 const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
-const _ = require('underscore')
+const _ = require('lodash')
 const Async = require('async')
 const Errors = require('../Errors/Errors')
 
@@ -41,7 +41,7 @@ module.exports = ReferencesHandler = {
   _findBibFileIds(project) {
     const ids = []
     function _process(folder) {
-      _.each(folder.fileRefs || [], function (file) {
+      _.forEach(folder.fileRefs || [], function (file) {
         if (
           __guard__(file != null ? file.name : undefined, x1 =>
             x1.match(/^.*\.bib$/)
@@ -50,16 +50,16 @@ module.exports = ReferencesHandler = {
           return ids.push(file._id)
         }
       })
-      return _.each(folder.folders || [], folder => _process(folder))
+      return _.forEach(folder.folders || [], folder => _process(folder))
     }
-    _.each(project.rootFolder || [], rootFolder => _process(rootFolder))
+    _.forEach(project.rootFolder || [], rootFolder => _process(rootFolder))
     return ids
   },
 
   _findBibDocIds(project) {
     const ids = []
     function _process(folder) {
-      _.each(folder.docs || [], function (doc) {
+      _.forEach(folder.docs || [], function (doc) {
         if (
           __guard__(doc != null ? doc.name : undefined, x1 =>
             x1.match(/^.*\.bib$/)
@@ -68,9 +68,9 @@ module.exports = ReferencesHandler = {
           return ids.push(doc._id)
         }
       })
-      return _.each(folder.folders || [], folder => _process(folder))
+      return _.forEach(folder.folders || [], folder => _process(folder))
     }
-    _.each(project.rootFolder || [], rootFolder => _process(rootFolder))
+    _.forEach(project.rootFolder || [], rootFolder => _process(rootFolder))
     return ids
   },
 

+ 1 - 1
services/web/app/src/Features/ServerAdmin/AdminController.js

@@ -16,7 +16,7 @@
 
 const metrics = require('@overleaf/metrics')
 const logger = require('@overleaf/logger')
-const _ = require('underscore')
+const _ = require('lodash')
 const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
 const Settings = require('@overleaf/settings')
 const TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')

+ 1 - 1
services/web/app/src/Features/StaticPages/StaticPageHelpers.js

@@ -18,7 +18,7 @@ const extensionsToProxy = [
   '.gif',
   '.jpg',
 ]
-const _ = require('underscore')
+const _ = require('lodash')
 
 module.exports = {
   shouldProxy(url) {

+ 2 - 2
services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js

@@ -8,7 +8,7 @@ const InstitutionsGetter = require('../Institutions/InstitutionsGetter')
 const InstitutionsManager = require('../Institutions/InstitutionsManager')
 const PublishersGetter = require('../Publishers/PublishersGetter')
 const sanitizeHtml = require('sanitize-html')
-const _ = require('underscore')
+const _ = require('lodash')
 const async = require('async')
 const SubscriptionHelper = require('./SubscriptionHelper')
 const { callbackify } = require('@overleaf/promise-utils')
@@ -424,7 +424,7 @@ function buildPlansList(currentPlan) {
   const result = { allPlans }
 
   if (currentPlan) {
-    result.planCodesChangingAtTermEnd = _.pluck(
+    result.planCodesChangingAtTermEnd = _.map(
       _.filter(plans, plan => {
         if (!plan.hideFromUsers) {
           return SubscriptionHelper.shouldPlanChangeAtTermEnd(currentPlan, plan)

+ 1 - 1
services/web/app/src/Features/Templates/TemplatesManager.js

@@ -13,7 +13,7 @@ const request = require('request')
 const settings = require('@overleaf/settings')
 const crypto = require('crypto')
 const Errors = require('../Errors/Errors')
-const _ = require('underscore')
+const _ = require('lodash')
 
 const TemplatesManager = {
   createProjectFromV1Template(

+ 1 - 1
services/web/app/src/Features/ThirdPartyDataStore/UpdateMerger.js

@@ -1,5 +1,5 @@
 const { callbackify } = require('util')
-const _ = require('underscore')
+const _ = require('lodash')
 const fsPromises = require('fs/promises')
 const fs = require('fs')
 const logger = require('@overleaf/logger')

+ 1 - 1
services/web/app/src/Features/Uploads/ArchiveManager.js

@@ -25,7 +25,7 @@ const {
   EmptyZipFileError,
   ZipContentsTooLargeError,
 } = require('./ArchiveErrors')
-const _ = require('underscore')
+const _ = require('lodash')
 const { promisifyAll } = require('@overleaf/promise-utils')
 
 const ONE_MEG = 1024 * 1024

+ 2 - 2
services/web/app/src/Features/User/UserSessionsManager.js

@@ -2,7 +2,7 @@ const OError = require('@overleaf/o-error')
 const Settings = require('@overleaf/settings')
 const logger = require('@overleaf/logger')
 const Async = require('async')
-const _ = require('underscore')
+const _ = require('lodash')
 const { promisify } = require('util')
 const UserSessionsRedis = require('./UserSessionsRedis')
 const rclient = UserSessionsRedis.client()
@@ -86,7 +86,7 @@ const UserSessionsManager = {
         })
         return callback(err)
       }
-      sessionKeys = _.filter(sessionKeys, k => !_.contains(exclude, k))
+      sessionKeys = _.filter(sessionKeys, k => !_.includes(exclude, k))
       if (sessionKeys.length === 0) {
         logger.debug({ userId: user._id }, 'no other sessions found, returning')
         return callback(null, [])

+ 1 - 1
services/web/app/src/infrastructure/FileWriter.js

@@ -14,7 +14,7 @@ const fs = require('fs')
 const OError = require('@overleaf/o-error')
 const logger = require('@overleaf/logger')
 const crypto = require('crypto')
-const _ = require('underscore')
+const _ = require('lodash')
 const Settings = require('@overleaf/settings')
 const request = require('request')
 const { Transform, pipeline } = require('stream')

+ 2 - 2
services/web/app/src/infrastructure/GeoIpLookup.js

@@ -1,6 +1,6 @@
 const request = require('request')
 const settings = require('@overleaf/settings')
-const _ = require('underscore')
+const _ = require('lodash')
 const logger = require('@overleaf/logger')
 const { URL } = require('url')
 const { promisify, promisifyMultiResult } = require('@overleaf/promise-utils')
@@ -65,7 +65,7 @@ const EuroCountries = [
   'ES',
 ]
 
-_.each(EuroCountries, country => (currencyMappings[country] = 'EUR'))
+_.forEach(EuroCountries, country => (currencyMappings[country] = 'EUR'))
 
 function isValidCurrencyParam(currency) {
   if (!currency) {

+ 3 - 3
services/web/app/src/models/Project.js

@@ -1,5 +1,5 @@
 const mongoose = require('../infrastructure/Mongoose')
-const _ = require('underscore')
+const _ = require('lodash')
 const { FolderSchema } = require('./Folder')
 const Errors = require('../Features/Errors/Errors')
 
@@ -129,8 +129,8 @@ ProjectSchema.statics.getProject = function (projectOrId, fields, callback) {
 }
 
 function applyToAllFilesRecursivly(folder, fun) {
-  _.each(folder.fileRefs, file => fun(file))
-  _.each(folder.folders, folder => applyToAllFilesRecursivly(folder, fun))
+  _.forEach(folder.fileRefs, file => fun(file))
+  _.forEach(folder.folders, folder => applyToAllFilesRecursivly(folder, fun))
 }
 ProjectSchema.statics.applyToAllFilesRecursivly = applyToAllFilesRecursivly
 

+ 1 - 1
services/web/app/src/router.js

@@ -66,7 +66,7 @@ const {
 } = require('./infrastructure/UnsupportedBrowserMiddleware')
 
 const logger = require('@overleaf/logger')
-const _ = require('underscore')
+const _ = require('lodash')
 const { plainTextResponse } = require('./infrastructure/Response')
 const PublicAccessLevels = require('./Features/Authorization/PublicAccessLevels')
 

+ 1 - 1
services/web/modules/history-v1/test/acceptance/src/LabelsTests.js

@@ -10,7 +10,7 @@
  * DS207: Consider shorter variations of null checks
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
-const _ = require('underscore')
+const _ = require('lodash')
 const { expect } = require('chai')
 const { ObjectId } = require('mongodb')
 

+ 5 - 5
services/web/modules/history-v1/test/acceptance/src/ProjectStructureTests.js

@@ -3,7 +3,7 @@ const { ObjectId } = require('mongodb')
 const Path = require('path')
 const fs = require('fs')
 const Settings = require('@overleaf/settings')
-const _ = require('underscore')
+const _ = require('lodash')
 
 const ProjectGetter = require('../../../../../app/src/Features/Project/ProjectGetter')
 
@@ -201,8 +201,8 @@ describe('ProjectStructureChanges', function () {
         expect(update.userId).to.equal(owner._id)
         expect(update.docLines).to.be.a('string')
       }
-      expect(_.where(updates, { pathname: '/main.tex' }).length).to.equal(1)
-      expect(_.where(updates, { pathname: '/sample.bib' }).length).to.equal(1)
+      expect(_.filter(updates, { pathname: '/main.tex' }).length).to.equal(1)
+      expect(_.filter(updates, { pathname: '/sample.bib' }).length).to.equal(1)
       expect(updates[2].type).to.equal('add-file')
       expect(updates[2].userId).to.equal(owner._id)
       expect(updates[2].pathname).to.equal('/frog.jpg')
@@ -249,8 +249,8 @@ describe('ProjectStructureChanges', function () {
         expect(update.userId).to.equal(owner._id)
         expect(update.docLines).to.be.a('string')
       }
-      expect(_.where(updates, { pathname: '/main.tex' }).length).to.equal(1)
-      expect(_.where(updates, { pathname: '/sample.bib' }).length).to.equal(1)
+      expect(_.filter(updates, { pathname: '/main.tex' }).length).to.equal(1)
+      expect(_.filter(updates, { pathname: '/sample.bib' }).length).to.equal(1)
       expect(updates[2].type).to.equal('add-file')
       expect(updates[2].userId).to.equal(owner._id)
       expect(updates[2].pathname).to.equal('/frog.jpg')

+ 1 - 1
services/web/modules/history-v1/test/acceptance/src/RestoringFilesTest.js

@@ -11,7 +11,7 @@
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
 const { expect } = require('chai')
-const _ = require('underscore')
+const _ = require('lodash')
 const fs = require('fs')
 const Path = require('path')
 

+ 1 - 1
services/web/modules/launchpad/app/src/LaunchpadController.js

@@ -19,7 +19,7 @@ const logger = require('@overleaf/logger')
 const metrics = require('@overleaf/metrics')
 const UserRegistrationHandler = require('../../../../app/src/Features/User/UserRegistrationHandler')
 const EmailHandler = require('../../../../app/src/Features/Email/EmailHandler')
-const _ = require('underscore')
+const _ = require('lodash')
 const UserGetter = require('../../../../app/src/Features/User/UserGetter')
 const { User } = require('../../../../app/src/models/User')
 const AuthenticationManager = require('../../../../app/src/Features/Authentication/AuthenticationManager')

+ 0 - 1
services/web/package.json

@@ -158,7 +158,6 @@
     "sanitize-html": "^2.8.1",
     "tough-cookie": "^4.0.0",
     "tsscmp": "^1.0.6",
-    "underscore": "^1.13.1",
     "utf-8-validate": "^5.0.2",
     "uuid": "^3.0.1",
     "valid-data-url": "^2.0.0",

+ 1 - 1
services/web/test/acceptance/src/LinkedFilesTests.js

@@ -1,5 +1,5 @@
 const { expect } = require('chai')
-const _ = require('underscore')
+const _ = require('lodash')
 const fs = require('fs')
 
 const Settings = require('@overleaf/settings')

+ 1 - 1
services/web/test/acceptance/src/ProjectDuplicateNameTests.js

@@ -2,7 +2,7 @@ const { expect } = require('chai')
 const sinon = require('sinon')
 const Path = require('path')
 const fs = require('fs')
-const _ = require('underscore')
+const _ = require('lodash')
 const User = require('./helpers/User')
 const UserHelper = require('./helpers/UserHelper')
 

+ 0 - 1
services/web/test/unit/bootstrap.js

@@ -77,7 +77,6 @@ function getSandboxedModuleRequires() {
     '@overleaf/o-error',
     'sanitize-html',
     'sshpk',
-    'underscore',
     'xml2js',
   ]
   for (const modulePath of internalModules) {

+ 1 - 1
services/web/test/unit/src/Notifications/NotificationsHandlerTests.js

@@ -18,7 +18,7 @@ const modulePath = require('path').join(
   __dirname,
   '../../../../app/src/Features/Notifications/NotificationsHandler.js'
 )
-const _ = require('underscore')
+const _ = require('lodash')
 
 describe('NotificationsHandler', function () {
   const userId = '123nd3ijdks'