UNPKG

babel-plugin-transform-private-to-weakmap

Version:

Transforms class properties prefixed with an underscore to weakmaps.

194 lines (154 loc) 4.85 kB
## Introduction The goal of this plugin is to add more 'real' privacy to properties in classes. Sometimes just using underscores isn't enough because using `Object.keys()` will still return them and break some automation logic where private properties end up being leaked. This plugin makes use of closure and weakmaps to completely remove any desired properties from a class. The WeakMap holds all the values for all the instances of a class, where the instances are the key and an object containing all the properties is the value. This is not perfect, but it added just the privacy I needed to my classes while allowing me to continue to write code the same way without bloating it with symbols or weird references. #### Acknowledgment Know that you can still view the private properties by accessing the static property `Classname.private` of your classes, but that means there is less of a chance of them being leaked than the original method (using underscore prefix) and you would require the instance itself as a key to access the values anyways. It also means it's easy to debug the values (just log them in the terminal/console). ## Requirements This plugins works by transforming class properties. That means you need to have the class properties proposal plugin installed, which requires babel 7+. #### Packages | Package | Version | Reason of requirement | | :-- | :---: | :-- | | @babel/core | ^7.1.6 | The minimum required to run babel | | @babel/preset-env | ^7.1.6 | Optional, recommended. Includes most of the nice ESNext features | | @babel/plugin-proposal-class-properties | ^7.1.0 | Adds the class properties syntax support | ``` npm install --save-dev @babel/core @babel/preset-env @babel/plugin-proposal-class-properties ``` ## Installation This plugin can be installed directly from NPM: ``` npm install --save-dev babel-plugin-transform-private-to-weakmap ``` Once it's installed, you can add it to your `.babelrc` file: ```json { "presets": [ "@babel/preset-env" ], "plugins": [ "@babel/plugin-proposal-class-properties", "babel-plugin-transform-private-to-weakmap" ] } ``` ## How to use Any class properties which are defined with a starting underscore will be transformed. Once weakmap per class is generated. **NOTE**: The private properties only require an underscore when they are defined, they do not take any underscore when they are referenced in the code. #### Example ```js class Person { _firstname; _lastname; age; constructor(first, last, age) { this.firstname = first; this.lastname = last; this.age = age; } get name() { return this.firstname + ' ' + this.lastname; } present() { console.log(`Hi! I am ${this.name} and I am ${this.age} years old.`); } } let bob = new Person('Bob', 'Holton', 26); bob.present(); console.log(bob); ``` The above outputs: ``` Hi! I am Bob Holton and I am 26 years old. Person { age: 26 } ``` #### Compiled Here is how the above looks after the transformation: ```js class Person { age; constructor(first, last, age) { Person.private.set(this, {}); Person.private.get(this).firstname = first; Person.private.get(this).lastname = last; this.age = age; } get name() { return Person.private.get(this).firstname + ' ' + Person.private.get(this).lastname; } present() { console.log(`Hi! I am ${this.name} and I am ${this.age} years old.`); } } Person.private = new WeakMap(); let bob = new Person('Bob', 'Holton', 26); bob.present(); console.log(bob); ``` ## Keep in mind #### Initializing private properties You are allowed to initialize your private properties as expected: ##### Source ```js class Point { _x=0; _y=1; constructor(x=null, y=null) { if(x !== null) this.x = x; if(y !== null) this.y = y; } } ``` ##### Output ```js class Point { constructor(x=null, y=null) { Point.private.set(this, { x: 0, y: 1 }); if(x !== null) Point.private.get(this).x = x; if(y !== null) Point.private.get(this).y = y; } } Point.private = new WeakMap(); ``` #### Classes without a constructor If your class does not have a constructor, one will automatically be added: ##### Source ```js class Test { _name = 'Test'; test() { console.log(this.name); } } ``` ##### Output ```js class Test { constructor() { Test.private.set(this, { name: 'Test' }); } test() { console.log(Test.private.get(this).name); } } ``` #### Extending classes without constructor If your class does not have a constructor and it extends another class, then this transform plugin will not be able to automatically call super() with the proper arguments. In that case, please take the time to add a proper constructor to your classes. This is a known limitation of this plugin.