You can run Python code now

This commit is contained in:
Radon Rosborough 2020-06-06 15:47:12 -06:00
parent 10c868fbd3
commit f4178588b7
5 changed files with 85 additions and 122 deletions

View File

@ -1,10 +1,14 @@
import * as fs from "fs";
import * as pty from "node-pty";
import { IPty } from "node-pty";
import * as path from "path";
import * as tmp from "tmp";
import * as WebSocket from "ws";
import { LangConfig, langs } from "./langs";
export class Session {
code: string;
config: LangConfig;
term: IPty;
ws: WebSocket;
@ -13,13 +17,14 @@ export class Session {
this.ws = ws;
this.config = langs[lang];
this.term = null;
this.code = "";
this.ws.send(
JSON.stringify({
event: "setMonacoLanguage",
monacoLanguage: this.config.monacoLang,
})
);
this.run();
this.run().catch(console.error);
ws.on("message", this.handleClientMessage);
}
handleClientMessage = (event: string) => {
@ -30,7 +35,7 @@ export class Session {
console.error(`failed to parse client message: ${msg}`);
return;
}
switch (msg.event) {
switch (msg?.event) {
case "terminalInput":
if (!this.term) {
console.error(`terminalInput: no terminal`);
@ -40,19 +45,52 @@ export class Session {
this.term.write(msg.input);
}
break;
case "runCode":
if (typeof msg.code !== "string") {
console.error(`runCode: missing or malformed code field`);
} else {
this.code = msg.code;
this.run();
}
break;
default:
console.error(`unknown client message type: ${msg.event}`);
break;
}
};
run = () => {
const cmdline = this.config.cmdline;
run = async () => {
const { repl, file, run } = this.config;
if (this.term) {
this.term.kill();
}
this.ws.send(JSON.stringify({ event: "terminalClear" }));
const tmpdir: string = await new Promise((resolve, reject) =>
tmp.dir({ unsafeCleanup: true }, (err, path) => {
if (err) {
reject(err);
} else {
resolve(path);
}
})
);
let cmdline: string[];
if (this.code || !repl) {
await new Promise((resolve, reject) =>
fs.writeFile(path.resolve(tmpdir, file), this.code, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
})
);
cmdline = run;
} else {
cmdline = repl;
}
this.term = pty.spawn(cmdline[0], cmdline.slice(1), {
name: "xterm-color",
cwd: process.env.PWD,
cwd: tmpdir,
env: process.env,
});
this.term.on("data", (data) =>

View File

@ -1,92 +1,91 @@
export interface LangConfig {
cmdline: string[];
repl?: string[];
file?: string;
run?: string[];
monacoLang: string;
name: string;
}
export const langs = {
bash: {
cmdline: ["bash"],
repl: ["bash"],
name: "Bash",
monacoLang: "shell",
},
c: {
cmdline: ["echo", "not implemented"],
name: "C",
monacoLang: "c",
},
"c++": {
cmdline: ["echo", "not implemented"],
name: "C++",
monacoLang: "cpp",
},
clojure: {
cmdline: ["clojure"],
repl: ["clojure"],
name: "Clojure",
monacoLang: "clojure",
},
emacs: {
cmdline: ["emacs"],
repl: ["emacs"],
name: "Emacs Lisp",
monacoLang: "plaintext",
},
fish: {
cmdline: ["env", "SHELL=/usr/bin/fish", "fish"],
repl: ["env", "SHELL=/usr/bin/fish", "fish"],
name: "Fish",
monacoLang: "plaintext",
},
go: {
cmdline: ["echo", "not implemented"],
name: "Go",
monacoLang: "go",
},
haskell: {
cmdline: ["ghci"],
repl: ["ghci"],
name: "Haskell",
monacoLang: "plaintext",
},
java: {
cmdline: ["echo", "not implemented"],
name: "Java",
monacoLang: "java",
},
julia: {
cmdline: ["julia"],
repl: ["julia"],
name: "Julia",
monacoLang: "plaintext",
},
lua: {
cmdline: ["lua"],
repl: ["lua"],
name: "Lua",
monacoLang: "lua",
},
nodejs: {
cmdline: ["node"],
repl: ["node"],
name: "Node.js",
monacoLang: "javascript",
},
python: {
cmdline: ["python3"],
repl: ["python3", "-u"],
file: "main.py",
run: ["python3", "-u", "-i", "main.py"],
name: "Python",
monacoLang: "python",
},
ruby: {
cmdline: ["irb"],
repl: ["irb"],
name: "Ruby",
monacoLang: "ruby",
},
rust: {
cmdline: ["echo", "not implemented"],
name: "Rust",
monacoLang: "rust",
},
vim: {
cmdline: ["vim"],
repl: ["vim"],
name: "Vimscript",
monacoLang: "plaintext",
},
zsh: {
cmdline: ["env", "SHELL=/usr/bin/zsh", "zsh"],
repl: ["env", "SHELL=/usr/bin/zsh", "zsh"],
name: "Zsh",
monacoLang: "shell",
},

View File

@ -32,6 +32,9 @@ socket.addEventListener("message", (event) => {
return;
}
switch (message?.event) {
case "terminalClear":
term.reset();
return;
case "terminalOutput":
if (typeof message.output !== "string") {
console.error("Unexpected message from server:", message);

View File

@ -7,6 +7,7 @@
"@types/app-root-path": "^1.2.4",
"@types/express": "^4.17.6",
"@types/express-ws": "^3.0.0",
"@types/tmp": "^0.2.0",
"app-root-path": "^3.0.0",
"css-loader": "^3.5.3",
"express": "^4.17.1",
@ -16,8 +17,8 @@
"monaco-editor": "^0.20.0",
"node-pty": "^0.9.0",
"style-loader": "^1.2.1",
"tmp": "^0.2.1",
"ts-loader": "^7.0.5",
"ttf-loader": "^1.0.2",
"typescript": "^3.9.5",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",

118
yarn.lock
View File

@ -83,6 +83,11 @@
"@types/express-serve-static-core" "*"
"@types/mime" "*"
"@types/tmp@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.0.tgz#e3f52b4d7397eaa9193592ef3fdd44dc0af4298c"
integrity sha512-flgpHJjntpBAdJD43ShRosQvNC0ME97DCfGvZEDlAThQmnerRXrLbX6YgzRBQCZTthET9eAWFAMaYP0m0Y4HzQ==
"@types/ws@*":
version "7.2.5"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.2.5.tgz#513f28b04a1ea1aa9dc2cad3f26e8e37c88aae49"
@ -316,13 +321,6 @@ aproba@^1.1.1:
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
argparse@^1.0.6:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@ -385,14 +383,6 @@ atob@^2.1.2:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
@ -817,11 +807,6 @@ copy-descriptor@^0.1.0:
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
core-js@^2.4.0:
version "2.6.11"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -1751,11 +1736,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
dependencies:
is-extglob "^2.1.1"
is-in-browser@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@ -1812,11 +1792,6 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
json-parse-better-errors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@ -1841,15 +1816,6 @@ json5@^2.1.2:
dependencies:
minimist "^1.2.5"
jss@^9.5.1:
version "9.8.7"
resolved "https://registry.yarnpkg.com/jss/-/jss-9.8.7.tgz#ed9763fc0f2f0260fc8260dac657af61e622ce05"
integrity sha512-awj3XRZYxbrmmrx9LUSj5pXSUfm12m8xzi/VKeqI1ZwWBtQ0kVPTs3vYs32t4rFw83CgFDukA8wKzOE9sMQnoQ==
dependencies:
is-in-browser "^1.1.3"
symbol-observable "^1.1.0"
warning "^3.0.0"
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@ -1921,13 +1887,6 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
loose-envify@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@ -2011,11 +1970,6 @@ methods@~1.1.2:
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
microbuffer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/microbuffer/-/microbuffer-1.0.0.tgz#8b3832ed40c87d51f47bb234913a698a756d19d2"
integrity sha1-izgy7UDIfVH0e7I0kTppinVtGdI=
micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
@ -2335,7 +2289,7 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
pako@^1.0.0, pako@~1.0.5:
pako@~1.0.5:
version "1.0.11"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
@ -2658,11 +2612,6 @@ readdirp@~3.4.0:
dependencies:
picomatch "^2.2.1"
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
@ -2733,6 +2682,13 @@ rimraf@^2.5.4, rimraf@^2.6.3:
dependencies:
glob "^7.1.3"
rimraf@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@ -2960,11 +2916,6 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
ssri@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
@ -3074,11 +3025,6 @@ supports-color@^5.3.0:
dependencies:
has-flag "^3.0.0"
symbol-observable@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
tapable@^1.0.0, tapable@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
@ -3123,6 +3069,13 @@ timers-browserify@^2.0.4:
dependencies:
setimmediate "^1.0.4"
tmp@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
dependencies:
rimraf "^3.0.0"
to-arraybuffer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
@ -3181,25 +3134,6 @@ tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
ttf-loader@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/ttf-loader/-/ttf-loader-1.0.2.tgz#ac1d5824ac9ff2fe3daaa9836170072e1fb6f6f2"
integrity sha512-IMlcqjkSxqfOD4UpPrJ+LGm98JQ5URDFami19mR7lfjNR1XAEoG93Xd3loGYUMSxuhfzSTxsnLXu8emIawx/6A==
dependencies:
babel-runtime "^6.26.0"
jss "^9.5.1"
ttf2woff "^2.0.1"
uuid "^3.2.1"
ttf2woff@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ttf2woff/-/ttf2woff-2.0.2.tgz#09a7cee59abd3c15282b57ed84ac7c7770749f1f"
integrity sha512-X68badwBjAy/+itU49scLjXUL094up+rHuYk+YAOTTBYSUMOmLZ7VyhZJuqQESj1gnyLAC2/5V8Euv+mExmyPA==
dependencies:
argparse "^1.0.6"
microbuffer "^1.0.0"
pako "^1.0.0"
tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@ -3319,11 +3253,6 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
uuid@^3.2.1:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
v8-compile-cache@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe"
@ -3339,13 +3268,6 @@ vm-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
warning@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=
dependencies:
loose-envify "^1.0.0"
watchpack-chokidar2@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0"