diff --git a/test/client/unit/specs/libs/deepFreeze.spec.js b/test/client/unit/specs/libs/deepFreeze.spec.js new file mode 100644 index 0000000000..5d028e969f --- /dev/null +++ b/test/client/unit/specs/libs/deepFreeze.spec.js @@ -0,0 +1,25 @@ +import deepFreeze from 'client/libs/deepFreeze'; + +describe('deepFreeze', () => { + it('works as expected', () => { + let obj = { + a: 1, + b () { + return this.a; + }, + nested: { + c: 2, + nestedTwice: { + d: 1, + }, + }, + }; + + let result = deepFreeze(obj); + expect(result).to.equal(obj); + + expect(Object.isFrozen(obj)).to.equal(true); + expect(Object.isFrozen(obj.nested)).to.equal(true); + expect(Object.isFrozen(obj.nested.nestedTwice)).to.equal(true); + }); +}); \ No newline at end of file diff --git a/website/client/components/inventory/stable.vue b/website/client/components/inventory/stable.vue index ce6743c585..4eb7684970 100644 --- a/website/client/components/inventory/stable.vue +++ b/website/client/components/inventory/stable.vue @@ -1,8 +1,52 @@ diff --git a/website/client/libs/deepFreeze.js b/website/client/libs/deepFreeze.js new file mode 100644 index 0000000000..f39ee67cce --- /dev/null +++ b/website/client/libs/deepFreeze.js @@ -0,0 +1,20 @@ +// Code taken from https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze +// and adapted +export default function deepFreeze (obj) { + // Retrieve the property names defined on obj + const propNames = Object.getOwnPropertyNames(obj); + const propNamesLength = propNames.length; + + // Freeze properties before freezing self + for (let i = 0; i < propNamesLength; i++) { + const prop = obj[propNames[i]]; + + // Freeze prop if it is an object + if (typeof prop === 'object' && prop !== null) { + deepFreeze(prop); + } + } + + // Freeze self (no-op if already frozen) + return Object.freeze(obj); +} diff --git a/website/client/store/state.js b/website/client/store/state.js index 3f8836bd21..cd0e4eccef 100644 --- a/website/client/store/state.js +++ b/website/client/store/state.js @@ -1,7 +1,14 @@ +import deepFreeze from '../libs/deepFreeze'; +import content from '../../common/script/content/index'; + const state = { title: 'Habitica', user: null, tasks: null, // user tasks + // content data, frozen to prevent Vue from modifying it since it's static data that never changes + // TODO apply freezing to the entire codebase (the server) and not only to the client side? + // NOTE this takes about 10-15ms on a fast computer + content: deepFreeze(content), }; export default state; \ No newline at end of file diff --git a/website/common/script/content/stable.js b/website/common/script/content/stable.js index f8f49a979d..59b3aa50ed 100644 --- a/website/common/script/content/stable.js +++ b/website/common/script/content/stable.js @@ -112,4 +112,4 @@ module.exports = { specialMounts, petInfo, mountInfo, -}; +}; \ No newline at end of file