aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-01-29 23:48:39 -0800
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-01-29 23:48:39 -0800
commit857e9bee0085b13320e3ded423ed8caff30c4e40 (patch)
tree333e1c3e524a46ed908fa921e5af2bb847e46841
parent711e0cef78eb14d92e6789f9c6fe3c434344bfab (diff)
downloadbun-857e9bee0085b13320e3ded423ed8caff30c4e40.tar.gz
bun-857e9bee0085b13320e3ded423ed8caff30c4e40.tar.zst
bun-857e9bee0085b13320e3ded423ed8caff30c4e40.zip
Embed React Fast Refresh in Bun
Fixes https://github.com/Jarred-Sumner/bun/issues/62 If the project has it's own copy of react fast refresh and is bundling, it will use that instead.
-rw-r--r--.gitignore1
-rw-r--r--src/import_record.zig9
-rw-r--r--src/js_parser/js_parser.zig135
-rw-r--r--src/linker.zig80
-rw-r--r--src/node_module_bundle.zig4
-rw-r--r--src/options.zig36
-rw-r--r--src/react-refresh.js9
-rw-r--r--src/resolver/resolver.zig7
-rw-r--r--src/runtime.footer.with-refresh.js28
-rw-r--r--src/runtime.js3
-rw-r--r--src/runtime.version2
-rw-r--r--src/runtime.zig40
-rw-r--r--src/runtime/hmr.ts43
-rw-r--r--src/runtime/index-with-refresh.ts10
14 files changed, 327 insertions, 80 deletions
diff --git a/.gitignore b/.gitignore
index e874a8ec3..91b8fe02d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ yarn.lock
dist
*.log
*.out.js
+*.out.refresh.js
/package-lock.json
build
*.wat
diff --git a/src/import_record.zig b/src/import_record.zig
index c73896a0b..31ad8185f 100644
--- a/src/import_record.zig
+++ b/src/import_record.zig
@@ -128,6 +128,15 @@ pub const ImportRecord = struct {
kind: ImportKind,
+ tag: Tag = Tag.none,
+
+ pub const Tag = enum {
+ none,
+ react_refresh,
+ jsx_import,
+ jsx_classic,
+ };
+
pub const PrintMode = enum {
normal,
import_path,
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig
index bfe7c3a2f..bb95d9934 100644
--- a/src/js_parser/js_parser.zig
+++ b/src/js_parser/js_parser.zig
@@ -845,6 +845,7 @@ const StaticSymbolName = struct {
pub const __HMRModule = NewStaticSymbol("HMR");
pub const __HMRClient = NewStaticSymbol("Bun");
pub const __FastRefreshModule = NewStaticSymbol("FastHMR");
+ pub const __FastRefreshRuntime = NewStaticSymbol("FastRefresh");
pub const @"$$m" = NewStaticSymbol("$$m");
@@ -2468,33 +2469,35 @@ pub const Parser = struct {
if (p.options.features.react_fast_refresh) {
defer did_import_fast_refresh = true;
p.resolveGeneratedSymbol(&p.jsx_refresh_runtime);
- const refresh_runtime_symbol: *const Symbol = &p.symbols.items[p.jsx_refresh_runtime.ref.inner_index];
+ if (!p.options.jsx.use_embedded_refresh_runtime) {
+ const refresh_runtime_symbol: *const Symbol = &p.symbols.items[p.jsx_refresh_runtime.ref.inner_index];
- declared_symbols[declared_symbols_i] = .{ .ref = p.jsx_refresh_runtime.ref, .is_top_level = true };
- declared_symbols_i += 1;
-
- const import_record_id = p.addImportRecord(.require, loc, p.options.jsx.refresh_runtime);
- p.import_records.items[import_record_id].tag = .react_refresh;
- jsx_part_stmts[stmt_i] = p.s(S.Import{
- .namespace_ref = p.jsx_refresh_runtime.ref,
- .star_name_loc = loc,
- .is_single_line = true,
- .import_record_index = import_record_id,
- }, loc);
+ declared_symbols[declared_symbols_i] = .{ .ref = p.jsx_refresh_runtime.ref, .is_top_level = true };
+ declared_symbols_i += 1;
- stmt_i += 1;
- p.named_imports.put(
- p.jsx_refresh_runtime.ref,
- js_ast.NamedImport{
- .alias = refresh_runtime_symbol.original_name,
- .alias_is_star = true,
- .alias_loc = loc,
+ const import_record_id = p.addImportRecord(.require, loc, p.options.jsx.refresh_runtime);
+ p.import_records.items[import_record_id].tag = .react_refresh;
+ jsx_part_stmts[stmt_i] = p.s(S.Import{
.namespace_ref = p.jsx_refresh_runtime.ref,
+ .star_name_loc = loc,
+ .is_single_line = true,
.import_record_index = import_record_id,
- },
- ) catch unreachable;
- p.is_import_item.put(p.allocator, p.jsx_refresh_runtime.ref, .{}) catch unreachable;
- import_records[import_record_i] = import_record_id;
+ }, loc);
+
+ stmt_i += 1;
+ p.named_imports.put(
+ p.jsx_refresh_runtime.ref,
+ js_ast.NamedImport{
+ .alias = refresh_runtime_symbol.original_name,
+ .alias_is_star = true,
+ .alias_loc = loc,
+ .namespace_ref = p.jsx_refresh_runtime.ref,
+ .import_record_index = import_record_id,
+ },
+ ) catch unreachable;
+ p.is_import_item.put(p.allocator, p.jsx_refresh_runtime.ref, .{}) catch unreachable;
+ import_records[import_record_i] = import_record_id;
+ }
p.recordUsage(p.jsx_refresh_runtime.ref);
}
@@ -2513,46 +2516,48 @@ pub const Parser = struct {
if (!did_import_fast_refresh and p.options.features.react_fast_refresh) {
p.resolveGeneratedSymbol(&p.jsx_refresh_runtime);
+ p.recordUsage(p.jsx_refresh_runtime.ref);
- std.debug.assert(!p.options.enable_bundling);
- var declared_symbols = try p.allocator.alloc(js_ast.DeclaredSymbol, 1);
- const loc = logger.Loc.Empty;
- const import_record_id = p.addImportRecord(.require, loc, p.options.jsx.refresh_runtime);
- p.import_records.items[import_record_id].tag = .react_refresh;
-
- var import_stmt = p.s(S.Import{
- .namespace_ref = p.jsx_refresh_runtime.ref,
- .star_name_loc = loc,
- .is_single_line = true,
- .import_record_index = import_record_id,
- }, loc);
+ if (!p.options.jsx.use_embedded_refresh_runtime) {
+ std.debug.assert(!p.options.enable_bundling);
+ var declared_symbols = try p.allocator.alloc(js_ast.DeclaredSymbol, 1);
+ const loc = logger.Loc.Empty;
+ const import_record_id = p.addImportRecord(.require, loc, p.options.jsx.refresh_runtime);
+ p.import_records.items[import_record_id].tag = .react_refresh;
- p.recordUsage(p.jsx_refresh_runtime.ref);
- const refresh_runtime_symbol: *const Symbol = &p.symbols.items[p.jsx_refresh_runtime.ref.inner_index];
-
- p.named_imports.put(
- p.jsx_refresh_runtime.ref,
- js_ast.NamedImport{
- .alias = refresh_runtime_symbol.original_name,
- .alias_is_star = true,
- .alias_loc = loc,
+ var import_stmt = p.s(S.Import{
.namespace_ref = p.jsx_refresh_runtime.ref,
+ .star_name_loc = loc,
+ .is_single_line = true,
.import_record_index = import_record_id,
- },
- ) catch unreachable;
- p.is_import_item.put(p.allocator, p.jsx_refresh_runtime.ref, .{}) catch unreachable;
- var import_records = try p.allocator.alloc(@TypeOf(import_record_id), 1);
- import_records[0] = import_record_id;
- declared_symbols[0] = .{ .ref = p.jsx_refresh_runtime.ref, .is_top_level = true };
- var part_stmts = try p.allocator.alloc(Stmt, 1);
- part_stmts[0] = import_stmt;
+ }, loc);
- before.append(js_ast.Part{
- .stmts = part_stmts,
- .declared_symbols = declared_symbols,
- .import_record_indices = import_records,
- .symbol_uses = SymbolUseMap{},
- }) catch unreachable;
+ const refresh_runtime_symbol: *const Symbol = &p.symbols.items[p.jsx_refresh_runtime.ref.inner_index];
+
+ p.named_imports.put(
+ p.jsx_refresh_runtime.ref,
+ js_ast.NamedImport{
+ .alias = refresh_runtime_symbol.original_name,
+ .alias_is_star = true,
+ .alias_loc = loc,
+ .namespace_ref = p.jsx_refresh_runtime.ref,
+ .import_record_index = import_record_id,
+ },
+ ) catch unreachable;
+ p.is_import_item.put(p.allocator, p.jsx_refresh_runtime.ref, .{}) catch unreachable;
+ var import_records = try p.allocator.alloc(@TypeOf(import_record_id), 1);
+ import_records[0] = import_record_id;
+ declared_symbols[0] = .{ .ref = p.jsx_refresh_runtime.ref, .is_top_level = true };
+ var part_stmts = try p.allocator.alloc(Stmt, 1);
+ part_stmts[0] = import_stmt;
+
+ before.append(js_ast.Part{
+ .stmts = part_stmts,
+ .declared_symbols = declared_symbols,
+ .import_record_indices = import_records,
+ .symbol_uses = SymbolUseMap{},
+ }) catch unreachable;
+ }
}
if (p.options.enable_bundling) p.resolveBundlingSymbols();
@@ -3734,7 +3739,13 @@ pub fn NewParser(
if (p.options.features.hot_module_reloading) {
p.hmr_module = try p.declareGeneratedSymbol(.other, "hmr");
if (is_react_fast_refresh_enabled) {
- p.jsx_refresh_runtime = try p.declareGeneratedSymbol(.other, "Refresher");
+ if (p.options.jsx.use_embedded_refresh_runtime) {
+ p.runtime_imports.__FastRefreshRuntime = try p.declareGeneratedSymbol(.other, "__FastRefreshRuntime");
+ p.recordUsage(p.runtime_imports.__FastRefreshRuntime.?.ref);
+ p.jsx_refresh_runtime = p.runtime_imports.__FastRefreshRuntime.?;
+ } else {
+ p.jsx_refresh_runtime = try p.declareGeneratedSymbol(.other, "Refresher");
+ }
p.runtime_imports.__FastRefreshModule = try p.declareGeneratedSymbol(.other, "__FastRefreshModule");
p.recordUsage(p.runtime_imports.__FastRefreshModule.?.ref);
@@ -3817,7 +3828,11 @@ pub fn NewParser(
pub fn resolveHMRSymbols(p: *P) void {
p.resolveGeneratedSymbol(&p.hmr_module);
- if (p.runtime_imports.__FastRefreshModule != null) p.resolveGeneratedSymbol(&p.runtime_imports.__FastRefreshModule.?);
+ if (p.runtime_imports.__FastRefreshModule != null) {
+ p.resolveGeneratedSymbol(&p.runtime_imports.__FastRefreshModule.?);
+ if (p.options.jsx.use_embedded_refresh_runtime)
+ p.resolveGeneratedSymbol(&p.runtime_imports.__FastRefreshRuntime.?);
+ }
if (p.runtime_imports.__HMRModule != null) p.resolveGeneratedSymbol(&p.runtime_imports.__HMRModule.?);
if (p.runtime_imports.__HMRClient != null) p.resolveGeneratedSymbol(&p.runtime_imports.__HMRClient.?);
}
diff --git a/src/linker.zig b/src/linker.zig
index 8e243a31e..fafd64dd2 100644
--- a/src/linker.zig
+++ b/src/linker.zig
@@ -54,11 +54,18 @@ pub const Linker = struct {
runtime_import_record: ?ImportRecord = null,
hashed_filenames: HashedFileNameMap,
import_counter: usize = 0,
+ tagged_resolutions: TaggedResolution = TaggedResolution{},
onImportCSS: ?OnImportCallback = null,
pub const runtime_source_path = "bun:wrap";
+ pub const TaggedResolution = struct {
+ react_refresh: ?Resolver.Result = null,
+ jsx_import: ?Resolver.Result = null,
+ jsx_classic: ?Resolver.Result = null,
+ };
+
pub fn init(
allocator: std.mem.Allocator,
log: *logger.Log,
@@ -212,7 +219,7 @@ pub const Linker = struct {
var record_i: u32 = 0;
const record_count = @truncate(u32, import_records.len);
- while (record_i < record_count) : (record_i += 1) {
+ outer: while (record_i < record_count) : (record_i += 1) {
var import_record = &import_records[record_i];
if (import_record.is_unused) continue;
@@ -265,8 +272,75 @@ pub const Linker = struct {
}
}
- if (linker.resolver.resolve(source_dir, import_record.path.text, import_record.kind)) |*_resolved_import| {
- const resolved_import: *const Resolver.Result = _resolved_import;
+ var resolved_import_ = brk: {
+ switch (import_record.tag) {
+ else => {},
+ .jsx_import => {
+ if (linker.tagged_resolutions.jsx_import != null) {
+ break :brk linker.tagged_resolutions.jsx_import.?;
+ }
+ },
+ .jsx_classic => {
+ if (linker.tagged_resolutions.jsx_classic != null) {
+ break :brk linker.tagged_resolutions.jsx_classic.?;
+ }
+ },
+ // for fast refresh, attempt to read the version directly from the bundle instead of resolving it
+ .react_refresh => {
+ if (linker.options.node_modules_bundle) |node_modules_bundle| {
+ const runtime = linker.options.jsx.refresh_runtime;
+ const package_name = runtime[0 .. strings.indexOfChar(runtime, '/') orelse runtime.len];
+
+ if (node_modules_bundle.getPackage(package_name)) |pkg| {
+ const import_path = runtime[@minimum(runtime.len, package_name.len + 1)..];
+ if (node_modules_bundle.findModuleInPackage(pkg, import_path)) |found_module| {
+ import_record.is_bundled = true;
+ node_module_bundle_import_path = node_module_bundle_import_path orelse
+ linker.nodeModuleBundleImportPath(origin);
+
+ import_record.path.text = node_module_bundle_import_path.?;
+ import_record.module_id = found_module.id;
+ needs_bundle = true;
+ continue :outer;
+ }
+ }
+ }
+
+ if (linker.options.jsx.use_embedded_refresh_runtime) {
+ import_record.path = Fs.Path.initWithNamespace(try origin.joinAlloc(linker.allocator, "", "", linker.options.jsx.refresh_runtime, "", ""), "bun");
+ continue :outer;
+ }
+
+ if (linker.tagged_resolutions.react_refresh != null) {
+ break :brk linker.tagged_resolutions.react_refresh.?;
+ }
+ },
+ }
+
+ if (linker.resolver.resolve(source_dir, import_record.path.text, import_record.kind)) |_resolved_import| {
+ switch (import_record.tag) {
+ else => {},
+ .jsx_import => {
+ linker.tagged_resolutions.jsx_import = _resolved_import;
+ linker.tagged_resolutions.jsx_import.?.path_pair.primary = linker.tagged_resolutions.jsx_import.?.path().?.dupeAlloc(_global.default_allocator) catch unreachable;
+ },
+ .jsx_classic => {
+ linker.tagged_resolutions.jsx_classic = _resolved_import;
+ linker.tagged_resolutions.jsx_classic.?.path_pair.primary = linker.tagged_resolutions.jsx_classic.?.path().?.dupeAlloc(_global.default_allocator) catch unreachable;
+ },
+ .react_refresh => {
+ linker.tagged_resolutions.react_refresh = _resolved_import;
+ linker.tagged_resolutions.react_refresh.?.path_pair.primary = linker.tagged_resolutions.react_refresh.?.path().?.dupeAlloc(_global.default_allocator) catch unreachable;
+ },
+ }
+
+ break :brk _resolved_import;
+ } else |err| {
+ break :brk err;
+ }
+ };
+
+ if (resolved_import_) |*resolved_import| {
if (resolved_import.is_external) {
externals.append(record_index) catch unreachable;
continue;
diff --git a/src/node_module_bundle.zig b/src/node_module_bundle.zig
index 2f0e23356..36de005b5 100644
--- a/src/node_module_bundle.zig
+++ b/src/node_module_bundle.zig
@@ -152,8 +152,8 @@ pub const NodeModuleBundle = struct {
}
pub fn getPackage(this: *const NodeModuleBundle, name: string) ?*const Api.JavascriptBundledPackage {
- const package_id = this.getPackageID(name) orelse return null;
- return &this.bundle.packages[@intCast(usize, package_id)];
+ const package_id = this.getPackageIDByName(name) orelse return null;
+ return &this.bundle.packages[@intCast(usize, package_id[0])];
}
pub fn hasModule(this: *const NodeModuleBundle, name: string) ?*const Api.JavascriptBundledPackage {
diff --git a/src/options.zig b/src/options.zig
index 17da46b2f..41b84c9b4 100644
--- a/src/options.zig
+++ b/src/options.zig
@@ -800,8 +800,9 @@ pub const JSX = struct {
import_source: string = "react/jsx-dev-runtime",
classic_import_source: string = "react",
package_name: []const u8 = "react",
- refresh_runtime: string = "react-refresh/runtime",
+ refresh_runtime: string = "react-refresh/runtime.js",
supports_fast_refresh: bool = true,
+ use_embedded_refresh_runtime: bool = false,
jsx: string = Defaults.JSXFunctionDev,
jsx_static: string = Defaults.JSXStaticFunction,
@@ -1461,7 +1462,10 @@ pub const BundleOptions = struct {
opts.routes.static_dir_enabled = opts.routes.static_dir_handle != null;
}
- if (opts.routes.static_dir_enabled and (opts.framework == null or !opts.framework.?.server.isEnabled()) and !opts.routes.routes_enabled) {
+ const should_try_to_find_a_index_html_file = (opts.framework == null or !opts.framework.?.server.isEnabled()) and
+ !opts.routes.routes_enabled;
+
+ if (opts.routes.static_dir_enabled and should_try_to_find_a_index_html_file) {
const dir = opts.routes.static_dir_handle.?;
var index_html_file = dir.openFile("index.html", .{ .read = true }) catch |err| brk: {
switch (err) {
@@ -1473,6 +1477,7 @@ pub const BundleOptions = struct {
);
},
}
+
opts.routes.single_page_app_routing = false;
break :brk null;
};
@@ -1483,6 +1488,33 @@ pub const BundleOptions = struct {
}
}
+ if (!opts.routes.single_page_app_routing and should_try_to_find_a_index_html_file) {
+ attempt: {
+ var abs_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ // If it's not in static-dir/index.html, check if it's in top level dir/index.html
+ var parts = [_]string{"index.html"};
+ var full_path = resolve_path.joinAbsStringBuf(fs.top_level_dir, &abs_buf, &parts, .auto);
+ abs_buf[full_path.len] = 0;
+ var abs_buf_z: [:0]u8 = abs_buf[0..full_path.len :0];
+
+ const file = std.fs.openFileAbsoluteZ(abs_buf_z, .{ .read = true }) catch |err| {
+ switch (err) {
+ error.FileNotFound => {},
+ else => {
+ Output.prettyErrorln(
+ "{s} when trying to open {s}/index.html. single page app routing is disabled.",
+ .{ @errorName(err), fs.top_level_dir },
+ );
+ },
+ }
+ break :attempt;
+ };
+
+ opts.routes.single_page_app_routing = true;
+ opts.routes.single_page_app_fd = file.handle;
+ }
+ }
+
// Windows has weird locking rules for file access.
// so it's a bad idea to keep a file handle open for a long time on Windows.
if (Environment.isWindows and opts.routes.static_dir_handle != null) {
diff --git a/src/react-refresh.js b/src/react-refresh.js
new file mode 100644
index 000000000..65c44002f
--- /dev/null
+++ b/src/react-refresh.js
@@ -0,0 +1,9 @@
+// This is based on v0.11.0 of react-refresh
+// The following changes:
+// - Removed __DEV__ checks
+// - inlined REACT_MEMO_TYPE & REACT_FORWARD_REF_TYPE
+// - minified
+
+const F="for"in Symbol?Symbol.for("react.forward_ref"):60112,C="for"in Symbol?Symbol.for("react.memo"):60115,O=typeof WeakMap=="function"?WeakMap:Map,T=new Map,k=new O,m=new O,M=new O;let g=[];const b=new Map,w=new Map,c=new Set,p=new Set,R=typeof WeakMap=="function"?new WeakMap:null;let S=!1;function _(e){if(e.fullKey!==null)return e.fullKey;let t=e.ownKey,n;try{n=e.getCustomHooks()}catch{return e.forceReset=!0,e.fullKey=t,t}for(let o=0;o<n.length;o++){const l=n[o];if(typeof l!="function")return e.forceReset=!0,e.fullKey=t,t;const s=m.get(l);if(s===void 0)continue;const r=_(s);s.forceReset&&(e.forceReset=!0),t+=`
+---
+`+r}return e.fullKey=t,t}function D(e,t){const n=m.get(e),o=m.get(t);return n===void 0&&o===void 0?!0:!(n===void 0||o===void 0||_(n)!==_(o)||o.forceReset)}function B(e){return e.prototype&&e.prototype.isReactComponent}function v(e,t){return B(e)||B(t)?!1:!!D(e,t)}function I(e){return M.get(e)}function P(e){const t=new Map;return e.forEach((n,o)=>{t.set(o,n)}),t}function L(e){const t=new Set;return e.forEach(n=>{t.add(n)}),t}function H(e,t){try{return e[t]}catch{return}}function j(){if(g.length===0||S)return null;S=!0;try{const e=new Set,t=new Set,n=g;g=[],n.forEach(f=>{let[i,u]=f;const a=i.current;M.set(a,i),M.set(u,i),i.current=u,v(a,u)?t.add(i):e.add(i)});const o={updatedFamilies:t,staleFamilies:e};b.forEach(f=>{f.setRefreshHandler(I)});let l=!1,s=null;const r=L(p),h=L(c),d=P(w);if(r.forEach(f=>{const i=d.get(f);if(i===void 0)throw new Error("Could not find helpers for a root. This is a bug in React Refresh.");if(!p.has(f),R===null||!R.has(f))return;const u=R.get(f);try{i.scheduleRoot(f,u)}catch(a){l||(l=!0,s=a)}}),h.forEach(f=>{const i=d.get(f);if(i===void 0)throw new Error("Could not find helpers for a root. This is a bug in React Refresh.");!c.has(f);try{i.scheduleRefresh(f,o)}catch(u){l||(l=!0,s=u)}}),l)throw s;return o}finally{S=!1}}function K(e,t){if(e===null||typeof e!="function"&&typeof e!="object"||k.has(e))return;let n=T.get(t);if(n===void 0?(n={current:e},T.set(t,n)):g.push([n,e]),k.set(e,n),typeof e=="object"&&e!==null)switch(H(e,"$$typeof")){case F:K(e.render,t+"$render");break;case C:K(e.type,t+"$type");break}}function E(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!1,o=arguments.length>3?arguments[3]:void 0;if(m.has(e)||m.set(e,{forceReset:n,ownKey:t,fullKey:null,getCustomHooks:o||(()=>[])}),typeof e=="object"&&e!==null)switch(H(e,"$$typeof")){case F:E(e.render,t,n,o);break;case C:E(e.type,t,n,o);break}}function A(e){const t=m.get(e);t!==void 0&&_(t)}function $(e){return T.get(e)}function W(e){return k.get(e)}function x(e){const t=new Set;return c.forEach(n=>{const o=w.get(n);if(o===void 0)throw new Error("Could not find helpers for a root. This is a bug in React Refresh.");o.findHostInstancesForRefresh(n,e).forEach(s=>{t.add(s)})}),t}function z(e){let t=e.__REACT_DEVTOOLS_GLOBAL_HOOK__;if(t===void 0){let s=0;e.__REACT_DEVTOOLS_GLOBAL_HOOK__=t={renderers:new Map,supportsFiber:!0,inject(r){return s++},onScheduleFiberRoot(r,h,d){},onCommitFiberRoot(r,h,d,f){},onCommitFiberUnmount(){}}}if(t.isDisabled){console.warn("Something has shimmed the React DevTools global hook (__REACT_DEVTOOLS_GLOBAL_HOOK__). Fast Refresh is not compatible with this shim and will be disabled.");return}const n=t.inject;t.inject=function(s){const r=n.apply(this,arguments);return typeof s.scheduleRefresh=="function"&&typeof s.setRefreshHandler=="function"&&b.set(r,s),r},t.renderers.forEach((s,r)=>{typeof s.scheduleRefresh=="function"&&typeof s.setRefreshHandler=="function"&&b.set(r,s)});const o=t.onCommitFiberRoot,l=t.onScheduleFiberRoot||(()=>{});t.onScheduleFiberRoot=function(s,r,h){return S||(p.delete(r),R!==null&&R.set(r,h)),l.apply(this,arguments)},t.onCommitFiberRoot=function(s,r,h,d){const f=b.get(s);if(f!==void 0){w.set(r,f);const i=r.current,u=i.alternate;if(u!==null){const a=u.memoizedState!=null&&u.memoizedState.element!=null&&c.has(r),y=i.memoizedState!=null&&i.memoizedState.element!=null;!a&&y?(c.add(r),p.delete(r)):a&&y||(a&&!y?(c.delete(r),d?p.add(r):w.delete(r)):!a&&!y&&d&&p.add(r))}else c.add(r)}return o.apply(this,arguments)}}function G(){return!1}function N(){return c.size}function U(){let e,t,n=!1;return function(o,l,s,r){if(typeof l=="string")return e||(e=o,t=typeof r=="function"),o!=null&&(typeof o=="function"||typeof o=="object")&&E(o,l,s,r),o;!n&&t&&(n=!0,A(e))}}function V(e){switch(typeof e){case"function":{if(e.prototype!=null){if(e.prototype.isReactComponent)return!0;const n=Object.getOwnPropertyNames(e.prototype);if(n.length>1||n[0]!=="constructor"||e.prototype.__proto__!==Object.prototype)return!1}const t=e.name||e.displayName;return typeof t=="string"&&/^[A-Z]/.test(t)}case"object":{if(e!=null)switch(H(e,"$$typeof")){case F:case C:return!0;default:return!1}return!1}default:return!1}}export{N as _getMountedRootCount,A as collectCustomHooksForSignature,U as createSignatureFunctionForTransform,x as findAffectedHostInstances,$ as getFamilyByID,W as getFamilyByType,G as hasUnrecoverableErrors,z as injectIntoGlobalHook,V as isLikelyComponentType,j as performReactRefresh,K as register,E as setSignature};
diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig
index ef56d3faa..205dab45b 100644
--- a/src/resolver/resolver.zig
+++ b/src/resolver/resolver.zig
@@ -839,6 +839,7 @@ pub const Resolver = struct {
.diff_case = entry.diff_case,
.package_json = entry.package_json,
.file_fd = entry.file_fd,
+ .jsx = r.opts.jsx,
};
}
@@ -897,6 +898,7 @@ pub const Resolver = struct {
.diff_case = _result.diff_case,
.dirname_fd = _result.dirname_fd,
.package_json = pkg,
+ .jsx = r.opts.jsx,
};
check_relative = false;
check_package = false;
@@ -913,6 +915,7 @@ pub const Resolver = struct {
.diff_case = res.diff_case,
.dirname_fd = res.dirname_fd,
.package_json = res.package_json,
+ .jsx = r.opts.jsx,
};
} else if (!check_package) {
return null;
@@ -1000,6 +1003,7 @@ pub const Resolver = struct {
.dirname_fd = node_module.dirname_fd,
.diff_case = node_module.diff_case,
.package_json = package_json,
+ .jsx = r.opts.jsx,
};
}
} else {
@@ -1009,6 +1013,7 @@ pub const Resolver = struct {
.path_pair = PathPair{ .primary = primary },
// this might not be null? i think it is
.diff_case = null,
+ .jsx = r.opts.jsx,
};
}
}
@@ -1022,6 +1027,7 @@ pub const Resolver = struct {
result.package_json = res.package_json;
result.diff_case = res.diff_case;
result.is_from_node_modules = result.is_from_node_modules or res.is_node_module;
+ result.jsx = r.opts.jsx;
if (res.path_pair.primary.is_disabled and res.path_pair.secondary == null) {
return result;
@@ -1045,6 +1051,7 @@ pub const Resolver = struct {
result.file_fd = remapped.file_fd;
result.package_json = remapped.package_json;
result.diff_case = remapped.diff_case;
+
result.is_from_node_modules = result.is_from_node_modules or remapped.is_node_module;
return result;
}
diff --git a/src/runtime.footer.with-refresh.js b/src/runtime.footer.with-refresh.js
new file mode 100644
index 000000000..618329baf
--- /dev/null
+++ b/src/runtime.footer.with-refresh.js
@@ -0,0 +1,28 @@
+// ---
+// Public exports from runtime
+// Compatible with bun's Runtime Environment and web browsers.
+export var $$m =
+ "$primordials" in globalThis ? $primordials.require : BUN_RUNTIME.$$m;
+export var __HMRModule = BUN_RUNTIME.__HMRModule;
+export var __FastRefreshModule = BUN_RUNTIME.__FastRefreshModule;
+export var __HMRClient = BUN_RUNTIME.__HMRClient;
+export var __markAsModule = BUN_RUNTIME.__markAsModule;
+export var $$lzy = BUN_RUNTIME.$$lzy;
+export var __toModule = BUN_RUNTIME.__toModule;
+export var __commonJS = BUN_RUNTIME.__commonJS;
+export var __require = BUN_RUNTIME.__require;
+export var __name = BUN_RUNTIME.__name;
+export var __export = BUN_RUNTIME.__export;
+export var __reExport = BUN_RUNTIME.__reExport;
+export var __cJS2eSM = BUN_RUNTIME.__cJS2eSM;
+export var regeneratorRuntime = BUN_RUNTIME.regeneratorRuntime;
+export var __exportValue = BUN_RUNTIME.__exportValue;
+export var __exportDefault = BUN_RUNTIME.__exportDefault;
+export var $$bun_runtime_json_parse = JSON.parse;
+export var __FastRefreshRuntime = BUN_RUNTIME.__FastRefreshRuntime;
+export var __internalIsCommonJSNamespace =
+ BUN_RUNTIME.__internalIsCommonJSNamespace;
+
+globalThis.__internalIsCommonJSNamespace ||= __internalIsCommonJSNamespace;
+globalThis.require ||= BUN_RUNTIME.__require;
+globalThis.self ||= globalThis;
diff --git a/src/runtime.js b/src/runtime.js
index 23cfc6062..38be10ea2 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -105,8 +105,9 @@ export var __commonJS = (cb, name) => {
export var __cJS2eSM = __commonJS;
export var __internalIsCommonJSNamespace = (namespace) =>
+ namespace != null &&
typeof namespace === "object" &&
- (("default" in namespace && namespace.default[cjsRequireSymbol]) ||
+ ((namespace.default && namespace.default[cjsRequireSymbol]) ||
namespace[cjsRequireSymbol]);
// require()
diff --git a/src/runtime.version b/src/runtime.version
index 3fabcd4a7..65a740c58 100644
--- a/src/runtime.version
+++ b/src/runtime.version
@@ -1 +1 @@
-f7965245ee95858c \ No newline at end of file
+f788d09f30e73401 \ No newline at end of file
diff --git a/src/runtime.zig b/src/runtime.zig
index c4d3c4f03..a5acd7e68 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -45,6 +45,8 @@ pub const ErrorCSS = struct {
}
};
+pub const ReactRefresh = @embedFile("./react-refresh.js");
+
pub const ErrorJS = struct {
const ErrorJSPath = "packages/bun-error/dist/index.js";
@@ -166,8 +168,9 @@ pub const Fallback = struct {
pub const Runtime = struct {
pub const ProdSourceContent = @embedFile("./runtime.out.js");
+ pub const ProdSourceContentWithRefresh = @embedFile("./runtime.out.refresh.js");
- pub inline fn sourceContent() string {
+ pub inline fn sourceContentWithoutRefresh() string {
if (comptime Environment.isDebug) {
var dirpath = std.fs.path.dirname(@src().file).?;
var env = std.process.getEnvMap(default_allocator) catch unreachable;
@@ -187,6 +190,33 @@ pub const Runtime = struct {
return ProdSourceContent;
}
}
+
+ pub inline fn sourceContent(with_refresh: bool) string {
+ if (with_refresh) return sourceContentWithRefresh();
+ return sourceContentWithoutRefresh();
+ }
+
+ pub inline fn sourceContentWithRefresh() string {
+ if (comptime Environment.isDebug) {
+ var dirpath = std.fs.path.dirname(@src().file).?;
+ var env = std.process.getEnvMap(default_allocator) catch unreachable;
+
+ const dir = std.mem.replaceOwned(
+ u8,
+ default_allocator,
+ dirpath,
+ "jarred",
+ env.get("USER").?,
+ ) catch unreachable;
+ var runtime_path = std.fs.path.join(default_allocator, &[_]string{ dir, "runtime.out.refresh.js" }) catch unreachable;
+ const file = std.fs.openFileAbsolute(runtime_path, .{}) catch @panic("Missing bun/src/runtime.out.refresh.js. " ++ "Please run \"make runtime_js_dev\"");
+ defer file.close();
+ return file.readToEndAlloc(default_allocator, (file.stat() catch unreachable).size) catch unreachable;
+ } else {
+ return ProdSourceContentWithRefresh;
+ }
+ }
+
pub const version_hash = @embedFile("./runtime.version");
var version_hash_int: u32 = 0;
pub fn versionHash() u32 {
@@ -242,6 +272,7 @@ pub const Runtime = struct {
__FastRefreshModule: ?GeneratedSymbol = null,
__exportValue: ?GeneratedSymbol = null,
__exportDefault: ?GeneratedSymbol = null,
+ __FastRefreshRuntime: ?GeneratedSymbol = null,
pub const all = [_][]const u8{
"__name",
@@ -258,6 +289,7 @@ pub const Runtime = struct {
"__FastRefreshModule",
"__exportValue",
"__exportDefault",
+ "__FastRefreshRuntime",
};
pub const Name = "bun:wrap";
pub const alt_name = "bun:wrap";
@@ -347,6 +379,11 @@ pub const Runtime = struct {
return Entry{ .key = 13, .value = val.ref };
}
},
+ 14 => {
+ if (@field(this.runtime_imports, all[14])) |val| {
+ return Entry{ .key = 14, .value = val.ref };
+ }
+ },
else => {
return null;
},
@@ -405,6 +442,7 @@ pub const Runtime = struct {
11 => (@field(imports, all[11]) orelse return null).ref,
12 => (@field(imports, all[12]) orelse return null).ref,
13 => (@field(imports, all[13]) orelse return null).ref,
+ 14 => (@field(imports, all[14]) orelse return null).ref,
else => null,
};
}
diff --git a/src/runtime/hmr.ts b/src/runtime/hmr.ts
index ebfe100f4..dd1700977 100644
--- a/src/runtime/hmr.ts
+++ b/src/runtime/hmr.ts
@@ -625,7 +625,7 @@ if (typeof window !== "undefined") {
}).then(() => Promise.resolve())
);
}
- static activate(verbose: boolean = false) {
+ static activate(verboseOrFastRefresh: boolean = false) {
// Support browser-like envirnments where location and WebSocket exist
// Maybe it'll work in Deno! Who knows.
if (
@@ -656,7 +656,7 @@ if (typeof window !== "undefined") {
// } catch (exception) {}
// }
// }
- this.client.verbose = verbose;
+ this.client.verbose = verboseOrFastRefresh;
this.client.start();
globalThis["__BUN_HMR"] = this.client;
}
@@ -871,12 +871,12 @@ if (typeof window !== "undefined") {
.subarray(0, HMRModule.dependencies.graph_used)
.indexOf(notification.id);
- if (index > -1 && index) {
+ if (index > -1) {
file_path = HMRModule.dependencies.modules[index].file_path;
}
break;
}
-
+
default: {
return;
}
@@ -1128,17 +1128,40 @@ if (typeof window !== "undefined") {
}
case API.WebsocketMessageKind.welcome: {
const now = performance.now();
- __hmrlog.log(
- "HMR connected in",
- formatDuration(now - clientStartTime),
- "ms"
- );
- clientStartTime = now;
+
this.hasWelcomed = true;
const welcome = API.decodeWebsocketMessageWelcome(buffer);
this.epoch = welcome.epoch;
this.javascriptReloader = welcome.javascriptReloader;
this.cwd = welcome.cwd;
+
+ switch (this.javascriptReloader) {
+ case API.Reloader.fast_refresh: {
+ __hmrlog.log(
+ "HMR connected in",
+ formatDuration(now - clientStartTime),
+ "ms"
+ );
+ break;
+ }
+ case API.Reloader.live: {
+ __hmrlog.log(
+ "Live reload connected in",
+ formatDuration(now - clientStartTime),
+ "ms"
+ );
+ break;
+ }
+ default: {
+ __hmrlog.log(
+ "Bun connected in",
+ formatDuration(now - clientStartTime),
+ "ms"
+ );
+ break;
+ }
+ }
+ clientStartTime = now;
if (!this.epoch) {
__hmrlog.warn("Internal HMR error");
}
diff --git a/src/runtime/index-with-refresh.ts b/src/runtime/index-with-refresh.ts
new file mode 100644
index 000000000..1aceecc67
--- /dev/null
+++ b/src/runtime/index-with-refresh.ts
@@ -0,0 +1,10 @@
+import { __injectFastRefresh } from "./hmr";
+export * from "./hmr";
+export * from "./errors";
+export * from "../runtime.js";
+export { default as regeneratorRuntime } from "./regenerator";
+import * as __FastRefreshRuntime from "../react-refresh";
+if (typeof window !== "undefined") {
+ __injectFastRefresh(__FastRefreshRuntime);
+}
+export { __FastRefreshRuntime };