1.0.3 • Published 5 years ago

babel-plugin-transform-private-to-weakmap v1.0.3

Weekly downloads
-
License
ISC
Repository
gitlab
Last release
5 years ago

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

PackageVersionReason of requirement
@babel/core^7.1.6The minimum required to run babel
@babel/preset-env^7.1.6Optional, recommended. Includes most of the nice ESNext features
@babel/plugin-proposal-class-properties^7.1.0Adds 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:

{
  "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

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:

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
class Point {
  _x=0; _y=1;
  constructor(x=null, y=null) {
    if(x !== null) this.x = x;
    if(y !== null) this.y = y;
  }
}
Output
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
class Test {
  _name = 'Test';
  test() {
    console.log(this.name);
  }
}
Output
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.

1.0.3

5 years ago

1.0.2

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago