@lihautan/babel-plugin-transform-quadruple-equal

==== ("really strict equals", same as{' '} https://t.co/LMVvSIFdpx) — getify (@getify) July 22, 2020

Usage no npm install needed!

<script type="module">
  import lihautanBabelPluginTransformQuadrupleEqual from 'https://cdn.skypack.dev/@lihautan/babel-plugin-transform-quadruple-equal';
</script>

README

transform-quadruple-equal

Inspiration

Babel Plugin

npm version

💡 The Idea

This is something you can't just do with babel transform plugin.

Because the following:

a ==== b

is not a valid JavaScript syntax.

So, we need to first fork the babel parser.

To add support a new operator, we update the tokenizer to recognise one extra = character:

// filename: https://github.com/babel/babel/tree/master/packages/babel-parser/src/tokenizer/index.js

export default class Tokenizer extends ParserErrors {
  // ...
  readToken_eq_excl(code: number): void {
    // '=!'
    const next = this.input.charCodeAt(this.state.pos + 1);
    if (next === charCodes.equalsTo) {
      this.finishOp(
        tt.equality,
-        this.input.charCodeAt(this.state.pos + 2) === charCodes.equalsTo ? 3 : 2
+        this.input.charCodeAt(this.state.pos + 2) === charCodes.equalsTo
+          ? this.input.charCodeAt(this.state.pos + 3) === charCodes.equalsTo
+            ? 4
+            : 3
+          : 2
      );
      return;
    }
    // ...
  }
}

Now as ==== and !=== become a valid token, babel will it treat it the same way as === and !== token.

The Babel plugin itself is straightforward.

a ==== b will form a binary expression, and we are going to transform it into Object.is(a, b).

a ==== b;
c !=== d;

// into
Object.is(a, b);
!Object.is(c, d);

📘 The Code

export default function ({ template }) {
  return {
    name: 'transform-quadruple-equal',
    visitor: {
      BinaryExpression(path) {
        if (path.node.operator === '====') {
          path.replaceWith(
            template.expression`Object.is(${path.node.left}, ${path.node.right})`()
          );
        } else if (path.node.operator === '!===') {
          path.replaceWith(
            template.expression`!Object.is(${path.node.left}, ${path.node.right})`()
          );
        }
      },
    },
  };
}

Over here I used babel.template which provides utility functions to create Babel AST using tagged template literal.