react-refresh-babel.development.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. /** @license React vundefined
  2. * react-refresh-babel.development.js
  3. *
  4. * Copyright (c) Facebook, Inc. and its affiliates.
  5. *
  6. * This source code is licensed under the MIT license found in the
  7. * LICENSE file in the root directory of this source tree.
  8. */
  9. 'use strict';
  10. if (process.env.NODE_ENV !== "production") {
  11. (function() {
  12. 'use strict';
  13. function ReactFreshBabelPlugin (babel) {
  14. var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  15. if (typeof babel.env === 'function') {
  16. // Only available in Babel 7.
  17. var env = babel.env();
  18. if (env !== 'development' && !opts.skipEnvCheck) {
  19. throw new Error('React Refresh Babel transform should only be enabled in development environment. ' + 'Instead, the environment is: "' + env + '". If you want to override this check, pass {skipEnvCheck: true} as plugin options.');
  20. }
  21. }
  22. var t = babel.types;
  23. var refreshReg = t.identifier(opts.refreshReg || '$RefreshReg$');
  24. var refreshSig = t.identifier(opts.refreshSig || '$RefreshSig$');
  25. var registrationsByProgramPath = new Map();
  26. function createRegistration(programPath, persistentID) {
  27. var handle = programPath.scope.generateUidIdentifier('c');
  28. if (!registrationsByProgramPath.has(programPath)) {
  29. registrationsByProgramPath.set(programPath, []);
  30. }
  31. var registrations = registrationsByProgramPath.get(programPath);
  32. registrations.push({
  33. handle: handle,
  34. persistentID: persistentID
  35. });
  36. return handle;
  37. }
  38. function isComponentishName(name) {
  39. return typeof name === 'string' && name[0] >= 'A' && name[0] <= 'Z';
  40. }
  41. function findInnerComponents(inferredName, path, callback) {
  42. var node = path.node;
  43. switch (node.type) {
  44. case 'Identifier':
  45. {
  46. if (!isComponentishName(node.name)) {
  47. return false;
  48. } // export default hoc(Foo)
  49. // const X = hoc(Foo)
  50. callback(inferredName, node, null);
  51. return true;
  52. }
  53. case 'FunctionDeclaration':
  54. {
  55. // function Foo() {}
  56. // export function Foo() {}
  57. // export default function Foo() {}
  58. callback(inferredName, node.id, null);
  59. return true;
  60. }
  61. case 'ArrowFunctionExpression':
  62. {
  63. if (node.body.type === 'ArrowFunctionExpression') {
  64. return false;
  65. } // let Foo = () => {}
  66. // export default hoc1(hoc2(() => {}))
  67. callback(inferredName, node, path);
  68. return true;
  69. }
  70. case 'FunctionExpression':
  71. {
  72. // let Foo = function() {}
  73. // const Foo = hoc1(forwardRef(function renderFoo() {}))
  74. // export default memo(function() {})
  75. callback(inferredName, node, path);
  76. return true;
  77. }
  78. case 'CallExpression':
  79. {
  80. var argsPath = path.get('arguments');
  81. if (argsPath === undefined || argsPath.length === 0) {
  82. return false;
  83. }
  84. var calleePath = path.get('callee');
  85. switch (calleePath.node.type) {
  86. case 'MemberExpression':
  87. case 'Identifier':
  88. {
  89. var calleeSource = calleePath.getSource();
  90. var firstArgPath = argsPath[0];
  91. var innerName = inferredName + '$' + calleeSource;
  92. var foundInside = findInnerComponents(innerName, firstArgPath, callback);
  93. if (!foundInside) {
  94. return false;
  95. } // const Foo = hoc1(hoc2(() => {}))
  96. // export default memo(React.forwardRef(function() {}))
  97. callback(inferredName, node, path);
  98. return true;
  99. }
  100. default:
  101. {
  102. return false;
  103. }
  104. }
  105. }
  106. case 'VariableDeclarator':
  107. {
  108. var init = node.init;
  109. if (init === null) {
  110. return false;
  111. }
  112. var name = node.id.name;
  113. if (!isComponentishName(name)) {
  114. return false;
  115. }
  116. switch (init.type) {
  117. case 'ArrowFunctionExpression':
  118. case 'FunctionExpression':
  119. // Likely component definitions.
  120. break;
  121. case 'CallExpression':
  122. {
  123. // Maybe a HOC.
  124. // Try to determine if this is some form of import.
  125. var callee = init.callee;
  126. var calleeType = callee.type;
  127. if (calleeType === 'Import') {
  128. return false;
  129. } else if (calleeType === 'Identifier') {
  130. if (callee.name.indexOf('require') === 0) {
  131. return false;
  132. } else if (callee.name.indexOf('import') === 0) {
  133. return false;
  134. } // Neither require nor import. Might be a HOC.
  135. // Pass through.
  136. }
  137. break;
  138. }
  139. case 'TaggedTemplateExpression':
  140. // Maybe something like styled.div`...`
  141. break;
  142. default:
  143. return false;
  144. }
  145. var initPath = path.get('init');
  146. var _foundInside = findInnerComponents(inferredName, initPath, callback);
  147. if (_foundInside) {
  148. return true;
  149. } // See if this identifier is used in JSX. Then it's a component.
  150. var binding = path.scope.getBinding(name);
  151. if (binding === undefined) {
  152. return;
  153. }
  154. var isLikelyUsedAsType = false;
  155. var referencePaths = binding.referencePaths;
  156. for (var i = 0; i < referencePaths.length; i++) {
  157. var ref = referencePaths[i];
  158. if (ref.node && ref.node.type !== 'JSXIdentifier' && ref.node.type !== 'Identifier') {
  159. continue;
  160. }
  161. var refParent = ref.parent;
  162. if (refParent.type === 'JSXOpeningElement') {
  163. isLikelyUsedAsType = true;
  164. } else if (refParent.type === 'CallExpression') {
  165. var _callee = refParent.callee;
  166. var fnName = void 0;
  167. switch (_callee.type) {
  168. case 'Identifier':
  169. fnName = _callee.name;
  170. break;
  171. case 'MemberExpression':
  172. fnName = _callee.property.name;
  173. break;
  174. }
  175. switch (fnName) {
  176. case 'createElement':
  177. case 'jsx':
  178. case 'jsxDEV':
  179. case 'jsxs':
  180. isLikelyUsedAsType = true;
  181. break;
  182. }
  183. }
  184. if (isLikelyUsedAsType) {
  185. // const X = ... + later <X />
  186. callback(inferredName, init, initPath);
  187. return true;
  188. }
  189. }
  190. }
  191. }
  192. return false;
  193. }
  194. function isBuiltinHook(hookName) {
  195. switch (hookName) {
  196. case 'useState':
  197. case 'React.useState':
  198. case 'useReducer':
  199. case 'React.useReducer':
  200. case 'useEffect':
  201. case 'React.useEffect':
  202. case 'useLayoutEffect':
  203. case 'React.useLayoutEffect':
  204. case 'useMemo':
  205. case 'React.useMemo':
  206. case 'useCallback':
  207. case 'React.useCallback':
  208. case 'useRef':
  209. case 'React.useRef':
  210. case 'useContext':
  211. case 'React.useContext':
  212. case 'useImperativeHandle':
  213. case 'React.useImperativeHandle':
  214. case 'useDebugValue':
  215. case 'React.useDebugValue':
  216. return true;
  217. default:
  218. return false;
  219. }
  220. }
  221. function getHookCallsSignature(functionNode) {
  222. var fnHookCalls = hookCalls.get(functionNode);
  223. if (fnHookCalls === undefined) {
  224. return null;
  225. }
  226. return {
  227. key: fnHookCalls.map(function (call) {
  228. return call.name + '{' + call.key + '}';
  229. }).join('\n'),
  230. customHooks: fnHookCalls.filter(function (call) {
  231. return !isBuiltinHook(call.name);
  232. }).map(function (call) {
  233. return t.cloneDeep(call.callee);
  234. })
  235. };
  236. }
  237. var hasForceResetCommentByFile = new WeakMap(); // We let user do /* @refresh reset */ to reset state in the whole file.
  238. function hasForceResetComment(path) {
  239. var file = path.hub.file;
  240. var hasForceReset = hasForceResetCommentByFile.get(file);
  241. if (hasForceReset !== undefined) {
  242. return hasForceReset;
  243. }
  244. hasForceReset = false;
  245. var comments = file.ast.comments;
  246. for (var i = 0; i < comments.length; i++) {
  247. var cmt = comments[i];
  248. if (cmt.value.indexOf('@refresh reset') !== -1) {
  249. hasForceReset = true;
  250. break;
  251. }
  252. }
  253. hasForceResetCommentByFile.set(file, hasForceReset);
  254. return hasForceReset;
  255. }
  256. function createArgumentsForSignature(node, signature, scope) {
  257. var key = signature.key,
  258. customHooks = signature.customHooks;
  259. var forceReset = hasForceResetComment(scope.path);
  260. var customHooksInScope = [];
  261. customHooks.forEach(function (callee) {
  262. // Check if a corresponding binding exists where we emit the signature.
  263. var bindingName;
  264. switch (callee.type) {
  265. case 'MemberExpression':
  266. if (callee.object.type === 'Identifier') {
  267. bindingName = callee.object.name;
  268. }
  269. break;
  270. case 'Identifier':
  271. bindingName = callee.name;
  272. break;
  273. }
  274. if (scope.hasBinding(bindingName)) {
  275. customHooksInScope.push(callee);
  276. } else {
  277. // We don't have anything to put in the array because Hook is out of scope.
  278. // Since it could potentially have been edited, remount the component.
  279. forceReset = true;
  280. }
  281. });
  282. var finalKey = key;
  283. if (typeof require === 'function' && !opts.emitFullSignatures) {
  284. // Prefer to hash when we can (e.g. outside of ASTExplorer).
  285. // This makes it deterministically compact, even if there's
  286. // e.g. a useState initializer with some code inside.
  287. // We also need it for www that has transforms like cx()
  288. // that don't understand if something is part of a string.
  289. finalKey = require('crypto').createHash('sha1').update(key).digest('base64');
  290. }
  291. var args = [node, t.stringLiteral(finalKey)];
  292. if (forceReset || customHooksInScope.length > 0) {
  293. args.push(t.booleanLiteral(forceReset));
  294. }
  295. if (customHooksInScope.length > 0) {
  296. args.push( // TODO: We could use an arrow here to be more compact.
  297. // However, don't do it until AMA can run them natively.
  298. t.functionExpression(null, [], t.blockStatement([t.returnStatement(t.arrayExpression(customHooksInScope))])));
  299. }
  300. return args;
  301. }
  302. function findHOCCallPathsAbove(path) {
  303. var calls = [];
  304. while (true) {
  305. if (!path) {
  306. return calls;
  307. }
  308. var parentPath = path.parentPath;
  309. if (!parentPath) {
  310. return calls;
  311. }
  312. if ( // hoc(_c = function() { })
  313. parentPath.node.type === 'AssignmentExpression' && path.node === parentPath.node.right) {
  314. // Ignore registrations.
  315. path = parentPath;
  316. continue;
  317. }
  318. if ( // hoc1(hoc2(...))
  319. parentPath.node.type === 'CallExpression' && path.node !== parentPath.node.callee) {
  320. calls.push(parentPath);
  321. path = parentPath;
  322. continue;
  323. }
  324. return calls; // Stop at other types.
  325. }
  326. }
  327. var seenForRegistration = new WeakSet();
  328. var seenForSignature = new WeakSet();
  329. var seenForOutro = new WeakSet();
  330. var hookCalls = new WeakMap();
  331. var HookCallsVisitor = {
  332. CallExpression: function (path) {
  333. var node = path.node;
  334. var callee = node.callee; // Note: this visitor MUST NOT mutate the tree in any way.
  335. // It runs early in a separate traversal and should be very fast.
  336. var name = null;
  337. switch (callee.type) {
  338. case 'Identifier':
  339. name = callee.name;
  340. break;
  341. case 'MemberExpression':
  342. name = callee.property.name;
  343. break;
  344. }
  345. if (name === null || !/^use[A-Z]/.test(name)) {
  346. return;
  347. }
  348. var fnScope = path.scope.getFunctionParent();
  349. if (fnScope === null) {
  350. return;
  351. } // This is a Hook call. Record it.
  352. var fnNode = fnScope.block;
  353. if (!hookCalls.has(fnNode)) {
  354. hookCalls.set(fnNode, []);
  355. }
  356. var hookCallsForFn = hookCalls.get(fnNode);
  357. var key = '';
  358. if (path.parent.type === 'VariableDeclarator') {
  359. // TODO: if there is no LHS, consider some other heuristic.
  360. key = path.parentPath.get('id').getSource();
  361. } // Some built-in Hooks reset on edits to arguments.
  362. var args = path.get('arguments');
  363. if (name === 'useState' && args.length > 0) {
  364. // useState second argument is initial state.
  365. key += '(' + args[0].getSource() + ')';
  366. } else if (name === 'useReducer' && args.length > 1) {
  367. // useReducer second argument is initial state.
  368. key += '(' + args[1].getSource() + ')';
  369. }
  370. hookCallsForFn.push({
  371. callee: path.node.callee,
  372. name: name,
  373. key: key
  374. });
  375. }
  376. };
  377. return {
  378. visitor: {
  379. ExportDefaultDeclaration: function (path) {
  380. var node = path.node;
  381. var decl = node.declaration;
  382. var declPath = path.get('declaration');
  383. if (decl.type !== 'CallExpression') {
  384. // For now, we only support possible HOC calls here.
  385. // Named function declarations are handled in FunctionDeclaration.
  386. // Anonymous direct exports like export default function() {}
  387. // are currently ignored.
  388. return;
  389. } // Make sure we're not mutating the same tree twice.
  390. // This can happen if another Babel plugin replaces parents.
  391. if (seenForRegistration.has(node)) {
  392. return;
  393. }
  394. seenForRegistration.add(node); // Don't mutate the tree above this point.
  395. // This code path handles nested cases like:
  396. // export default memo(() => {})
  397. // In those cases it is more plausible people will omit names
  398. // so they're worth handling despite possible false positives.
  399. // More importantly, it handles the named case:
  400. // export default memo(function Named() {})
  401. var inferredName = '%default%';
  402. var programPath = path.parentPath;
  403. findInnerComponents(inferredName, declPath, function (persistentID, targetExpr, targetPath) {
  404. if (targetPath === null) {
  405. // For case like:
  406. // export default hoc(Foo)
  407. // we don't want to wrap Foo inside the call.
  408. // Instead we assume it's registered at definition.
  409. return;
  410. }
  411. var handle = createRegistration(programPath, persistentID);
  412. targetPath.replaceWith(t.assignmentExpression('=', handle, targetExpr));
  413. });
  414. },
  415. FunctionDeclaration: {
  416. enter: function (path) {
  417. var node = path.node;
  418. var programPath;
  419. var insertAfterPath;
  420. var modulePrefix = '';
  421. switch (path.parent.type) {
  422. case 'Program':
  423. insertAfterPath = path;
  424. programPath = path.parentPath;
  425. break;
  426. case 'TSModuleBlock':
  427. insertAfterPath = path;
  428. programPath = insertAfterPath.parentPath.parentPath;
  429. break;
  430. case 'ExportNamedDeclaration':
  431. insertAfterPath = path.parentPath;
  432. programPath = insertAfterPath.parentPath;
  433. break;
  434. case 'ExportDefaultDeclaration':
  435. insertAfterPath = path.parentPath;
  436. programPath = insertAfterPath.parentPath;
  437. break;
  438. default:
  439. return;
  440. } // These types can be nested in typescript namespace
  441. // We need to find the export chain
  442. // Or return if it stays local
  443. if (path.parent.type === 'TSModuleBlock' || path.parent.type === 'ExportNamedDeclaration') {
  444. while (programPath.type !== 'Program') {
  445. if (programPath.type === 'TSModuleDeclaration') {
  446. if (programPath.parentPath.type !== 'Program' && programPath.parentPath.type !== 'ExportNamedDeclaration') {
  447. return;
  448. }
  449. modulePrefix = programPath.node.id.name + '$' + modulePrefix;
  450. }
  451. programPath = programPath.parentPath;
  452. }
  453. }
  454. var id = node.id;
  455. if (id === null) {
  456. // We don't currently handle anonymous default exports.
  457. return;
  458. }
  459. var inferredName = id.name;
  460. if (!isComponentishName(inferredName)) {
  461. return;
  462. } // Make sure we're not mutating the same tree twice.
  463. // This can happen if another Babel plugin replaces parents.
  464. if (seenForRegistration.has(node)) {
  465. return;
  466. }
  467. seenForRegistration.add(node); // Don't mutate the tree above this point.
  468. var innerName = modulePrefix + inferredName; // export function Named() {}
  469. // function Named() {}
  470. findInnerComponents(innerName, path, function (persistentID, targetExpr) {
  471. var handle = createRegistration(programPath, persistentID);
  472. insertAfterPath.insertAfter(t.expressionStatement(t.assignmentExpression('=', handle, targetExpr)));
  473. });
  474. },
  475. exit: function (path) {
  476. var node = path.node;
  477. var id = node.id;
  478. if (id === null) {
  479. return;
  480. }
  481. var signature = getHookCallsSignature(node);
  482. if (signature === null) {
  483. return;
  484. } // Make sure we're not mutating the same tree twice.
  485. // This can happen if another Babel plugin replaces parents.
  486. if (seenForSignature.has(node)) {
  487. return;
  488. }
  489. seenForSignature.add(node); // Don't mutate the tree above this point.
  490. var sigCallID = path.scope.generateUidIdentifier('_s');
  491. path.scope.parent.push({
  492. id: sigCallID,
  493. init: t.callExpression(refreshSig, [])
  494. }); // The signature call is split in two parts. One part is called inside the function.
  495. // This is used to signal when first render happens.
  496. path.get('body').unshiftContainer('body', t.expressionStatement(t.callExpression(sigCallID, []))); // The second call is around the function itself.
  497. // This is used to associate a type with a signature.
  498. // Unlike with $RefreshReg$, this needs to work for nested
  499. // declarations too. So we need to search for a path where
  500. // we can insert a statement rather than hard coding it.
  501. var insertAfterPath = null;
  502. path.find(function (p) {
  503. if (p.parentPath.isBlock()) {
  504. insertAfterPath = p;
  505. return true;
  506. }
  507. });
  508. if (insertAfterPath === null) {
  509. return;
  510. }
  511. insertAfterPath.insertAfter(t.expressionStatement(t.callExpression(sigCallID, createArgumentsForSignature(id, signature, insertAfterPath.scope))));
  512. }
  513. },
  514. 'ArrowFunctionExpression|FunctionExpression': {
  515. exit: function (path) {
  516. var node = path.node;
  517. var signature = getHookCallsSignature(node);
  518. if (signature === null) {
  519. return;
  520. } // Make sure we're not mutating the same tree twice.
  521. // This can happen if another Babel plugin replaces parents.
  522. if (seenForSignature.has(node)) {
  523. return;
  524. }
  525. seenForSignature.add(node); // Don't mutate the tree above this point.
  526. var sigCallID = path.scope.generateUidIdentifier('_s');
  527. path.scope.parent.push({
  528. id: sigCallID,
  529. init: t.callExpression(refreshSig, [])
  530. }); // The signature call is split in two parts. One part is called inside the function.
  531. // This is used to signal when first render happens.
  532. if (path.node.body.type !== 'BlockStatement') {
  533. path.node.body = t.blockStatement([t.returnStatement(path.node.body)]);
  534. }
  535. path.get('body').unshiftContainer('body', t.expressionStatement(t.callExpression(sigCallID, []))); // The second call is around the function itself.
  536. // This is used to associate a type with a signature.
  537. if (path.parent.type === 'VariableDeclarator') {
  538. var insertAfterPath = null;
  539. path.find(function (p) {
  540. if (p.parentPath.isBlock()) {
  541. insertAfterPath = p;
  542. return true;
  543. }
  544. });
  545. if (insertAfterPath === null) {
  546. return;
  547. } // Special case when a function would get an inferred name:
  548. // let Foo = () => {}
  549. // let Foo = function() {}
  550. // We'll add signature it on next line so that
  551. // we don't mess up the inferred 'Foo' function name.
  552. insertAfterPath.insertAfter(t.expressionStatement(t.callExpression(sigCallID, createArgumentsForSignature(path.parent.id, signature, insertAfterPath.scope)))); // Result: let Foo = () => {}; __signature(Foo, ...);
  553. } else {
  554. // let Foo = hoc(() => {})
  555. var paths = [path].concat(findHOCCallPathsAbove(path));
  556. paths.forEach(function (p) {
  557. p.replaceWith(t.callExpression(sigCallID, createArgumentsForSignature(p.node, signature, p.scope)));
  558. }); // Result: let Foo = __signature(hoc(__signature(() => {}, ...)), ...)
  559. }
  560. }
  561. },
  562. VariableDeclaration: function (path) {
  563. var node = path.node;
  564. var programPath;
  565. var insertAfterPath;
  566. var modulePrefix = '';
  567. switch (path.parent.type) {
  568. case 'Program':
  569. insertAfterPath = path;
  570. programPath = path.parentPath;
  571. break;
  572. case 'TSModuleBlock':
  573. insertAfterPath = path;
  574. programPath = insertAfterPath.parentPath.parentPath;
  575. break;
  576. case 'ExportNamedDeclaration':
  577. insertAfterPath = path.parentPath;
  578. programPath = insertAfterPath.parentPath;
  579. break;
  580. case 'ExportDefaultDeclaration':
  581. insertAfterPath = path.parentPath;
  582. programPath = insertAfterPath.parentPath;
  583. break;
  584. default:
  585. return;
  586. } // These types can be nested in typescript namespace
  587. // We need to find the export chain
  588. // Or return if it stays local
  589. if (path.parent.type === 'TSModuleBlock' || path.parent.type === 'ExportNamedDeclaration') {
  590. while (programPath.type !== 'Program') {
  591. if (programPath.type === 'TSModuleDeclaration') {
  592. if (programPath.parentPath.type !== 'Program' && programPath.parentPath.type !== 'ExportNamedDeclaration') {
  593. return;
  594. }
  595. modulePrefix = programPath.node.id.name + '$' + modulePrefix;
  596. }
  597. programPath = programPath.parentPath;
  598. }
  599. } // Make sure we're not mutating the same tree twice.
  600. // This can happen if another Babel plugin replaces parents.
  601. if (seenForRegistration.has(node)) {
  602. return;
  603. }
  604. seenForRegistration.add(node); // Don't mutate the tree above this point.
  605. var declPaths = path.get('declarations');
  606. if (declPaths.length !== 1) {
  607. return;
  608. }
  609. var declPath = declPaths[0];
  610. var inferredName = declPath.node.id.name;
  611. var innerName = modulePrefix + inferredName;
  612. findInnerComponents(innerName, declPath, function (persistentID, targetExpr, targetPath) {
  613. if (targetPath === null) {
  614. // For case like:
  615. // export const Something = hoc(Foo)
  616. // we don't want to wrap Foo inside the call.
  617. // Instead we assume it's registered at definition.
  618. return;
  619. }
  620. var handle = createRegistration(programPath, persistentID);
  621. if (targetPath.parent.type === 'VariableDeclarator') {
  622. // Special case when a variable would get an inferred name:
  623. // let Foo = () => {}
  624. // let Foo = function() {}
  625. // let Foo = styled.div``;
  626. // We'll register it on next line so that
  627. // we don't mess up the inferred 'Foo' function name.
  628. // (eg: with @babel/plugin-transform-react-display-name or
  629. // babel-plugin-styled-components)
  630. insertAfterPath.insertAfter(t.expressionStatement(t.assignmentExpression('=', handle, declPath.node.id))); // Result: let Foo = () => {}; _c1 = Foo;
  631. } else {
  632. // let Foo = hoc(() => {})
  633. targetPath.replaceWith(t.assignmentExpression('=', handle, targetExpr)); // Result: let Foo = hoc(_c1 = () => {})
  634. }
  635. });
  636. },
  637. Program: {
  638. enter: function (path) {
  639. // This is a separate early visitor because we need to collect Hook calls
  640. // and "const [foo, setFoo] = ..." signatures before the destructuring
  641. // transform mangles them. This extra traversal is not ideal for perf,
  642. // but it's the best we can do until we stop transpiling destructuring.
  643. path.traverse(HookCallsVisitor);
  644. },
  645. exit: function (path) {
  646. var registrations = registrationsByProgramPath.get(path);
  647. if (registrations === undefined) {
  648. return;
  649. } // Make sure we're not mutating the same tree twice.
  650. // This can happen if another Babel plugin replaces parents.
  651. var node = path.node;
  652. if (seenForOutro.has(node)) {
  653. return;
  654. }
  655. seenForOutro.add(node); // Don't mutate the tree above this point.
  656. registrationsByProgramPath.delete(path);
  657. var declarators = [];
  658. path.pushContainer('body', t.variableDeclaration('var', declarators));
  659. registrations.forEach(function (_ref) {
  660. var handle = _ref.handle,
  661. persistentID = _ref.persistentID;
  662. path.pushContainer('body', t.expressionStatement(t.callExpression(refreshReg, [handle, t.stringLiteral(persistentID)])));
  663. declarators.push(t.variableDeclarator(handle));
  664. });
  665. }
  666. }
  667. }
  668. };
  669. }
  670. module.exports = ReactFreshBabelPlugin;
  671. })();
  672. }