Migrating the project to typescript.
This commit is contained in:
parent
d7b9d9d664
commit
13a948b21a
@ -1,7 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import {MIN_WIDTH, MIN_HEIGHT} from '/constants';
|
||||
|
||||
export default function CanvasGBAEmulator(props) {
|
||||
return (<canvas ref={props.canvasRef} width={MIN_WIDTH} height={MIN_HEIGHT}></canvas>);
|
||||
}
|
11
js-src/components/canvas-gba-emulator.tsx
Normal file
11
js-src/components/canvas-gba-emulator.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import {MIN_WIDTH, MIN_HEIGHT} from '/constants';
|
||||
|
||||
export interface CanvasGBAEmulatorProps {
|
||||
canvasRef: React.RefObject<HTMLCanvasElement>
|
||||
}
|
||||
|
||||
export default function CanvasGBAEmulator(props: CanvasGBAEmulatorProps) {
|
||||
return (<canvas ref={props.canvasRef} width={MIN_WIDTH} height={MIN_HEIGHT}></canvas>);
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function CenterElement(props) {
|
||||
let hidden = props.hidden;
|
||||
if (hidden == null) {
|
||||
hidden = false;
|
||||
}
|
||||
return (
|
||||
<div style={{
|
||||
display: hidden ? 'none':''
|
||||
}} className="center-content">{props.children}</div>
|
||||
);
|
||||
}
|
22
js-src/components/center-element.tsx
Normal file
22
js-src/components/center-element.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface CenterElementProps {
|
||||
hidden?: boolean | undefined,
|
||||
children?: React.ReactNode,
|
||||
}
|
||||
|
||||
type IHash = {
|
||||
[id: string]: string;
|
||||
}
|
||||
|
||||
export default function CenterElement(props: CenterElementProps) {
|
||||
const styles: IHash = {};
|
||||
let hidden = props.hidden;
|
||||
if (hidden == null) {
|
||||
hidden = false;
|
||||
}
|
||||
styles["display"] = hidden ? 'none' : '';
|
||||
return (
|
||||
<div style={styles} className="center-content">{props.children}</div>
|
||||
);
|
||||
}
|
@ -1,6 +1,12 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
export default function FormSelectFiles(props) {
|
||||
export interface FormSelectFilesProps {
|
||||
refInputRom: React.RefObject<HTMLInputElement>;
|
||||
refInputSaveState: React.RefObject<HTMLInputElement>;
|
||||
onStartEmulation: React.MouseEventHandler<HTMLInputElement>;
|
||||
}
|
||||
|
||||
export default function FormSelectFiles(props: FormSelectFilesProps) {
|
||||
const inputRom = props.refInputRom ? props.refInputRom : React.useRef(null);
|
||||
const inputSaveState = props.refInputSaveState ? props.refInputSaveState : React.useRef(null);
|
||||
const onStartEmulation = props.onStartEmulation;
|
||||
@ -15,7 +21,7 @@ export default function FormSelectFiles(props) {
|
||||
Savestate (A ss file from mgba...)
|
||||
<input type="file" ref={inputSaveState} name="savestate"/>
|
||||
</label>
|
||||
<input type="button" value="Start emulation" onClick={onStartEmulation} ref={props.startEmulationButton}/>
|
||||
<input type="button" value="Start emulation" onClick={onStartEmulation}/>
|
||||
</form>
|
||||
);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
import CenterElement from '/components/center-element';
|
||||
import FormSelectFiles from '/components/form-select-files';
|
||||
@ -8,9 +8,32 @@ import Endian from '/endian';
|
||||
|
||||
import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME} from '/constants';
|
||||
|
||||
function handleClickStartEmulationButton({e, inputRom, inputSaveState, setHiddenFormSelectFiles, canvas}) {
|
||||
type setBooleanCallback = (c: boolean) => boolean;
|
||||
|
||||
export interface handleClickStartEmulationButtonObjectArgs {
|
||||
e: React.MouseEvent<HTMLInputElement>;
|
||||
inputRom: HTMLInputElement | null;
|
||||
inputSaveState: HTMLInputElement | null;
|
||||
setHiddenFormSelectFiles: (c: setBooleanCallback) => void;
|
||||
canvas: HTMLCanvasElement | null;
|
||||
printingFrame: boolean;
|
||||
setPrintingFrame: (c: setBooleanCallback) => void;
|
||||
};
|
||||
|
||||
function handleClickStartEmulationButton({e, inputRom, inputSaveState, setHiddenFormSelectFiles, canvas, printingFrame, setPrintingFrame}: handleClickStartEmulationButtonObjectArgs) {
|
||||
if (canvas == null) {
|
||||
alert('Canvas does not exists?');
|
||||
return;
|
||||
}
|
||||
const ctx = canvas.getContext('2d')
|
||||
e.preventDefault();
|
||||
if (ctx == null) {
|
||||
alert('Unable to create canvas context, doing nothing');
|
||||
return;
|
||||
}
|
||||
if (inputRom == null || inputSaveState == null || inputRom.files == null || inputSaveState.files == null) {
|
||||
alert('Unable to read the files ');
|
||||
return;
|
||||
}
|
||||
if (inputRom.files.length == 0) {
|
||||
alert('There is no rom still');
|
||||
return;
|
||||
@ -23,42 +46,37 @@ function handleClickStartEmulationButton({e, inputRom, inputSaveState, setHidden
|
||||
const savestate_file = inputSaveState.files[0];
|
||||
rom_file.arrayBuffer().then((rom_buffer) => {
|
||||
savestate_file.arrayBuffer().then((savestate_buffer) => {
|
||||
setHiddenFormSelectFiles((c: boolean) => true);
|
||||
const rom_array = new Uint8Array(rom_buffer);
|
||||
const savestate_array = new Uint8Array(savestate_buffer);
|
||||
const websocket = new WebSocket(`ws://localhost:3000/ws`);
|
||||
websocket.binaryType = 'arraybuffer';
|
||||
websocket.onclose = (message) => console.log('CLOSE', message);
|
||||
websocket.onopen = () => {
|
||||
websocket.onclose = (message) => {
|
||||
setHiddenFormSelectFiles(c => false);
|
||||
console.log('Closing websocket.');
|
||||
}
|
||||
websocket.onopen = () => {
|
||||
console.log('Opened websocket.');
|
||||
sendHello(websocket, rom_array, savestate_array);
|
||||
};
|
||||
websocket.addEventListener('message', (event) => onWebSocketPacket(event, canvas, ctx));
|
||||
setPrintingFrame(c => false);
|
||||
websocket.addEventListener('message', (event) => {
|
||||
onWebSocketPacket(event, canvas, ctx, printingFrame, setPrintingFrame)
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function concatU8Array(array1, array2) {
|
||||
function concatU8Array(array1: Uint8Array, array2: Uint8Array) {
|
||||
const final_array = new Uint8Array(array1.length + array2.length);
|
||||
final_array.set(array1);
|
||||
final_array.set(array2, array1.length);
|
||||
return final_array;
|
||||
}
|
||||
|
||||
function u64ToByteArrayBigEndian(input_number) {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const buffer8 = new Uint8Array(buffer);
|
||||
const buffer64 = new BigUint64Array(buffer);
|
||||
buffer64[0] = input_number;
|
||||
if (Endian.isLittleEndian()) {
|
||||
buffer8.reverse();
|
||||
}
|
||||
return buffer8;
|
||||
}
|
||||
|
||||
function sendPacket(websocket, id, raw_data) {
|
||||
function sendPacket(websocket: WebSocket, id: bigint, raw_data: Uint8Array) {
|
||||
const packet_u8 = concatU8Array(
|
||||
concatU8Array(u64ToByteArrayBigEndian(id), u64ToByteArrayBigEndian(BigInt(raw_data.length))),
|
||||
concatU8Array(Endian.u64ToByteArrayBigEndian(id), Endian.u64ToByteArrayBigEndian(BigInt(raw_data.length))),
|
||||
raw_data
|
||||
);
|
||||
const packet_buffer = packet_u8.buffer;
|
||||
@ -66,50 +84,48 @@ function sendPacket(websocket, id, raw_data) {
|
||||
websocket.send(packet_buffer);
|
||||
}
|
||||
|
||||
function sendHello(websocket, rom_array, savestate_array) {
|
||||
function sendHello(websocket: WebSocket, rom_array: Uint8Array, savestate_array: Uint8Array) {
|
||||
console.log('Sending hello.');
|
||||
const length_rom = BigInt(rom_array.length);
|
||||
const length_savestate = BigInt(savestate_array.length);
|
||||
const raw_data =
|
||||
concatU8Array(
|
||||
concatU8Array(
|
||||
concatU8Array(u64ToByteArrayBigEndian(length_rom), rom_array),
|
||||
u64ToByteArrayBigEndian(length_savestate)
|
||||
concatU8Array(Endian.u64ToByteArrayBigEndian(length_rom), rom_array),
|
||||
Endian.u64ToByteArrayBigEndian(length_savestate)
|
||||
),
|
||||
savestate_array
|
||||
);
|
||||
sendPacket(websocket, PACKET_ID_HELLO, raw_data);
|
||||
}
|
||||
|
||||
function onWebSocketPacket(event, canvas, ctx) {
|
||||
function onWebSocketPacket(event: MessageEvent, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean, setPrintingFrame: (c: setBooleanCallback) => void) {
|
||||
const buffer = event.data;
|
||||
let packet_u8 = new Uint8Array(buffer);
|
||||
const id = byteArrayToU64BigEndian(packet_u8.slice(0, 8));
|
||||
let packet_u8: Uint8Array | null = new Uint8Array(buffer);
|
||||
const id = Endian.byteArrayToU64BigEndian(packet_u8.slice(0, 8));
|
||||
packet_u8 = packet_u8.slice(8, packet_u8.length);
|
||||
const size = byteArrayToU64BigEndian(packet_u8.slice(0, 8));
|
||||
const size = Endian.byteArrayToU64BigEndian(packet_u8.slice(0, 8));
|
||||
const raw_data = packet_u8.slice(8, packet_u8.length);
|
||||
packet_u8 = null;
|
||||
switch (id) {
|
||||
case PACKET_ID_SEND_FRAME:
|
||||
handleSendFrame(raw_data, canvas, ctx);
|
||||
handleSendFrame(raw_data, canvas, ctx, printingFrame, setPrintingFrame);
|
||||
break;
|
||||
default:
|
||||
console.log(`Received unknown packet ${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
let printing_frame = false;
|
||||
function handleSendFrame(raw_data, canvas, ctx) {
|
||||
if (printing_frame) {
|
||||
function handleSendFrame(raw_data: Uint8Array, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean, setPrintingFrame: (c: setBooleanCallback) => void) {
|
||||
if (printingFrame) {
|
||||
return;
|
||||
}
|
||||
printing_frame = true;
|
||||
let data = raw_data;
|
||||
const stride = byteArrayToU32BigEndian(data.slice(0, 4));
|
||||
setPrintingFrame(c => true);
|
||||
let data: Uint8Array | null = raw_data;
|
||||
const stride = Endian.byteArrayToU32BigEndian(data.slice(0, 4));
|
||||
data = data.slice(4, data.length);
|
||||
const output_buffer_size = byteArrayToU32BigEndian(data.slice(0, 8));
|
||||
const output_buffer_size = Endian.byteArrayToU64BigEndian(data.slice(0, 8));
|
||||
data = data.slice(8, data.length);
|
||||
console.log(data.length / 4 / MIN_WIDTH);
|
||||
const img_data = ctx.createImageData(MIN_WIDTH, MIN_HEIGHT);
|
||||
const img_data_u8 = new Uint8Array(img_data.data.buffer);
|
||||
for (let i = 0; i<data.length; i++) {
|
||||
@ -120,56 +136,48 @@ function handleSendFrame(raw_data, canvas, ctx) {
|
||||
img_data_u8[i] = data[i];
|
||||
}
|
||||
data = null;
|
||||
createImageBitmap(img_data).then((bitmap) => drawBitmap(bitmap, canvas, ctx));
|
||||
createImageBitmap(img_data).then((bitmap) => drawBitmap(bitmap, canvas, ctx, printingFrame));
|
||||
}
|
||||
|
||||
function byteArrayToU32BigEndian(input_array) {
|
||||
if (Endian.isLittleEndian()) {
|
||||
input_array = input_array.reverse();
|
||||
}
|
||||
const buffer = input_array.buffer;
|
||||
const output_u32_array = new Uint32Array(buffer);
|
||||
return output_u32_array[0];
|
||||
}
|
||||
|
||||
function byteArrayToU64BigEndian(input_array) {
|
||||
if (Endian.isLittleEndian()) {
|
||||
input_array = input_array.reverse();
|
||||
}
|
||||
const buffer = input_array.buffer;
|
||||
const output_u64_array = new BigUint64Array(buffer);
|
||||
return output_u64_array[0];
|
||||
}
|
||||
|
||||
function drawBitmap(bitmap, canvas, ctx) {
|
||||
function drawBitmap(bitmap: ImageBitmap, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean) {
|
||||
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
||||
printing_frame = false;
|
||||
printingFrame = false;
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const screenDimensions = useScreenDimensions();
|
||||
const emulatorDimensions = calculateSizeEmulator(screenDimensions);
|
||||
const canvasRef = React.useRef(null);
|
||||
function resizeCanvas(node) {
|
||||
const canvasRef = React.useRef<HTMLCanvasElement>(null);
|
||||
function resizeCanvas() {
|
||||
const canvas = canvasRef.current;
|
||||
if (canvas) {
|
||||
canvas.width = emulatorDimensions.width;
|
||||
canvas.height = emulatorDimensions.height;
|
||||
const ctx = canvas.getContext('2d')
|
||||
fillBlack(canvas, ctx);
|
||||
if (canvas == null) {
|
||||
return;
|
||||
}
|
||||
if (emulatorDimensions.width === undefined || emulatorDimensions.height === undefined) {
|
||||
return;
|
||||
}
|
||||
canvas.width = emulatorDimensions.width;
|
||||
canvas.height = emulatorDimensions.height;
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (ctx == null) {
|
||||
return;
|
||||
}
|
||||
fillBlack(canvas, ctx);
|
||||
};
|
||||
const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = React.useState(false);
|
||||
const [hiddenFormSelectFiles, setHiddenFormSelectFiles] = React.useState<boolean>(false);
|
||||
const [printingFrame, setPrintingFrame] = React.useState<boolean>(false);
|
||||
React.useEffect(resizeCanvas, [emulatorDimensions]);
|
||||
const refInputRom = React.useRef(null);
|
||||
const refInputSaveState = React.useRef(null);
|
||||
const onStartEmulation = (e) => {
|
||||
const refInputRom = React.useRef<HTMLInputElement | null>(null);
|
||||
const refInputSaveState = React.useRef<HTMLInputElement | null>(null);
|
||||
const onStartEmulation = (e: React.MouseEvent<HTMLInputElement>) => {
|
||||
handleClickStartEmulationButton({
|
||||
e: e,
|
||||
setHiddenFormSelectFiles: setHiddenFormSelectFiles,
|
||||
inputRom: refInputRom.current,
|
||||
inputSaveState: refInputSaveState.current,
|
||||
canvas: canvasRef.current,
|
||||
setPrintingFrame: setPrintingFrame,
|
||||
printingFrame: printingFrame,
|
||||
});
|
||||
};
|
||||
return (
|
||||
@ -198,7 +206,7 @@ function getScreenDimensions() {
|
||||
}
|
||||
|
||||
function useScreenDimensions() {
|
||||
const [screenDimensions, setScreenDimensions] = React.useState(getScreenDimensions());
|
||||
const [screenDimensions, setScreenDimensions] = React.useState<EmulatorDimensions>(getScreenDimensions());
|
||||
|
||||
React.useEffect(() => {
|
||||
function onResize() {
|
||||
@ -215,17 +223,25 @@ function useScreenDimensions() {
|
||||
return screenDimensions;
|
||||
}
|
||||
|
||||
function fillBlack(canvas, ctx) {
|
||||
function fillBlack(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
|
||||
ctx.beginPath();
|
||||
ctx.rect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
function calculateSizeEmulator(screenDimensions) {
|
||||
export interface EmulatorDimensions {
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
function calculateSizeEmulator(screenDimensions: EmulatorDimensions): EmulatorDimensions {
|
||||
if (screenDimensions.width === undefined || screenDimensions.height === undefined) {
|
||||
console.error(screenDimensions, 'screenDimensions has undefined fields');
|
||||
return {};
|
||||
}
|
||||
const width = screenDimensions.width;
|
||||
const height = screenDimensions.height * 0.75;
|
||||
const emulatorDimensions = {};
|
||||
const emulatorDimensions: EmulatorDimensions = {};
|
||||
if (width < MIN_WIDTH || height < MIN_HEIGHT) {
|
||||
return {
|
||||
width: MIN_WIDTH,
|
@ -3,6 +3,11 @@ export const MIN_HEIGHT = 160;
|
||||
export const PACKET_ID_HELLO = 0n;
|
||||
export const PACKET_ID_SEND_FRAME = 1n;
|
||||
export default class Constants {
|
||||
public static MIN_WIDTH: number = MIN_WIDTH;
|
||||
public static MIN_HEIGHT: number = MIN_HEIGHT;
|
||||
public static PACKET_ID_HELLO: bigint = PACKET_ID_HELLO;
|
||||
public static PACKET_ID_SEND_FRAME: bigint = PACKET_ID_SEND_FRAME;
|
||||
|
||||
};
|
||||
Constants.MIN_WIDTH = MIN_WIDTH;
|
||||
Constants.MIN_HEIGHT = MIN_HEIGHT;
|
@ -1,18 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
let littleEndian = true;
|
||||
(()=>{
|
||||
let buf = new ArrayBuffer(4);
|
||||
let buf8 = new Uint8ClampedArray(buf);
|
||||
let data = new Uint32Array(buf);
|
||||
data[0] = 0xdeadbeef;
|
||||
if(buf8[0] === 0xde){
|
||||
littleEndian = false;
|
||||
}
|
||||
})()
|
||||
|
||||
export default class Endian {
|
||||
static isLittleEndian() {
|
||||
return littleEndian;
|
||||
}
|
||||
}
|
40
js-src/endian.ts
Normal file
40
js-src/endian.ts
Normal file
@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
|
||||
let littleEndian = true; (()=>{ let buf = new ArrayBuffer(4); let buf8 = new
|
||||
Uint8ClampedArray(buf); let data = new Uint32Array(buf); data[0] = 0xdeadbeef;
|
||||
if(buf8[0] === 0xde){ littleEndian = false; } })()
|
||||
|
||||
export default class Endian {
|
||||
static isLittleEndian() {
|
||||
return littleEndian;
|
||||
}
|
||||
|
||||
static byteArrayToU32BigEndian(inputArray: Uint8Array) {
|
||||
if (Endian.isLittleEndian()) {
|
||||
inputArray = inputArray.reverse();
|
||||
}
|
||||
const buffer = inputArray.buffer;
|
||||
const outputU32Array = new Uint32Array(buffer);
|
||||
return outputU32Array[0];
|
||||
}
|
||||
|
||||
static byteArrayToU64BigEndian(inputArray: Uint8Array) {
|
||||
if (Endian.isLittleEndian()) {
|
||||
inputArray = inputArray.reverse();
|
||||
}
|
||||
const buffer = inputArray.buffer;
|
||||
const outputU64Array = new BigUint64Array(buffer);
|
||||
return outputU64Array[0];
|
||||
}
|
||||
|
||||
static u64ToByteArrayBigEndian(inputNumber: bigint) {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const buffer8 = new Uint8Array(buffer);
|
||||
const buffer64 = new BigUint64Array(buffer);
|
||||
buffer64[0] = inputNumber;
|
||||
if (Endian.isLittleEndian()) {
|
||||
buffer8.reverse();
|
||||
}
|
||||
return buffer8;
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
"use strict";
|
||||
import React from 'react';
|
||||
import ReactDOMClient from 'react-dom/client';
|
||||
|
||||
import Endian from '/endian';
|
||||
import Page from '/components/page';
|
||||
|
||||
const body = document.querySelector('body');
|
||||
const app = document.createElement('div');
|
||||
body.appendChild(app);
|
||||
const root = ReactDOMClient.createRoot(app);
|
||||
root.render(<Page/>);
|
18
js-src/index.tsx
Normal file
18
js-src/index.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
import * as React from 'react';
|
||||
import * as ReactDOMClient from 'react-dom/client';
|
||||
|
||||
import Endian from '/endian';
|
||||
import Page from '/components/page';
|
||||
|
||||
const body = document.querySelector('body');
|
||||
if (body != null) {
|
||||
fillBody(body);
|
||||
}
|
||||
|
||||
function fillBody(body: HTMLElement) {
|
||||
const app = document.createElement('div');
|
||||
body.appendChild(app);
|
||||
const root = ReactDOMClient.createRoot(app);
|
||||
root.render(<Page/>);
|
||||
}
|
@ -11,8 +11,12 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"babel-loader": "^9.1.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^5.0.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.38.1",
|
||||
"webpack-cli": "^4.7.2"
|
||||
|
File diff suppressed because one or more lines are too long
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./public/js/",
|
||||
"noImplicitAny": true,
|
||||
"module": "es2020",
|
||||
"target": "es2020",
|
||||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node",
|
||||
"strictNullChecks": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": ["js-src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["js-src/*.ts", "js-src/*/*.ts" ]
|
||||
}
|
@ -1,23 +1,25 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: './js-src/index.jsx',
|
||||
entry: './js-src/index.tsx',
|
||||
mode: 'development',
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, 'public/js/'),
|
||||
},
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.js',
|
||||
'.jsx',
|
||||
],
|
||||
extensions: [ '.js', '.jsx','.ts', '.tsx' ],
|
||||
roots: [
|
||||
path.resolve(__dirname, 'js-src/')
|
||||
]
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.jpe?g|png$/,
|
||||
exclude: /node_modules/,
|
||||
|
Loading…
Reference in New Issue
Block a user