switch to Bun
This commit is contained in:
parent
ef586df9a6
commit
7159558e5e
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@ plugins/*
|
|||
!plugins/commands.ts
|
||||
!plugins/world.ts
|
||||
worlds/*
|
||||
node_modules
|
|
@ -108,7 +108,6 @@ export class PacketWriter {
|
|||
}
|
||||
}
|
||||
|
||||
import { gzip } from "https://cdn.skypack.dev/pako";
|
||||
import { Position, World } from "./classes.ts";
|
||||
import { Player } from "./Player.ts";
|
||||
|
||||
|
@ -137,7 +136,7 @@ export class PacketDefinitions {
|
|||
|
||||
player.position = world.getSpawn();
|
||||
|
||||
const compressedMap = gzip(world.data)!;
|
||||
const compressedMap = Bun.gzipSync(world.data)!;
|
||||
|
||||
for (let i = 0; i < compressedMap.length; i += 1024) {
|
||||
const chunk = compressedMap.slice(
|
||||
|
|
|
@ -3,8 +3,10 @@ import { PacketDefinitions, PacketWriter } from "./Packets.ts";
|
|||
import { config, log } from "../deps.ts";
|
||||
import { Server } from "./Server.ts";
|
||||
|
||||
import {Socket} from 'bun';
|
||||
|
||||
export class Player {
|
||||
socket: Deno.Conn;
|
||||
socket: Socket<{dataBuffer?: Buffer}>;
|
||||
private server: Server;
|
||||
|
||||
username: string;
|
||||
|
@ -15,7 +17,7 @@ export class Player {
|
|||
rotation: Rotation = { yaw: 0, pitch: 0 };
|
||||
|
||||
constructor(
|
||||
socket: Deno.Conn,
|
||||
socket: Socket<{dataBuffer?: Buffer}>,
|
||||
username: string,
|
||||
position: Position,
|
||||
server: Server,
|
||||
|
@ -24,7 +26,7 @@ export class Player {
|
|||
this.username = username;
|
||||
this.position = position;
|
||||
this.server = server;
|
||||
this.ip = (this.socket.remoteAddr as Deno.NetAddr).hostname;
|
||||
this.ip = this.socket.remoteAddress;
|
||||
|
||||
let id = Math.floor(Math.random() * server.maxUsers);
|
||||
|
||||
|
@ -37,13 +39,15 @@ export class Player {
|
|||
}
|
||||
|
||||
async writeToSocket(ar: Uint8Array) {
|
||||
await this.socket.write(ar).catch(async (e) => {
|
||||
try {
|
||||
this.socket.write(ar)
|
||||
} catch(e) {
|
||||
log.critical(e);
|
||||
await this.server.removeUser(
|
||||
this.socket,
|
||||
"Write failed" + e.message.split("\n")[0],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
message(text: string, id = 0) {
|
||||
|
|
|
@ -5,8 +5,10 @@ import {
|
|||
Plugin,
|
||||
World,
|
||||
} from "./classes.ts";
|
||||
import { Socket, TCPSocketListener } from "bun"
|
||||
|
||||
import { config, crypto, log, toHexString } from "../deps.ts";
|
||||
import { config, log } from "../deps.ts";
|
||||
import * as fs from "node:fs/promises"
|
||||
|
||||
type PlayerFunction = (a: Player) => void;
|
||||
|
||||
|
@ -16,7 +18,7 @@ interface PluginUpdateTime {
|
|||
}
|
||||
|
||||
export class Server {
|
||||
server!: Deno.Listener;
|
||||
server!: TCPSocketListener;
|
||||
|
||||
players: Player[] = [];
|
||||
plugins: Map<string, PluginUpdateTime> = new Map();
|
||||
|
@ -33,17 +35,54 @@ export class Server {
|
|||
worlds: World[] = [new World({ x: 64, y: 64, z: 64 }, config.main)];
|
||||
|
||||
async start(port: number) {
|
||||
this.server = Deno.listen({ port: port });
|
||||
this.server = Bun.listen<{dataBuffer?: Buffer}>({
|
||||
hostname: "localhost",
|
||||
port: +process.env.PORT!,
|
||||
socket: {
|
||||
data: (socket, data) => {
|
||||
if(socket.data.dataBuffer) {
|
||||
const newBuffer = Buffer.concat([socket.data.dataBuffer, data]);
|
||||
socket.data.dataBuffer = newBuffer;
|
||||
} else {
|
||||
socket.data.dataBuffer = data;
|
||||
}
|
||||
|
||||
const parseBuffer = () => {
|
||||
if(!socket.data.dataBuffer) return;
|
||||
|
||||
const packetId = socket.data.dataBuffer.readUint8(0);
|
||||
const packetLength = this.lengthMap.get(packetId);
|
||||
if(!packetLength) {
|
||||
return;
|
||||
};
|
||||
if(socket.data.dataBuffer.byteLength < packetLength) return;
|
||||
|
||||
this.handlePacket(socket.data.dataBuffer.copyWithin(0, 1), packetId, socket);
|
||||
socket.data.dataBuffer = socket.data.dataBuffer.subarray(packetLength+1);
|
||||
parseBuffer();
|
||||
}
|
||||
|
||||
parseBuffer();
|
||||
},
|
||||
open: (socket) => {
|
||||
socket.data = {}
|
||||
},
|
||||
close(socket) {},
|
||||
drain(socket) {},
|
||||
error(socket, error) {},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
await Deno.stat("worlds/");
|
||||
for await (const dirEntry of Deno.readDir("worlds/")) {
|
||||
await fs.stat("worlds/");
|
||||
for await (const dirEntry of await fs.readdir("worlds/", {withFileTypes: true})) {
|
||||
const world = new World({ x: 0, y: 0, z: 0 }, dirEntry.name.replace(".buf", ""));
|
||||
|
||||
this.worlds.push(world);
|
||||
}
|
||||
} catch {
|
||||
await Deno.mkdir("worlds")
|
||||
await fs.mkdir("worlds")
|
||||
}
|
||||
|
||||
if (config.onlineMode) {
|
||||
|
@ -65,9 +104,6 @@ export class Server {
|
|||
|
||||
log.info(`Listening on port ${config.port}!`);
|
||||
|
||||
for await (const socket of this.server) {
|
||||
this.startSocket(socket);
|
||||
}
|
||||
}
|
||||
|
||||
broadcast(text: string) {
|
||||
|
@ -89,13 +125,13 @@ export class Server {
|
|||
}
|
||||
|
||||
async updatePlugins() {
|
||||
for await (const file of Deno.readDir("./plugins")) {
|
||||
if (file.isFile) {
|
||||
for await (const file of await fs.readdir("./plugins", {withFileTypes:true})) {
|
||||
if (file.isFile()) {
|
||||
const name = file.name.split(".ts")[0];
|
||||
|
||||
if (!this.plugins.has(name)) {
|
||||
this.plugins.set(name, {
|
||||
lastUpdated: Deno.statSync(`./plugins/${file.name}`).mtime!,
|
||||
lastUpdated: (await fs.stat(`./plugins/${file.name}`)).mtime!,
|
||||
plugin: new ((await import(`../plugins/${file.name}`)).default)(
|
||||
this,
|
||||
),
|
||||
|
@ -104,12 +140,12 @@ export class Server {
|
|||
const plugin = this.plugins.get(name);
|
||||
|
||||
if (
|
||||
Deno.statSync(`./plugins/${file.name}`).mtime!.getTime() !==
|
||||
(await fs.stat(`./plugins/${file.name}`)).mtime!.getTime() !==
|
||||
plugin?.lastUpdated.getTime()
|
||||
) {
|
||||
plugin?.plugin.emit("stop");
|
||||
this.plugins.set(name, {
|
||||
lastUpdated: Deno.statSync(`./plugins/${file.name}`).mtime!,
|
||||
lastUpdated: (await fs.stat(`./plugins/${file.name}`)).mtime!,
|
||||
plugin:
|
||||
new ((await import(`../plugins/${file.name}#` + Math.random()))
|
||||
.default)(
|
||||
|
@ -121,7 +157,7 @@ export class Server {
|
|||
}
|
||||
}
|
||||
}
|
||||
async removeUser(conn: Deno.Conn, text: string) {
|
||||
async removeUser(conn: Socket<{dataBuffer?: Buffer}>, text: string) {
|
||||
const player = this.players.find((e) => e.socket == conn);
|
||||
|
||||
if (!player) return;
|
||||
|
@ -129,7 +165,7 @@ export class Server {
|
|||
this.players = this.players.filter((e) => e != player);
|
||||
|
||||
try {
|
||||
conn.close();
|
||||
conn.end();
|
||||
} catch {
|
||||
// whatever
|
||||
}
|
||||
|
@ -147,7 +183,7 @@ export class Server {
|
|||
async handlePacket(
|
||||
buffer: Uint8Array,
|
||||
packetType: number,
|
||||
connection: Deno.Conn,
|
||||
connection: Socket<{dataBuffer?: Buffer}>,
|
||||
) {
|
||||
const packet = new PacketReader(buffer);
|
||||
if (packetType == 0x00) {
|
||||
|
@ -158,7 +194,7 @@ export class Server {
|
|||
const verification = packet.readString();
|
||||
|
||||
if (this.players.length >= this.maxUsers) {
|
||||
connection.close();
|
||||
connection.end()
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -170,31 +206,25 @@ export class Server {
|
|||
);
|
||||
|
||||
if (!verification) {
|
||||
player.socket.close();
|
||||
player.socket.end();
|
||||
return true;
|
||||
}
|
||||
const hasher = new Bun.CryptoHasher("md5");
|
||||
hasher.update(config.hash + player.username);
|
||||
|
||||
const str = toHexString(
|
||||
new Uint8Array(
|
||||
await crypto.subtle.digest(
|
||||
"MD5",
|
||||
new TextEncoder().encode(config.hash + player.username),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (
|
||||
config.onlineMode && verification != config.hash &&
|
||||
!this.players.find((e) => e.socket == connection)
|
||||
) {
|
||||
if (
|
||||
str !== verification
|
||||
hasher.digest("hex") !== verification
|
||||
) {
|
||||
await PacketDefinitions.disconnect(
|
||||
"Refresh your playerlist! Incorrect hash!",
|
||||
player,
|
||||
);
|
||||
|
||||
player.socket.close();
|
||||
player.socket.end();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -205,7 +235,7 @@ export class Server {
|
|||
"Your name is already being used!",
|
||||
player,
|
||||
);
|
||||
player.socket.close();
|
||||
player.socket.end();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -295,63 +325,4 @@ export class Server {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
async startSocket(connection: Deno.Conn) {
|
||||
while (true) {
|
||||
const packetID = new Uint8Array(1);
|
||||
let packetIDReadAttempt;
|
||||
|
||||
try {
|
||||
packetIDReadAttempt = await connection.read(packetID);
|
||||
} catch {
|
||||
await this.removeUser(connection, "Packet ID read failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (packetIDReadAttempt) {
|
||||
const packetLength = this.lengthMap.get(packetID[0]);
|
||||
|
||||
if (!packetLength) {
|
||||
log.critical("Unknown Packet: " + packetID[0]);
|
||||
await this.removeUser(connection, "Unknown packet ID " + packetID[0]); // TODO: add a reason to this
|
||||
break;
|
||||
}
|
||||
|
||||
let rawPacket = new Uint8Array(packetLength);
|
||||
let packetReadAttempt;
|
||||
|
||||
try {
|
||||
packetReadAttempt = await connection.read(rawPacket);
|
||||
} catch {
|
||||
await this.removeUser(connection, "Packet read attempt failed."); // TODO: add a reason to this
|
||||
break;
|
||||
}
|
||||
|
||||
let fullRead = packetReadAttempt!;
|
||||
|
||||
while (fullRead < packetLength) {
|
||||
const halfPacket = new Uint8Array(packetLength - fullRead);
|
||||
rawPacket = new Uint8Array([...rawPacket, ...halfPacket]);
|
||||
|
||||
try {
|
||||
fullRead += (await connection.read(halfPacket))!;
|
||||
} catch {
|
||||
await this.removeUser(
|
||||
connection,
|
||||
"Couldn't read all of packet " + packetID[0],
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.handlePacket(rawPacket, packetID[0], connection);
|
||||
} else {
|
||||
await this.removeUser(
|
||||
connection,
|
||||
"Packet ID read returned null. Packet " + packetID[0],
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { gzip, ungzip } from "https://cdn.skypack.dev/pako";
|
||||
import { decode, encode } from "cbor-x";
|
||||
import { Position } from "./classes.ts";
|
||||
import { cbor } from "../deps.ts";
|
||||
|
||||
import { unlink, readFile, writeFile} from "node:fs/promises";
|
||||
|
||||
export class World {
|
||||
size: Position;
|
||||
data: Uint8Array;
|
||||
|
@ -32,7 +34,7 @@ export class World {
|
|||
}
|
||||
|
||||
findID(block: number): Position[] {
|
||||
const position = [];
|
||||
const position: Position[] = [];
|
||||
for (let z = 0; z < this.size.z; z++) {
|
||||
for (let y = 0; y < this.size.y; y++) {
|
||||
for (let x = 0; x < this.size.x; x++) {
|
||||
|
@ -71,7 +73,7 @@ export class World {
|
|||
|
||||
async delete() {
|
||||
try {
|
||||
await Deno.remove(`worlds/${this.name}.buf`)
|
||||
await unlink(`worlds/${this.name}.buf`)
|
||||
} catch {
|
||||
// gang
|
||||
}
|
||||
|
@ -79,15 +81,15 @@ export class World {
|
|||
|
||||
private async load() {
|
||||
try {
|
||||
const ungziped = ungzip(
|
||||
await Deno.readFile(`worlds/${this.name}.buf`)
|
||||
const ungziped = Bun.gunzipSync(
|
||||
await readFile(`worlds/${this.name}.buf`)
|
||||
);
|
||||
|
||||
if (!(ungziped instanceof Uint8Array)) return;
|
||||
const dv = new DataView(ungziped.buffer);
|
||||
|
||||
const cborSize = dv.getUint32(0);
|
||||
this.metadata = cbor.decode(new Uint8Array(ungziped.buffer.slice(4, cborSize+4)));
|
||||
this.metadata = decode(new Uint8Array(ungziped.buffer.slice(4, cborSize+4)));
|
||||
|
||||
this.size = {
|
||||
x: this.metadata.x!,
|
||||
|
@ -117,12 +119,12 @@ export class World {
|
|||
z: this.size.z!,
|
||||
...this.metadata
|
||||
}
|
||||
const cborData = cbor.encode(metadata);
|
||||
const cborData = encode(metadata);
|
||||
const buffer = new Uint8Array(4 + cborData.byteLength + this.data.byteLength);
|
||||
const dv = new DataView(buffer.buffer);
|
||||
dv.setUint32(0, cborData.byteLength);
|
||||
buffer.set(cborData, 4);
|
||||
buffer.set(this.data, 4 + cborData.byteLength);
|
||||
await Deno.writeFile(`worlds/${this.name}.buf`, gzip(buffer)!);
|
||||
await writeFile(`worlds/${this.name}.buf`, Bun.gzipSync(buffer)!);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { EventEmitter } from "../deps.ts";
|
||||
import EventEmitter from "./../events";
|
||||
import { Player } from "./Player.ts";
|
||||
import { Server } from "./Server.ts";
|
||||
|
||||
|
|
30
deno.lock
30
deno.lock
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"version": "3",
|
||||
"remote": {
|
||||
"https://cdn.skypack.dev/-/pako@v2.1.0-45rMaYN3B7tIGOhONLdV/dist=es2019,mode=imports/optimized/pako.js": "0e70f8f81aaa277e359308e2f07d560fd6c9f98e2bb8968301f237bf7f34c1a8",
|
||||
"https://cdn.skypack.dev/pako": "cc52bc53fdee35b4854461706ea3655b8bda1a3b48ed170f16d0050f6b8534d5",
|
||||
"https://deno.land/std@0.136.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
|
||||
"https://deno.land/std@0.136.0/_wasm_crypto/crypto.mjs": "3b383eb715e8bfe61b4450ef0644b2653429c88d494807c86c5235979f62e56b",
|
||||
"https://deno.land/std@0.136.0/_wasm_crypto/crypto.wasm.mjs": "0ad9ecc0d03ca8a083d9109db22e7507f019f63cf55b82ea618ab58855617577",
|
||||
"https://deno.land/std@0.136.0/_wasm_crypto/mod.ts": "30a93c8b6b6c5b269e96a3e95d2c045d86a496814a8737443b77cad941d6a0b5",
|
||||
"https://deno.land/std@0.136.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d",
|
||||
"https://deno.land/std@0.136.0/bytes/equals.ts": "a60ef9f01fb6e06a4e0343fc44d53d39d12dd66bc3f09ac8e6eb9cc1a6335e48",
|
||||
"https://deno.land/std@0.136.0/bytes/mod.ts": "4cef6fe8f0de217b9babbcbb0a566402b667f18a8e6d094a45e5fb3fc1afff70",
|
||||
"https://deno.land/std@0.136.0/crypto/mod.ts": "a10fee951ddf2c91ae5938ba2a1d123580f773e533008988ba6089ddd2b65d67",
|
||||
"https://deno.land/std@0.136.0/encoding/base64.ts": "c8c16b4adaa60d7a8eee047c73ece26844435e8f7f1328d74593dbb2dd58ea4f",
|
||||
"https://deno.land/std@0.136.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37",
|
||||
"https://deno.land/std@0.136.0/fs/exists.ts": "cb734d872f8554ea40b8bff77ad33d4143c1187eac621a55bf37781a43c56f6d",
|
||||
"https://deno.land/std@0.136.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b",
|
||||
"https://deno.land/std@0.136.0/log/handlers.ts": "b88c24df61eaeee8581dbef3622f21aebfd061cd2fda49affc1711c0e54d57da",
|
||||
"https://deno.land/std@0.136.0/log/levels.ts": "82c965b90f763b5313e7595d4ba78d5095a13646d18430ebaf547526131604d1",
|
||||
"https://deno.land/std@0.136.0/log/logger.ts": "30770bf29053d1c8d1054ec52ab385da8e5b8a43973da32c84dea819e5a11b52",
|
||||
"https://deno.land/std@0.136.0/log/mod.ts": "e200a8600a9e9ac2b10668fda0e6537a1444c5050ea6a04c826df52519bf35fd",
|
||||
"https://deno.land/x/cbor@v1.5.9/decode.js": "631f504e9e811609cb388a7f226d2054fd4f432eb188423e46be10443746b305",
|
||||
"https://deno.land/x/cbor@v1.5.9/encode.js": "c28da2b6291e271e1f55bc3add9119fa23ddee5274940c09ee3c4c67f7ba45d3",
|
||||
"https://deno.land/x/cbor@v1.5.9/index.js": "cc8678819d77aa34b6fa9293658d85d5e53e53eaf555f85b0f98a8a18dbfaa12",
|
||||
"https://deno.land/x/cbor@v1.5.9/iterators.js": "744e0469fe37c33bab3787608ced2f2cda014cb9352b3adbd949a2701f043aea",
|
||||
"https://deno.land/x/dotenv@v3.2.0/load.ts": "cbd76a0aee01aad8d09222afaa1dd04b84d9d3e44637503b01bf77a91df9e041",
|
||||
"https://deno.land/x/dotenv@v3.2.0/mod.ts": "077b48773de9205266a0b44c3c3a3c3083449ed64bb0b6cc461b95720678d38e",
|
||||
"https://deno.land/x/dotenv@v3.2.0/util.ts": "693730877b13f8ead2b79b2aa31e2a0652862f7dc0c5f6d2f313f4d39c7b7670"
|
||||
}
|
||||
}
|
35
deps.ts
35
deps.ts
|
@ -1,20 +1,23 @@
|
|||
import "https://deno.land/x/dotenv@v3.2.0/load.ts";
|
||||
|
||||
export * as cbor from "https://deno.land/x/cbor@v1.5.9/index.js"
|
||||
export * as log from "https://deno.land/std@0.136.0/log/mod.ts";
|
||||
export { crypto } from "https://deno.land/std@0.136.0/crypto/mod.ts";
|
||||
export { EventEmitter } from "./events.ts";
|
||||
import "https://deno.land/x/dotenv@v3.2.0/load.ts";
|
||||
export const toHexString = (bytes: Uint8Array) =>
|
||||
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
|
||||
|
||||
export const log = {
|
||||
info: (...a) => {
|
||||
console.log("[INFO]", ...a);
|
||||
},
|
||||
warning: (...a) => {
|
||||
console.warn("[WARNING]", ...a);
|
||||
},
|
||||
critical: (...a) => {
|
||||
console.error("[ERROR]", ...a);
|
||||
}
|
||||
}
|
||||
export const config = {
|
||||
ops: Deno.env.get("OPS") ? JSON.parse(Deno.env.get("OPS")!) : [],
|
||||
port: +Deno.env.get("PORT")!,
|
||||
hash: Deno.env.get("HASH"),
|
||||
onlineMode: Deno.env.get("ONLINEMODE") == "true",
|
||||
main: Deno.env.get("MAIN") || "main",
|
||||
maxUsers: +(Deno.env.get("USERS") || 24) > 255 ? 255 : +(Deno.env.get("USERS") || 24),
|
||||
software: Deno.env.get("SOFTWARE") || "Custom Cla66ic",
|
||||
name: Deno.env.get("NAME") || "Cla66ic Server"
|
||||
ops: process.env.OPS ? JSON.parse(process.env.OPS!) : [],
|
||||
port: +process.env.PORT!,
|
||||
hash: process.env.HASH,
|
||||
onlineMode: process.env.ONLINEMODE == "true",
|
||||
main: process.env.MAIN || "main",
|
||||
maxUsers: +(process.env.USERS || 24) > 255 ? 255 : +(process.env.USERS || 24),
|
||||
software: process.env.SOFTWARE || "Custom Cla66ic",
|
||||
name: process.env.NAME || "Cla66ic Server"
|
||||
};
|
||||
|
|
|
@ -208,7 +208,7 @@ export class EventEmitter<E extends EventsType = {}> {
|
|||
timeout?: number,
|
||||
): Promise<Parameters<Callback>> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let timeoutId: number | null;
|
||||
let timeoutId: Timer | null;
|
||||
|
||||
let listener = (...args: any[]) => {
|
||||
if (timeoutId !== null) clearTimeout(timeoutId);
|
||||
|
|
15
package.json
Normal file
15
package.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "cla66ic",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cbor-x": "^1.5.9",
|
||||
"netlinkwrapper": "^2.0.2"
|
||||
}
|
||||
}
|
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue