Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
01d43a20b9 fix(bundler): set is_export_star_target in dev server mode (#27521)
In dev server mode, patchImportRecordSourceIndices intentionally skips
saving source_index on JS import records. This caused the
is_export_star_target flag to never be set on export-star targets,
because the flag-setting code relied on source_index being valid.

Without this flag, the barrel optimizer could incorrectly defer
submodules of packages that are targets of `export *` from another
barrel (e.g., @tanstack/query-core being star-exported from
@tanstack/react-query), leading to classes like QueryClient becoming
undefined and causing "Right-hand side of 'instanceof' is not an
object" errors.

Fix: fall back to resolving the source index via pathToSourceIndexMap
when source_index is not set, matching the same pattern used elsewhere
in the barrel optimization code.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-27 15:17:41 +00:00
2 changed files with 72 additions and 4 deletions

View File

@@ -4209,13 +4209,18 @@ pub const BundleV2 = struct {
});
result.ast.import_records = import_records;
// Set is_export_star_target for barrel optimization
// Set is_export_star_target for barrel optimization.
// In dev server mode, source_index is not saved on JS import
// records, so fall back to resolving via the path map.
const path_to_source_index_map = this.pathToSourceIndexMap(result.ast.target);
for (result.ast.export_star_import_records) |star_record_idx| {
if (star_record_idx < import_records.len) {
const star_ir = import_records.slice()[star_record_idx];
if (star_ir.source_index.isValid()) {
graph.input_files.items(.flags)[star_ir.source_index.get()].is_export_star_target = true;
}
const resolved_index = if (star_ir.source_index.isValid())
star_ir.source_index.get()
else
path_to_source_index_map.getPath(&star_ir.path) orelse continue;
graph.input_files.items(.flags)[resolved_index].is_export_star_target = true;
}
}

View File

@@ -550,6 +550,69 @@ devTest("barrel optimization: multi-file imports preserved across rebuilds", {
},
});
devTest("barrel optimization: export star target not deferred (#27521)", {
files: {
"index.html": emptyHtmlFile({ scripts: ["index.ts"] }),
// The user imports from consumer-lib, which is a non-barrel package
// that imports QueryClient from outer-lib.
"index.ts": `
import { useQuery } from 'consumer-lib';
console.log('result: ' + useQuery());
`,
// consumer-lib is NOT a barrel — it has real code that uses
// QueryClient from outer-lib. This mirrors @refinedev/core
// importing QueryClient from @tanstack/react-query.
"node_modules/consumer-lib/package.json": JSON.stringify({
name: "consumer-lib",
version: "1.0.0",
main: "./index.js",
}),
"node_modules/consumer-lib/index.js": `
import { QueryClient } from 'outer-lib';
export function useQuery() {
const client = new QueryClient();
return client instanceof QueryClient ? 'PASS' : 'FAIL';
}
`,
// outer-lib is a barrel with sideEffects:false that re-exports
// everything from inner-lib via export *. Mirrors @tanstack/react-query.
"node_modules/outer-lib/package.json": JSON.stringify({
name: "outer-lib",
version: "1.0.0",
main: "./index.js",
sideEffects: false,
}),
"node_modules/outer-lib/index.js": `
export * from 'inner-lib';
export { Unrelated } from './unrelated.js';
`,
"node_modules/outer-lib/unrelated.js": `export const Unrelated = "X";`,
// inner-lib is a barrel with sideEffects:false that re-exports
// from submodules. Mirrors @tanstack/query-core. Without the fix,
// the barrel optimizer defers queryClient.js because it doesn't
// know inner-lib is an export-star target (source_index is not
// set in dev-server mode), so QueryClient becomes undefined.
"node_modules/inner-lib/package.json": JSON.stringify({
name: "inner-lib",
version: "1.0.0",
main: "./index.js",
sideEffects: false,
}),
"node_modules/inner-lib/index.js": `
export { QueryClient } from './queryClient.js';
export { Other } from './other.js';
`,
"node_modules/inner-lib/queryClient.js": `
export class QueryClient { constructor() { this.ready = true; } }
`,
"node_modules/inner-lib/other.js": `export const Other = "OTHER";`,
},
async test(dev) {
await using c = await dev.client("/");
await c.expectMessage("result: PASS");
},
});
devTest("barrel optimization: two export-from blocks pointing to the same source", {
files: {
"index.html": emptyHtmlFile({ scripts: ["index.ts"] }),