-
Notifications
You must be signed in to change notification settings - Fork 27.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HMR support + React Refresh (vercel/turborepo#252)
This PR implements HMR support with React Refresh built-in. For now, in order for React Refresh to be enabled, you'll need the `@next/react-refresh-utils` package to be resolveable: `yarn add @next/react-refresh-utils` in your app folder. * Depends on vercel/turborepo#266 * Integrated both HMR-and-React-Refresh-specific logic directly into the ES chunks' runtime. Webpack has more complex setup here, but for now this makes the logic much more easy to follow since everything is in one place. I have yet to implement the "dependencies" signature for `hot.accept`/`hot.dispose`, since React Refresh does not depend on them. We'll have to see if they're even used in the wild or if we should deprecate them. * Only implemented the [module API](https://webpack.js.org/api/hot-module-replacement/#module-api), not the [management API](https://webpack.js.org/api/hot-module-replacement/#management-api). We apply all updates as soon as we receive them. * Added support for "runtime entries" to ES chunks. These are assets that will be executed *before* the main entry of an ES chunk. They'll be useful for polyfills in the future, but for now they're here to evaluate the react refresh runtime before any module is instantiated. Next steps for HMR: * Implement CSS HMR * Implement (or decide to deprecate) the [dependencies form](https://webpack.js.org/api/hot-module-replacement/#accept) of `hot.accept`/`hot.dispose` * Clean up `runtime.js` some more: switch to TypeScript, split into multiple files, etc. It'd be nice if all of this could be done at compile time, but how to achieve this is unclear at the moment. _Can we run turbopack to compile turbopack?_
- Loading branch information
Showing
7 changed files
with
156 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
packages/next-swc/crates/next-core/src/react_refresh.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
use anyhow::{anyhow, Result}; | ||
use turbo_tasks::primitives::{BoolVc, StringVc}; | ||
use turbo_tasks_fs::FileSystemPathVc; | ||
use turbopack::ecmascript::{ | ||
chunk::EcmascriptChunkPlaceableVc, | ||
resolve::{apply_cjs_specific_options, cjs_resolve}, | ||
}; | ||
use turbopack_core::{ | ||
context::AssetContextVc, | ||
environment::EnvironmentVc, | ||
issue::{Issue, IssueSeverity, IssueSeverityVc, IssueVc}, | ||
resolve::{parse::RequestVc, ResolveResult}, | ||
}; | ||
|
||
#[turbo_tasks::function] | ||
fn react_refresh_request() -> RequestVc { | ||
RequestVc::parse_string("@next/react-refresh-utils/dist/runtime".to_string()) | ||
} | ||
|
||
/// Checks whether we can resolve the React Refresh runtime module from the | ||
/// given path. Emits an issue if we can't. | ||
/// | ||
/// Differs from `resolve_react_refresh` in that we don't have access to an | ||
/// [AssetContextVc] when we first want to check for RR. | ||
#[turbo_tasks::function] | ||
pub async fn assert_can_resolve_react_refresh( | ||
path: FileSystemPathVc, | ||
environment: EnvironmentVc, | ||
) -> Result<BoolVc> { | ||
let resolve_options = apply_cjs_specific_options(turbopack::resolve_options(path, environment)); | ||
let result = turbopack_core::resolve::resolve(path, react_refresh_request(), resolve_options); | ||
|
||
Ok(match &*result.await? { | ||
ResolveResult::Single(_, _) => BoolVc::cell(true), | ||
_ => { | ||
ReactRefreshResolvingIssue { | ||
path, | ||
description: StringVc::cell( | ||
"could not resolve the `@next/react-refresh-utils/dist/runtime` module" | ||
.to_string(), | ||
), | ||
} | ||
.cell() | ||
.as_issue() | ||
.emit(); | ||
BoolVc::cell(false) | ||
} | ||
}) | ||
} | ||
|
||
/// Resolves the React Refresh runtime module from the given [AssetContextVc]. | ||
#[turbo_tasks::function] | ||
pub async fn resolve_react_refresh(context: AssetContextVc) -> Result<EcmascriptChunkPlaceableVc> { | ||
match &*cjs_resolve(react_refresh_request(), context).await? { | ||
ResolveResult::Single(asset, _) => { | ||
if let Some(placeable) = EcmascriptChunkPlaceableVc::resolve_from(asset).await? { | ||
Ok(placeable) | ||
} else { | ||
Err(anyhow!("React Refresh runtime asset is not placeable")) | ||
} | ||
} | ||
// The react-refresh-runtime module is not installed. | ||
ResolveResult::Unresolveable(_) => Err(anyhow!( | ||
"could not resolve the `@next/react-refresh-utils/dist/runtime` module" | ||
)), | ||
_ => Err(anyhow!("invalid React Refresh runtime asset")), | ||
} | ||
} | ||
|
||
/// An issue that occurred while resolving the React Refresh runtime module. | ||
#[turbo_tasks::value(shared)] | ||
pub struct ReactRefreshResolvingIssue { | ||
path: FileSystemPathVc, | ||
description: StringVc, | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl Issue for ReactRefreshResolvingIssue { | ||
#[turbo_tasks::function] | ||
fn severity(&self) -> IssueSeverityVc { | ||
IssueSeverity::Warning.into() | ||
} | ||
|
||
#[turbo_tasks::function] | ||
async fn title(&self) -> Result<StringVc> { | ||
Ok(StringVc::cell( | ||
"An issue occurred while resolving the React Refresh runtime. React Refresh will be \ | ||
disabled.\nTo enable React Refresh, install the `react-refresh` and \ | ||
`@next/react-refresh-utils` modules." | ||
.to_string(), | ||
)) | ||
} | ||
|
||
#[turbo_tasks::function] | ||
fn category(&self) -> StringVc { | ||
StringVc::cell("other".to_string()) | ||
} | ||
|
||
#[turbo_tasks::function] | ||
fn context(&self) -> FileSystemPathVc { | ||
self.path | ||
} | ||
|
||
#[turbo_tasks::function] | ||
fn description(&self) -> StringVc { | ||
self.description | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters