diff --git a/package.json b/package.json index 56e1877645..b0b3ec4e46 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "npm": "^6" }, "scripts": { - "lint": "eslint --ext .js --fix ./website/server", + "lint": "eslint --ext .js --fix ./website/common", "test": "npm run lint && gulp test && gulp apidoc", "test:build": "gulp test:prepare:build", "test:api-v3": "gulp test:api-v3", diff --git a/website/client/.eslintrc.js b/website/client/.eslintrc.js index de3a53ac1a..ff10689de7 100644 --- a/website/client/.eslintrc.js +++ b/website/client/.eslintrc.js @@ -9,18 +9,9 @@ module.exports = { rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', + 'import/no-unresolved': 'off', }, parserOptions: { parser: 'babel-eslint', }, - overrides: [ - { - files: [ - '**/__tests__/*.{j,t}s?(x)', - ], - env: { - mocha: true, - }, - }, - ], }; diff --git a/website/client/src/app.vue b/website/client/src/app.vue index 4cc73fee6a..531f37984f 100644 --- a/website/client/src/app.vue +++ b/website/client/src/app.vue @@ -247,7 +247,7 @@ export default { const min = 1; const randomNumber = Math.random() * (numberOfTips - min) + min; const tipNumber = Math.floor(randomNumber); - this.currentTipNumber = tipNumber; // eslint-disable-line vue/no-side-effects-in-computed-properties + this.currentTipNumber = tipNumber; // eslint-disable-line vue/no-side-effects-in-computed-properties, max-len return this.$t(`tip${tipNumber}`); }, @@ -283,7 +283,8 @@ export default { this.$refs.sound.load(); }); - // @TODO: I'm not sure these should be at the app level. Can we move these back into shop/inventory or maybe they need a lateral move? + // @TODO: I'm not sure these should be at the app level. + // Can we move these back into shop/inventory or maybe they need a lateral move? this.$root.$on('buyModal::showItem', item => { this.selectedItemToBuy = item; this.$root.$emit('bv::show::modal', 'buy-modal'); @@ -398,7 +399,8 @@ export default { // if (isApiCall && !serverAppVersionState) { // this.$store.state.serverAppVersion = serverAppVersion; // } else if (isApiCall && serverAppVersionState !== serverAppVersion) { - // if (document.activeElement.tagName !== 'INPUT' || confirm(this.$t('habiticaHasUpdated'))) { + // if (document.activeElement.tagName !== 'INPUT' + // || confirm(this.$t('habiticaHasUpdated'))) { // location.reload(true); // } // } @@ -537,12 +539,13 @@ export default { const modalCount = {}; const prevAndCurrent = 2; - for (const index in modalStack) { - const current = modalStack[index]; - + for (const current of modalStack) { if (!modalCount[current.modalId]) modalCount[current.modalId] = 0; modalCount[current.modalId] += 1; - if (modalCount[current.modalId] > prevAndCurrent && modalsThatCanShowTwice.indexOf(current.modalId) === -1) { + if ( + modalCount[current.modalId] > prevAndCurrent + && modalsThatCanShowTwice.indexOf(current.modalId) === -1 + ) { this.$store.state.modalStack = []; return false; } @@ -550,7 +553,10 @@ export default { if (!current.prev) continue; // eslint-disable-line if (!modalCount[current.prev]) modalCount[current.prev] = 0; modalCount[current.prev] += 1; - if (modalCount[current.prev] > prevAndCurrent && modalsThatCanShowTwice.indexOf(current.prev) === -1) { + if ( + modalCount[current.prev] > prevAndCurrent + && modalsThatCanShowTwice.indexOf(current.prev) === -1 + ) { this.$store.state.modalStack = []; return false; } diff --git a/website/client/src/main.js b/website/client/src/main.js index b0836c73cc..f3587c5c12 100644 --- a/website/client/src/main.js +++ b/website/client/src/main.js @@ -1,4 +1,5 @@ import Vue from 'vue'; +import BootstrapVue from 'bootstrap-vue'; import AppComponent from './app'; import { setup as setupAnalytics, @@ -9,9 +10,7 @@ import getStore from './store'; import StoreModule from './libs/store'; import './filters/registerGlobals'; import i18n from './libs/i18n'; -import 'smartbanner.js/dist/smartbanner.js'; - -import BootstrapVue from 'bootstrap-vue'; +import 'smartbanner.js/dist/smartbanner'; const IS_PRODUCTION = process.env.NODE_ENV === 'production'; // eslint-disable-line no-process-env @@ -34,7 +33,7 @@ setUpLogging(); setupAnalytics(); // just create queues for analytics, no scripts loaded at this time const store = getStore(); -new Vue({ +export default new Vue({ el: '#app', router, store, diff --git a/website/client/src/router/index.js b/website/client/src/router/index.js index 86bb56a370..6438b13505 100644 --- a/website/client/src/router/index.js +++ b/website/client/src/router/index.js @@ -29,7 +29,8 @@ const Logout = () => import(/* webpackChunkName: "auth" */'@/components/auth/log // User Pages // const StatsPage = () => import(/* webpackChunkName: "user" */'./components/userMenu/stats'); -// const AchievementsPage = () => import(/* webpackChunkName: "user" */'./components/userMenu/achievements'); +// const AchievementsPage = +// () => import(/* webpackChunkName: "user" */'./components/userMenu/achievements'); const ProfilePage = () => import(/* webpackChunkName: "user" */'@/components/userMenu/profilePage'); // Settings @@ -413,7 +414,8 @@ router.beforeEach((to, from, next) => { startingPage, path: to.path, }); - return; + + return null; } if ((to.name === 'stats' || to.name === 'achievements' || to.name === 'profile') && from.name !== null) { @@ -421,7 +423,7 @@ router.beforeEach((to, from, next) => { startingPage: to.name, path: to.path, }); - return; + return null; } if (from.name === 'userProfile' || from.name === 'userProfilePage' || from.name === 'stats' || from.name === 'achievements' || from.name === 'profile') { diff --git a/website/client/src/store/actions/challenges.js b/website/client/src/store/actions/challenges.js index c23dc7844e..75aabf5487 100644 --- a/website/client/src/store/actions/challenges.js +++ b/website/client/src/store/actions/challenges.js @@ -76,7 +76,9 @@ export async function exportChallengeCsv (store, payload) { export async function updateChallenge (store, payload) { const challengeDataToSend = omit(payload.challenge, ['tasks', 'habits', 'todos', 'rewards', 'group']); - if (challengeDataToSend.leader && challengeDataToSend.leader._id) challengeDataToSend.leader = challengeDataToSend.leader._id; + if (challengeDataToSend.leader && challengeDataToSend.leader._id) { + challengeDataToSend.leader = challengeDataToSend.leader._id; + } const response = await axios.put(`/api/v4/challenges/${payload.challenge._id}`, challengeDataToSend); diff --git a/website/client/src/store/actions/guilds.js b/website/client/src/store/actions/guilds.js index 84bcb60a96..f48f02f0cf 100644 --- a/website/client/src/store/actions/guilds.js +++ b/website/client/src/store/actions/guilds.js @@ -60,7 +60,7 @@ export async function join (store, payload) { response = await axios.post(`/api/v4/groups/${groupId}/join`); } catch (err) { alert(err.response.data.message); - return; + return null; } if (type === 'guild') { @@ -120,7 +120,9 @@ export async function create (store, payload) { export async function update (store, payload) { // Remove populated fields const groupDetailsToSend = omit(payload.group, ['chat', 'challenges', 'members', 'invites']); - if (groupDetailsToSend.leader && groupDetailsToSend.leader._id) groupDetailsToSend.leader = groupDetailsToSend.leader._id; + if (groupDetailsToSend.leader && groupDetailsToSend.leader._id) { + groupDetailsToSend.leader = groupDetailsToSend.leader._id; + } const response = await axios.put(`/api/v4/groups/${payload.group.id}`, groupDetailsToSend); diff --git a/website/client/src/store/actions/quests.js b/website/client/src/store/actions/quests.js index 03633d0211..af340d5aa5 100644 --- a/website/client/src/store/actions/quests.js +++ b/website/client/src/store/actions/quests.js @@ -4,7 +4,7 @@ import * as Analytics from '@/libs/analytics'; // export async function initQuest (store) { // } -export async function sendAction (store, payload) { +export async function sendAction (store, payload) { // eslint-disable-line import/prefer-default-export, max-len // @TODO: Maybe move this to server let partyData = {}; if (store.state.party && store.state.party.data) { diff --git a/website/client/src/store/actions/shops.js b/website/client/src/store/actions/shops.js index 6e261c809f..e79beb187f 100644 --- a/website/client/src/store/actions/shops.js +++ b/website/client/src/store/actions/shops.js @@ -11,7 +11,8 @@ import releaseBothOp from '@/../../common/script/ops/releaseBoth'; import { getDropClass } from '@/libs/notifications'; -// @TODO: Purchase means gems and buy means gold. That wording is misused below, but we should also change +// @TODO: Purchase means gems and buy means gold. +// That wording is misused below, but we should also change // the generic buy functions to something else. Or have a Gold Vendor and Gem Vendor, etc function buyItem (store, params) { @@ -63,7 +64,9 @@ async function buyArmoire (store, params) { } if (item.type === 'food') { - if (!store.state.user.data.items.food[item.dropKey]) store.state.user.data.items.food[item.dropKey] = 0; + if (!store.state.user.data.items.food[item.dropKey]) { + store.state.user.data.items.food[item.dropKey] = 0; + } store.state.user.data.items.food[item.dropKey] += 1; } @@ -136,9 +139,8 @@ export async function genericPurchase (store, params) { switch (params.pinType) { case 'mystery_set': return purchaseMysterySet(store, params); - case 'armoire': // eslint-disable-line - await buyArmoire(store, params); - return; + case 'armoire': + return buyArmoire(store, params); case 'fortify': { const rerollResult = rerollOp(store.state.user.data, store.state.tasks.data); diff --git a/website/client/src/store/actions/snackbars.js b/website/client/src/store/actions/snackbars.js index 8742afca79..025b2b4640 100644 --- a/website/client/src/store/actions/snackbars.js +++ b/website/client/src/store/actions/snackbars.js @@ -7,5 +7,6 @@ export function add (store, payload) { } export function remove (store, payload) { - store.state.notificationStore = store.state.notificationStore.filter(notification => notification.uuid !== payload.uuid); + store.state.notificationStore = store.state.notificationStore + .filter(notification => notification.uuid !== payload.uuid); } diff --git a/website/client/src/store/actions/user.js b/website/client/src/store/actions/user.js index fbb80b1849..87697b39af 100644 --- a/website/client/src/store/actions/user.js +++ b/website/client/src/store/actions/user.js @@ -22,7 +22,7 @@ export function fetch (store, options = {}) { // eslint-disable-line no-shadow export async function set (store, changes) { const user = store.state.user.data; - for (const key in changes) { + for (const key of Object.keys(changes)) { if (key === 'tags') { // Keep challenge and group tags const oldTags = user.tags.filter(t => t.group); @@ -37,7 +37,7 @@ export async function set (store, changes) { const tagsIndexesToRemove = []; task.tags.forEach((tagId, tagIndex) => { - if (user.tags.find(tag => tag.id === tagId)) return; // eslint-disable-line max-nested-callbacks + if (user.tags.find(tag => tag.id === tagId)) return; tagsIndexesToRemove.push(tagIndex); }); diff --git a/website/client/src/store/actions/world-state.js b/website/client/src/store/actions/world-state.js index 9149898dba..19d0a893f9 100644 --- a/website/client/src/store/actions/world-state.js +++ b/website/client/src/store/actions/world-state.js @@ -1,6 +1,6 @@ import axios from 'axios'; -export async function getWorldState () { +export async function getWorldState () { // eslint-disable-line import/prefer-default-export const url = '/api/v4/world-state'; const response = await axios.get(url); return response.data.data; diff --git a/website/client/src/store/getters/party.js b/website/client/src/store/getters/party.js index 24861a9d57..2bb1d8775d 100644 --- a/website/client/src/store/getters/party.js +++ b/website/client/src/store/getters/party.js @@ -1,3 +1,3 @@ -export function members (store) { +export function members (store) { // eslint-disable-line import/prefer-default-export return store.state.partyMembers.data; } diff --git a/website/client/src/store/getters/tasks.js b/website/client/src/store/getters/tasks.js index a604745a6d..42ccc0cc7a 100644 --- a/website/client/src/store/getters/tasks.js +++ b/website/client/src/store/getters/tasks.js @@ -2,8 +2,8 @@ import sortBy from 'lodash/sortBy'; import { shouldDo } from '@/../../common/script/cron'; // Library / Utility function -import { orderSingleTypeTasks } from '@/libs/store/helpers/orderTasks.js'; -import { getActiveFilter } from '@/libs/store/helpers/filterTasks.js'; +import { orderSingleTypeTasks } from '@/libs/store/helpers/orderTasks'; +import { getActiveFilter } from '@/libs/store/helpers/filterTasks'; // Return all the tags belonging to an user task @@ -37,7 +37,8 @@ function getTaskColor (task) { export function canDelete () { return task => { const isUserChallenge = Boolean(task.userId); - const activeChallenge = isUserChallenge && task.challenge && task.challenge.id && !task.challenge.broken; + const activeChallenge = isUserChallenge + && task.challenge && task.challenge.id && !task.challenge.broken; return !activeChallenge; }; } @@ -50,7 +51,7 @@ export function getTaskClasses (store) { // Create Modal: create-modal-bg, create-modal-text, create-modal-icon // Control: 'control' return (task, purpose, dueDate) => { - if (!dueDate) dueDate = new Date(); + if (!dueDate) dueDate = new Date(); // eslint-disable-line no-param-reassign const { type } = task; const color = getTaskColor(task); @@ -78,7 +79,7 @@ export function getTaskClasses (store) { case 'control': if (type === 'todo' || type === 'daily') { - if (task.completed || !shouldDo(dueDate, task, userPreferences) && type === 'daily') { + if (task.completed || (!shouldDo(dueDate, task, userPreferences) && type === 'daily')) { return { bg: 'task-disabled-daily-todo-control-bg', checkbox: 'task-disabled-daily-todo-control-checkbox', @@ -107,7 +108,7 @@ export function getTaskClasses (store) { : { bg: 'task-disabled-habit-control-bg', inner: 'task-disabled-habit-control-inner', icon: `task-${color}-control-icon` }, }; } - break; + return null; default: return 'not a valid class'; } diff --git a/website/client/src/store/index.js b/website/client/src/store/index.js index 909c3d949a..a02b69fc7f 100644 --- a/website/client/src/store/index.js +++ b/website/client/src/store/index.js @@ -16,7 +16,10 @@ const IS_TEST = process.env.NODE_ENV === 'test'; // eslint-disable-line no-proce // Load user auth parameters and determine if it's logged in // before trying to load data let isUserLoggedIn = false; -const browserTimezoneOffset = moment().zone(); // eg, 240 - this will be converted on server as -(offset/60) + +// eg, 240 - this will be converted on server as -(offset/60) +const browserTimezoneOffset = moment().zone(); + axios.defaults.headers.common['x-client'] = 'habitica-web'; let AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings'); diff --git a/website/client/tests/unit/.eslintrc.js b/website/client/tests/unit/.eslintrc.js index 42275b7cd9..2b628845dc 100644 --- a/website/client/tests/unit/.eslintrc.js +++ b/website/client/tests/unit/.eslintrc.js @@ -1,5 +1 @@ -module.exports = { - env: { - mocha: true, - }, -}; +module.exports = require('../../../../test/.eslintrc.js'); diff --git a/website/common/script/cron.js b/website/common/script/cron.js index 9bcc5d07a0..53be939a4e 100644 --- a/website/common/script/cron.js +++ b/website/common/script/cron.js @@ -128,7 +128,7 @@ export function shouldDo (day, dailyTask, options = {}) { const daysOfTheWeek = []; if (dailyTask.repeat) { for (const [repeatDay, active] of Object.entries(dailyTask.repeat)) { - if (!Number.isFinite(DAY_MAPPING_STRING_TO_NUMBER[repeatDay])) continue; // eslint-disable-line no-continue, max-len + if (!Number.isFinite(parseInt(DAY_MAPPING_STRING_TO_NUMBER[repeatDay], 10))) continue; // eslint-disable-line no-continue, max-len if (active) daysOfTheWeek.push(parseInt(DAY_MAPPING_STRING_TO_NUMBER[repeatDay], 10)); } } diff --git a/website/common/script/ops/buy/buyGem.js b/website/common/script/ops/buy/buyGem.js index 7295a77278..e2793fcb35 100644 --- a/website/common/script/ops/buy/buyGem.js +++ b/website/common/script/ops/buy/buyGem.js @@ -27,7 +27,7 @@ export class BuyGemOperation extends AbstractGoldItemOperation { // eslint-disab extractAndValidateParams (user, req) { this.key = get(req, 'params.key'); - const { key } = this.key; + const { key } = this; if (!key) throw new BadRequest(this.i18n('missingKeyParam')); let { convCap } = planGemLimits; diff --git a/website/common/script/ops/buy/buyMarketGear.js b/website/common/script/ops/buy/buyMarketGear.js index ae70bf4c6e..f3fcf8d35b 100644 --- a/website/common/script/ops/buy/buyMarketGear.js +++ b/website/common/script/ops/buy/buyMarketGear.js @@ -34,7 +34,7 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { // eslin extractAndValidateParams (user, req) { this.key = get(req, 'params.key'); - const { key } = this.key; + const { key } = this; if (!key) throw new BadRequest(errorMessage('missingKeyParam')); const item = content.gear.flat[key]; diff --git a/website/common/script/ops/buy/buyQuestGem.js b/website/common/script/ops/buy/buyQuestGem.js index 04f3252d11..3f099d2a3a 100644 --- a/website/common/script/ops/buy/buyQuestGem.js +++ b/website/common/script/ops/buy/buyQuestGem.js @@ -28,7 +28,7 @@ export class BuyQuestWithGemOperation extends AbstractGemItemOperation { // esli extractAndValidateParams (user, req) { this.key = get(req, 'params.key'); - const { key } = this.key; + const { key } = this; if (!key) throw new BadRequest(errorMessage('missingKeyParam')); const item = content.quests[key]; diff --git a/website/server/controllers/api-v3/challenges.js b/website/server/controllers/api-v3/challenges.js index 1734eb01df..5e7eb7162e 100644 --- a/website/server/controllers/api-v3/challenges.js +++ b/website/server/controllers/api-v3/challenges.js @@ -608,7 +608,7 @@ api.exportChallengeCsv = { } while (task.userId !== lastUserId) { index += 1; - lastUserId = [resArray[index]]; // resArray[index][0] is an user id + [lastUserId] = resArray[index]; // resArray[index][0] is an user id } const streak = task.streak || 0; diff --git a/website/server/libs/cron.js b/website/server/libs/cron.js index b0882b9ab7..36cc759d1c 100644 --- a/website/server/libs/cron.js +++ b/website/server/libs/cron.js @@ -45,6 +45,7 @@ export async function recoverCron (status, locals) { } } else { locals.user = reloadedUser; + return null; } } diff --git a/website/server/libs/payments/stripe.js b/website/server/libs/payments/stripe.js index aaa9e02813..a0e8bc15cb 100644 --- a/website/server/libs/payments/stripe.js +++ b/website/server/libs/payments/stripe.js @@ -136,7 +136,7 @@ api.cancelSubscription = async function cancelSubscription (options, stripeInc) if (customer && (customer.subscription || customer.subscriptions)) { let { subscription } = customer; if (!subscription && customer.subscriptions) { - subscription = [customer.subscriptions.data]; + [subscription] = customer.subscriptions.data; } await stripeApi.customers.del(customerId);