diff options
author | 2022-01-29 23:48:39 -0800 | |
---|---|---|
committer | 2022-01-29 23:48:39 -0800 | |
commit | 857e9bee0085b13320e3ded423ed8caff30c4e40 (patch) | |
tree | 333e1c3e524a46ed908fa921e5af2bb847e46841 | |
parent | 711e0cef78eb14d92e6789f9c6fe3c434344bfab (diff) | |
download | bun-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-- | .gitignore | 1 | ||||
-rw-r--r-- | src/import_record.zig | 9 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 135 | ||||
-rw-r--r-- | src/linker.zig | 80 | ||||
-rw-r--r-- | src/node_module_bundle.zig | 4 | ||||
-rw-r--r-- | src/options.zig | 36 | ||||
-rw-r--r-- | src/react-refresh.js | 9 | ||||
-rw-r--r-- | src/resolver/resolver.zig | 7 | ||||
-rw-r--r-- | src/runtime.footer.with-refresh.js | 28 | ||||
-rw-r--r-- | src/runtime.js | 3 | ||||
-rw-r--r-- | src/runtime.version | 2 | ||||
-rw-r--r-- | src/runtime.zig | 40 | ||||
-rw-r--r-- | src/runtime/hmr.ts | 43 | ||||
-rw-r--r-- | src/runtime/index-with-refresh.ts | 10 |
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 }; |