diff --git a/CHANGELOG.md b/CHANGELOG.md index 34b600ed..5a5fbe90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to ## [Unreleased][unreleased] +- Implement `deepFreeze` and `freezeNamespaces` + ## [2.2.0][] - 2020-07-10 ### Added diff --git a/README.md b/README.md index 2fa3add6..ed1380d5 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,8 @@ $ npm install @metarhia/common - [parseHost](#parsehosthost) - [override](#overrideobj-fn) - [mixin](#mixintarget-source) +- [deepFreeze](#deepfreezeinstance) +- [freezeNamespaces](#freezenamespacesnamespaces-skipnames) - [Pool](#class-pool) - [Pool.prototype.constructor](#poolprototypeconstructorfactory--null) - [Pool.prototype.get](#poolprototypeget) @@ -1334,6 +1336,24 @@ Previous function will be accessible by obj.fnName.inherited Mixin for ES6 classes without overriding existing methods +### deepFreeze(instance) + +- `instance`: [``][object] target nested objects structure + +_Returns:_ [``][object] - deep freezed instance + +Deep freeze object structure + +### freezeNamespaces(namespaces, skipNames) + +- `namespaces`: [``][object] target nested objects structure +- `skipNames`: [``][array] nemes to be skipped for freezing at first + level + +_Returns:_ undefined + +Deep freeze namespaces + ### class Pool #### Pool.prototype.constructor(factory = null) diff --git a/lib/oop.js b/lib/oop.js index 92e056e3..264c9bf7 100644 --- a/lib/oop.js +++ b/lib/oop.js @@ -23,7 +23,43 @@ const mixin = (target, source) => { Object.assign(target, mix); }; +const REF_TYPES = ['object', 'function']; + +// Deep freeze object structure +// instance - , target nested objects structure +// Returns: - deep freezed instance +// +const deepFreeze = instance => { + Object.freeze(instance); + const fields = Object.getOwnPropertyNames(instance); + for (const fieldName of fields) { + const value = instance[fieldName]; + if (value === null) continue; + if (!REF_TYPES.includes(typeof value) || Object.isFrozen(value)) continue; + deepFreeze(value); + } + return instance; +}; + +// Deep freeze namespaces +// namespaces - , target nested objects structure +// skipNames - , nemes to be skipped for freezing at first level +// Returns: undefined +// +const freezeNamespaces = (namespaces, skipNames = []) => { + for (const namespace of namespaces) { + const names = Object.keys(namespace); + for (const name of names) { + const target = namespace[name]; + if (skipNames.includes(name)) continue; + deepFreeze(target); + } + } +}; + module.exports = { override, mixin, + deepFreeze, + freezeNamespaces, };