Refactor of asset bundles

This commit is contained in:
Zef Hemel
2022-10-12 11:47:13 +02:00
parent 9dd94c5397
commit 4c19ab21f2
96 changed files with 6279 additions and 770 deletions
+12
View File
@@ -0,0 +1,12 @@
import { assertEquals } from "../../test_deps.ts";
import { base64Decode } from "./base64.ts";
import { base64Encode } from "./base64.ts";
Deno.test("Base 64 encoding", () => {
const buf = new Uint8Array(3);
buf[0] = 1;
buf[1] = 2;
buf[2] = 3;
assertEquals(buf, base64Decode(base64Encode(buf)));
});
+20
View File
@@ -0,0 +1,20 @@
import { buf } from "https://deno.land/x/sqlite3@0.6.1/src/util.ts";
export function base64Decode(s: string): Uint8Array {
const binString = atob(s);
const len = binString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binString.charCodeAt(i);
}
return bytes;
}
export function base64Encode(buffer: Uint8Array): string {
let binary = "";
const len = buffer.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(buffer[i]);
}
return btoa(binary);
}
+34
View File
@@ -0,0 +1,34 @@
import { globToRegExp, path, walk } from "../deps.ts";
import { AssetBundle } from "./bundle.ts";
export async function bundleAssets(
rootPath: string,
patterns: string[],
): Promise<AssetBundle> {
const bundle = new AssetBundle();
for await (
const file of walk(rootPath, {
match: patterns.map((pat) => globToRegExp(pat)),
})
) {
const cleanPath = file.path.substring("".length);
await bundle.writeFileSync(cleanPath, await Deno.readFile(file.path));
}
return bundle;
}
export async function bundleFolder(rootPath: string, bundlePath: string) {
const bundle = new AssetBundle();
await Deno.mkdir(path.dirname(bundlePath), { recursive: true });
for await (
const { path: filePath } of walk(rootPath, { includeDirs: false })
) {
console.log("Bundling", filePath);
const cleanPath = filePath.substring(`${rootPath}/`.length);
await bundle.writeFileSync(cleanPath, await Deno.readFile(filePath));
}
await Deno.writeTextFile(
bundlePath,
JSON.stringify(bundle.toJSON(), null, 2),
);
}
+16
View File
@@ -0,0 +1,16 @@
import { AssetBundle } from "./bundle.ts";
import { assertEquals } from "../../test_deps.ts";
Deno.test("Asset bundle", () => {
const assetBundle = new AssetBundle();
assetBundle.writeTextFileSync("test.txt", "Sup yo");
assertEquals("text/plain", assetBundle.getMimeType("test.txt"));
assertEquals("Sup yo", assetBundle.readTextFileSync("test.txt"));
const buf = new Uint8Array(3);
buf[0] = 1;
buf[1] = 2;
buf[2] = 3;
assetBundle.writeFileSync("test.bin", buf);
assertEquals("application/octet-stream", assetBundle.getMimeType("test.bin"));
assertEquals(buf, assetBundle.readFileSync("test.bin"));
});
+69
View File
@@ -0,0 +1,69 @@
import { base64Decode, base64Encode } from "./base64.ts";
import { mime } from "../deps.ts";
export type AssetJson = Record<string, string>;
export class AssetBundle {
readonly bundle: AssetJson;
constructor(bundle: AssetJson = {}) {
this.bundle = bundle;
}
has(path: string): boolean {
return path in this.bundle;
}
listFiles(): string[] {
return Object.keys(this.bundle);
}
readFileSync(
path: string,
): Uint8Array {
const content = this.bundle[path];
if (!content) {
throw new Error(`No such file ${path}`);
}
const data = content.split(",", 2)[1];
return base64Decode(data);
}
readFileAsDataUrl(path: string): string {
const content = this.bundle[path];
if (!content) {
throw new Error(`No such file ${path}`);
}
return content;
}
readTextFileSync(
path: string,
): string {
return new TextDecoder().decode(this.readFileSync(path));
}
getMimeType(
path: string,
): string {
const content = this.bundle[path];
if (!content) {
throw new Error(`No such file ${path}`);
}
return content.split(";")[0].split(":")[1];
}
writeFileSync(path: string, data: Uint8Array) {
const encoded = base64Encode(data);
const mimeType = mime.getType(path);
this.bundle[path] = `data:${mimeType};base64,${encoded}`;
}
writeTextFileSync(path: string, s: string) {
this.writeFileSync(path, new TextEncoder().encode(s));
}
toJSON(): AssetJson {
return this.bundle;
}
}