xref: /expo/packages/html-elements/babel.js (revision 9155fd2f)
1// Based on https://github.com/gregberge/svgr/tree/master/packages/babel-plugin-transform-react-native-svg
2
3const elementToComponent = {
4  a: 'A',
5  article: 'Article',
6  b: 'B',
7  br: 'BR',
8  caption: 'Caption',
9  code: 'Code',
10  footer: 'Footer',
11  h1: 'H1',
12  h2: 'H2',
13  pre: 'Pre',
14  h3: 'H3',
15  h4: 'H4',
16  h5: 'H5',
17  h6: 'H6',
18  header: 'Header',
19  time: 'Time',
20  hr: 'HR',
21  i: 'I',
22  mark: 'Mark',
23  del: 'Del',
24  em: 'EM',
25  li: 'LI',
26  main: 'Main',
27  nav: 'Nav',
28  p: 'P',
29  s: 'S',
30  section: 'Section',
31  table: 'Table',
32  tbody: 'TBody',
33  td: 'TD',
34  th: 'TH',
35  thead: 'THead',
36  tr: 'TR',
37  ul: 'UL',
38  strong: 'Strong',
39  aside: 'Aside',
40  tfoot: 'TFoot',
41};
42
43module.exports = ({ types: t }, { expo }) => {
44  function replaceElement(path, state) {
45    const { name } = path.node.openingElement.name;
46
47    // Replace element with @expo/html-elements
48    const component = elementToComponent[name];
49
50    if (!component) {
51      return;
52    }
53    const prefixedComponent = component;
54    const openingElementName = path.get('openingElement.name');
55    openingElementName.replaceWith(t.jsxIdentifier(prefixedComponent));
56    if (path.has('closingElement')) {
57      const closingElementName = path.get('closingElement.name');
58      closingElementName.replaceWith(t.jsxIdentifier(prefixedComponent));
59    }
60    state.replacedComponents.add(prefixedComponent);
61  }
62
63  const htmlElementVisitor = {
64    JSXElement(path, state) {
65      replaceElement(path, state);
66      path.traverse(jsxElementVisitor, state);
67    },
68  };
69
70  const jsxElementVisitor = {
71    JSXElement(path, state) {
72      replaceElement(path, state);
73    },
74  };
75
76  const importDeclarationVisitor = {
77    ImportDeclaration(path, state) {
78      if (path.get('source').isStringLiteral({ value: '@expo/html-elements' })) {
79        state.replacedComponents.forEach(component => {
80          if (
81            path
82              .get('specifiers')
83              .some(specifier => specifier.get('local').isIdentifier({ name: component }))
84          ) {
85            return;
86          }
87          path.pushContainer(
88            'specifiers',
89            t.importSpecifier(t.identifier(component), t.identifier(component))
90          );
91        });
92      }
93    },
94  };
95
96  return {
97    name: 'Rewrite React DOM to universal Expo elements',
98    visitor: {
99      Program(path, state) {
100        state.replacedComponents = new Set();
101        state.unsupportedComponents = new Set();
102
103        path.traverse(htmlElementVisitor, state);
104        path.traverse(importDeclarationVisitor, state);
105      },
106    },
107  };
108};
109