wasm-idle 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,6 +18,7 @@ export default class Clang {
18
18
  log: boolean;
19
19
  lastCode: string;
20
20
  path: string;
21
+ wasm?: WebAssembly.Instance;
21
22
  constructor(options: APIOption);
22
23
  hostLog(message: string): void;
23
24
  hostLogAsync(message: string, promise: Promise<any>): Promise<any>;
@@ -25,6 +26,7 @@ export default class Clang {
25
26
  compile(options: any): Promise<App | null>;
26
27
  link(obj: string, wasm: string): Promise<App | null>;
27
28
  run(module: WebAssembly.Module, out: boolean, ...args: string[]): Promise<App | null>;
29
+ compileLink(code: string): Promise<any>;
28
30
  compileLinkRun(code: string): Promise<App | null>;
29
31
  }
30
32
  export {};
@@ -22,6 +22,7 @@ export default class Clang {
22
22
  log;
23
23
  lastCode = '';
24
24
  path;
25
+ wasm;
25
26
  constructor(options) {
26
27
  this.moduleCache = {};
27
28
  this.stdout = options.stdout;
@@ -94,14 +95,16 @@ export default class Clang {
94
95
  this.stdout(`${green}(${start - instantiate}ms/${end - instantiate}ms)${normal}\n`);
95
96
  return stillRunning ? app : null;
96
97
  }
97
- async compileLinkRun(code) {
98
+ async compileLink(code) {
98
99
  const input = `test.cc`, obj = `test.o`, wasm = `test.wasm`;
99
- if (this.lastCode !== code) {
100
- await this.compile({ input, code, obj });
101
- await this.link(obj, wasm);
102
- }
100
+ if (this.lastCode === code)
101
+ return this.wasm;
102
+ await this.compile({ input, code, obj });
103
+ await this.link(obj, wasm);
103
104
  this.lastCode = code;
104
- const testMod = await this.hostLogAsync(`Compiling ${wasm}`, WebAssembly.compile(this.memfs.getFileContents(wasm)));
105
- return await this.run(testMod, true, wasm);
105
+ return this.wasm = await this.hostLogAsync(`Compiling ${wasm}`, WebAssembly.compile(this.memfs.getFileContents(wasm)));
106
+ }
107
+ async compileLinkRun(code) {
108
+ return await this.run(await this.compileLink(code), true, `test.wasm`);
106
109
  }
107
110
  }
@@ -1,7 +1,7 @@
1
1
  import type Sandbox from './sandbox';
2
2
  declare class Clang implements Sandbox {
3
3
  ts: number;
4
- output: null;
4
+ output: any;
5
5
  worker: Worker;
6
6
  buffer: SharedArrayBuffer;
7
7
  interruptBuffer: SharedArrayBuffer;
@@ -13,7 +13,7 @@ declare class Clang implements Sandbox {
13
13
  write(input: string): void;
14
14
  _write(input: string): void;
15
15
  eof(): void;
16
- run(code: string): Promise<string>;
16
+ run(code: string, prepare: boolean): Promise<string>;
17
17
  terminate(): void;
18
18
  clear(): Promise<void>;
19
19
  }
@@ -1,4 +1,3 @@
1
- import workerUrl from './worker/clang?worker&url';
2
1
  class Clang {
3
2
  ts = Date.now();
4
3
  output = null;
@@ -13,10 +12,8 @@ class Clang {
13
12
  return new Promise(async (resolve) => {
14
13
  this.internalBuffer = [];
15
14
  if (!this.worker) {
16
- this.worker = new Worker(workerUrl, { type: 'module' });
17
- this.worker.onmessage = (event) => {
18
- resolve();
19
- };
15
+ this.worker = new (await import('./worker/clang?worker')).default();
16
+ this.worker.onmessage = () => resolve();
20
17
  this.worker.postMessage({ load: true, path, log, code });
21
18
  }
22
19
  else
@@ -38,14 +35,12 @@ class Clang {
38
35
  }
39
36
  eof() {
40
37
  }
41
- run(code) {
38
+ run(code, prepare) {
42
39
  return new Promise(async (resolve, reject) => {
43
40
  const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
44
41
  const handler = (event) => {
45
- if (_uid !== this.uid) {
46
- this.worker.onmessage = null;
47
- return;
48
- }
42
+ if (_uid !== this.uid)
43
+ return this.worker.onmessage = null;
49
44
  const { id, output, results, log, error, buffer } = event.data;
50
45
  if (buffer && this.internalBuffer.length)
51
46
  this._write(this.internalBuffer.splice(0, 1)[0]);
@@ -67,6 +62,7 @@ class Clang {
67
62
  this.begin = Date.now();
68
63
  this.worker.postMessage({
69
64
  code,
65
+ prepare,
70
66
  buffer: this.buffer,
71
67
  interrupt: this.interruptBuffer,
72
68
  context: {}
@@ -74,8 +70,7 @@ class Clang {
74
70
  });
75
71
  }
76
72
  terminate() {
77
- const interrupt = new Uint8Array(this.interruptBuffer);
78
- interrupt[0] = 2;
73
+ new Uint8Array(this.interruptBuffer)[0] = 2;
79
74
  }
80
75
  async clear() {
81
76
  this.terminate();
@@ -1,7 +1,7 @@
1
1
  import type Sandbox from './sandbox';
2
2
  declare class Python implements Sandbox {
3
3
  ts: number;
4
- output: null;
4
+ output: any;
5
5
  worker: Worker;
6
6
  buffer: SharedArrayBuffer;
7
7
  interruptBuffer: SharedArrayBuffer;
@@ -13,7 +13,7 @@ declare class Python implements Sandbox {
13
13
  write(input: string): void;
14
14
  _write(input: string): void;
15
15
  eof(): void;
16
- run(code: string): Promise<string>;
16
+ run(code: string, prepare: boolean): Promise<string>;
17
17
  terminate(): void;
18
18
  clear(): Promise<void>;
19
19
  }
@@ -1,4 +1,3 @@
1
- import workerUrl from './worker/python?worker&url';
2
1
  class Python {
3
2
  ts = Date.now();
4
3
  output = null;
@@ -13,10 +12,8 @@ class Python {
13
12
  return new Promise(async (resolve) => {
14
13
  this.internalBuffer = [];
15
14
  if (!this.worker) {
16
- this.worker = new Worker(workerUrl, { type: 'module' });
17
- this.worker.onmessage = (event) => {
18
- resolve();
19
- };
15
+ this.worker = new (await import('./worker/python?worker')).default();
16
+ this.worker.onmessage = () => resolve();
20
17
  this.worker.postMessage({ load: true, path, log, code });
21
18
  }
22
19
  else
@@ -38,14 +35,12 @@ class Python {
38
35
  }
39
36
  eof() {
40
37
  }
41
- run(code) {
38
+ run(code, prepare) {
42
39
  return new Promise(async (resolve, reject) => {
43
40
  const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
44
41
  const handler = (event) => {
45
- if (_uid !== this.uid) {
46
- this.worker.onmessage = null;
47
- return;
48
- }
42
+ if (_uid !== this.uid)
43
+ return this.worker.onmessage = null;
49
44
  const { id, output, results, log, error, buffer } = event.data;
50
45
  if (buffer && this.internalBuffer.length)
51
46
  this._write(this.internalBuffer.splice(0, 1)[0]);
@@ -67,6 +62,7 @@ class Python {
67
62
  this.begin = Date.now();
68
63
  this.worker.postMessage({
69
64
  code,
65
+ prepare,
70
66
  buffer: this.buffer,
71
67
  interrupt: this.interruptBuffer,
72
68
  context: {}
@@ -2,7 +2,7 @@ interface sandbox {
2
2
  constructor: any;
3
3
  eof: () => void;
4
4
  load: (path: string, code?: string, log?: boolean) => Promise<void>;
5
- run: (code: string) => Promise<string>;
5
+ run: (code: string, prepare: boolean) => Promise<string>;
6
6
  terminate: () => void;
7
7
  clear: () => Promise<void>;
8
8
  }
@@ -26,11 +26,22 @@ async function loadClang(path, log) {
26
26
  });
27
27
  }
28
28
  self.onmessage = async (event) => {
29
- const { code, buffer, load, interrupt, log, path } = event.data;
29
+ const { code, buffer, load, interrupt, log, path, prepare } = event.data;
30
30
  if (load) {
31
31
  await loadClang(path, log);
32
32
  postMessage({ load: true });
33
33
  }
34
+ else if (prepare) {
35
+ stdinBufferClang = new Int32Array(buffer);
36
+ interruptBufferClang = new Uint8Array(interrupt);
37
+ try {
38
+ await clang.compileLink(code);
39
+ self.postMessage({ results: true });
40
+ }
41
+ catch (error) {
42
+ self.postMessage({ error: error.message });
43
+ }
44
+ }
34
45
  else if (code) {
35
46
  stdinBufferClang = new Int32Array(buffer);
36
47
  interruptBufferClang = new Uint8Array(interrupt);
@@ -1,16 +1,34 @@
1
- let stdinBufferPyodide, interruptBufferPyodide, pyodide;
2
- async function loadPyodideAndPackages(path) {
1
+ let stdinBufferPyodide, interruptBufferPyodide, pyodide, path = '';
2
+ async function loadPyodide(path) {
3
+ if (pyodide)
4
+ return;
3
5
  const { loadPyodide } = await import('pyodide');
4
6
  pyodide = await loadPyodide({ indexURL: path + '/pyodide' });
5
7
  }
6
8
  self.onmessage = async (event) => {
7
- const { code, buffer, load, interrupt, path } = event.data;
9
+ const { code, buffer, load, interrupt, path: _p, prepare } = event.data;
8
10
  if (load) {
9
- await loadPyodideAndPackages(path);
10
- await pyodide.loadPackagesFromImports(code);
11
+ path = _p;
12
+ postMessage({ output: 'Loading Pyodide...' });
13
+ await loadPyodide(path);
14
+ postMessage({ output: ' Done.\n\r' });
11
15
  postMessage({ load: true });
12
16
  }
17
+ else if (prepare) {
18
+ postMessage({ output: 'Loading packages...' });
19
+ try {
20
+ await loadPyodide(path);
21
+ await pyodide.loadPackagesFromImports(code);
22
+ postMessage({ output: ' Done.\n\r' });
23
+ self.postMessage({ results: true });
24
+ }
25
+ catch (e) {
26
+ self.postMessage({ error: e.message || 'Unknown error' });
27
+ }
28
+ }
13
29
  else if (code) {
30
+ await loadPyodide(path);
31
+ await pyodide.loadPackagesFromImports(code);
14
32
  const ts = Date.now();
15
33
  stdinBufferPyodide = new Int32Array(buffer);
16
34
  interruptBufferPyodide = new Uint8Array(interrupt);
@@ -57,7 +75,7 @@ self.onmessage = async (event) => {
57
75
  postMessage({ output });
58
76
  };
59
77
  try {
60
- let results = await pyodide.runPythonAsync(`import asyncio
78
+ await pyodide.runPythonAsync(`import asyncio
61
79
  from js import __pyodide__input_${ts}, __pyodide__output_${ts}
62
80
 
63
81
  input = __pyodide__input_${ts}
@@ -69,8 +87,8 @@ __builtins__.print = __pyodide__output_${ts}
69
87
  ${code}`);
70
88
  self.postMessage({ results: true });
71
89
  }
72
- catch (error) {
73
- self.postMessage({ error: error.message });
90
+ catch (e) {
91
+ self.postMessage({ error: e.message || 'Unknown error' });
74
92
  }
75
93
  }
76
94
  };
@@ -1,64 +1,70 @@
1
- <script>import { onMount } from "svelte";
2
- import { Theme, registerAllPlugins } from "./";
3
- import { createEventDispatcher } from "svelte";
1
+ <script>import { Theme, registerAllPlugins } from "./";
4
2
  import { load } from "../playground";
3
+ import { onMount, createEventDispatcher } from "svelte";
5
4
  import "xterm/css/xterm.css";
6
5
  const dispatch = createEventDispatcher();
7
- let ref, clientWidth, clientHeight, term, finish = true, input = "", sandbox, first = true;
8
- const wait = () => new Promise((r) => {
9
- const i = setInterval(() => {
10
- if (term) {
11
- clearInterval(i);
12
- r();
13
- }
14
- }, 100);
15
- });
6
+ export let dark = false, path = "", font = "'D2 coding', monospace";
7
+ let ref, clientWidth, clientHeight, term, finish = true, input = "", sandbox, first = true, tc = 0, plugin, ll = null;
8
+ function wait() {
9
+ return new Promise((r) => {
10
+ const i = setInterval(() => {
11
+ if (term) {
12
+ clearInterval(i);
13
+ r();
14
+ }
15
+ }, 100);
16
+ });
17
+ }
18
+ async function initSandbox(language) {
19
+ let _tc = ++tc;
20
+ await wait();
21
+ if (sandbox)
22
+ await sandbox.clear();
23
+ input = "";
24
+ finish = false;
25
+ if (ll !== language) {
26
+ sandbox = await load(language, path);
27
+ await sandbox.clear();
28
+ ll = language;
29
+ }
30
+ sandbox.output = (output) => _tc === tc && term.write(output.replaceAll("\n", "\r\n"));
31
+ }
32
+ function runSandbox(pr) {
33
+ return pr.then(() => term.write(`\r
34
+ Process finished after ${sandbox.elapse}ms\x1B[?25l`)).catch((msg) => term.write(`\r
35
+ \x1B[1;3;31m${msg}\x1B[?25l`)).finally(() => {
36
+ dispatch("finish");
37
+ finish = true;
38
+ term.options.cursorBlink = false;
39
+ });
40
+ }
41
+ async function initTerm(blink = true) {
42
+ await wait();
43
+ term.options.cursorBlink = blink;
44
+ term.focus();
45
+ if (!first)
46
+ term.write(`\r
47
+ \x1B[0m`);
48
+ first = false;
49
+ }
16
50
  export const terminal = {
17
51
  async clear() {
18
52
  await wait();
19
- term.clear();
20
- term.write(`\x1B[?25l`);
21
- term.write("\x1B[0m");
22
- term.write("\x1B[?25h");
53
+ term.reset();
54
+ term.write(`\x1B[?25l\x1B[0m\x1B[?25h`);
23
55
  term.options.cursorBlink = false;
24
56
  first = true;
25
57
  await new Promise((r) => setTimeout(r, 100));
26
58
  },
59
+ async prepare(language, code, log = true) {
60
+ await Promise.all([initSandbox(language).then(() => sandbox.load(path, code, log)), initTerm(false)]);
61
+ await runSandbox(sandbox.run(code, true));
62
+ },
27
63
  async run(language, code, log = true) {
28
- await wait();
29
- if (sandbox)
30
- await sandbox.clear();
31
- input = "";
32
- finish = false;
33
- sandbox = await load(language, path);
34
- await sandbox.clear();
35
- sandbox.output = (output) => {
36
- term.write(output.replaceAll("\n", "\r\n"));
37
- };
38
- term.options.cursorBlink = true;
39
- if (!first)
40
- term.write(`\r
41
- \x1B[0m`);
42
- await sandbox.load(path, code, log);
43
- term.focus();
44
- first = false;
45
- sandbox.run(code).then(() => {
46
- dispatch("finish");
47
- finish = true;
48
- term.write(`\r
49
- Process finished after ${sandbox.elapse}ms\r
50
- \x1B[?25l`);
51
- term.options.cursorBlink = false;
52
- }).catch((msg) => {
53
- dispatch("finish");
54
- finish = true;
55
- term.write(`\r
56
- \x1B[1;3;31m${msg}`);
57
- term.options.cursorBlink = false;
58
- });
64
+ await Promise.all([initSandbox(language).then(() => sandbox.load(path, code, log)), initTerm()]);
65
+ await runSandbox(sandbox.run(code, false));
59
66
  }
60
67
  };
61
- export let dark = false, path = "";
62
68
  $:
63
69
  if (term) {
64
70
  if (dark)
@@ -66,23 +72,17 @@ $:
66
72
  else
67
73
  term.options.theme = Theme.Tango_Light;
68
74
  }
69
- let plugin;
70
- $:
71
- if (plugin) {
72
- void 0, clientHeight;
73
- try {
74
- plugin.fit.fit();
75
- } catch (e) {
76
- }
77
- }
75
+ $: {
76
+ void 0, clientHeight;
77
+ plugin?.fit?.fit?.();
78
+ }
78
79
  onMount(() => {
79
80
  import("xterm").then(async ({ Terminal }) => {
80
81
  term = new Terminal({
81
82
  theme: dark ? Theme.Tango_Dark : Theme.Tango_Light,
82
83
  cursorBlink: false,
83
84
  allowTransparency: true,
84
- fontFamily: "'D2 coding', monospace",
85
- fontWeight: "bold",
85
+ fontFamily: font,
86
86
  allowProposedApi: true
87
87
  });
88
88
  term.open(ref);
@@ -98,9 +98,8 @@ onMount(() => {
98
98
  } else if (ev.key === "Backspace") {
99
99
  if (term._core.buffer.x > 0) {
100
100
  term.write("\b \b");
101
- if (input.length > 0) {
101
+ if (input.length > 0)
102
102
  input = input.substring(0, input.length - 1);
103
- }
104
103
  }
105
104
  } else if (printable) {
106
105
  if (e.key >= String.fromCharCode(32) && e.key <= String.fromCharCode(126) || e.key >= "\xA0") {
@@ -119,8 +118,8 @@ onMount(() => {
119
118
  }
120
119
  });
121
120
  plugin = await registerAllPlugins(term);
121
+ dispatch("load");
122
122
  });
123
- dispatch("load");
124
123
  return async () => {
125
124
  term.dispose();
126
125
  if (sandbox)
@@ -2,12 +2,14 @@ import { SvelteComponentTyped } from "svelte";
2
2
  import 'xterm/css/xterm.css';
3
3
  declare const __propDef: {
4
4
  props: {
5
+ dark?: boolean | undefined;
6
+ path?: string | undefined;
7
+ font?: string | undefined;
5
8
  terminal?: {
6
9
  clear(): Promise<void>;
10
+ prepare(language: any, code: any, log?: boolean): Promise<void>;
7
11
  run(language: any, code: any, log?: boolean): Promise<void>;
8
12
  } | undefined;
9
- dark?: boolean | undefined;
10
- path?: string | undefined;
11
13
  };
12
14
  events: {
13
15
  finish: CustomEvent<any>;
@@ -23,6 +25,7 @@ export type TerminalSlots = typeof __propDef.slots;
23
25
  export default class Terminal extends SvelteComponentTyped<TerminalProps, TerminalEvents, TerminalSlots> {
24
26
  get terminal(): {
25
27
  clear(): Promise<void>;
28
+ prepare(language: any, code: any, log?: boolean): Promise<void>;
26
29
  run(language: any, code: any, log?: boolean): Promise<void>;
27
30
  };
28
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wasm-idle",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "A simple web app to run C++/Python code in the browser",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,7 +13,7 @@
13
13
  "lint": "prettier --plugin-search-dir . --check . && eslint .",
14
14
  "format": "prettier --plugin-search-dir . --write .",
15
15
  "deploy": "svelte-package && npm publish",
16
- "page": "pnpm run build && gh-pages -d build"
16
+ "page": "pnpm run build && node ./gh-pages.js"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@sveltejs/adapter-auto": "^2.0.0",
@@ -33,7 +33,7 @@
33
33
  "svelte-check": "^3.2.0",
34
34
  "tslib": "^2.5.0",
35
35
  "typescript": "^5.0.4",
36
- "vite": "^4.3.0-beta.6"
36
+ "vite": "^4.1.4"
37
37
  },
38
38
  "dependencies": {
39
39
  "pyodide": "^0.23.1",