diff options
-rw-r--r-- | packages/bun-framework-next/appInjector.js | 15 | ||||
-rw-r--r-- | packages/bun-framework-next/client.development.tsx | 123 | ||||
-rw-r--r-- | packages/bun-framework-next/fallback.development.tsx | 68 | ||||
-rw-r--r-- | packages/bun-framework-next/package.json | 2 | ||||
-rw-r--r-- | packages/bun-framework-next/page-loader.ts | 8 | ||||
-rw-r--r-- | packages/bun-framework-next/renderDocument.tsx | 18 | ||||
-rw-r--r-- | src/bundler.zig | 10 | ||||
-rw-r--r-- | src/http.zig | 12 | ||||
-rw-r--r-- | src/js_printer.zig | 12 | ||||
-rw-r--r-- | src/linker.zig | 188 | ||||
-rw-r--r-- | src/runtime/hmr.ts | 10 |
11 files changed, 320 insertions, 146 deletions
diff --git a/packages/bun-framework-next/appInjector.js b/packages/bun-framework-next/appInjector.js new file mode 100644 index 000000000..e8bf22d21 --- /dev/null +++ b/packages/bun-framework-next/appInjector.js @@ -0,0 +1,15 @@ +export function maybeInjectApp(expr) { + var app; + try { + const path = Bun.routesDir + "/_app"; + app = Bun.resolveSync(path, Bun.cwd + "/"); + } catch (exception) { + return undefined; + } + + return ( + <> + <import path={app} /> + </> + ); +} diff --git a/packages/bun-framework-next/client.development.tsx b/packages/bun-framework-next/client.development.tsx index 1c51a97e8..fd8e43a20 100644 --- a/packages/bun-framework-next/client.development.tsx +++ b/packages/bun-framework-next/client.development.tsx @@ -41,6 +41,8 @@ import { createRouter, makePublicRouterInstance, } from "next/dist/client/router"; +import { packageVersion } from "macro:./packageVersion"; +import NextHead from "next/head"; export const emitter: MittEmitter<string> = mitt(); @@ -61,6 +63,7 @@ function nextDataFromBunData() { const { router: { routes, route, params: paramsList }, } = globalThis.__BUN_DATA__; + const appStyles = globalThis.__BUN_APP_STYLES || []; const paramsMap = new Map(); for (let i = 0; i < paramsList.keys.length; i++) { @@ -76,8 +79,19 @@ function nextDataFromBunData() { Object.assign(params, Object.fromEntries(paramsMap.entries())); const pages = routes.keys.reduce((acc, routeName, i) => { + if (!routes.values[i].startsWith("/_next/")) { + routes.values[i] = + "/_next/" + + routes.values[i].substring(routes.values[i].startsWith("/") ? 1 : 0); + } + const routePath = routes.values[i]; - acc[routeName] = [routePath]; + if (routeName === "/_app" && appStyles.length) { + acc[routeName] = [routePath, ...appStyles]; + } else { + acc[routeName] = [routePath]; + } + return acc; }, {}); @@ -108,6 +122,11 @@ const data: NEXT_DATA & { pages: Record<string, string[]> } = nextDataTag ? JSON.parse(document.getElementById("__NEXT_DATA__")!.textContent!) : nextDataFromBunData(); +var headManager: { + mountedInstances: Set<unknown>; + updateHead: (head: JSX.Element[]) => void; +} = initHeadManager(); + window.__NEXT_DATA__ = data; const { @@ -142,19 +161,33 @@ if (hasBasePath(asPath)) { asPath = delBasePath(asPath); } +window.__DEV_PAGES_MANIFEST = pages; export const pageLoader: PageLoader = new PageLoader(buildId, prefix, pages); -const headManager: { - mountedInstances: Set<unknown>; - updateHead: (head: JSX.Element[]) => void; -} = initHeadManager(); - export let router: Router; let CachedApp: AppComponent = null; - +var ranBoot = false; export default function boot(EntryPointNamespace) { - _boot(EntryPointNamespace, false); + if (ranBoot) return; + ranBoot = true; + switch (document.readyState) { + case "loading": { + document.addEventListener( + "DOMContentLoaded", + () => boot(EntryPointNamespace), + { + once: true, + passive: true, + } + ); + break; + } + case "interactive": + case "complete": { + return _boot(EntryPointNamespace, false); + } + } } class Container extends React.Component<{ @@ -280,7 +313,9 @@ class BootError extends Error { } export async function _boot(EntryPointNamespace, isError) { - NextRouteLoader.getClientBuildManifest = () => Promise.resolve({}); + NextRouteLoader.getClientBuildManifest = () => { + return Promise.resolve({}); + }; const PageComponent = EntryPointNamespace.default; @@ -291,6 +326,7 @@ export async function _boot(EntryPointNamespace, isError) { // @ts-expect-error CachedApp = NextApp; CachedComponent = PageComponent; + const styleSheets = []; if (appScripts && appScripts.length > 0) { let appSrc; @@ -302,6 +338,7 @@ export async function _boot(EntryPointNamespace, isError) { } if (appSrc) { + const initialHeadCount = document?.head?.children?.length ?? 0; const AppModule = await import(appSrc); console.assert( @@ -312,9 +349,50 @@ export async function _boot(EntryPointNamespace, isError) { if ("default" in AppModule) { CachedApp = AppModule.default; } + + if (pageLoader.cssQueue.length > 0) { + await Promise.allSettled(pageLoader.cssQueue.slice()); + pageLoader.cssQueue.length = 0; + } + + const newCount = document?.head?.children?.length ?? 0; + if (newCount > initialHeadCount && newCount > 1) { + // Move any <App />-inserted nodes to the beginning, preserving the order + // This way if there are stylesheets they appear in the expected order + var firstNonMetaTag = document.head.children.length; + + for (let i = 0; i < document.head.childNodes.length; i++) { + if (document.head.children[i].tagName !== "META") { + firstNonMetaTag = i; + break; + } + } + + if (firstNonMetaTag !== document.head.children.length) { + outer: for (let i = newCount - 1; i > initialHeadCount - 1; i--) { + const node = document.head.children[i]; + if ( + node.tagName === "LINK" && + node.hasAttribute("href") && + node.href + ) { + const normalized = new URL(node.href, location.origin).href; + for (let script of appScripts) { + if (new URL(script, location.origin).href === normalized) + continue outer; + } + + appScripts.push(normalized); + } + styleSheets.push(node); + } + } + } } } + headManager = initHeadManager(); + router = createRouter(page, query, asPath, { initialProps: hydrateProps, pageLoader, @@ -356,12 +434,27 @@ export async function _boot(EntryPointNamespace, isError) { domEl = nextEl; } + const StylePreserver = () => { + React.useEffect(() => { + for (let i = 0; i < styleSheets.length; i++) { + if (!document.head.contains(styleSheets[i])) { + document.head.appendChild(styleSheets[i]); + } + } + }, []); + + return null; + }; + const reactEl = ( - <TopLevelRender - App={CachedApp} - Component={PageComponent} - props={hydrateProps} - /> + <> + <TopLevelRender + App={CachedApp} + Component={PageComponent} + props={hydrateProps} + /> + <StylePreserver /> + </> ); if (USE_REACT_18) { @@ -436,7 +529,7 @@ export function renderError(e) { } globalThis.next = { - version: "12.0.4", + version: packageVersion("next"), emitter, render, renderError, diff --git a/packages/bun-framework-next/fallback.development.tsx b/packages/bun-framework-next/fallback.development.tsx index 460b5e165..67a6a387f 100644 --- a/packages/bun-framework-next/fallback.development.tsx +++ b/packages/bun-framework-next/fallback.development.tsx @@ -1,31 +1,50 @@ -import { insertStyleSheet } from "./page-loader"; import type { FallbackMessageContainer } from "../../src/api/schema"; +import { maybeInjectApp } from "macro:./appInjector"; + +var globalStyles = []; +function insertGlobalStyleSheet({ detail: url }) { + globalStyles.push( + new Promise((resolve, reject) => { + const link: HTMLLinkElement = document.createElement("link"); + link.rel = "stylesheet"; + link.href = url; + link.onload = resolve; + link.onabort = reject; + link.onerror = reject; + document.head.appendChild(link); + }) + ); +} + +const nCSS = document.createElement("noscript"); +nCSS.setAttribute("data-n-css", ""); +document.head.appendChild(nCSS); + +document.addEventListener("onimportcss", insertGlobalStyleSheet); var once = false; function insertNextHeadCount() { if (!once) { document.head.insertAdjacentHTML( "beforeend", - `<meta name="next-head-count" content="${document.head.childElementCount}">` + `<meta name="next-head-count" content="0">` ); once = true; } } -function insertGlobalStyleSheet(detail) { - pageLoader.cssQueue.push( - insertStyleSheet(detail).then(() => { - insertNextHeadCount(); - }) - ); -} -[...globalThis["__BUN"].allImportedStyles].map((detail) => - insertGlobalStyleSheet(detail) -); +maybeInjectApp(); + +globalThis.__BUN_APP_STYLES = [...globalThis["__BUN"].allImportedStyles].map( + (style) => { + const url = new URL(style, location.origin); + if (url.origin === location.origin && url.href === style) { + return url.pathname; + } -document.addEventListener("onimportcss", insertGlobalStyleSheet, { - passive: true, -}); + return style; + } +); import { _boot, pageLoader } from "./client.development"; @@ -39,24 +58,21 @@ function renderFallback({ router }: FallbackMessageContainer) { } document.removeEventListener("onimportcss", insertGlobalStyleSheet); - document.addEventListener("onimportcss", pageLoader.onImportCSS, { - passive: true, - }); - - globalThis.__NEXT_DATA__.pages["/_app"] = [ - ...(globalThis.__NEXT_DATA__.pages["/_app"] || []), - ...globalThis["__BUN"].allImportedStyles, - ]; - + document.addEventListener("onimportcss", pageLoader.onImportCSS); + var cssQueue; return import(route) .then((Namespace) => { + nCSS.remove(); + document.head.appendChild(nCSS); + cssQueue = [...globalStyles, ...pageLoader.cssQueue]; + pageLoader.cssQueue = []; insertNextHeadCount(); return _boot(Namespace, true); }) .then(() => { - const cssQueue = pageLoader.cssQueue.slice(); + cssQueue = [...cssQueue, ...pageLoader.cssQueue.slice()]; pageLoader.cssQueue = []; - return Promise.all([...cssQueue]); + return Promise.allSettled(cssQueue); }) .finally(() => { document.body.style.visibility = "visible"; diff --git a/packages/bun-framework-next/package.json b/packages/bun-framework-next/package.json index 46602ed0b..38043222c 100644 --- a/packages/bun-framework-next/package.json +++ b/packages/bun-framework-next/package.json @@ -1,6 +1,6 @@ { "name": "bun-framework-next", - "version": "12.1.1", + "version": "12.1.2", "main": "empty.js", "module": "empty.js", "description": "bun compatibility layer for Next.js v12.x.x", diff --git a/packages/bun-framework-next/page-loader.ts b/packages/bun-framework-next/page-loader.ts index 3b31387d8..08919cb39 100644 --- a/packages/bun-framework-next/page-loader.ts +++ b/packages/bun-framework-next/page-loader.ts @@ -3,13 +3,13 @@ import NextPageLoader, { } from "next/dist/client/page-loader"; import getAssetPathFromRoute from "next/dist/shared/lib/router/utils/get-asset-path-from-route"; -export function insertStyleSheet(url: string) { +export function insertStyleSheet(url: string, isFallback: boolean = false) { if (document.querySelector(`link[href="${url}"]`)) { return Promise.resolve(); } return new Promise((resolve, reject) => { - const link = document.createElement("link"); + const link: HTMLLinkElement = document.createElement("link"); link.rel = "stylesheet"; // marking this resolve as void seems to break other things @@ -18,6 +18,10 @@ export function insertStyleSheet(url: string) { link.href = url; + if (isFallback) { + link.setAttribute("data-href", url); + } + document.head.appendChild(link); }); } diff --git a/packages/bun-framework-next/renderDocument.tsx b/packages/bun-framework-next/renderDocument.tsx index 1bf2086f9..694cb5f8c 100644 --- a/packages/bun-framework-next/renderDocument.tsx +++ b/packages/bun-framework-next/renderDocument.tsx @@ -112,9 +112,9 @@ function getScripts(files: DocumentFiles) { var entryPointIndex = -1; const scripts = [...normalScripts, ...lowPriorityScripts].map( (file, index) => { - if (file.includes(".entry.")) { - entryPointIndex = index; - } + // if (file.includes(".entry.")) { + // entryPointIndex = index; + // } return ( <script @@ -128,10 +128,10 @@ function getScripts(files: DocumentFiles) { ); } ); - if (entryPointIndex > 0) { - const entry = scripts.splice(entryPointIndex, 1); - scripts.unshift(...entry); - } + // if (entryPointIndex > 0) { + // const entry = scripts.splice(entryPointIndex, 1); + // scripts.unshift(...entry); + // } return scripts; } @@ -787,13 +787,13 @@ export async function render({ }, // Only enabled in production as development mode has features relying on HMR (style injection for example) // @ts-expect-error - unstable_runtimeJS: true, + unstable_runtimeJS: false, // process.env.NODE_ENV === "production" // ? pageConfig.unstable_runtimeJS // : undefined, // unstable_JsPreload: pageConfig.unstable_JsPreload, // @ts-expect-error - unstable_JsPreload: true, + unstable_JsPreload: false, dangerousAsPath: router.asPath, ampState: undefined, props, diff --git a/src/bundler.zig b/src/bundler.zig index 63ebdcce5..bb6d73db9 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -1763,7 +1763,15 @@ pub const Bundler = struct { const resolved_import: *const _resolver.Result = _resolved_import; - const _module_data = BundledModuleData.getForceBundle(this, resolved_import) orelse unreachable; + const _module_data = BundledModuleData.getForceBundle(this, resolved_import) orelse { + // if a macro imports code that cannot be bundled + // we just silently disable it + // because...we need some kind of hook to say "don't bundle this" + import_record.path.is_disabled = true; + import_record.is_bundled = false; + + continue; + }; import_record.module_id = _module_data.module_id; std.debug.assert(import_record.module_id != 0); import_record.is_bundled = true; diff --git a/src/http.zig b/src/http.zig index 22c7da30f..4a93da2b3 100644 --- a/src/http.zig +++ b/src/http.zig @@ -344,7 +344,15 @@ pub const RequestContext = struct { bundler_parse_options, @as(?*bundler.FallbackEntryPoint, &fallback_entry_point), )) |*result| { - try bundler_.linker.link(fallback_entry_point.source.path, result, this.origin, .absolute_url, false); + try bundler_.linker.linkAllowImportingFromBundle( + fallback_entry_point.source.path, + result, + this.origin, + .absolute_url, + false, + false, + ); + var buffer_writer = try JSPrinter.BufferWriter.init(default_allocator); var writer = JSPrinter.BufferPrinter.init(buffer_writer); _ = try bundler_.print( @@ -3316,7 +3324,7 @@ pub const Server = struct { const kinds = slice.items(.kind); const hashes = slice.items(.hash); var file_descriptors = slice.items(.fd); - var header = fbs.getWritten(); + const header = fbs.getWritten(); defer ctx.watcher.flushEvictions(); defer Output.flush(); diff --git a/src/js_printer.zig b/src/js_printer.zig index 2e03c9092..e4a641389 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -484,7 +484,7 @@ const ImportVariant = enum { }; } - pub fn determine(record: *const importRecord.ImportRecord, _: *const Symbol, s_import: *const S.Import) ImportVariant { + pub fn determine(record: *const importRecord.ImportRecord, s_import: *const S.Import) ImportVariant { var variant = ImportVariant.path_only; if (record.contains_import_star) { @@ -3883,7 +3883,15 @@ pub fn NewPrinter( const is_disabled = import_record.path.is_disabled; const module_id = import_record.module_id; - switch (ImportVariant.determine(&record, p.symbols.get(s.namespace_ref).?, s)) { + // If the bundled import was disabled and only imported for side effects + // we can skip it + + if (record.path.is_disabled) { + if (p.symbols.get(s.namespace_ref) == null) + return; + } + + switch (ImportVariant.determine(&record, s)) { .path_only => { if (!is_disabled) { p.printCallModuleID(module_id); diff --git a/src/linker.zig b/src/linker.zig index 7d0a3ac41..7bc682a17 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -195,6 +195,18 @@ pub const Linker = struct { comptime import_path_format: Options.BundleOptions.ImportPathFormat, comptime ignore_runtime: bool, ) !void { + return linkAllowImportingFromBundle(linker, file_path, result, origin, import_path_format, ignore_runtime, true); + } + + pub fn linkAllowImportingFromBundle( + linker: *ThisLinker, + file_path: Fs.Path, + result: *_bundler.ParseResult, + origin: URL, + comptime import_path_format: Options.BundleOptions.ImportPathFormat, + comptime ignore_runtime: bool, + comptime allow_import_from_bundle: bool, + ) !void { const source_dir = file_path.sourceDir(); var externals = std.ArrayList(u32).init(linker.allocator); var needs_bundle = false; @@ -293,34 +305,36 @@ pub const Linker = struct { } } - if (linker.options.node_modules_bundle) |node_modules_bundle| { - if (Resolver.isPackagePath(import_record.path.text)) { - const text = import_record.path.text; + if (comptime allow_import_from_bundle) { + if (linker.options.node_modules_bundle) |node_modules_bundle| { + if (Resolver.isPackagePath(import_record.path.text)) { + const text = import_record.path.text; - var package_name = text; - if (text[0] == '@') { - if (std.mem.indexOfScalar(u8, text, '/')) |i| { - if (std.mem.indexOfScalar(u8, text[i + 1 ..], '/')) |j| { - package_name = text[0 .. i + 1 + j]; + var package_name = text; + if (text[0] == '@') { + if (std.mem.indexOfScalar(u8, text, '/')) |i| { + if (std.mem.indexOfScalar(u8, text[i + 1 ..], '/')) |j| { + package_name = text[0 .. i + 1 + j]; + } + } + } else { + if (std.mem.indexOfScalar(u8, text, '/')) |i| { + package_name = text[0..i]; } } - } else { - if (std.mem.indexOfScalar(u8, text, '/')) |i| { - package_name = text[0..i]; - } - } - if (package_name.len != text.len) { - if (node_modules_bundle.getPackage(package_name)) |pkg| { - const import_path = text[@minimum(text.len, package_name.len + 1)..]; - if (node_modules_bundle.findModuleIDInPackageIgnoringExtension(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 = node_modules_bundle.bundle.modules[found_module].id; - needs_bundle = true; - continue :outer; + if (package_name.len != text.len) { + if (node_modules_bundle.getPackage(package_name)) |pkg| { + const import_path = text[@minimum(text.len, package_name.len + 1)..]; + if (node_modules_bundle.findModuleIDInPackageIgnoringExtension(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 = node_modules_bundle.bundle.modules[found_module].id; + needs_bundle = true; + continue :outer; + } } } } @@ -332,21 +346,23 @@ pub const Linker = struct { else => {}, // 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 (comptime allow_import_from_bundle) { + 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; + } } } } @@ -390,54 +406,56 @@ pub const Linker = struct { const loader = linker.options.loader(path.name.ext); if (loader.isJavaScriptLikeOrJSON()) { - bundled: { - if (linker.options.node_modules_bundle) |node_modules_bundle| { - const package_json = resolved_import.package_json orelse break :bundled; - const package_base_dir = package_json.source.path.sourceDir(); - if (node_modules_bundle.getPackageIDByHash(package_json.hash)) |pkg_id| { - const package = node_modules_bundle.bundle.packages[pkg_id]; - - if (comptime Environment.isDebug) { - std.debug.assert(strings.eql(node_modules_bundle.str(package.name), package_json.name)); - std.debug.assert(strings.eql(node_modules_bundle.str(package.version), package_json.version)); - } + if (comptime allow_import_from_bundle) { + bundled: { + if (linker.options.node_modules_bundle) |node_modules_bundle| { + const package_json = resolved_import.package_json orelse break :bundled; + const package_base_dir = package_json.source.path.sourceDir(); + if (node_modules_bundle.getPackageIDByHash(package_json.hash)) |pkg_id| { + const package = node_modules_bundle.bundle.packages[pkg_id]; + + if (comptime Environment.isDebug) { + std.debug.assert(strings.eql(node_modules_bundle.str(package.name), package_json.name)); + std.debug.assert(strings.eql(node_modules_bundle.str(package.version), package_json.version)); + } + + const package_relative_path = linker.fs.relative( + package_base_dir, + if (!strings.eqlComptime(path.namespace, "node")) path.pretty else path.text, + ); - const package_relative_path = linker.fs.relative( - package_base_dir, - if (!strings.eqlComptime(path.namespace, "node")) path.pretty else path.text, - ); + const found_module = node_modules_bundle.findModuleInPackage(&package, package_relative_path) orelse { + // linker.log.addErrorFmt( + // null, + // logger.Loc.Empty, + // linker.allocator, + // "New dependency import: \"{s}/{s}\"\nPlease run `bun bun` to update the .bun.", + // .{ + // package_json.name, + // package_relative_path, + // }, + // ) catch {}; + break :bundled; + }; + + if (comptime Environment.isDebug) { + const module_path = node_modules_bundle.str(found_module.path); + std.debug.assert( + strings.eql( + module_path, + package_relative_path, + ), + ); + } - const found_module = node_modules_bundle.findModuleInPackage(&package, package_relative_path) orelse { - // linker.log.addErrorFmt( - // null, - // logger.Loc.Empty, - // linker.allocator, - // "New dependency import: \"{s}/{s}\"\nPlease run `bun bun` to update the .bun.", - // .{ - // package_json.name, - // package_relative_path, - // }, - // ) catch {}; - break :bundled; - }; - - if (comptime Environment.isDebug) { - const module_path = node_modules_bundle.str(found_module.path); - std.debug.assert( - strings.eql( - module_path, - package_relative_path, - ), - ); + 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; } - - 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; } } } diff --git a/src/runtime/hmr.ts b/src/runtime/hmr.ts index 14350146e..afffc4847 100644 --- a/src/runtime/hmr.ts +++ b/src/runtime/hmr.ts @@ -1338,9 +1338,11 @@ if (typeof window !== "undefined") { // we cannot export new modules. we can only mutate existing ones. const oldGraphUsed = HMRModule.dependencies.graph_used; - var oldModule = HMRModule.dependencies.modules[this.module_index]; + var oldModule = + HMRModule.dependencies.modules.length > this.module_index && + HMRModule.dependencies.modules[this.module_index]; HMRModule.dependencies = orig_deps.fork(this.module_index); - var blobURL = null; + var blobURL = ""; // We inject the source map URL into the end of the file. // We do that here for a few reasons: @@ -1366,6 +1368,8 @@ if (typeof window !== "undefined") { blobURL = URL.createObjectURL(blob); HMRModule.dependencies.blobToID.set(blobURL, this.module_id); await import(blobURL); + this.bytes = null; + URL.revokeObjectURL(blobURL); this.timings.import = performance.now() - importStart; } catch (exception) { HMRModule.dependencies = orig_deps; @@ -1392,7 +1396,7 @@ if (typeof window !== "undefined") { // If we do import a new module, we have to do a full page reload for now } - URL.revokeObjectURL(blobURL); + blobURL = ""; // Ensure we don't keep the bytes around longer than necessary this.bytes = null; |