react-refresh-babel.development.js 26 KB

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