diff --git a/examples/styled-components/src/App.js b/examples/styled-components/src/App.js
index 80602f2d6..fcc01c9e3 100644
--- a/examples/styled-components/src/App.js
+++ b/examples/styled-components/src/App.js
@@ -51,11 +51,13 @@ const Hook = () => {
() => {
console.log('hot effected 0');
setState(state => ({
- x: state.x + 0.1,
+ x: state.x + 0.5,
}));
},
['hot'],
);
+
+ //React.useState(0);
return (
hook state 1: {state.x}
diff --git a/examples/styled-components/webpack.config.babel.js b/examples/styled-components/webpack.config.babel.js
index 86f5ba2b1..1a771f952 100644
--- a/examples/styled-components/webpack.config.babel.js
+++ b/examples/styled-components/webpack.config.babel.js
@@ -6,7 +6,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: ['./src/index'],
mode: process.env.NODE_ENV || 'development',
- devtool: false,
+ //devtool: false,
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
diff --git a/src/babel.dev.js b/src/babel.dev.js
index 470193511..5672bb859 100644
--- a/src/babel.dev.js
+++ b/src/babel.dev.js
@@ -1,4 +1,5 @@
import { REGENERATE_METHOD } from './internal/constants';
+import fresh from './fresh/babel';
const templateOptions = {
placeholderPattern: /^([A-Z0-9]+)([A-Z0-9_]+)$/,
@@ -10,9 +11,10 @@ const shouldIgnoreFile = file =>
.split('\\')
.join('/')
.match(/node_modules\/(react|react-hot-loader)([\/]|$)/);
+
/* eslint-enable */
-module.exports = function plugin(args, options = {}) {
+function plugin(args, options = {}) {
// This is a Babel plugin, but the user put it in the Webpack config.
if (this && this.callback) {
throw new Error(
@@ -30,6 +32,12 @@ module.exports = function plugin(args, options = {}) {
const { safetyNet = true } = options;
const buildRegistration = template('reactHotLoader.register(ID, NAME, FILENAME);', templateOptions);
+
+ const signatureHeader = template(
+ `var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {return a;}`,
+ templateOptions,
+ );
+
const headerTemplate = template(
`(function () {
var enterModule = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).enterModule;
@@ -53,7 +61,7 @@ module.exports = function plugin(args, options = {}) {
`
(function () {
- var reactHotLoader = (typeof reactHotLoaderGlobal !== 'undefined' ?reactHotLoaderGlobal : require('react-hot-loader')).default;
+ var reactHotLoader = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).default;
if (!reactHotLoader) {
return;
@@ -119,10 +127,12 @@ module.exports = function plugin(args, options = {}) {
},
Program: {
- enter({ scope }, state) {
+ enter({ scope, node }, state) {
const { file } = state;
state[REGISTRATIONS] = []; // eslint-disable-line no-param-reassign
+ node.body.unshift(signatureHeader());
+
// Everything in the top level scope, when reasonable,
// is going to get tagged with __source.
/* eslint-disable guard-for-in,no-restricted-syntax */
@@ -208,6 +218,54 @@ module.exports = function plugin(args, options = {}) {
},
},
};
+}
+
+const mergeRecord = (sourceRecord, newRecord) => {
+ Object.keys(newRecord).forEach(key => {
+ const action = newRecord[key];
+ if (typeof action === 'function') {
+ if (!sourceRecord[key]) {
+ sourceRecord[key] = () => ({});
+ }
+ const prev = sourceRecord[key];
+ sourceRecord[key] = (...args) => {
+ prev(...args);
+ action(...args);
+ };
+ } else if (typeof action === 'object') {
+ if (!sourceRecord[key]) {
+ sourceRecord[key] = {};
+ }
+ mergeRecord(sourceRecord[key], action);
+ }
+ });
+};
+
+const composePlugins = plugins => (...args) => {
+ const result = {};
+ plugins.forEach(creator => {
+ const plugin = creator(...args);
+ mergeRecord(result, plugin);
+ });
+ return result;
};
+module.exports = composePlugins([
+ plugin,
+ (...args) => {
+ const p = fresh(...args);
+ // removing everything we dont want right now
+
+ // registration
+ delete p.visitor.Program;
+
+ // registrations
+ delete p.visitor.FunctionDeclaration.enter;
+ delete p.visitor.FunctionDeclaration.leave;
+ delete p.visitor.VariableDeclaration;
+
+ return p;
+ },
+]);
+
module.exports.shouldIgnoreFile = shouldIgnoreFile;
diff --git a/src/fresh/babel.js b/src/fresh/babel.js
new file mode 100644
index 000000000..a5925c38e
--- /dev/null
+++ b/src/fresh/babel.js
@@ -0,0 +1,579 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+const SIGNATURE = '__signature__';
+
+export default function(babel) {
+ const {types: t} = babel;
+
+ const registrationsByProgramPath = new Map();
+ function createRegistration(programPath, persistentID) {
+ const handle = programPath.scope.generateUidIdentifier('c');
+ if (!registrationsByProgramPath.has(programPath)) {
+ registrationsByProgramPath.set(programPath, []);
+ }
+ const registrations = registrationsByProgramPath.get(programPath);
+ registrations.push({
+ handle,
+ persistentID,
+ });
+ return handle;
+ }
+
+ function isComponentishName(name) {
+ return typeof name === 'string' && name[0] >= 'A' && name[0] <= 'Z';
+ }
+
+ function findInnerComponents(inferredName, path, callback) {
+ const node = path.node;
+ switch (node.type) {
+ case 'Identifier': {
+ if (!isComponentishName(node.name)) {
+ return false;
+ }
+ // export default hoc(Foo)
+ // const X = hoc(Foo)
+ callback(inferredName, node, null);
+ return true;
+ }
+ case 'FunctionDeclaration': {
+ // function Foo() {}
+ // export function Foo() {}
+ // export default function Foo() {}
+ callback(inferredName, node.id, null);
+ return true;
+ }
+ case 'ArrowFunctionExpression': {
+ if (node.body.type === 'ArrowFunctionExpression') {
+ return false;
+ }
+ // let Foo = () => {}
+ // export default hoc1(hoc2(() => {}))
+ callback(inferredName, node, path);
+ return true;
+ }
+ case 'FunctionExpression': {
+ // let Foo = function() {}
+ // const Foo = hoc1(forwardRef(function renderFoo() {}))
+ // export default memo(function() {})
+ callback(inferredName, node, path);
+ return true;
+ }
+ case 'CallExpression': {
+ const argsPath = path.get('arguments');
+ if (argsPath === undefined || argsPath.length === 0) {
+ return false;
+ }
+ const calleePath = path.get('callee');
+ switch (calleePath.node.type) {
+ case 'MemberExpression':
+ case 'Identifier': {
+ const calleeSource = calleePath.getSource();
+ const firstArgPath = argsPath[0];
+ const innerName = inferredName + '$' + calleeSource;
+ const foundInside = findInnerComponents(
+ innerName,
+ firstArgPath,
+ callback,
+ );
+ if (!foundInside) {
+ return false;
+ }
+ // const Foo = hoc1(hoc2(() => {}))
+ // export default memo(React.forwardRef(function() {}))
+ callback(inferredName, node, path);
+ return true;
+ }
+ default: {
+ return false;
+ }
+ }
+ }
+ case 'VariableDeclarator': {
+ const init = node.init;
+ if (init === null) {
+ return false;
+ }
+ const name = node.id.name;
+ if (!isComponentishName(name)) {
+ return false;
+ }
+ if (init.type === 'Identifier' || init.type === 'MemberExpression') {
+ return false;
+ }
+ const initPath = path.get('init');
+ const foundInside = findInnerComponents(
+ inferredName,
+ initPath,
+ callback,
+ );
+ if (foundInside) {
+ return true;
+ }
+ // See if this identifier is used in JSX. Then it's a component.
+ const binding = path.scope.getBinding(name);
+ if (binding === undefined) {
+ return;
+ }
+ let isLikelyUsedAsType = false;
+ const referencePaths = binding.referencePaths;
+ for (let i = 0; i < referencePaths.length; i++) {
+ const ref = referencePaths[i];
+ if (
+ ref.node.type !== 'JSXIdentifier' &&
+ ref.node.type !== 'Identifier'
+ ) {
+ continue;
+ }
+ const refParent = ref.parent;
+ if (refParent.type === 'JSXOpeningElement') {
+ isLikelyUsedAsType = true;
+ } else if (refParent.type === 'CallExpression') {
+ const callee = refParent.callee;
+ let fnName;
+ switch (callee.type) {
+ case 'Identifier':
+ fnName = callee.name;
+ break;
+ case 'MemberExpression':
+ fnName = callee.property.name;
+ break;
+ }
+ switch (fnName) {
+ case 'createElement':
+ case 'jsx':
+ case 'jsxDEV':
+ case 'jsxs':
+ isLikelyUsedAsType = true;
+ break;
+ }
+ }
+ if (isLikelyUsedAsType) {
+ // const X = ... + later
+ callback(inferredName, init, initPath);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ let hookCalls = new WeakMap();
+
+ function recordHookCall(functionNode, hookCallPath, hookName) {
+ if (!hookCalls.has(functionNode)) {
+ hookCalls.set(functionNode, []);
+ }
+ let hookCallsForFn = hookCalls.get(functionNode);
+ let key = '';
+ if (hookCallPath.parent.type === 'VariableDeclarator') {
+ // TODO: if there is no LHS, consider some other heuristic.
+ key = hookCallPath.parentPath.get('id').getSource();
+ }
+ hookCallsForFn.push({
+ name: hookName,
+ callee: hookCallPath.node.callee,
+ key,
+ });
+ }
+
+ function isBuiltinHook(hookName) {
+ switch (hookName) {
+ case 'useState':
+ case 'React.useState':
+ case 'useReducer':
+ case 'React.useReducer':
+ case 'useEffect':
+ case 'React.useEffect':
+ case 'useLayoutEffect':
+ case 'React.useLayoutEffect':
+ case 'useMemo':
+ case 'React.useMemo':
+ case 'useCallback':
+ case 'React.useCallback':
+ case 'useRef':
+ case 'React.useRef':
+ case 'useContext':
+ case 'React.useContext':
+ case 'useImperativeMethods':
+ case 'React.useImperativeMethods':
+ case 'useDebugValue':
+ case 'React.useDebugValue':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ function getHookCallsSignature(functionNode) {
+ const fnHookCalls = hookCalls.get(functionNode);
+ if (fnHookCalls === undefined) {
+ return null;
+ }
+ return {
+ key: fnHookCalls.map(call => call.name + '{' + call.key + '}').join('\n'),
+ customHooks: fnHookCalls
+ .filter(call => !isBuiltinHook(call.name))
+ .map(call => call.callee),
+ };
+ }
+
+ function createArgumentsForSignature(node, signature) {
+ const {key, customHooks} = signature;
+ const args = [node, t.stringLiteral(key)];
+ if (customHooks.length > 0) {
+ args.push(t.arrowFunctionExpression([], t.arrayExpression(customHooks)));
+ }
+ return args;
+ }
+
+ let seenForRegistration = new WeakSet();
+ let seenForSignature = new WeakSet();
+ let seenForHookCalls = new WeakSet();
+ let seenForOutro = new WeakSet();
+
+ return {
+ visitor: {
+ ExportDefaultDeclaration(path) {
+ const node = path.node;
+ const decl = node.declaration;
+ const declPath = path.get('declaration');
+ if (decl.type !== 'CallExpression') {
+ // For now, we only support possible HOC calls here.
+ // Named function declarations are handled in FunctionDeclaration.
+ // Anonymous direct exports like export default function() {}
+ // are currently ignored.
+ return;
+ }
+
+ // Make sure we're not mutating the same tree twice.
+ // This can happen if another Babel plugin replaces parents.
+ if (seenForRegistration.has(node)) {
+ return;
+ }
+ seenForRegistration.add(node);
+ // Don't mutate the tree above this point.
+
+ // This code path handles nested cases like:
+ // export default memo(() => {})
+ // In those cases it is more plausible people will omit names
+ // so they're worth handling despite possible false positives.
+ // More importantly, it handles the named case:
+ // export default memo(function Named() {})
+ const inferredName = '%default%';
+ const programPath = path.parentPath;
+ findInnerComponents(
+ inferredName,
+ declPath,
+ (persistentID, targetExpr, targetPath) => {
+ if (targetPath === null) {
+ // For case like:
+ // export default hoc(Foo)
+ // we don't want to wrap Foo inside the call.
+ // Instead we assume it's registered at definition.
+ return;
+ }
+ const handle = createRegistration(programPath, persistentID);
+ targetPath.replaceWith(
+ t.assignmentExpression('=', handle, targetExpr),
+ );
+ },
+ );
+ },
+ FunctionDeclaration: {
+ enter(path) {
+ const node = path.node;
+ let programPath;
+ let insertAfterPath;
+ switch (path.parent.type) {
+ case 'Program':
+ insertAfterPath = path;
+ programPath = path.parentPath;
+ break;
+ case 'ExportNamedDeclaration':
+ insertAfterPath = path.parentPath;
+ programPath = insertAfterPath.parentPath;
+ break;
+ case 'ExportDefaultDeclaration':
+ insertAfterPath = path.parentPath;
+ programPath = insertAfterPath.parentPath;
+ break;
+ default:
+ return;
+ }
+ const id = node.id;
+ if (id === null) {
+ // We don't currently handle anonymous default exports.
+ return;
+ }
+ const inferredName = id.name;
+ if (!isComponentishName(inferredName)) {
+ return;
+ }
+
+ // Make sure we're not mutating the same tree twice.
+ // This can happen if another Babel plugin replaces parents.
+ if (seenForRegistration.has(node)) {
+ return;
+ }
+ seenForRegistration.add(node);
+ // Don't mutate the tree above this point.
+
+ // export function Named() {}
+ // function Named() {}
+ findInnerComponents(
+ inferredName,
+ path,
+ (persistentID, targetExpr) => {
+ const handle = createRegistration(programPath, persistentID);
+ insertAfterPath.insertAfter(
+ t.expressionStatement(
+ t.assignmentExpression('=', handle, targetExpr),
+ ),
+ );
+ },
+ );
+ },
+ exit(path) {
+ const node = path.node;
+ const id = node.id;
+ if (id === null) {
+ return;
+ }
+ const signature = getHookCallsSignature(node);
+ if (signature === null) {
+ return;
+ }
+
+ // Make sure we're not mutating the same tree twice.
+ // This can happen if another Babel plugin replaces parents.
+ if (seenForSignature.has(node)) {
+ return;
+ }
+ seenForSignature.add(node);
+ // Don't muatte the tree above this point.
+
+ // Unlike with __register__, this needs to work for nested
+ // declarations too. So we need to search for a path where
+ // we can insert a statement rather than hardcoding it.
+ let insertAfterPath = null;
+ path.find(p => {
+ if (p.parentPath.isBlock()) {
+ insertAfterPath = p;
+ return true;
+ }
+ });
+ if (insertAfterPath === null) {
+ return;
+ }
+
+ insertAfterPath.insertAfter(
+ t.expressionStatement(
+ t.callExpression(
+ t.identifier(SIGNATURE),
+ createArgumentsForSignature(id, signature),
+ ),
+ ),
+ );
+ },
+ },
+ 'ArrowFunctionExpression|FunctionExpression': {
+ exit(path) {
+ const node = path.node;
+ const signature = getHookCallsSignature(node);
+ if (signature === null) {
+ return;
+ }
+
+ // Make sure we're not mutating the same tree twice.
+ // This can happen if another Babel plugin replaces parents.
+ if (seenForSignature.has(node)) {
+ return;
+ }
+ seenForSignature.add(node);
+ // Don't mutate the tree above this point.
+
+ if (path.parent.type === 'VariableDeclarator') {
+ let insertAfterPath = null;
+ path.find(p => {
+ if (p.parentPath.isBlock()) {
+ insertAfterPath = p;
+ return true;
+ }
+ });
+ if (insertAfterPath === null) {
+ return;
+ }
+ // Special case when a function would get an inferred name:
+ // let Foo = () => {}
+ // let Foo = function() {}
+ // We'll add signature it on next line so that
+ // we don't mess up the inferred 'Foo' function name.
+ insertAfterPath.insertAfter(
+ t.expressionStatement(
+ t.callExpression(
+ t.identifier(SIGNATURE),
+ createArgumentsForSignature(path.parent.id, signature),
+ ),
+ ),
+ );
+ // Result: let Foo = () => {}; __signature(Foo, ...);
+ } else {
+ // let Foo = hoc(() => {})
+ path.replaceWith(
+ t.callExpression(
+ t.identifier(SIGNATURE),
+ createArgumentsForSignature(node, signature),
+ ),
+ );
+ // Result: let Foo = hoc(__signature(() => {}, ...))
+ }
+ },
+ },
+ VariableDeclaration(path) {
+ const node = path.node;
+ let programPath;
+ let insertAfterPath;
+ switch (path.parent.type) {
+ case 'Program':
+ insertAfterPath = path;
+ programPath = path.parentPath;
+ break;
+ case 'ExportNamedDeclaration':
+ insertAfterPath = path.parentPath;
+ programPath = insertAfterPath.parentPath;
+ break;
+ case 'ExportDefaultDeclaration':
+ insertAfterPath = path.parentPath;
+ programPath = insertAfterPath.parentPath;
+ break;
+ default:
+ return;
+ }
+
+ // Make sure we're not mutating the same tree twice.
+ // This can happen if another Babel plugin replaces parents.
+ if (seenForRegistration.has(node)) {
+ return;
+ }
+ seenForRegistration.add(node);
+ // Don't mutate the tree above this point.
+
+ const declPaths = path.get('declarations');
+ if (declPaths.length !== 1) {
+ return;
+ }
+ const declPath = declPaths[0];
+ const inferredName = declPath.node.id.name;
+ findInnerComponents(
+ inferredName,
+ declPath,
+ (persistentID, targetExpr, targetPath) => {
+ if (targetPath === null) {
+ // For case like:
+ // export const Something = hoc(Foo)
+ // we don't want to wrap Foo inside the call.
+ // Instead we assume it's registered at definition.
+ return;
+ }
+ const handle = createRegistration(programPath, persistentID);
+ if (
+ (targetExpr.type === 'ArrowFunctionExpression' ||
+ targetExpr.type === 'FunctionExpression') &&
+ targetPath.parent.type === 'VariableDeclarator'
+ ) {
+ // Special case when a function would get an inferred name:
+ // let Foo = () => {}
+ // let Foo = function() {}
+ // We'll register it on next line so that
+ // we don't mess up the inferred 'Foo' function name.
+ insertAfterPath.insertAfter(
+ t.expressionStatement(
+ t.assignmentExpression('=', handle, declPath.node.id),
+ ),
+ );
+ // Result: let Foo = () => {}; _c1 = Foo;
+ } else {
+ // let Foo = hoc(() => {})
+ targetPath.replaceWith(
+ t.assignmentExpression('=', handle, targetExpr),
+ );
+ // Result: let Foo = _c1 = hoc(() => {})
+ }
+ },
+ );
+ },
+ CallExpression(path) {
+ const node = path.node;
+ const callee = node.callee;
+
+ let name = null;
+ switch (callee.type) {
+ case 'Identifier':
+ name = callee.name;
+ break;
+ case 'MemberExpression':
+ name = callee.property.name;
+ break;
+ }
+ if (name === null || !/^use[A-Z]/.test(name)) {
+ return;
+ }
+
+ // Make sure we're not recording the same calls twice.
+ // This can happen if another Babel plugin replaces parents.
+ if (seenForHookCalls.has(node)) {
+ return;
+ }
+ seenForHookCalls.add(node);
+ // Don't mutate the tree above this point.
+
+ const fn = path.scope.getFunctionParent();
+ if (fn === null) {
+ return;
+ }
+ recordHookCall(fn.block, path, name);
+ },
+ Program: {
+ exit(path) {
+ const registrations = registrationsByProgramPath.get(path);
+ if (registrations === undefined) {
+ return;
+ }
+
+ // Make sure we're not mutating the same tree twice.
+ // This can happen if another Babel plugin replaces parents.
+ const node = path.node;
+ if (seenForOutro.has(node)) {
+ return;
+ }
+ seenForOutro.add(node);
+ // Don't mutate the tree above this point.
+
+ registrationsByProgramPath.delete(path);
+ const declarators = [];
+ path.pushContainer('body', t.variableDeclaration('var', declarators));
+ registrations.forEach(({handle, persistentID}) => {
+ path.pushContainer(
+ 'body',
+ t.expressionStatement(
+ t.callExpression(t.identifier('__register__'), [
+ handle,
+ t.stringLiteral(persistentID),
+ ]),
+ ),
+ );
+ declarators.push(t.variableDeclarator(handle));
+ });
+ },
+ },
+ },
+ };
+}
\ No newline at end of file
diff --git a/src/reactHotLoader.js b/src/reactHotLoader.js
index d682a53a9..602e52aa2 100644
--- a/src/reactHotLoader.js
+++ b/src/reactHotLoader.js
@@ -15,6 +15,7 @@ import {
isTypeBlacklisted,
registerComponent,
updateFunctionProxyById,
+ addSignature,
} from './reconciler/proxies';
import configuration from './configuration';
import logger from './logger';
@@ -33,8 +34,14 @@ const hookWrapper = hook => (cb, deps) => {
return hook(cb, deps);
};
+const noDeps = () => [];
+
const reactHotLoader = {
IS_REACT_MERGE_ENABLED: false,
+ signature(type, key, getCustomHooks = noDeps) {
+ addSignature(type, { key, getCustomHooks });
+ return type;
+ },
register(type, uniqueLocalName, fileName, options = {}) {
const id = `${fileName}#${uniqueLocalName}`;
diff --git a/src/reconciler/componentComparator.js b/src/reconciler/componentComparator.js
index 72ee0c7db..f2de14757 100644
--- a/src/reconciler/componentComparator.js
+++ b/src/reconciler/componentComparator.js
@@ -1,4 +1,11 @@
-import { getIdByType, getProxyByType, isColdType, isRegisteredComponent, updateProxyById } from './proxies';
+import {
+ getIdByType,
+ getProxyByType,
+ getSignature,
+ isColdType,
+ isRegisteredComponent,
+ updateProxyById,
+} from './proxies';
import { hotComparisonOpen } from '../global/generation';
import { isForwardType, isMemoType, isReactClass, isReloadableComponent } from '../internal/reactUtils';
import { areSwappable } from './utils';
@@ -11,12 +18,53 @@ const getInnerComponentType = component => {
return unwrapper ? unwrapper() : component;
};
+function haveEqualSignatures(prevType, nextType) {
+ const prevSignature = getSignature(prevType);
+ const nextSignature = getSignature(nextType);
+
+ if (prevSignature === undefined && nextSignature === undefined) {
+ return true;
+ }
+ if (prevSignature === undefined || nextSignature === undefined) {
+ return false;
+ }
+ if (prevSignature.key !== nextSignature.key) {
+ return false;
+ }
+
+ // TODO: we might need to calculate previous signature earlier in practice,
+ // such as during the first time a component is resolved. We'll revisit this.
+ const prevCustomHooks = prevSignature.getCustomHooks();
+ const nextCustomHooks = nextSignature.getCustomHooks();
+ if (prevCustomHooks.length !== nextCustomHooks.length) {
+ return false;
+ }
+
+ for (let i = 0; i < nextCustomHooks.length; i++) {
+ if (!haveEqualSignatures(prevCustomHooks[i], nextCustomHooks[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
const compareRegistered = (a, b) => {
if (isRegisteredComponent(a) || isRegisteredComponent(b)) {
if (resolveType(a) !== resolveType(b)) {
return false;
}
}
+
+ // compare signatures of two components
+ // non-equal component have to remount and there is two options to do it
+ // - fail the comparison, remounting all tree below
+ // - fulfill it, but set `_debugNeedsRemount` on a fiber to drop only local state
+ // the second way is not published yet, so going with the first one
+ if (!haveEqualSignatures(a, b)) {
+ logger.warn('Hook order change detected:', a, 'state has been reset');
+ return false;
+ }
return true;
};
diff --git a/src/reconciler/proxies.js b/src/reconciler/proxies.js
index c5dc64d13..6411737e8 100644
--- a/src/reconciler/proxies.js
+++ b/src/reconciler/proxies.js
@@ -6,6 +6,7 @@ import { incrementHotGeneration } from '../global/generation';
const merge = require('lodash/merge');
+let signatures;
let proxiesByID;
let blackListedProxies;
let registeredComponents;
@@ -78,12 +79,16 @@ export const blacklistByType = type => blackListedProxies.set(type, true);
export const setComponentOptions = (component, options) => componentOptions.set(component, options);
+export const addSignature = (type, signature) => signatures.set(type, signature);
+export const getSignature = type => signatures.get(type);
+
export const resetProxies = () => {
proxiesByID = {};
idsByType = new WeakMap();
blackListedProxies = new WeakMap();
registeredComponents = new WeakMap();
componentOptions = new WeakMap();
+ signatures = new WeakMap();
resetClassProxies();
};
diff --git a/test/__babel_fixtures__/hooks.js b/test/__babel_fixtures__/hooks.js
new file mode 100644
index 000000000..13c997a46
--- /dev/null
+++ b/test/__babel_fixtures__/hooks.js
@@ -0,0 +1,40 @@
+import React, {useState} from 'react';
+
+const NoHooks = () =>
no hooks
;
+
+const UseStateHook = () => {
+ useState(42);
+};
+
+const UseStateHooks = () => {
+ useState(42);
+ useState(24);
+};
+
+const useEffectHook = () => {
+ React.useEffect(() => ({}));
+ useState(24);
+ React.useState(42);
+};
+
+const useForwardRefHook = React.forwardRef(() => {
+ React.useEffect(() => ({}));
+ useState(24);
+ React.useState(42);
+});
+
+const useForwardRefFunctionHook = React.forwardRef(function () {
+ React.useEffect(() => ({}));
+ useState(24);
+ React.useState(42);
+});
+
+const useCustomHook = () => {
+ useState(42);
+ useEffectHook();
+};
+
+function useFunc () {
+ useState(42);
+ useEffectHook();
+};
\ No newline at end of file
diff --git a/test/__snapshots__/babel.test.js.snap b/test/__snapshots__/babel.test.js.snap
index 05f476cb2..d8868f1f0 100644
--- a/test/__snapshots__/babel.test.js.snap
+++ b/test/__snapshots__/babel.test.js.snap
@@ -12,6 +12,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
var _arguments = arguments;
@@ -69,6 +73,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -124,6 +132,10 @@ function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, a
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
var _this = this;
@@ -202,6 +214,10 @@ function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, a
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
var _this = this;
@@ -278,6 +294,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -331,6 +351,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -386,6 +410,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -442,6 +470,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -495,6 +527,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
var _arguments = arguments;
@@ -554,6 +590,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -611,6 +651,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -666,6 +710,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -717,6 +765,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -770,6 +822,10 @@ var _createClass = function () { function defineProperties(target, props) { for
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
@@ -826,6 +882,10 @@ exports[`babel Targetting "es2015" copies arrow function body block onto hidden
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Foo = function Foo() {
_classCallCheck(this, Foo);
};
@@ -877,6 +937,10 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var A = 42;
function B() {
function R() {}
@@ -967,6 +1031,10 @@ Object.defineProperty(exports, \\"__esModule\\", {
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var Counter = function Counter() {
_classCallCheck(this, Counter);
};
@@ -1058,6 +1126,116 @@ exports.e = e;
exports.z = z;"
`;
+exports[`babel Targetting "es2015" tags potential React components hooks.js 1`] = `
+"'use strict';
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+(function () {
+ var enterModule = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).enterModule;
+ enterModule && enterModule(module);
+})();
+
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
+var NoHooks = function NoHooks() {
+ return _react2.default.createElement(
+ 'div',
+ null,
+ 'no hooks'
+ );
+};
+
+var UseStateHook = function UseStateHook() {
+ (0, _react.useState)(42);
+};
+
+__signature__(UseStateHook, 'useState{}');
+
+var UseStateHooks = function UseStateHooks() {
+ (0, _react.useState)(42);
+ (0, _react.useState)(24);
+};
+
+__signature__(UseStateHooks, 'useState{}\\\\nuseState{}');
+
+var useEffectHook = function useEffectHook() {
+ _react2.default.useEffect(function () {
+ return {};
+ });
+ (0, _react.useState)(24);
+ _react2.default.useState(42);
+};
+
+__signature__(useEffectHook, 'useEffect{}\\\\nuseState{}\\\\nuseState{}');
+
+var useForwardRefHook = _react2.default.forwardRef(__signature__(function () {
+ _react2.default.useEffect(function () {
+ return {};
+ });
+ (0, _react.useState)(24);
+ _react2.default.useState(42);
+}, 'useEffect{}\\\\nuseState{}\\\\nuseState{}'));
+
+var useForwardRefFunctionHook = _react2.default.forwardRef(__signature__(function () {
+ _react2.default.useEffect(function () {
+ return {};
+ });
+ (0, _react.useState)(24);
+ _react2.default.useState(42);
+}, 'useEffect{}\\\\nuseState{}\\\\nuseState{}'));
+
+var useCustomHook = function useCustomHook() {
+ (0, _react.useState)(42);
+ useEffectHook();
+};
+
+__signature__(useCustomHook, 'useState{}\\\\nuseEffectHook{}', function () {
+ return [useEffectHook];
+});
+
+function useFunc() {
+ (0, _react.useState)(42);
+ useEffectHook();
+}
+__signature__(useFunc, 'useState{}\\\\nuseEffectHook{}', function () {
+ return [useEffectHook];
+});
+
+;
+;
+
+(function () {
+ var reactHotLoader = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).default;
+
+ if (!reactHotLoader) {
+ return;
+ }
+
+ reactHotLoader.register(NoHooks, 'NoHooks', __FILENAME__);
+ reactHotLoader.register(UseStateHook, 'UseStateHook', __FILENAME__);
+ reactHotLoader.register(UseStateHooks, 'UseStateHooks', __FILENAME__);
+ reactHotLoader.register(useEffectHook, 'useEffectHook', __FILENAME__);
+ reactHotLoader.register(useForwardRefHook, 'useForwardRefHook', __FILENAME__);
+ reactHotLoader.register(useForwardRefFunctionHook, 'useForwardRefFunctionHook', __FILENAME__);
+ reactHotLoader.register(useCustomHook, 'useCustomHook', __FILENAME__);
+ reactHotLoader.register(useFunc, 'useFunc', __FILENAME__);
+})();
+
+;
+
+(function () {
+ var leaveModule = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).leaveModule;
+ leaveModule && leaveModule(module);
+})();"
+`;
+
exports[`babel Targetting "es2015" tags potential React components issue 246.js 1`] = `
"\\"use strict\\";
@@ -1071,6 +1249,10 @@ exports.spread = spread;
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
function spread() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
@@ -1110,6 +1292,10 @@ Object.defineProperty(exports, \\"__esModule\\", {
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
var _default = 10;
var _default2 = 42;
exports.default = _default2;
@@ -1142,6 +1328,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = (a, b) => {
@@ -1186,6 +1376,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.onClick = e => e.target.value;
@@ -1226,6 +1420,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = async (a, b) => await b(a);
@@ -1266,6 +1464,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = async (a, b) => {
@@ -1308,6 +1510,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = (a, b) => {
@@ -1350,6 +1556,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = (a = \\"foo\\") => {
@@ -1392,6 +1602,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = ({ a }, { b }) => {
@@ -1434,6 +1648,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.onClick = e => e.target.value;
@@ -1474,6 +1692,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = (a, b) => {
@@ -1520,6 +1742,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = (a, b) => {
@@ -1566,6 +1792,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = (a, b) => {
@@ -1610,6 +1840,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = 2;
@@ -1650,6 +1884,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = function (a, b) {
@@ -1692,6 +1930,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {
constructor() {
this.bar = (a, b) => {
@@ -1738,6 +1980,10 @@ exports[`babel Targetting "modern" copies arrow function body block onto hidden
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Foo {}
Foo.bar = (a, b) => {
@@ -1783,6 +2029,10 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
const A = 42;
function B() {
function R() {}
@@ -1851,6 +2101,10 @@ Object.defineProperty(exports, \\"__esModule\\", {
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
class Counter {}
const _default = () => React.createElement(
@@ -1936,6 +2190,104 @@ exports.e = e;
exports.z = z;"
`;
+exports[`babel Targetting "modern" tags potential React components hooks.js 1`] = `
+"'use strict';
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+(function () {
+ var enterModule = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).enterModule;
+ enterModule && enterModule(module);
+})();
+
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
+const NoHooks = () => _react2.default.createElement(
+ 'div',
+ null,
+ 'no hooks'
+);
+
+const UseStateHook = () => {
+ (0, _react.useState)(42);
+};
+
+__signature__(UseStateHook, 'useState{}');
+
+const UseStateHooks = () => {
+ (0, _react.useState)(42);
+ (0, _react.useState)(24);
+};
+
+__signature__(UseStateHooks, 'useState{}\\\\nuseState{}');
+
+const useEffectHook = () => {
+ _react2.default.useEffect(() => ({}));
+ (0, _react.useState)(24);
+ _react2.default.useState(42);
+};
+
+__signature__(useEffectHook, 'useEffect{}\\\\nuseState{}\\\\nuseState{}');
+
+const useForwardRefHook = _react2.default.forwardRef(__signature__(() => {
+ _react2.default.useEffect(() => ({}));
+ (0, _react.useState)(24);
+ _react2.default.useState(42);
+}, 'useEffect{}\\\\nuseState{}\\\\nuseState{}'));
+
+const useForwardRefFunctionHook = _react2.default.forwardRef(__signature__(function () {
+ _react2.default.useEffect(() => ({}));
+ (0, _react.useState)(24);
+ _react2.default.useState(42);
+}, 'useEffect{}\\\\nuseState{}\\\\nuseState{}'));
+
+const useCustomHook = () => {
+ (0, _react.useState)(42);
+ useEffectHook();
+};
+
+__signature__(useCustomHook, 'useState{}\\\\nuseEffectHook{}', () => [useEffectHook]);
+
+function useFunc() {
+ (0, _react.useState)(42);
+ useEffectHook();
+}
+__signature__(useFunc, 'useState{}\\\\nuseEffectHook{}', () => [useEffectHook]);
+
+;
+;
+
+(function () {
+ var reactHotLoader = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).default;
+
+ if (!reactHotLoader) {
+ return;
+ }
+
+ reactHotLoader.register(NoHooks, 'NoHooks', __FILENAME__);
+ reactHotLoader.register(UseStateHook, 'UseStateHook', __FILENAME__);
+ reactHotLoader.register(UseStateHooks, 'UseStateHooks', __FILENAME__);
+ reactHotLoader.register(useEffectHook, 'useEffectHook', __FILENAME__);
+ reactHotLoader.register(useForwardRefHook, 'useForwardRefHook', __FILENAME__);
+ reactHotLoader.register(useForwardRefFunctionHook, 'useForwardRefFunctionHook', __FILENAME__);
+ reactHotLoader.register(useCustomHook, 'useCustomHook', __FILENAME__);
+ reactHotLoader.register(useFunc, 'useFunc', __FILENAME__);
+})();
+
+;
+
+(function () {
+ var leaveModule = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : require('react-hot-loader')).leaveModule;
+ leaveModule && leaveModule(module);
+})();"
+`;
+
exports[`babel Targetting "modern" tags potential React components issue 246.js 1`] = `
"\\"use strict\\";
@@ -1949,6 +2301,10 @@ exports.spread = spread;
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
function spread(...args) {
return args.push(1);
}
@@ -1984,6 +2340,10 @@ Object.defineProperty(exports, \\"__esModule\\", {
enterModule && enterModule(module);
})();
+var __signature__ = typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal.default.signature : function (a) {
+ return a;
+};
+
const _default = 10;
const _default2 = 42;
exports.default = _default2;