mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
### What does this PR do?
This PR adds builtin YAML parsing with `Bun.YAML.parse`
```js
import { YAML } from "bun";
const items = YAML.parse("- item1");
console.log(items); // [ "item1" ]
```
Also YAML imports work just like JSON and TOML imports
```js
import pkg from "./package.yaml"
console.log({ pkg }); // { pkg: { name: "pkg", version: "1.1.1" } }
```
### How did you verify your code works?
Added some tests for YAML imports and parsed values.
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
222 lines
6.4 KiB
TypeScript
222 lines
6.4 KiB
TypeScript
import { fileURLToPath, Loader } from "bun";
|
|
import { describe, expect } from "bun:test";
|
|
import fs, { readdirSync } from "node:fs";
|
|
import { join } from "path";
|
|
import { itBundled } from "./expectBundled";
|
|
|
|
describe("bundler", async () => {
|
|
for (let target of ["bun", "node"] as const) {
|
|
describe(`${target} loader`, async () => {
|
|
itBundled("bun/loader-yaml-file", {
|
|
target,
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import hello from './hello.notyaml' with {type: "yaml"};
|
|
console.write(JSON.stringify(hello));
|
|
`,
|
|
"/hello.notyaml": `hello: world`,
|
|
},
|
|
run: { stdout: '{"hello":"world"}' },
|
|
});
|
|
itBundled("bun/loader-text-file", {
|
|
target,
|
|
outfile: "",
|
|
outdir: "/out",
|
|
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import hello from './hello.foo' with {type: "text"};
|
|
console.log(hello);
|
|
`,
|
|
"/hello.foo": "Hello, world!",
|
|
},
|
|
run: { stdout: "Hello, world!" },
|
|
});
|
|
itBundled("bun/loader-json-file", {
|
|
target,
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import hello from './hello.notjson' with {type: "json"};
|
|
console.write(JSON.stringify(hello));
|
|
`,
|
|
"/hello.notjson": JSON.stringify({ hello: "world" }),
|
|
},
|
|
run: { stdout: '{"hello":"world"}' },
|
|
});
|
|
itBundled("bun/loader-toml-file", {
|
|
target,
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import hello from './hello.nottoml' with {type: "toml"};
|
|
console.write(JSON.stringify(hello));
|
|
`,
|
|
"/hello.nottoml": `hello = "world"`,
|
|
},
|
|
run: { stdout: '{"hello":"world"}' },
|
|
});
|
|
itBundled("bun/loader-text-file", {
|
|
target,
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import hello from './hello.json' with {type: "text"};
|
|
console.write(hello);
|
|
`,
|
|
"/hello.json": JSON.stringify({ hello: "world" }),
|
|
},
|
|
run: { stdout: '{"hello":"world"}' },
|
|
});
|
|
});
|
|
}
|
|
|
|
itBundled("bun/loader-text-file", {
|
|
target: "bun",
|
|
outfile: "",
|
|
outdir: "/out",
|
|
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import first from './1.boo' with {type: "text"};
|
|
import second from './2.boo' with {type: "text"};
|
|
console.write(first + second);
|
|
`,
|
|
"/1.boo": "'`Hello, \nworld!`",
|
|
"/2.boo": "`${Hello}\n, world!`'",
|
|
},
|
|
run: {
|
|
stdout: "'`Hello, \nworld!``${Hello}\n, world!`'",
|
|
},
|
|
});
|
|
|
|
itBundled("bun/wasm-is-copied-to-outdir", {
|
|
target: "bun",
|
|
outdir: "/out",
|
|
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import wasm from './add.wasm';
|
|
import { join } from 'path';
|
|
const { instance } = await WebAssembly.instantiate(await Bun.file(join(import.meta.dir, wasm)).arrayBuffer());
|
|
console.log(instance.exports.add(1, 2));
|
|
`,
|
|
"/add.wasm": fs.readFileSync(join(import.meta.dir, "fixtures", "add.wasm")),
|
|
},
|
|
run: {
|
|
stdout: "3",
|
|
},
|
|
});
|
|
|
|
const moon = await Bun.file(
|
|
fileURLToPath(import.meta.resolve("../js/bun/util/text-loader-fixture-text-file.backslashes.txt")),
|
|
).text();
|
|
|
|
// https://github.com/oven-sh/bun/issues/3449
|
|
itBundled("bun/loader-text-file-#3449", {
|
|
target: "bun",
|
|
outfile: "",
|
|
outdir: "/out",
|
|
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import first from './1.boo' with {type: "text"};
|
|
console.write(first);
|
|
`,
|
|
"/1.boo": moon,
|
|
},
|
|
run: {
|
|
stdout: moon,
|
|
},
|
|
});
|
|
|
|
const loaders: Loader[] = ["wasm", "json", "file" /* "napi" */, "text"];
|
|
const exts = ["wasm", "json", "lmao" /* ".node" */, "txt"];
|
|
for (let i = 0; i < loaders.length; i++) {
|
|
const loader = loaders[i];
|
|
const ext = exts[i];
|
|
itBundled(`bun/loader-copy-file-entry-point-with-onLoad-${loader}`, {
|
|
target: "bun",
|
|
outdir: "/out",
|
|
files: {
|
|
[`/entry.${ext}`]: /* js */ `{ "hello": "friends" }`,
|
|
},
|
|
entryNaming: "[dir]/[name]-[hash].[ext]",
|
|
plugins(builder) {
|
|
builder.onLoad({ filter: new RegExp(`.${loader}$`) }, async ({ path }) => {
|
|
const result = await Bun.file(path).text();
|
|
return { contents: result, loader };
|
|
});
|
|
},
|
|
onAfterBundle(api) {
|
|
const jsFile = readdirSync(api.outdir).find(x => x.endsWith(".js"))!;
|
|
const module = require(join(api.outdir, jsFile));
|
|
|
|
if (loader === "json") {
|
|
expect(module.default).toStrictEqual({ hello: "friends" });
|
|
} else if (loader === "text") {
|
|
expect(module.default).toStrictEqual('{ "hello": "friends" }');
|
|
} else {
|
|
api.assertFileExists(join("out", module.default));
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
for (let i = 0; i < loaders.length; i++) {
|
|
const loader = loaders[i];
|
|
const ext = exts[i];
|
|
itBundled(`bun/loader-copy-file-entry-point-${loader}`, {
|
|
target: "bun",
|
|
outfile: "",
|
|
outdir: "/out",
|
|
files: {
|
|
[`/entry.${ext}`]: /* js */ `{ "hello": "friends" }`,
|
|
},
|
|
entryNaming: "[dir]/[name]-[hash].[ext]",
|
|
onAfterBundle(api) {
|
|
const jsFile = readdirSync(api.outdir).find(x => x.endsWith(".js"))!;
|
|
const module = require(join(api.outdir, jsFile));
|
|
|
|
if (loader === "json") {
|
|
expect(module.default).toStrictEqual({ hello: "friends" });
|
|
} else if (loader === "text") {
|
|
expect(module.default).toStrictEqual('{ "hello": "friends" }');
|
|
} else {
|
|
api.assertFileExists(join("out", module.default));
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
describe("handles empty files", () => {
|
|
for (const target of ["bun", "node", "browser"] as const) {
|
|
itBundled(`${target}/loader-empty-text-file`, {
|
|
target: target,
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import empty from './empty.txt' with {type: "text"};
|
|
console.write(JSON.stringify(empty));
|
|
`,
|
|
"/empty.txt": "",
|
|
},
|
|
run: { stdout: '""' },
|
|
});
|
|
|
|
itBundled(`${target}/loader-empty-file-loader`, {
|
|
target: target,
|
|
outdir: "/out",
|
|
files: {
|
|
"/entry.ts": /* js */ `
|
|
import empty from './empty.txt' with {type: "file"};
|
|
export default empty;
|
|
`,
|
|
"/empty.txt": "",
|
|
},
|
|
onAfterBundle(api) {
|
|
const jsFile = readdirSync(api.outdir).find(x => x.endsWith(".js"))!;
|
|
const module = require(join(api.outdir, jsFile));
|
|
api.assertFileExists(join("out", module.default));
|
|
},
|
|
});
|
|
}
|
|
});
|
|
});
|