refactoring in playground, use zip file for web
This commit is contained in:
@@ -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
76
playground/src/frame.ts
Normal 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");
|
||||
}
|
||||
@@ -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 };
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user