refactoring in playground, use zip file for web

This commit is contained in:
2024-12-08 20:19:55 -08:00
parent 0f5a909cce
commit d6aaaaabf1
47 changed files with 1932 additions and 88 deletions

View File

@@ -135,7 +135,7 @@ export let shim: NodeShim = {
buf = shim.files[name];
} else if (shim.archive?.entries[name]) {
// keep a copy of the uncompressed version for speed
buf = shim.files[name] = shim.archive.getData(name);
buf = shim.files[name] = shim.archive.getData(name)!;
} else {
shim.process.__lasterr.errno = 1;
throw new Error(`${name} not found`);

76
playground/src/frame.ts Normal file
View File

@@ -0,0 +1,76 @@
import { archive } from "./preload";
import { Message } from "./types";
// fs emulation for frame
const shim = {
stdout: "",
fs: {
// made just for Node.newt...
readFileSync(fn: string, encoding: string) {
let data = archive?.getData(fn);
if (data) {
return new TextDecoder().decode(data);
} else {
throw new Error(`${fn} not found`);
}
},
},
};
// we intercept require to return our fake node modules
declare global {
interface Window {
require: (x: string) => any;
}
}
const requireStub: any = (x: string) => (shim as any)[x];
self.require = requireStub;
self.process = {
platform: "linux",
argv: ["", ""],
stdout: {
// We'll want to replace this one
write(s) {
console.log("*", s);
shim.stdout += s;
},
},
exit(v: number) {
console.log("exit", v);
},
cwd() {
return "";
},
env: {
NO_COLOR: "true",
IDRIS2_CG: "javascript",
IDRIS2_PREFIX: "",
},
__lasterr: {
errno: 0,
},
// stdin: { fd: 0 },
};
let realLog = console.log;
console.log = (...args) => {
sendMessage({ type: "pushConsole", message: args.join(" ") });
realLog(...args);
};
window.addEventListener("message", (ev: MessageEvent<Message>) => {
realLog("got", ev.data);
if (ev.data.type === "exec") {
let { src } = ev.data;
try {
sendMessage({ type: "setConsole", messages: [] });
eval(src);
} catch (e) {
console.log(e);
}
}
});
const sendMessage = (msg: Message) => window.parent.postMessage(msg, "*");
realLog("IFRAME INITIALIZED");
if (shim) {
realLog("shim imported for effect");
}

View File

@@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from "preact/hooks";
import { h, render } from "preact";
import { ChangeEvent } from "preact/compat";
import { archive, preload } from "./preload.ts";
import { CompileReq, CompileRes } from "./types.ts";
import { CompileReq, CompileRes, Message } from "./types.ts";
// editor.(createModel / setModel / getModels) to switch files
@@ -102,14 +102,15 @@ document.body.appendChild(iframe);
function run(src: string) {
console.log("SEND TO", iframe.contentWindow);
postMessage({ src });
const fileName = state.currentFile.value
postMessage({ type: 'compileRequest', fileName, src });
}
function runOutput() {
const src = state.javascript.value;
console.log("RUN", iframe.contentWindow);
try {
iframe.contentWindow?.postMessage({ cmd: "exec", src }, "*");
iframe.contentWindow?.postMessage({ type: "exec", src }, "*");
} catch (e) {
console.error(e);
}
@@ -132,23 +133,15 @@ function setOutput(output: string) {
state.output.value = output;
}
interface ConsoleList {
messages: string[]
}
interface ConsoleItem {
message: string
}
type WinMessage = CompileRes | ConsoleList | ConsoleItem
window.onmessage = (ev: MessageEvent<WinMessage>) => {
window.onmessage = (ev: MessageEvent<Message>) => {
console.log("window got", ev.data);
if ('messages' in ev.data) state.messages.value = ev.data.messages;
if ('message' in ev.data) {
if ("messages" in ev.data) state.messages.value = ev.data.messages;
if ("message" in ev.data) {
state.messages.value = [...state.messages.value, ev.data.message];
}
// safari callback
if ('output' in ev.data) {
if ("output" in ev.data) {
setOutput(ev.data.output);
state.javascript.value = ev.data.javascript;
}
@@ -172,8 +165,11 @@ const state = {
messages: signal<string[]>([]),
editor: signal<monaco.editor.IStandaloneCodeEditor | null>(null),
dark: signal(false),
files: signal<string[]>(["Tour.newt"]),
currentFile: signal<string>(localStorage.currentFile ?? 'Tour.newt')
};
// Monitor dark mode state (TODO - let user override system setting)
if (window.matchMedia) {
function checkDark(ev: { matches: boolean }) {
console.log("CHANGE", ev);
@@ -199,8 +195,10 @@ async function loadFile(fn: string) {
let text = new TextDecoder().decode(data);
state.editor.value!.setValue(text);
} else {
state.editor.value!.setValue("module Main\n");
state.editor.value!.setValue("module Main\n\n-- File not found\n");
}
state.currentFile.value = fn
localStorage.currentFile = fn
}
// I keep pressing this.
@@ -213,8 +211,6 @@ const LOADING = "module Loading\n";
let value = localStorage.code || LOADING;
let initialVertical = localStorage.vertical == "true";
// let result = document.getElementById("result")!;
// the editor might handle this itself with the right prodding.
effect(() => {
let text = state.output.value;
@@ -325,20 +321,16 @@ function Tabs() {
);
}
const SAMPLES = [
"Tour.newt",
"Hello.newt",
"DSL.newt",
"Tree.newt",
"Reasoning.newt",
"Lists.newt",
"Day1.newt",
"Day2.newt",
"Node.newt",
"Prelude.newt",
"TypeClass.newt",
"Combinatory.newt",
];
preload.then(() => {
if (archive) {
let files = [];
for (let name in archive.entries) {
if (name.endsWith(".newt")) files.push(name);
}
files.sort();
state.files.value = files;
}
});
function EditWrap({
vertical,
@@ -347,9 +339,12 @@ function EditWrap({
vertical: boolean;
toggle: () => void;
}) {
const options = SAMPLES.map((value) => h("option", { value }, value));
// const [file, setFile] = useState("Tour.newt");
const options = state.files.value.map((value) =>
h("option", { value }, value)
);
const onChange = async (ev: ChangeEvent) => {
const selectFile = async (ev: ChangeEvent) => {
if (ev.target instanceof HTMLSelectElement) {
let fn = ev.target.value;
ev.target.value = "";
@@ -368,12 +363,7 @@ function EditWrap({
h(
"div",
{ className: "tabBar" },
h(
"select",
{ onChange },
h("option", { value: "" }, "choose sample"),
options
),
h("select", { onChange: selectFile, value: state.currentFile.value }, options),
h("div", { style: { flex: "1 1" } }),
h("button", { onClick: runOutput }, svg(play)),
h("button", { onClick: toggle }, svg(d))
@@ -420,7 +410,8 @@ const processOutput = (
let [_full, kind, file, line, col, message] = match;
let lineNumber = +line + 1;
let column = +col + 1;
if (fn && file !== fn) {
// FIXME - pass the real path in
if (fn && fn == file) {
lineNumber = column = 0;
}
let start = { column, lineNumber };

View File

@@ -1,9 +1,28 @@
export interface CompileReq {
type: "compileRequest";
fileName: string;
src: string;
}
export interface CompileRes {
output: string
javascript: string
duration: number
type: "compileResult";
output: string;
javascript: string;
duration: number;
}
export interface ConsoleList {
type: 'setConsole'
messages: string[];
}
export interface ConsoleItem {
type: 'pushConsole'
message: string;
}
export interface ExecCode {
type: 'exec'
src: string
}
export type Message = CompileReq | CompileRes | ConsoleList | ConsoleItem | ExecCode

View File

@@ -6,15 +6,11 @@ const handleMessage = async function (ev: { data: CompileReq }) {
console.log("message", ev.data);
await preload;
shim.archive = archive;
let { src } = ev.data;
let module = "Main";
let m = src.match(/module (\w+)/);
if (m) module = m[1];
let fn = `${module}.newt`;
let { src, fileName } = ev.data;
const outfile = "out.js";
shim.process.argv = ["", "", fn, "-o", outfile, "--top"];
shim.process.argv = ["", "", fileName, "-o", outfile, "--top"];
console.log("Using args", shim.process.argv);
shim.files[fn] = new TextEncoder().encode(src);
shim.files[fileName] = new TextEncoder().encode(src);
shim.files[outfile] = new TextEncoder().encode("No JS output");
shim.stdout = "";
const start = +new Date();
@@ -27,10 +23,10 @@ const handleMessage = async function (ev: { data: CompileReq }) {
shim.stdout += "\n" + String(e);
}
let duration = +new Date() - start;
console.log(`process ${fn} in ${duration} ms`);
console.log(`process ${fileName} in ${duration} ms`);
let javascript = new TextDecoder().decode(shim.files[outfile]);
let output = shim.stdout;
sendResponse({ javascript, output, duration });
sendResponse({ type: 'compileResult', javascript, output, duration });
};
// hooks for worker.html to override

View File

@@ -298,6 +298,7 @@ export class ZipFile {
}
}
getData(name: string) {
if (!(name in this.entries)) return
let { start, end, size } = this.entries[name];
return inflate(new Uint8Array(this.data.slice(start, end)));
}