diff --git a/packages/react-fast-refresh/client-runtime.js b/packages/react-fast-refresh/client-runtime.js index c65aff1ad7..d0feab6ec0 100644 --- a/packages/react-fast-refresh/client-runtime.js +++ b/packages/react-fast-refresh/client-runtime.js @@ -1,6 +1,11 @@ -const enabled = __meteor_runtime_config__.reactFastRefreshEnabled; +let initialized = false; + +window.___INIT_METEOR_FAST_REFRESH = function init() { + if (initialized) { + return; + } + initialized = true; -if (enabled && process.env.NODE_ENV !== 'production' && module.hot) { const runtime = require('react-refresh/runtime'); let timeout = null; @@ -113,3 +118,10 @@ if (enabled && process.env.NODE_ENV !== 'production' && module.hot) { } }); } + +if ( + __meteor_runtime_config__ && + __meteor_runtime_config__.reactFastRefreshEnabled +) { + window.___INIT_METEOR_FAST_REFRESH(); +} diff --git a/packages/react-fast-refresh/server.js b/packages/react-fast-refresh/server.js index 1fde5134d8..ac24948182 100644 --- a/packages/react-fast-refresh/server.js +++ b/packages/react-fast-refresh/server.js @@ -1,20 +1,20 @@ - let enabled = !process.env.DISABLE_REACT_FAST_REFRESH; if (enabled) { try { // React fast refresh requires react 16.9.0 or newer - const semver = require('semver'); + const semverGte = require('semver/functions/gte'); const pkg = require('react/package.json'); enabled = pkg && pkg.version && - semver.gte(pkg.version, '16.9.0'); + semverGte(pkg.version, '16.9.0'); } catch (e) { // If the app doesn't directly depend on react, leave react-refresh // enabled in case a package or indirect dependency uses react. } } +// Needed for compatibility when build plugins use ReactFastRefresh.babelPlugin if (typeof __meteor_runtime_config__ === 'object') { __meteor_runtime_config__.reactFastRefreshEnabled = enabled; } @@ -23,6 +23,48 @@ const babelPlugin = enabled ? require('react-refresh/babel') : null; +// Babel plugin that adds a call to global.___INIT_METEOR_FAST_REFRESH() +// at the start of every file compiled with react-refresh to ensure the runtime +// is enabled if it is used. +function enableReactRefreshBabelPlugin(babel) { + const { types: t } = babel; + + return { + name: "meteor-enable-react-fast-refresh", + post(state) { + // This is the path for the Program node + let path = state.path; + let method = t.identifier("___INIT_METEOR_FAST_REFRESH"); + let call = t.callExpression( + t.memberExpression(t.identifier('global'), method), + [] + ); + path.unshiftContainer("body", t.expressionStatement(call)); + }, + }; +} + +let deprecationWarned = false; + ReactFastRefresh = { - babelPlugin, + get babelPlugin() { + if (!deprecationWarned) { + console.warn( + 'ReactFastRefresh.babelPlugin is deprecated and is incompatible with HMR on Cordova. Use ReactFastRefresh.getBabelPlugins() instead.' + ); + deprecationWarned = true; + } + + return babelPlugin; + }, + getBabelPlugins() { + if (!babelPlugin) { + return []; + } + + return [ + babelPlugin, + enableReactRefreshBabelPlugin, + ] + } };