Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
66 changes: 36 additions & 30 deletions src/bundler/transpiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2057,38 +2057,44 @@ fn parse_data_loader<'a>(
}

decls.truncate(count);
let stmt0 = bun_ast::Stmt::alloc(
bun_ast::S::Local {
decls: bun_ast::G::DeclList::move_from_list(decls),
kind: bun_ast::S::Kind::KVar,
..Default::default()
},
bun_ast::Loc { start: 0 },
);
let stmt1 = bun_ast::Stmt::alloc(
bun_ast::S::ExportClause {
items: bun_ast::StoreSlice::new_mut(&mut export_clauses[..count]),
is_single_line: false,
},
bun_ast::Loc { start: 0 },
);
let stmt2 = bun_ast::Stmt::alloc(
bun_ast::S::ExportDefault {
value: bun_ast::StmtOrExpr::Expr(expr),
default_name: bun_ast::LocRef {
loc: bun_ast::Loc::default(),
ref_: bun_ast::Ref::NONE,
symbols.truncate(count);

// `print_decls` aborts on a `var` with no declarations (`{"default": 1}`).
if count > 0 {
let stmt0 = bun_ast::Stmt::alloc(
bun_ast::S::Local {
decls: bun_ast::G::DeclList::move_from_list(decls),
kind: bun_ast::S::Kind::KVar,
..Default::default()
},
},
bun_ast::Loc { start: 0 },
);
bun_ast::Loc { start: 0 },
);
let stmt1 = bun_ast::Stmt::alloc(
bun_ast::S::ExportClause {
items: bun_ast::StoreSlice::new_mut(&mut export_clauses[..count]),
is_single_line: false,
},
bun_ast::Loc { start: 0 },
);
let stmt2 = bun_ast::Stmt::alloc(
bun_ast::S::ExportDefault {
value: bun_ast::StmtOrExpr::Expr(expr),
default_name: bun_ast::LocRef {
loc: bun_ast::Loc::default(),
ref_: bun_ast::Ref::NONE,
},
},
bun_ast::Loc { start: 0 },
);

let stmts =
bun_ast::StoreSlice::new_mut(arena.alloc_slice_copy(&[stmt0, stmt1, stmt2]));
break 'parts Box::new([bun_ast::Part {
stmts,
..Default::default()
}]);
let stmts = bun_ast::StoreSlice::new_mut(
arena.alloc_slice_copy(&[stmt0, stmt1, stmt2]),
);
break 'parts Box::new([bun_ast::Part {
stmts,
..Default::default()
}]);
}
}
}

Expand Down
22 changes: 22 additions & 0 deletions test/bundler/bundler_loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,28 @@ describe("bundler", async () => {
},
});

// A "default" key gets no named export. When no key gets one, the module
// has no `var` statement: it is only `export default`.
const onlyDefaultKey = (ext: string, contents: string, value: unknown, loader?: Record<`.${string}`, Loader>) =>
itBundled(`bun/loader-${ext}-only-default-key-no-bundle`, {
target: "bun",
bundling: false,
entryPoints: [`/data.${ext}`],
loader,
files: {
[`/data.${ext}`]: contents,
"/check.js": `import * as data from "./out.js"; console.write(JSON.stringify(data));`,
},
run: { file: "/check.js", stdout: JSON.stringify({ default: { default: value } }) },
});
onlyDefaultKey("json", `{"default": 1}`, 1);
onlyDefaultKey("jsonc", `{"default": 1} // comment`, 1);
onlyDefaultKey("json5", `{default: 1}`, 1);
onlyDefaultKey("toml", `default = 1`, 1);
onlyDefaultKey("yaml", `default: 1`, 1);
// `bun build --no-bundle` gives ".xml" the file loader unless --loader names it.
onlyDefaultKey("xml", `<default>1</default>`, "1", { ".xml": "xml" });

// The CSS-modules lazy export builds its object through `E::Object::put`.
itBundled("bun/loader-css-module-proto-class-is-own-property", {
target: "bun",
Expand Down
2 changes: 1 addition & 1 deletion test/bundler/expectBundled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ function expectBundled(
}
if (!ESBUILD && loader) {
const loaderValues = [...new Set(Object.values(loader))];
const supportedLoaderTypes = ["js", "jsx", "ts", "tsx", "css", "json", "text", "file", "wtf", "toml"];
const supportedLoaderTypes = ["js", "jsx", "ts", "tsx", "css", "json", "text", "file", "wtf", "toml", "xml"];
const unsupportedLoaderTypes = loaderValues.filter(x => !supportedLoaderTypes.includes(x));
if (unsupportedLoaderTypes.length > 0) {
throw new UnsupportedOptionError(`loader '${unsupportedLoaderTypes.join("', '")}' not implemented in bun build`);
Expand Down
25 changes: 25 additions & 0 deletions test/bundler/transpiler/transpiler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5256,6 +5256,31 @@ describe.concurrent("minify.identifiers on an empty source or a data loader", ()
});
});

// The printer aborted the process on this input, so it runs in a subprocess.
it.concurrent('a data loader source whose only key is "default" is only `export default`', async () => {
const result = await bunRun([
"-e",
`const transpiler = new Bun.Transpiler({ minify: { whitespace: true } });
const output = [];
for (const [loader, source] of [
["json", '{"default": 1}'],
["jsonc", '{"default": 1} // comment'],
["json5", "{default: 1}"],
["toml", "default = 1"],
["yaml", "default: 1"],
["xml", "<default>1</default>"],
]) {
output.push([loader, transpiler.transformSync(source, loader), await transpiler.transform(source, loader)]);
}
console.log(JSON.stringify(output));`,
]);
const number = "export default {default:1};";
const string = 'export default {default:"1"};';
const expected = ["json", "jsonc", "json5", "toml", "yaml"].map(loader => [loader, number, number]);
expected.push(["xml", string, string]);
expect(result).toEqual({ stdout: JSON.stringify(expected), stderr: "", exitCode: 0, signalCode: null });
});

it("runtime transpiler stack overflows", async () => {
expect(async () => await import("./fixtures/lots-of-for-loop.js")).toThrow(`Maximum call stack size exceeded`);
});
Expand Down
Loading