Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/js_parser/visit/visit_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,12 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
},
);

if e_.optional_chain == Some(js_ast::OptionalChain::Start)
&& matches!(e_.target.data, Data::ESpecial(E::Special::HotEnabled))
{
e_.optional_chain = None;
}

match e_.index.data {
Data::EPrivateIdentifier(mut private) => {
let name = p.load_name_from_ref(private.ref_);
Expand Down Expand Up @@ -1424,6 +1430,18 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
},
);

// `import.meta.hot?.accept()` should be treated like a direct
// `import.meta.hot.accept()` in development. `import.meta.hot` is
// always defined when HMR is enabled, so the `?.` guard is a no-op
// and would otherwise prevent `maybe_rewrite_property_access` from
// lowering the access to the HMR module member, leaving the
// throwing `indirectHot` proxy in place.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
if e_.optional_chain == Some(js_ast::OptionalChain::Start)
&& matches!(e_.target.data, Data::ESpecial(E::Special::HotEnabled))
{
e_.optional_chain = None;
}

// 'require.resolve' -> .e_require_resolve_call_target
if matches!(e_.target.data, Data::ERequireCallTarget) && e_.name == b"resolve" {
// we do not need to call p.recordUsageOfRuntimeRequire(); because `require`
Expand Down
23 changes: 23 additions & 0 deletions test/bake/dev/hot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,29 @@
await c.expectMessage("Cleaning up", "Third setup");
},
});
devTest("import.meta.hot with optional chaining", {
files: {
"index.html": emptyHtmlFile({
scripts: ["index.ts"],
}),
"index.ts": `
const data = import.meta.hot?.data;
data.count ??= 0;
import.meta.hot?.dispose(() => console.log("Cleaning up"));
import.meta.hot?.on("bun:invalidate", () => {});
import.meta.hot?.accept();
console.log("Initial count: " + data.count++);
`,

Check warning on line 403 in test/bake/dev/hot.test.ts

View check run for this annotation

Claude / Claude Code Review

e_index optional-chain fix has no test coverage

The new test only exercises the dot form (`?.data`, `?.dispose`, `?.on`, `?.accept`), so the `e_index` hunk at visit_expr.rs:932-936 has no coverage — in dev mode `minify_syntax` is off, so `import.meta.hot?.["accept"]()` stays in `e_index` and never converts to `e_dot`. Add one bracket-access line (e.g. `import.meta.hot?.["on"]("bun:invalidate", () => {})`) so deleting either hunk breaks a test.
Comment thread
robobun marked this conversation as resolved.
Resolved
},
async test(dev) {
await using c = await dev.client("/");
await c.expectMessage("Initial count: 0");
await dev.writeNoChanges("index.ts");
await c.expectMessage("Cleaning up", "Initial count: 1");
await dev.writeNoChanges("index.ts");
await c.expectMessage("Cleaning up", "Initial count: 2");
},
});
devTest("import.meta.hot invalid usage", {
files: {
"index.html": emptyHtmlFile({
Expand Down
Loading