Skip to content
Open

Sfb #22

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
16 changes: 16 additions & 0 deletions packages/samengine-build/src/buildconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export interface buildconfig {

devMode: profile;
releaseMode: profile;

// TODO
sfb: SingleFileBundlerConfig;
}

// Samegui Settigs
Expand Down Expand Up @@ -80,6 +83,7 @@ export function new_buildconfig(): buildconfig {
enable_mobile_css: false,
devMode: newDevProfile(),
releaseMode: newReleaseProfile(),
sfb: newSingleFileBundlerConfig(),
}
}

Expand Down Expand Up @@ -185,3 +189,15 @@ export function newReleaseProfile(): profile {
singlefile: false,
}
}

// SFB
// SingleFileBundler Config
export interface SingleFileBundlerConfig {
active: boolean;
}

export function newSingleFileBundlerConfig(): SingleFileBundlerConfig {
return {
active: false
}
}
45 changes: 41 additions & 4 deletions packages/samengine-build/src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { WebSocket, WebSocketServer } from "ws";

import { createProject } from "./new.js";
import { copyFolder, flog, getContentType, scanResourcesAsDataURIs, filterResourcesByUsage } from "../buildhelper.js";
import { GetDefaultHTML, GetSingleFileHTML } from "../exporthtml.js";
import { GetDefaultHTML, getSingleFileBundlerHTML, GetSingleFileHTML } from "../exporthtml.js";
import { loadUserConfig } from "./config.js";
import { compressHTML } from "../utils/utils.js";
import { parseArgs } from "./argparser.js";
Expand Down Expand Up @@ -65,7 +65,40 @@ function createBuilder(config: buildconfig, isRelease: boolean) {
await rm(`./${config.outdir}/main.js`, { recursive: true, force: true });

flog("✅ Single-file export created!");
} else {
}

//
else if (config.sfb.active) {
// if (isRelease)

// Static File Bundler

const bundledJsPath = path.join(".", config.outdir, `${config.entryname.replace(/\.[^.]*$/, "")}.js`);
const bundledJsContent = await readFile(bundledJsPath, "utf-8");

// Scan resources and convert to data URIs
// let resourcesMap = await scanResourcesAsDataURIs("./resources");

// Filter resources by usage in the bundled code
// resourcesMap = filterResourcesByUsage(bundledJsContent, resourcesMap);

let html = getSingleFileBundlerHTML(config, bundledJsContent);
if (isRelease) html = await compressHTML(html);

// Add comment at the beginning after minification
const htmlComment = `<!-- Game made with samengine v${version} - https://github.com/Shadowdara/samengine ${config.gameauthor} (Game Author) -->\n`;
html = htmlComment + html;

await writeFile(`./${config.outdir}/index.html`, html);

// Delete the JS File
await rm(`./${config.outdir}/main.js`, { recursive: true, force: true });

flog("✅ Single-file export created!");
}

