UNPKG

2.2 kBJavaScriptView Raw
1module.exports = function babelPluginReactRequire({ types: t }) {
2 const plugin = {
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
16 // Add an import early, so that other plugins get to see it
17 const [newPath] = path.unshiftContainer("body", ourNode);
18 newPath.get("specifiers").forEach((specifier) => {
19 path.scope.registerBinding("module", specifier);
20 });
21 file.set("ourPath", newPath);
22 },
23
24 exit(_, { file }) {
25 // If our import is still intact and we haven't encountered any JSX in
26 // the program, then we just remove it. There's an edge case, where
27 // some other plugin could add JSX in its `Program.exit`, so our
28 // `JSXOpeningElement` will trigger only after this method, but it's
29 // likely that said plugin will also add a React import too.
30 const ourPath = file.get("ourPath");
31 if (ourPath && !file.get("hasJSX")) {
32 if (!ourPath.removed) {
33 ourPath.remove();
34 }
35 file.set("ourPath", undefined);
36 }
37 },
38 },
39
40 ImportDeclaration(path, { file }) {
41 // Return early if this has nothing to do with React
42 if (path.node.specifiers.every((x) => x.local.name !== "React")) {
43 return;
44 }
45
46 // If our import is still intact and we encounter some other import
47 // which also imports `React`, then we remove ours.
48 const ourPath = file.get("ourPath");
49 if (ourPath && path !== ourPath) {
50 if (!ourPath.removed) {
51 ourPath.remove();
52 }
53 file.set("ourPath", undefined);
54 }
55 },
56
57 JSXOpeningElement(_, { file }) {
58 file.set("hasJSX", true);
59 },
60
61 JSXOpeningFragment(_, { file }) {
62 file.set("hasJSX", true);
63 },
64 },
65 };
66
67 return plugin;
68};