Skip to content

Commit 3c8eafd

Browse files
committed
fix: mount binary files properly
1 parent 9784b75 commit 3c8eafd

File tree

7 files changed

+43
-60
lines changed

7 files changed

+43
-60
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
"typecheck": "tsc --noEmit"
4646
},
4747
"dependencies": {
48-
"@webcontainer/api": "^1.6.1"
48+
"@webcontainer/api": "^1.6.1",
49+
"@webcontainer/snapshot": "^0.1.0"
4950
},
5051
"peerDependencies": {
5152
"@vitest/browser": "^3.1",

pnpm-lock.yaml

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/index.ts

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import { readdir, readFile, stat } from "node:fs/promises";
2-
import { relative, resolve, sep } from "node:path";
3-
import { FileSystemTree } from "@webcontainer/api";
1+
import { resolve } from "node:path";
2+
import { snapshot } from "@webcontainer/snapshot";
43
import type { BrowserCommand } from "vitest/node";
54

65
// custom command to read directories from browser context. Used for mounting directories inside webcontainer
76
export const readDirectory: BrowserCommand<[directory: string]> = async (
87
context,
98
directory,
10-
): Promise<FileSystemTree> => {
9+
): Promise<string> => {
1110
const root = context.project.config.root;
1211
const resolved = resolve(root, directory);
1312

@@ -21,55 +20,7 @@ export const readDirectory: BrowserCommand<[directory: string]> = async (
2120
);
2221
}
2322

24-
const files = await recursiveRead(directory);
25-
const tree: FileSystemTree = {};
23+
const tree = await snapshot(directory);
2624

27-
for (const { filePath, contents } of files) {
28-
const segments = filePath.split("/");
29-
30-
let currentTree: FileSystemTree = tree;
31-
32-
for (let i = 0; i < segments.length; i++) {
33-
const name = segments[i];
34-
35-
if (i === segments.length - 1) {
36-
currentTree[name] = { file: { contents } };
37-
} else {
38-
let folder = currentTree[name];
39-
40-
if (!folder || !("directory" in folder)) {
41-
folder = { directory: {} };
42-
currentTree[name] = folder;
43-
}
44-
45-
currentTree = folder.directory;
46-
}
47-
}
48-
}
49-
50-
return tree;
51-
52-
async function recursiveRead(
53-
dir: string,
54-
): Promise<{ filePath: string; contents: string }[]> {
55-
const files = await readdir(dir);
56-
57-
const output = await Promise.all(
58-
files.map(async (file) => {
59-
const filePath = resolve(dir, file);
60-
const fileStat = await stat(filePath);
61-
62-
if (fileStat.isDirectory()) {
63-
return recursiveRead(filePath);
64-
}
65-
66-
return {
67-
filePath: relative(directory, filePath).replaceAll(sep, "/"),
68-
contents: await readFile(filePath, "utf-8"),
69-
};
70-
}),
71-
);
72-
73-
return output.flat();
74-
}
25+
return tree.toString("base64");
7526
};

src/fixtures/file-system.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ export class FileSystem {
1717
*/
1818
async mount(filesOrPath: string | FileSystemTree) {
1919
if (typeof filesOrPath === "string") {
20-
filesOrPath = await commands.readDirectory(filesOrPath);
20+
const tree = await commands.readDirectory(filesOrPath);
21+
const binary = Uint8Array.from(atob(tree), (c) => c.charCodeAt(0));
22+
23+
return await this._instance.mount(binary);
2124
}
2225

23-
return await this._instance.mount(filesOrPath as FileSystemTree);
26+
return await this._instance.mount(filesOrPath);
2427
}
2528

2629
/**

src/types.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type {} from "@vitest/browser/context";
2-
import { FileSystemTree } from "@webcontainer/api";
32

43
declare module "@vitest/browser/context" {
54
interface BrowserCommands {
6-
readDirectory: (directory: string) => Promise<FileSystemTree>;
5+
readDirectory: (directory: string) => Promise<string>;
76
}
87
}
116 Bytes
Loading

test/mount.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ test("user can mount directories from file-system to webcontainer", async ({
77
await webcontainer.mount("test/fixtures/mount-example");
88

99
const ls = await webcontainer.runCommand("ls");
10-
expect(ls).toMatchInlineSnapshot(`"file-1.ts nested"`);
10+
expect(ls).toMatchInlineSnapshot(`"file-1.ts image.png nested"`);
1111

1212
const lsNested = await webcontainer.runCommand("ls", ["nested"]);
1313
expect(lsNested).toMatchInlineSnapshot(`"file-2.ts"`);
@@ -21,6 +21,18 @@ test("user can mount directories from file-system to webcontainer", async ({
2121
expect(catNestedFile).toMatchInlineSnapshot(
2222
`"export default "Hello from nested file";"`,
2323
);
24+
25+
const pngFile = await webcontainer.runCommand("xxd", ["image.png"]);
26+
expect(pngFile).toMatchInlineSnapshot(`
27+
"00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
28+
00000010: 0000 0001 0000 0001 0103 0000 0025 db56 .............%.V
29+
00000020: ca00 0000 0173 5247 4201 d9c9 2c7f 0000 .....sRGB...,...
30+
00000030: 0009 7048 5973 0000 0b13 0000 0b13 0100 ..pHYs..........
31+
00000040: 9a9c 1800 0000 0350 4c54 45ff ffff a7c4 .......PLTE.....
32+
00000050: 1bc8 0000 000a 4944 4154 789c 6364 0000 ......IDATx.cd..
33+
00000060: 0004 0002 2164 ad6a 0000 0000 4945 4e44 ....!d.j....IEND
34+
00000070: ae42 6082 .B\`."
35+
`);
2436
});
2537

2638
test("user can mount inlined FileSystemTree to webcontainer", async ({

0 commit comments

Comments
 (0)