buschtoens/ember-joda

View on GitHub
lib/transform-joda-to-ember/index.js

Summary

Maintainability
C
7 hrs
Test Coverage
/* eslint-env node */

const GetterRegex = /^(?:chronology|dayOf(?:Month|Week|Year)|hashCode|hour|instant|millis|minute|month|monthValue|name|nano|lengthOf(?:Month|Year)|offset|ordinal|second|value|year)$/;

module.exports = function({ types: t }) {
  const idEmber = t.identifier('Ember');
  const idEmberObject = t.identifier('EmberObject');
  const idGet = t.identifier('get');
  const ComparableMixin = t.memberExpression(
    idEmber,
    t.identifier('Comparable')
  );
  const assert = t.memberExpression(idEmber, t.identifier('assert'));

  return {
    visitor: {
      Program(path) {
        path.unshiftContainer(
          'body',
          t.importDeclaration(
            [
              t.importDefaultSpecifier(idEmberObject),
              t.importSpecifier(idGet, idGet)
            ],
            t.stringLiteral('@ember/object')
          ),
          t.importDeclaration(
            [t.importDefaultSpecifier(idEmber)],
            t.stringLiteral('ember')
          )
        );
      },
      ClassDeclaration(path) {
        let didExtend = false;
        if (!path.node.superClass) {
          if (
            ![
              'OffsetIdPrinterParser',
              'ValueRange',
              'DateTimeFormatter',
              'FractionPrinterParser'
            ].includes(path.node.id.name) // FIXME: this is stupid
          ) {
            // extend leaf super classes from EmberObject
            path.node.superClass = idEmberObject;
            didExtend = true;
          }
        }

        for (const methodPath of path.get('body.body')) {
          if (
            methodPath.node.type === 'ClassMethod' &&
            !methodPath.node.static
          ) {
            if (didExtend && methodPath.node.kind === 'constructor') {
              // insert missing super call
              methodPath
                .get('body')
                .unshiftContainer('body', t.callExpression(t.super(), []));
            } else if (methodPath.node.kind == 'method') {
              if (methodPath.node.key.name === 'compareTo') {
                // add compare method
                const a = t.identifier('a');
                const b = t.identifier('b');
                methodPath.insertAfter(
                  t.classMethod(
                    'method',
                    t.identifier('compare'),
                    [a, b],
                    t.blockStatement([
                      t.expressionStatement(
                        t.callExpression(assert, [
                          t.stringLiteral(
                            'ember-joda: a must be equal to the object you are comparing on (this).'
                          ),
                          t.binaryExpression('===', t.thisExpression(), a)
                        ])
                      ),
                      t.returnStatement(
                        t.callExpression(
                          t.memberExpression(
                            t.thisExpression(),
                            methodPath.node.key
                          ),
                          [b]
                        )
                      )
                    ])
                  )
                );

                // mixin Comparable mixin for comparable classes
                path
                  .getStatementParent()
                  .insertAfter(
                    t.expressionStatement(
                      t.callExpression(
                        t.memberExpression(
                          path.node.id,
                          t.identifier('reopen')
                        ),
                        [ComparableMixin]
                      )
                    )
                  );
              } else if (methodPath.node.key.name === 'get') {
                // re-wrap Ember get method
                const bodyPath = methodPath.get('body');

                bodyPath.replaceWith(
                  t.blockStatement([
                    t.ifStatement(
                      t.binaryExpression(
                        '===',
                        t.unaryExpression('typeof', methodPath.node.params[0]),
                        t.stringLiteral('string')
                      ),
                      t.blockStatement([
                        t.returnStatement(
                          t.callExpression(idGet, [
                            t.thisExpression(),
                            methodPath.node.params[0]
                          ])
                        )
                      ]),
                      bodyPath.node
                    )
                  ])
                );
              } else if (
                methodPath.node.params.length === 0 &&
                GetterRegex.test(methodPath.node.key.name)
              ) {
                // turn all getter methods into real getters
                methodPath.node.kind = 'get';
              }
            }
          }
        }
      },
      CallExpression(path) {
        // turn all getter call expressions into member expressions
        if (
          path.node.arguments.length === 0 &&
          t.isMemberExpression(path.node.callee) &&
          t.isIdentifier(path.node.callee.property) &&
          GetterRegex.test(path.node.callee.property.name)
        ) {
          path.replaceWith(path.node.callee);
        }
      }
    }
  };
};

module.exports.baseDir = function() {
  return __dirname;
};