aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bundler.zig16
-rw-r--r--src/cli.zig12
-rw-r--r--src/options.zig31
3 files changed, 56 insertions, 3 deletions
diff --git a/src/bundler.zig b/src/bundler.zig
index 8b4263c53..87c34a5ff 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -225,6 +225,7 @@ pub fn NewBundler(cache_files: bool) type {
log: *logger.Log,
tmpfile_byte_offset: u32 = 0,
code_end_byte_offset: u32 = 0,
+ has_jsx: bool = false,
pub const current_version: u32 = 1;
@@ -317,6 +318,19 @@ pub fn NewBundler(cache_files: bool) type {
while (this.resolve_queue.readItem()) |resolved| {
try this.processFile(resolved);
}
+
+ if (this.has_jsx and this.bundler.options.jsx.supports_fast_refresh) {
+ if (this.bundler.resolver.resolve(
+ this.bundler.fs.top_level_dir,
+ "react-refresh/runtime",
+ .require,
+ )) |refresh_runtime| {
+ if (!this.resolved_paths.contains(refresh_runtime.path_pair.primary.text)) {
+ try this.processFile(refresh_runtime);
+ }
+ } else |err| {}
+ }
+
// Ensure we never overflow
this.code_end_byte_offset = @truncate(
u32,
@@ -719,6 +733,7 @@ pub fn NewBundler(cache_files: bool) type {
.hash = package.hash,
},
);
+ this.has_jsx = this.has_jsx or strings.eql(package.name, this.bundler.options.jsx.package_name);
}
var path_extname_length = @truncate(u8, std.fs.path.extension(package_relative_path).len);
@@ -1229,6 +1244,7 @@ pub fn NewBundler(cache_files: bool) type {
opts.transform_require_to_import = true;
opts.can_import_from_bundle = bundler.options.node_modules_bundle != null;
opts.features.hot_module_reloading = bundler.options.hot_module_reloading;
+ opts.features.react_fast_refresh = opts.features.hot_module_reloading and jsx.parse and bundler.options.jsx.supports_fast_refresh;
opts.filepath_hash_for_hmr = file_hash orelse 0;
const value = (bundler.resolver.caches.js.parse(allocator, opts, bundler.options.define, bundler.log, &source) catch null) orelse return null;
return ParseResult{
diff --git a/src/cli.zig b/src/cli.zig
index 744369472..f5d3ed4c0 100644
--- a/src/cli.zig
+++ b/src/cli.zig
@@ -123,7 +123,7 @@ pub const Cli = struct {
clap.parseParam("--jsx-runtime <STR> \"automatic\" (default) or \"classic\"") catch unreachable,
clap.parseParam("--jsx-production Use jsx instead of jsxDEV (default) for the automatic runtime") catch unreachable,
clap.parseParam("--extension-order <STR>... defaults to: .tsx,.ts,.jsx,.js,.json ") catch unreachable,
- clap.parseParam("--react-fast-refresh Enable React Fast Refresh (not implemented yet)") catch unreachable,
+ clap.parseParam("--disable-react-fast-refresh Disable React Fast Refresh. Enabled if --serve is set and --jsx-production is not set. Otherwise, it's a noop.") catch unreachable,
clap.parseParam("--tsconfig-override <STR> Load tsconfig from path instead of cwd/tsconfig.json") catch unreachable,
clap.parseParam("--platform <STR> \"browser\" or \"node\". Defaults to \"browser\"") catch unreachable,
clap.parseParam("--main-fields <STR>... Main fields to lookup in package.json. Defaults to --platform dependent") catch unreachable,
@@ -181,7 +181,15 @@ pub const Cli = struct {
var jsx_import_source = args.option("--jsx-import-source");
var jsx_runtime = args.option("--jsx-runtime");
var jsx_production = args.flag("--jsx-production");
- var react_fast_refresh = args.flag("--react-fast-refresh");
+ var react_fast_refresh = false;
+
+ if (serve or args.flag("--new-jsb")) {
+ react_fast_refresh = true;
+ if (args.flag("--disable-react-fast-refresh") or jsx_production) {
+ react_fast_refresh = false;
+ }
+ }
+
var main_fields = args.options("--main-fields");
var node_modules_bundle_path = args.option("--jsb") orelse brk: {
diff --git a/src/options.zig b/src/options.zig
index e9f48ee17..54483e801 100644
--- a/src/options.zig
+++ b/src/options.zig
@@ -389,11 +389,36 @@ pub const JSX = struct {
/// /** @jsxImportSource @emotion/core */
import_source: string = "react/jsx-dev-runtime",
classic_import_source: string = "react",
+ package_name: []const u8 = "react",
+ supports_fast_refresh: bool = false,
jsx: string = "jsxDEV",
development: bool = true,
parse: bool = true,
+
+ pub fn parsePackageName(str: string) string {
+ if (str[0] == '@') {
+ if (strings.indexOfChar(str[1..], '/')) |first_slash| {
+ var remainder = str[1 + first_slash + 1 ..];
+
+ if (strings.indexOfChar(remainder, '/')) |last_slash| {
+ return str[0 .. first_slash + 1 + last_slash + 1];
+ }
+ }
+ }
+
+ if (strings.indexOfChar(str, '/')) |first_slash| {
+ return str[0..first_slash];
+ }
+
+ return str;
+ }
+
+ pub fn isReactLike(pragma: *const Pragma) bool {
+ return strings.eqlComptime(pragma.package_name, "react") or strings.eqlComptime(pragma.package_name, "@emotion/jsx") or strings.eqlComptime(pragma.package_name, "@emotion/react");
+ }
+
pub const Defaults = struct {
pub var Factory = [_]string{ "React", "createElement" };
pub var Fragment = [_]string{ "React", "Fragment" };
@@ -453,12 +478,17 @@ pub const JSX = struct {
if (jsx.import_source.len > 0) {
pragma.import_source = jsx.import_source;
+ pragma.package_name = parsePackageName(pragma.import_source);
+ pragma.supports_fast_refresh = pragma.development and pragma.isReactLike();
} else if (jsx.development) {
pragma.import_source = Defaults.ImportSourceDev;
pragma.jsx = Defaults.JSXFunctionDev;
+ pragma.supports_fast_refresh = true;
+ pragma.package_name = "react";
} else {
pragma.import_source = Defaults.ImportSource;
pragma.jsx = Defaults.JSXFunction;
+ pragma.supports_fast_refresh = false;
}
pragma.development = jsx.development;
@@ -565,7 +595,6 @@ pub const BundleOptions = struct {
resolve_dir: string = "/",
jsx: JSX.Pragma = JSX.Pragma{},
- react_fast_refresh: bool = false,
hot_module_reloading: bool = false,
inject: ?[]string = null,
public_url: string = "",