p-raj / random-ideas

1 stars 0 forks source link

React Native Apps -> V3 Templates #37

Closed veris-ankitpopli closed 5 years ago

veris-ankitpopli commented 7 years ago

Convert existing React Native Applications to V3 Templates. Why re-write screens when we can have scripts transform them ?

Babel Transform Plugins

Sample Transform:

Courtesy: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-jsx

export default function({ types: t, ...props }) {
  const generateElement = (path, state) => {
    const FILE = state.file,
      OPTIONS = Object.assign({}, { type: "type", value: "value" }, state.opts);

    const NODE = path.node;

    if (!/JSXElement/.test(NODE.type)) return NODE.expression ? NODE.expression : t.StringLiteral(NODE.value);

    const OPENING_ELEMENT = NODE.openingElement,
      CHILDREN = path.get("children"),
      ELEMENT_ATTRIBUTES = OPENING_ELEMENT.attributes,
      EXTENDS = isExtension(OPENING_ELEMENT, path);

    let type = t.StringLiteral(OPENING_ELEMENT.name.name.split(/(?=[A-Z])/).join("-").toLowerCase()),
      attributes = ELEMENT_ATTRIBUTES.length ? buildAttributeObject(ELEMENT_ATTRIBUTES, FILE) : t.NullLiteral(),
      children = CHILDREN.length ? t.ArrayExpression(CHILDREN.map(child => generateElement(child, state))) : t.NullLiteral();

    const props = attributes.properties.map(({ key, value }) => t.ObjectProperty(t.StringLiteral(key.value), value));
    return t.ObjectExpression([
      ...props,
      t.ObjectProperty(t.StringLiteral(OPTIONS.type), EXTENDS ? t.NullLiteral() : type),
      t.ObjectProperty(t.StringLiteral(OPTIONS.value), children)
    ]);
  };

  const generateAttrObject = nodes => {
    let arr = nodes.map(node => {
      let name = t.StringLiteral(node.name.name);
      let value = !node.value
        ? t.BooleanLiteral(true)
        : /JSXExpressionContainer/i.test(node.value.type) ? node.value.expression : node.value;

      return t.ObjectProperty(name, value);
    });

    return [t.ObjectExpression(arr)];
  };

  const buildAttributeObject = function(attrs, file) {
    let _expressions = [],
      _spreads = [];

    while (attrs.length) {
      let attr = attrs.shift();

      /^JSXSpreadAttribute$/i.test(attr.type) ? _spreads.push(attr.argument) : _expressions.push(attr);
    }

    let attrObject = _expressions.length ? generateAttrObject(_expressions) : null;

    if (_spreads.length) {
      let extension = attrObject ? _spreads.concat(attrObject) : _spreads;

      if (extension.length > 1) extension.unshift(t.ObjectExpression([]));

      attrObject = t.callExpression(file.addHelper("extends"), extension);
    } else {
      attrObject = attrObject[0];
    }

    return attrObject;
  };

  const isExtension = (openingElement, path) => path.scope.hasBinding(openingElement.name.name);

  return {
    visitor: {
      JSXElement: function(path, state) {
        path.replaceWith(generateElement(path, state));
      }
    }
  };
}

Input

<Image style={{width: 128, height: 128, alignSelf: 'center'}}
           source={'assets/images/successful_check_icon.png'}/>;

Output (V3 Component Declaration)

({
           "style": {width: 128, height: 128, alignSelf: 'center'},
           "source": 'assets/images/successful_check_icon.png',
           "type": "image",
           "value": null
});

Caveats (as of now)