// Esle
else {
// Multi-file export (original behavior)
let html = GetDefaultHTML(config, isRelease);
if (isRelease) html = await compressHTML(html);
Expand Down Expand Up @@ -174,9 +207,13 @@ async function main() {
process.exit(0);
}

const config = await loadUserConfig();
const config: buildconfig = await loadUserConfig();
let builder = createBuilder(config, args.release);

if (!config.enable_audio) {
flog("[INFO]: Audio is disabled!");
}

let isBuilding = false;
let pendingRestart = false;

Expand All @@ -191,7 +228,7 @@ async function main() {
pendingRestart = false;
try {
// Load the New Config
const newConfig = await loadUserConfig();
const newConfig: buildconfig = await loadUserConfig();

// Dev Server Stoppen
devServer?.stop();
Expand Down
3 changes: 2 additions & 1 deletion packages/samengine-build/src/cli/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { build } from "esbuild";
import path from "path";
import { pathToFileURL } from "url";
import fs from "fs/promises";
import { buildconfig } from "../buildconfig";

export async function loadUserConfig() {
export async function loadUserConfig(): Promise<buildconfig> {
const root = process.cwd();

const configPath = path.resolve(root, "samengine.config.ts");
Expand Down
114 changes: 114 additions & 0 deletions packages/samengine-build/src/exporthtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,3 +615,117 @@ document.getElementById("mdnotes").remove();

return defaulthtml;
}

/////////////////////////////////////////////////////////////////////

export function getSingleFileBundlerHTML(c: buildconfig, js: string): string {
return `<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Mini TSX Runtime</title>
</head>

<body>
<div id="app"></div>

<script>
// -----------------------------
// MINI VIRTUAL DOM FORMAT
// -----------------------------
// { tag: "div", props: {}, children: [] }

function h(tag, props, ...children) {
return {
tag,
props: props || {},
children: children.flat()
};
}

// -----------------------------
// RENDER FUNCTION
// -----------------------------
function render(node) {
if (node == null || node === false) return "";

if (typeof node === "string" || typeof node === "number") {
return escapeHtml(String(node));
}

if (typeof node.tag === "function") {
return render(node.tag(node.props || {}));
}

const props = node.props || {};

const attrs = Object.entries(props)
.filter(([k]) => k !== "children")
.map(([k, v]) => \` \${k}="\${escapeAttr(v)}"\`)
.join("");

const children = (node.children || [])
.map(render)
.join("");

return \`<\${node.tag}\${attrs}>\${children}</\${node.tag}>\`;
}

function escapeHtml(str) {
return str
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
}

function escapeAttr(str) {
return String(str)
.replaceAll("&", "&amp;")
.replaceAll("\\"", "&quot;");
}

// -----------------------------
// MINI "COMPONENTS"
// -----------------------------
function App() {
return h(
"div",
{ class: "app" },
h("h1", null, "Hallo Welt"),
h("p", null, "Das ist eine Mini-TSX Runtime ohne React."),
h(
"button",
{
onclick: "alert('Hi!')"
},
"Klick mich"
)
);
}

// -----------------------------
// MOUNT
// -----------------------------
document.getElementById("app").innerHTML = render(h(App));
</script>

<style>
body {
font-family: system-ui;
padding: 20px;
}

.app {
border: 1px solid #ddd;
padding: 12px;
border-radius: 8px;
}

button {
margin-top: 10px;
padding: 8px 12px;
}
</style>
</body>
</html>`;
}
8 changes: 6 additions & 2 deletions packages/samengine-build/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export type {
SameGUI,
HTMLMenuSettingOption,
HTMLMenuSetting,
HTMLMenu
HTMLMenu,

SingleFileBundlerConfig
} from "./buildconfig.js";

export {
Expand All @@ -15,5 +17,7 @@ export {
svgfile,
newHTMLMenu,
newDevProfile,
newReleaseProfile
newReleaseProfile,

newSingleFileBundlerConfig
} from "./buildconfig.js";
3 changes: 2 additions & 1 deletion packages/samengine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"./utils/csv": "./dist/utils/csv/index.js",
"./build": "./dist/build/index.js",
"./physics": "./dist/physics/index.js",
"./samegui": "./dist/samegui/index.js"
"./samegui": "./dist/samegui/index.js",
"./site/sfb/hashrouter": "./dist/site/sfb/hashrouter/index.js"
},
"keywords": [
"game",
Expand Down
28 changes: 28 additions & 0 deletions packages/samengine/src/site/sfb/hashrouter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export interface Route {
path: string;
component: () => any;
}

export class HashRouter {
constructor(private routes: Route[]) {}

getCurrentComponent() {
let path = "/";

if (typeof window !== "undefined") {
path = window.location.hash.slice(1) || "/";
}

const route = this.routes.find((r) => r.path === path);

return route?.component ?? (() => "<h1>404</h1>");
}

clientScript() {
return `
window.addEventListener("hashchange", () => {
location.reload();
});
`;
}
}
Loading