UNPKG

1.89 kBJavaScriptView Raw
1export default function ({ types: t }) {
2 return {
3 visitor: {
4 Program: {
5 enter(path, { file }) {
6 // Do nothing if React is already declared
7 if (path.scope.hasBinding('React')) {
8 return;
9 }
10
11 const ourNode = t.importDeclaration([
12 t.importDefaultSpecifier(t.identifier('React')),
13 ], t.stringLiteral('react'));
14
15 // Add an import early, so that other plugins get to see it
16 file.set('ourPath', path.unshiftContainer('body', ourNode)[0]);
17 },
18
19 exit(_, { file }) {
20 // If our import is still intact and we haven't encountered any JSX in
21 // the program, then we just remove it. There's an edge case, where
22 // some other plugin could add JSX in its `Program.exit`, so our
23 // `JSXOpeningElement` will trigger only after this method, but it's
24 // likely that said plugin will also add a React import too.
25 const ourPath = file.get('ourPath');
26 if (ourPath && !file.get('hasJSX')) {
27 if (!ourPath.removed) ourPath.remove();
28 file.set('ourPath', undefined);
29 }
30 },
31 },
32
33 ImportDeclaration(path, { file }) {
34 // Return early if this has nothing to do with React
35 if (path.node.specifiers.every(x => x.local.name !== 'React')) return;
36
37 // If our import is still intact and we encounter some other import
38 // which also imports `React`, then we remove ours.
39 const ourPath = file.get('ourPath');
40 if (ourPath && path !== ourPath) {
41 if (!ourPath.removed) ourPath.remove();
42 file.set('ourPath', undefined);
43 }
44 },
45
46 JSXOpeningElement(_, { file }) {
47 file.set('hasJSX', true);
48 },
49
50 JSXOpeningFragment(_, { file }) {
51 file.set('hasJSX', true);
52 },
53 },
54 };
55}