Skip to content

Commit 7768cbd

Browse files
Add TypeScript support. (#96)
* Add TypeScript support. Vite does not type check in dev mode, only in build mode. * Type simulator.js * Add window globals * Move old JS files to typescript * Properly type rust functions/worker
1 parent dc62fcb commit 7768cbd

21 files changed

+262
-130
lines changed

.eslintrc.json

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
{
2+
"root": true,
23
"env": {
34
"browser": true,
45
"es2021": true
56
},
67
"extends": [
78
"eslint:recommended",
8-
"plugin:react/recommended"
9+
"plugin:@typescript-eslint/recommended",
10+
"plugin:react-hooks/recommended"
911
],
1012
"parserOptions": {
1113
"ecmaVersion": "latest",
@@ -14,9 +16,11 @@
1416
},
1517
"sourceType": "module"
1618
},
19+
"parser": "@typescript-eslint/parser",
1720
"plugins": [
1821
"react",
19-
"react-hooks"
22+
"react-hooks",
23+
"react-refresh"
2024
],
2125
"rules": {
2226
"indent": [
@@ -40,7 +44,11 @@
4044
"indent-legacy": "warn",
4145
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
4246
"react-hooks/exhaustive-deps": "warn", // Checks effect dependencies
43-
"react/prop-types": "off"
47+
"react/prop-types": "off",
48+
"react-refresh/only-export-components": [
49+
"warn",
50+
{ "allowConstantExport": true }
51+
]
4452
},
4553
"settings": {
4654
"react": {

package.json

+13-6
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
"build": "npm run tailwind-build && npm run vite-build ",
99
"preview": "vite preview",
1010
"format": "eslint --fix",
11+
"lint": "eslint . --ext ts,tsx,js,jsx --report-unused-disable-directives --max-warnings 0",
1112
"tailwind-dev": "tailwindcss -i ./src/styles.css -o ./dist/output.css --watch",
1213
"tailwind-build": "tailwindcss -i ./src/styles.css -o ./dist/output.css",
13-
"vite-dev": "npm run wasm-dev && vite dev",
14-
"vite-build": "npm run wasm-build && vite build",
14+
"vite-dev": "npm run wasm-dev && vite",
15+
"vite-build": "npm run wasm-build && tsc && vite build",
1516
"wasm-dev": "wasm-pack build -d ../../wasm --target web --dev rezasm-app/rezasm-wasm/",
1617
"wasm-build": "wasm-pack build -d ../../wasm --target web rezasm-app/rezasm-wasm/"
1718
},
@@ -29,13 +30,19 @@
2930
"devDependencies": {
3031
"@babel/core": "^7.22.17",
3132
"@tauri-apps/cli": "^1.4.0",
32-
"@vitejs/plugin-react": "^4.0.3",
33-
"eslint": "^8.48.0",
3433
"eslint-plugin-react": "^7.33.2",
35-
"eslint-plugin-react-hooks": "^4.6.0",
3634
"lodash": "^4.17.21",
3735
"npm-run-all": "^4.1.5",
3836
"tailwindcss": "^3.3.3",
39-
"vite": "^4.4.4"
37+
"@types/react": "^18.2.66",
38+
"@types/react-dom": "^18.2.22",
39+
"@typescript-eslint/eslint-plugin": "^7.2.0",
40+
"@typescript-eslint/parser": "^7.2.0",
41+
"@vitejs/plugin-react": "^4.2.1",
42+
"eslint": "^8.57.0",
43+
"eslint-plugin-react-hooks": "^4.6.0",
44+
"eslint-plugin-react-refresh": "^0.4.6",
45+
"typescript": "^5.2.2",
46+
"vite": "^5.2.0"
4047
}
4148
}

rezasm-app/rezasm-wasm/src/lib.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rezasm_core::util::as_any::AsAny;
1313
use rezasm_web_core::{
1414
get_exit_status, get_memory_bounds, get_memory_slice, get_register_names, get_register_value,
1515
get_register_values, get_simulator_mut, get_word_size, initialize_simulator, is_completed,
16-
load, reset, step, stop,
16+
load, reset, step, step_back, stop,
1717
};
1818
use wasm_bindgen::prelude::*;
1919

@@ -37,6 +37,11 @@ pub fn wasm_step() -> Result<(), String> {
3737
step()
3838
}
3939

40+
#[wasm_bindgen]
41+
pub fn wasm_step_back() -> Result<(), String> {
42+
step_back()
43+
}
44+
4045
#[wasm_bindgen]
4146
pub fn wasm_is_completed() -> bool {
4247
is_completed()

src/App.jsx src/App.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import React from "react";
21
import {HashRouter, Route, Routes} from "react-router-dom";
32
import Code from "./components/Code.jsx";
43
import Home from "./components/Home.jsx";

src/components/Code.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React, {useEffect, useState} from "react";
22
import RegistryView from "./RegistryView.jsx";
3-
import {loadWasm} from "../rust_functions.js";
3+
import {loadWasm} from "../rust_functions.ts";
44
import {Tabs, Tab} from "./Tabs.jsx";
55

66
import MemoryView from "./MemoryView.jsx";
77
import Console from "./Console.jsx";
88
import Controls from "./Controls.jsx";
99
import Editor from "./Editor.jsx";
10-
import {useSimulator} from "./simulator.js";
10+
import {useSimulator} from "./simulator.ts";
1111

1212
function Code() {
1313

src/components/Console.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, {useCallback, useEffect, useReducer, useRef, useState} from "react";
22
import {listen} from "@tauri-apps/api/event";
3-
import {CALLBACKS_TRIGGERS, CALLBACK_TYPES, STATE} from "./simulator.js";
4-
import {RUST} from "../rust_functions.js";
3+
import {CALLBACKS_TRIGGERS, CALLBACK_TYPES, STATE} from "./simulator.ts";
4+
import {RUST} from "../rust_functions.ts";
55
import { debounce } from "lodash";
66

77
const ENTER = 13;

src/components/Controls.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from "react";
2-
import {STATE} from "./simulator.js";
2+
import {STATE} from "./simulator.ts";
33
import _ from "lodash";
44

55
const debounce =

src/components/Editor.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from "react";
2-
import {STATE} from "./simulator.js";
2+
import {STATE} from "./simulator.ts";
33

44
function Editor({state, setCode}) {
55
return (

src/components/Home.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22
import {useNavigate} from "react-router-dom";
3-
import {CODE_PATH} from "../App.jsx";
3+
import {CODE_PATH} from "../App.tsx";
44

55
function Home() {
66

src/components/MemoryView.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, {useCallback, useEffect, useRef, useState} from "react";
2-
import {RUST} from "../rust_functions.js";
3-
import {CALLBACK_TYPES, CALLBACKS_TRIGGERS} from "./simulator.js";
2+
import {RUST} from "../rust_functions.ts";
3+
import {CALLBACK_TYPES, CALLBACKS_TRIGGERS} from "./simulator.ts";
44

55
const WIDTH = 4;
66
const HEIGHT = 4;

src/components/RegistryView.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, {useEffect, useState} from "react";
2-
import {RUST} from "../rust_functions.js";
3-
import {CALLBACK_TYPES, CALLBACKS_TRIGGERS} from "./simulator.js";
2+
import {RUST} from "../rust_functions.ts";
3+
import {CALLBACK_TYPES, CALLBACKS_TRIGGERS} from "./simulator.ts";
44

55
function RegistryView({loaded, registerCallback}) {
66
const [registers, setRegisters] = useState([]);

src/components/simulator.js src/components/simulator.ts

+45-34
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,80 @@
11
import {useCallback, useReducer, useRef, useState} from "react";
22
import {RUST} from "../rust_functions.js";
33

4-
const STATE = {
5-
IDLE: 1,
6-
LOADING: 2,
7-
LOADED: 3,
8-
RUNNING: 4,
9-
PAUSED: 5,
10-
STOPPED: 6,
11-
};
4+
enum STATE {
5+
IDLE = 1,
6+
LOADING = 2,
7+
LOADED = 3,
8+
RUNNING = 4,
9+
PAUSED = 5,
10+
STOPPED = 6,
11+
}
1212

1313
const CALLBACKS_TRIGGERS = {
1414
RESET: "RESET",
1515
STEP: "STEP"
16-
};
16+
} as const;
17+
18+
type ValidCallbackTriggers = keyof typeof CALLBACKS_TRIGGERS;
1719

1820
const CALLBACK_TYPES = {
1921
CONSOLE: "CONSOLE",
2022
MEMORY: "MEMORY",
2123
REGISTRY: "REGISTRY",
22-
};
24+
} as const;
25+
26+
type ValidCallbackTypes = keyof typeof CALLBACK_TYPES;
27+
2328

24-
let initialCallbacks = {};
25-
Object.values(CALLBACKS_TRIGGERS).map(x => initialCallbacks[x] = {});
29+
type CallbackTriggerObject = Partial<Record<ValidCallbackTypes, () => unknown>>;
30+
type CallbackObject = Record<ValidCallbackTriggers, CallbackTriggerObject>;
31+
const initialCallbacks: CallbackObject = Object.values(CALLBACKS_TRIGGERS).reduce((callbacks, x) => {
32+
callbacks[x] = {};
33+
return callbacks;
34+
}, {} as Partial<CallbackObject>) as CallbackObject;
2635

2736
export const useSimulator = () => {
2837
const state = useRef(STATE.IDLE);
2938
const error = useRef("");
3039
const [exitCode, setExitCode] = useState("");
3140
const [code, setCode] = useState("");
3241

33-
const timerId = useRef(null);
42+
const timerId = useRef<number|null>(null);
3443
const [instructionDelay, setInstructionDelay] = useState(5);
3544
const callbacks = useRef(initialCallbacks);
3645

3746
//Still kind of a hack
38-
const [, forceUpdate] = useReducer(() => Date.now());
47+
const [, forceUpdate] = useReducer(() => Date.now(), 0);
3948

40-
const setState = (newState) => {
49+
const setState = useCallback((newState: STATE) => {
4150
state.current = newState;
4251
forceUpdate();
43-
};
52+
}, []);
4453

45-
const setError = (newError) => {
54+
const setError = useCallback((newError: string) => {
4655
error.current = newError;
4756
forceUpdate();
48-
};
57+
}, []);
4958

50-
const registerCallback = (trigger, type, callback) => {
59+
const registerCallback = (trigger: ValidCallbackTriggers, type: ValidCallbackTypes, callback: () => unknown) => {
5160
callbacks.current[trigger][type] = callback;
5261
};
5362

54-
const callStepCallbacks = () => {
63+
const callStepCallbacks = useCallback(() => {
5564
Object.values(callbacks.current[CALLBACKS_TRIGGERS.STEP]).map(callback => callback());
56-
};
65+
}, [callbacks]);
5766

58-
const callResetCallbacks = () => {
67+
const callResetCallbacks = useCallback(() => {
5968
Object.values(callbacks.current[CALLBACKS_TRIGGERS.RESET]).map(callback => callback());
60-
};
69+
}, [callbacks]);
6170

62-
const haltExecution = (newState) => {
71+
const haltExecution = useCallback((newState: STATE) => {
6372
setState(newState ?? STATE.STOPPED);
6473
if (timerId.current !== null) {
6574
clearTimeout(timerId.current);
6675
timerId.current = null;
6776
}
68-
};
77+
}, [timerId]);
6978

7079
const isError = () => {
7180
return error.current !== "";
@@ -138,20 +147,20 @@ export const useSimulator = () => {
138147
}, [checkProgramCompletion, handleStepCall, load, reset, state]);
139148

140149
const stepBack = useCallback(async () => {
141-
if (state.current > STATE.RUNNING || state.current == STATE.AWAITING) {
150+
if (state.current > STATE.RUNNING) {
142151
console.log(state.current);
143152
RUST.STEP_BACK({})
144-
.catch((error) => {
145-
setError(error);
146-
setState(STATE.STOPPED)
147-
})
148-
.finally(() => {
149-
callStepCallbacks();
150-
})
153+
.catch((error) => {
154+
setError(error);
155+
setState(STATE.STOPPED);
156+
})
157+
.finally(() => {
158+
callStepCallbacks();
159+
});
151160

152161
}
153162
}
154-
, [setError, setState, callStepCallbacks])
163+
, [setError, setState, callStepCallbacks]);
155164

156165
const recursiveStep = useCallback(async () => {
157166
if (state.current === STATE.STOPPED) {
@@ -160,6 +169,8 @@ export const useSimulator = () => {
160169
checkProgramCompletion().then(async completed => {
161170
if (!completed && state.current === STATE.RUNNING) {
162171
handleStepCall().then(() => {
172+
// @ts-expect-error -- It assumes that setTimeout returns a NodeJS.Timeout object,
173+
// which does not exist in the browser
163174
timerId.current = setTimeout(recursiveStep, instructionDelay);
164175
}).catch((e) => {
165176
timerId.current = null;

src/globals.d.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import WorkerPromise from "webworker-promise";
2+
3+
declare global {
4+
interface Window {
5+
__WASM_DEFINED__?: boolean;
6+
__WASM_LOADED__?: boolean;
7+
worker: WorkerPromise;
8+
// eslint-disable-next-line no-unused-vars
9+
emitPrintString?: (string: string) => void;
10+
}
11+
}

src/main.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22
import ReactDOM from "react-dom/client";
33
import "../dist/output.css";
4-
import App from "./App.jsx";
4+
import App from "./App.tsx";
55

66
ReactDOM.createRoot(document.getElementById("root")).render(
77
<React.StrictMode>

0 commit comments

Comments
 (0)