1 | export 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 | }
|