feat: remove lodash dependency
continuous-integration/drone/pr Build is passing Details

Lodash is quite big and all helpers got imported even thought not all needed. With new JS syntax it's easy to replace most helpers.
This commit is contained in:
Dominik Pschenitschni 2021-09-12 18:45:05 +02:00
parent 49fcce043d
commit d9f2d879d7
Signed by: dpschen
GPG Key ID: B257AC0149F43A77
20 changed files with 116 additions and 89 deletions

View File

@ -353,7 +353,7 @@ describe('Task', () => {
.first()
.click()
cy.get('.global-notification')
cy.get('.global-notification', { timeout: 4000 })
.should('contain', 'Success')
cy.get('.task-view .details.labels-list .multiselect .input-wrapper span.tag')
.should('exist')

View File

@ -1,5 +1,4 @@
import {seed} from './seed'
import merge from 'lodash/merge'
/**
* A factory makes it easy to seed the database with data.
@ -25,7 +24,10 @@ export class Factory {
const data = []
for (let i = 1; i <= count; i++) {
const entry = merge(this.factory(), override)
const entry = {
...this.factory(),
...override,
}
for (const e in entry) {
if(typeof entry[e] === 'function') {
entry[e] = entry[e](i)

View File

@ -22,7 +22,6 @@
"dompurify": "2.3.3",
"highlight.js": "11.2.0",
"is-touch-device": "1.0.1",
"lodash": "4.17.21",
"marked": "3.0.4",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",

View File

@ -178,9 +178,8 @@ import Fancycheckbox from '../../input/fancycheckbox'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import {includesById} from '@/helpers/utils'
import {formatISO} from 'date-fns'
import differenceWith from 'lodash/differenceWith'
import PrioritySelect from '@/components/tasks/partials/prioritySelect.vue'
import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect.vue'
import Multiselect from '@/components/input/multiselect.vue'
@ -269,13 +268,7 @@ export default {
},
computed: {
foundLabels() {
const labels = (Object.values(this.$store.state.labels.labels).filter(l => {
return l.title.toLowerCase().includes(this.labelQuery.toLowerCase())
}) ?? [])
return differenceWith(labels, this.labels, (first, second) => {
return first.id === second.id
})
return this.$store.getters['labels/filterLabelsByQuery'](this.labels, this.query)
},
flatPickerConfig() {
return {
@ -309,8 +302,13 @@ export default {
this.prepareRelatedObjectFilter('namespace')
this.prepareSingleValue('labels')
const labelIds = (typeof this.filters.labels === 'string' ? this.filters.labels : '').split(',').map(i => parseInt(i))
this.labels = (Object.values(this.$store.state.labels.labels).filter(l => labelIds.includes(l.id)) ?? [])
const labels = typeof this.filters.labels === 'string'
? this.filters.labels
: ''
const labelIds = labels.split(',').map(i => parseInt(i))
this.labels = this.$store.getters['labels/getLabelsByIds'](labelIds)
},
removePropertyFromFilter(propertyName) {
// Because of the way arrays work, we can only ever remove one element at once.
@ -533,10 +531,10 @@ export default {
this[`${kind}Service`].getAll({}, {s: query})
.then(response => {
// Filter the results to not include users who are already assigneid
this.$set(this, `found${kind}`, differenceWith(response, this[kind], (first, second) => {
return first.id === second.id
}))
// Filter users from the results who are already assigned
const unassignedUsers = response.filter(({id}) => !includesById(this[kind], id))
this.$set(this, `found${kind}`, unassignedUsers)
})
.catch(e => {
this.$message.error(e)

View File

@ -1,5 +1,4 @@
import TaskCollectionService from '@/services/taskCollection'
import cloneDeep from 'lodash/cloneDeep'
// FIXME: merge with DEFAULT_PARAMS in filters.vue
const DEFAULT_PARAMS = {
@ -83,7 +82,7 @@ export default {
this.tasks = r
this.currentPage = page
this.loadedList = cloneDeep(currentList)
this.loadedList = JSON.parse(JSON.stringify(currentList))
})
.catch(e => {
this.$message.error(e)

View File

@ -29,8 +29,7 @@
</template>
<script>
import differenceWith from 'lodash/differenceWith'
import {includesById} from '@/helpers/utils'
import UserModel from '../../../models/user'
import ListUserService from '../../../services/listUsers'
import TaskAssigneeService from '../../../services/taskAssignee'
@ -111,9 +110,9 @@ export default {
this.listUserService.getAll({listId: this.listId}, {s: query})
.then(response => {
// Filter the results to not include users who are already assigned
this.$set(this, 'foundUsers', differenceWith(response, this.assignees, (first, second) => {
return first.id === second.id
}))
const filteredResponse = response.filter(({id}) => !includesById(this.assignees, id))
this.$set(this, 'foundUsers', filteredResponse)
})
.catch(e => {
this.$message.error(e)

View File

@ -38,8 +38,6 @@
</template>
<script>
import differenceWith from 'lodash/differenceWith'
import LabelModel from '../../../models/label'
import LabelTaskService from '../../../services/labelTask'
@ -83,13 +81,7 @@ export default {
},
computed: {
foundLabels() {
const labels = (Object.values(this.$store.state.labels.labels).filter(l => {
return l.title.toLowerCase().includes(this.query.toLowerCase())
}) ?? [])
return differenceWith(labels, this.labels, (first, second) => {
return first.id === second.id
})
return this.$store.getters['labels/filterLabelsByQuery'](this.labels, this.query)
},
loading() {
return this.labelTaskService.loading || (this.$store.state[LOADING] && this.$store.state[LOADING_MODULE] === 'labels')

View File

@ -1,3 +0,0 @@
export function findIndexById(array : [], id : string | number) {
return array.findIndex(({id: currentId}) => currentId === id)
}

22
src/helpers/utils.ts Normal file
View File

@ -0,0 +1,22 @@
export function findIndexById(array : [], id : string | number) {
return array.findIndex(({id: currentId}) => currentId === id)
}
export function includesById(array: [], id: string | number) {
return array.some(({id: currentId}) => currentId === id)
}
// https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_isnil
export function isNil(value: any) {
return value == null
}
export function omitBy(obj: {}, check: (value: any) => Boolean): {} {
if (isNil(obj)) {
return {}
}
return Object.fromEntries(
Object.entries(obj).filter(([, value]) => !check(value)),
)
}

View File

@ -1,7 +1,5 @@
import defaults from 'lodash/defaults'
import isNil from 'lodash/isNil'
import omitBy from 'lodash/omitBy'
import {objectToCamelCase} from '@/helpers/case'
import {omitBy, isNil} from '@/helpers/utils'
export default class AbstractModel {
@ -16,11 +14,14 @@ export default class AbstractModel {
* @param data
*/
constructor(data) {
data = objectToCamelCase(data)
// Put all data in our model while overriding those with a value of null or undefined with their defaults
defaults(this, omitBy(data, isNil), this.defaults())
Object.assign(
this,
this.defaults(),
omitBy(data, isNil),
)
}
/**

View File

@ -1,13 +1,10 @@
import TeamShareBaseModel from './teamShareBase'
import merge from 'lodash/merge'
export default class TeamListModel extends TeamShareBaseModel {
defaults() {
return merge(
super.defaults(),
{
listId: 0,
},
)
return {
...super.defaults(),
listId: 0,
}
}
}

View File

@ -1,14 +1,11 @@
import UserModel from './user'
import merge from 'lodash/merge'
export default class TeamMemberModel extends UserModel {
defaults() {
return merge(
super.defaults(),
{
admin: false,
teamId: 0,
},
)
return {
...super.defaults(),
admin: false,
teamId: 0,
}
}
}

View File

@ -1,13 +1,10 @@
import TeamShareBaseModel from './teamShareBase'
import merge from 'lodash/merge'
export default class TeamNamespaceModel extends TeamShareBaseModel {
defaults() {
return merge(
super.defaults(),
{
namespaceId: 0,
},
)
return {
...super.defaults(),
namespaceId: 0,
}
}
}

View File

@ -1,14 +1,11 @@
import UserShareBaseModel from './userShareBase'
import merge from 'lodash/merge'
// This class extends the user share model with a 'rights' parameter which is used in sharing
export default class UserListModel extends UserShareBaseModel {
defaults() {
return merge(
super.defaults(),
{
listId: 0,
},
)
return {
...super.defaults(),
listId: 0,
}
}
}

View File

@ -1,14 +1,11 @@
import UserShareBaseModel from './userShareBase'
import merge from 'lodash/merge'
// This class extends the user share model with a 'rights' parameter which is used in sharing
export default class UserNamespaceModel extends UserShareBaseModel {
defaults() {
return merge(
super.defaults(),
{
namespaceId: 0,
},
)
return {
...super.defaults(),
namespaceId: 0,
}
}
}

View File

@ -1,6 +1,4 @@
import axios from 'axios'
import reduce from 'lodash/reduce'
import replace from 'lodash/replace'
import {objectToSnakeCase} from '@/helpers/case'
import {getToken} from '@/helpers/auth'
@ -157,9 +155,10 @@ export default class AbstractService {
*/
getReplacedRoute(path, pathparams) {
let replacements = this.getRouteReplacements(path, pathparams)
return reduce(replacements, function (result, value, parameter) {
return replace(result, parameter, value)
}, path)
return Object.entries(replacements).reduce(
(result, [parameter, value]) => result.replace(parameter, value),
path,
)
}
/**

View File

@ -1,6 +1,6 @@
import Vue from 'vue'
import {findIndexById} from '@/helpers/find'
import {findIndexById} from '@/helpers/utils'
export default {
namespaced: true,

View File

@ -1,5 +1,4 @@
import Vue from 'vue'
import cloneDeep from 'lodash/cloneDeep'
import BucketService from '../../services/bucket'
import {filterObject} from '@/helpers/filterObject'
@ -206,7 +205,7 @@ export default {
const cancel = setLoading(ctx, 'kanban')
ctx.commit('setBucketLoading', {bucketId: bucketId, loading: true})
const params = cloneDeep(ps)
const params = JSON.parse(JSON.stringify(ps))
params.sort_by = 'kanban_position'
params.order_by = 'asc'

View File

@ -2,10 +2,37 @@ import LabelService from '@/services/label'
import Vue from 'vue'
import {setLoading} from '@/store/helper'
/**
* Returns the labels by id if found
* @param {Object} state
* @param {Array} ids
* @returns {Array}
*/
function getLabelsByIds(state, ids) {
return Object.values(state.labels).filter(({id}) => ids.includes(id))
}
/**
* Checks if a list of labels is available in the store and filters them then query
* @param {Object} state
* @param {Array} labels
* @param {String} query
* @returns {Array}
*/
function filterLabelsByQuery(state, labels, query) {
const labelIds = labels.map(({id}) => id)
const foundLabels = getLabelsByIds(state, labelIds)
const labelQuery = query.toLowerCase()
return foundLabels.filter(({title}) => {
return !title.toLowerCase().includes(labelQuery)
})
}
export default {
namespaced: true,
// The state is an object which has the label ids as keys.
state: () => ({
// The labels are stored as an object which has the label ids as keys.
labels: {},
loaded: false,
}),
@ -25,6 +52,14 @@ export default {
state.loaded = loaded
},
},
getters: {
getLabelsByIds(state) {
return (ids) => getLabelsByIds(state, ids)
},
filterLabelsByQuery(state) {
return (...arr) => filterLabelsByQuery(state, ...arr)
},
},
actions: {
loadAllLabels(ctx, {forceLoad} = {}) {
if (ctx.state.loaded && !forceLoad) {

View File

@ -5511,7 +5511,7 @@ lodash.truncate@^4.4.2:
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
lodash@4.17.21, lodash@4.x, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0:
lodash@4.x, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==