mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-19 12:45:45 +01:00
CODEBASE: Fix lint errors 3 (#1758)
This is a really big refactor because it actually *fixes* a lot of the lint errors instead of disabling them.
This commit is contained in:
parent
97ca8c5f5e
commit
75cf9c88b5
.eslintrc.jspackage-lock.jsonpackage.jsonSaveObject.ts
src
Bladeburner
CodingContracts.tsCorporation
Gang
Infiltration/ui
NetscriptFunctions.tsNetscriptFunctions
PersonObjects
Player
Sleeve/Work
ScriptEditor/ui
Server
StockMarket
Terminal
ThirdParty
Types
utils
14
.eslintrc.js
14
.eslintrc.js
@ -42,4 +42,18 @@ module.exports = {
|
||||
version: "detect",
|
||||
},
|
||||
},
|
||||
overrides: [
|
||||
/**
|
||||
* Some enums are subsets of other enums. For example, UniversityLocationName contains locations of 3 universities.
|
||||
* With each member, we refer to the respective LocationName's member instead of using a literal string. This usage
|
||||
* is okay, but it triggers the "prefer-literal-enum-member" rule. This rule is not useful in this case, so we
|
||||
* suppress it in NetscriptDefinitions.d.ts.
|
||||
*/
|
||||
{
|
||||
files: ["src/ScriptEditor/NetscriptDefinitions.d.ts"],
|
||||
rules: {
|
||||
"@typescript-eslint/prefer-literal-enum-member": ["off"],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
8
package-lock.json
generated
8
package-lock.json
generated
@ -69,6 +69,7 @@
|
||||
"@types/react-beautiful-dnd": "^13.1.5",
|
||||
"@types/react-dom": "^17.0.21",
|
||||
"@types/react-resizable": "^3.0.5",
|
||||
"@types/sprintf-js": "^1.1.4",
|
||||
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||
"@typescript-eslint/parser": "^6.9.1",
|
||||
"babel-jest": "^29.7.0",
|
||||
@ -5124,6 +5125,13 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sprintf-js": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/sprintf-js/-/sprintf-js-1.1.4.tgz",
|
||||
"integrity": "sha512-aWK1reDYWxcjgcIIPmQi3u+OQDuYa9b+lr6eIsGWrekJ9vr1NSjr4Eab8oQ1iKuH1ltFHpXGyerAv1a3FMKxzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/stack-utils": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"@types/react-beautiful-dnd": "^13.1.5",
|
||||
"@types/react-dom": "^17.0.21",
|
||||
"@types/react-resizable": "^3.0.5",
|
||||
"@types/sprintf-js": "^1.1.4",
|
||||
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||
"@typescript-eslint/parser": "^6.9.1",
|
||||
"babel-jest": "^29.7.0",
|
||||
|
@ -51,6 +51,7 @@ import { Sleeve } from "../PersonObjects/Sleeve/Sleeve";
|
||||
import { autoCompleteTypeShorthand } from "./utils/terminalShorthands";
|
||||
import { resolveTeamCasualties, type OperationTeam } from "./Actions/TeamCasualties";
|
||||
import { shuffleArray } from "../Infiltration/ui/BribeGame";
|
||||
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||
|
||||
export const BladeburnerPromise: PromisePair<number> = { promise: null, resolve: null };
|
||||
|
||||
@ -1403,9 +1404,10 @@ export class Bladeburner implements OperationTeam {
|
||||
|
||||
/** Initializes a Bladeburner object from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): Bladeburner {
|
||||
objectAssert(value.data);
|
||||
// operations and contracts are not loaded directly from the save, we load them in using a different method
|
||||
const contractsData = value.data?.contracts;
|
||||
const operationsData = value.data?.operations;
|
||||
const contractsData = value.data.contracts;
|
||||
const operationsData = value.data.operations;
|
||||
const bladeburner = Generic_fromJSON(Bladeburner, value.data, Bladeburner.keysToLoad);
|
||||
// Loading this way allows better typesafety and also allows faithfully reconstructing contracts/operations
|
||||
// even from save data that is missing a lot of static info about the objects.
|
||||
|
@ -4,6 +4,7 @@ import { codingContractTypesMetadata } from "./data/codingcontracttypes";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "./utils/JSONReviver";
|
||||
import { CodingContractEvent } from "./ui/React/CodingContractModal";
|
||||
import { ContractFilePath, resolveContractFilePath } from "./Paths/ContractFilePath";
|
||||
import { objectAssert } from "./utils/helpers/typeAssertion";
|
||||
|
||||
/* Contract Types */
|
||||
export const CodingContractTypes = Object.fromEntries(codingContractTypesMetadata.map((x) => [x.name, x]));
|
||||
@ -127,6 +128,7 @@ export class CodingContract {
|
||||
|
||||
/** Initializes a CodingContract from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): CodingContract {
|
||||
objectAssert(value.data);
|
||||
// In previous versions, there was a data field instead of a state field.
|
||||
if ("data" in value.data) {
|
||||
value.data.state = value.data.data;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { CorpStateName } from "@nsdefs";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
import { stateNames } from "./data/Constants";
|
||||
|
||||
export class CorporationState {
|
||||
// Number representing what state the Corporation is in. The number
|
||||
// is an index for the array that holds all Corporation States
|
||||
|
@ -100,12 +100,6 @@ export class GangMember {
|
||||
}
|
||||
|
||||
getTask(): GangMemberTask {
|
||||
// TODO unplanned: transfer that to a save file migration function
|
||||
// Backwards compatibility
|
||||
if ((this.task as any) instanceof GangMemberTask) {
|
||||
this.task = (this.task as any).name;
|
||||
}
|
||||
|
||||
if (Object.hasOwn(GangMemberTasks, this.task)) {
|
||||
return GangMemberTasks[this.task];
|
||||
}
|
||||
|
@ -165,9 +165,9 @@ function fieldEquals(a: boolean[][], b: boolean[][]): boolean {
|
||||
}
|
||||
|
||||
function generateEmptyField(difficulty: Difficulty): boolean[][] {
|
||||
const field = [];
|
||||
const field: boolean[][] = [];
|
||||
for (let i = 0; i < Math.round(difficulty.height); i++) {
|
||||
field.push(new Array(Math.round(difficulty.width)).fill(false));
|
||||
field.push(new Array<boolean>(Math.round(difficulty.width)).fill(false));
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
@ -20,11 +20,12 @@ import {
|
||||
GymType,
|
||||
JobName,
|
||||
JobField,
|
||||
LiteratureName,
|
||||
type LiteratureName,
|
||||
LocationName,
|
||||
ToastVariant,
|
||||
UniversityClassType,
|
||||
CompanyName,
|
||||
type MessageFilename,
|
||||
} from "@enums";
|
||||
import { PromptEvent } from "./ui/React/PromptManager";
|
||||
import { GetServer, DeleteServer, AddToAllServers, createUniqueRandomIp } from "./Server/AllServers";
|
||||
@ -145,8 +146,19 @@ export const ns: InternalAPI<NSFull> = {
|
||||
stock: NetscriptStockMarket(),
|
||||
grafting: NetscriptGrafting(),
|
||||
hacknet: NetscriptHacknet(),
|
||||
sprintf: () => sprintf,
|
||||
vsprintf: () => vsprintf,
|
||||
sprintf:
|
||||
(ctx) =>
|
||||
(_format, ...args) => {
|
||||
const format = helpers.string(ctx, "format", _format);
|
||||
return sprintf(format, ...(args as unknown[]));
|
||||
},
|
||||
vsprintf: (ctx) => (_format, _args) => {
|
||||
const format = helpers.string(ctx, "format", _format);
|
||||
if (!Array.isArray(_args)) {
|
||||
throw helpers.errorMessage(ctx, `args must be an array.`);
|
||||
}
|
||||
return vsprintf(format, _args);
|
||||
},
|
||||
scan: (ctx) => (_hostname) => {
|
||||
const hostname = _hostname ? helpers.string(ctx, "hostname", _hostname) : ctx.workerScript.hostname;
|
||||
const server = helpers.getServer(ctx, hostname);
|
||||
@ -1163,7 +1175,8 @@ export const ns: InternalAPI<NSFull> = {
|
||||
if (!path) return false;
|
||||
if (hasScriptExtension(path)) return server.scripts.has(path);
|
||||
if (hasTextExtension(path)) return server.textFiles.has(path);
|
||||
if (path.endsWith(".lit") || path.endsWith(".msg")) return server.messages.includes(path as any);
|
||||
if (path.endsWith(".lit") || path.endsWith(".msg"))
|
||||
return server.messages.includes(path as LiteratureName | MessageFilename);
|
||||
if (hasContractExtension(path)) return !!server.contracts.find(({ fn }) => fn === path);
|
||||
const lowerPath = path.toLowerCase();
|
||||
return server.programs.map((programName) => programName.toLowerCase()).includes(lowerPath);
|
||||
|
@ -3,6 +3,7 @@ import { toNative } from "./toNative";
|
||||
import libarg from "arg";
|
||||
import { NetscriptContext } from "../Netscript/APIWrapper";
|
||||
|
||||
export type Schema = [string, string | number | boolean | string[]][];
|
||||
type FlagType = StringConstructor | NumberConstructor | BooleanConstructor | StringConstructor[];
|
||||
type FlagsRet = Record<string, ScriptArg | string[]>;
|
||||
export function Flags(ctx: NetscriptContext | string[]): (data: unknown) => FlagsRet {
|
||||
@ -12,7 +13,7 @@ export function Flags(ctx: NetscriptContext | string[]): (data: unknown) => Flag
|
||||
if (!Array.isArray(schema)) throw new Error("flags schema passed in is invalid.");
|
||||
const args: Record<string, FlagType> = {};
|
||||
|
||||
for (const d of schema) {
|
||||
for (const d of schema as Schema) {
|
||||
let t: FlagType = String;
|
||||
if (typeof d[1] === "number") {
|
||||
t = Number;
|
||||
@ -24,13 +25,15 @@ export function Flags(ctx: NetscriptContext | string[]): (data: unknown) => Flag
|
||||
const numDashes = d[0].length > 1 ? 2 : 1;
|
||||
args["-".repeat(numDashes) + d[0]] = t;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||
const ret: FlagsRet = libarg(args, { argv: vargs });
|
||||
for (const d of schema) {
|
||||
for (const d of schema as Schema) {
|
||||
if (!Object.hasOwn(ret, "--" + d[0]) || !Object.hasOwn(ret, "-" + d[0])) ret[d[0]] = d[1];
|
||||
}
|
||||
for (const key of Object.keys(ret)) {
|
||||
if (!key.startsWith("-")) continue;
|
||||
const value = ret[key];
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete ret[key];
|
||||
const numDashes = key.length === 2 ? 1 : 2;
|
||||
ret[key.slice(numDashes)] = value;
|
||||
|
@ -82,14 +82,14 @@ export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
|
||||
setStyles: (ctx) => (newStyles) => {
|
||||
const styleValidator: Record<string, string | number | undefined> = {};
|
||||
assertObjectType(ctx, "newStyles", newStyles, styleValidator);
|
||||
const currentStyles = { ...Settings.styles };
|
||||
const currentStyles: Record<string, unknown> = { ...Settings.styles };
|
||||
const errors: string[] = [];
|
||||
for (const key of Object.keys(newStyles)) {
|
||||
if (!(currentStyles as any)[key]) {
|
||||
if (!currentStyles[key]) {
|
||||
// Invalid key
|
||||
errors.push(`Invalid key "${key}"`);
|
||||
} else {
|
||||
(currentStyles as any)[key] = newStyles[key];
|
||||
currentStyles[key] = newStyles[key];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,6 +203,7 @@ export class PlayerObject extends Person implements IPlayer {
|
||||
// Remove any invalid jobs
|
||||
for (const [loadedCompanyName, loadedJobName] of Object.entries(player.jobs)) {
|
||||
if (!isMember("CompanyName", loadedCompanyName) || !isMember("JobName", loadedJobName)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete player.jobs[loadedCompanyName as CompanyName];
|
||||
}
|
||||
}
|
||||
|
@ -377,6 +377,7 @@ export function quitJob(this: PlayerObject, company: CompanyName, suppressDialog
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete this.jobs[company];
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import { scaleWorkStats } from "../../../Work/WorkStats";
|
||||
import { getKeyList } from "../../../utils/helpers/getKeyList";
|
||||
import { loadActionIdentifier } from "../../../Bladeburner/utils/loadActionIdentifier";
|
||||
import { invalidWork } from "../../../Work/InvalidWork";
|
||||
import { objectAssert } from "../../../utils/helpers/typeAssertion";
|
||||
|
||||
interface SleeveBladeburnerWorkParams {
|
||||
actionId: ActionIdentifier & { type: BladeburnerActionType.General | BladeburnerActionType.Contract };
|
||||
@ -98,6 +99,7 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
|
||||
|
||||
/** Initializes a BladeburnerWork object from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): SleeveBladeburnerWork {
|
||||
objectAssert(value.data);
|
||||
const actionId = loadActionIdentifier(value.data?.actionId);
|
||||
if (!actionId) return invalidWork();
|
||||
value.data.actionId = actionId;
|
||||
|
@ -7,6 +7,7 @@ import { Sleeve } from "../Sleeve";
|
||||
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
|
||||
import { Locations } from "../../../Locations/Locations";
|
||||
import { isMember } from "../../../utils/EnumHelper";
|
||||
import { objectAssert } from "../../../utils/helpers/typeAssertion";
|
||||
|
||||
export const isSleeveClassWork = (w: SleeveWorkClass | null): w is SleeveClassWork =>
|
||||
w !== null && w.type === SleeveWorkType.CLASS;
|
||||
@ -54,8 +55,13 @@ export class SleeveClassWork extends SleeveWorkClass {
|
||||
|
||||
/** Initializes a ClassWork object from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): SleeveClassWork {
|
||||
if (!(value.data.classType in Classes)) value.data.classType = "Computer Science";
|
||||
if (!(value.data.location in Locations)) value.data.location = LocationName.Sector12RothmanUniversity;
|
||||
objectAssert(value.data);
|
||||
if (typeof value.data.classType !== "string" || !(value.data.classType in Classes)) {
|
||||
value.data.classType = "Computer Science";
|
||||
}
|
||||
if (typeof value.data.location !== "string" || !(value.data.location in Locations)) {
|
||||
value.data.location = LocationName.Sector12RothmanUniversity;
|
||||
}
|
||||
return Generic_fromJSON(SleeveClassWork, value.data);
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +225,12 @@ class BitburnerSaveObject {
|
||||
|
||||
let parsedSaveData;
|
||||
try {
|
||||
parsedSaveData = JSON.parse(decodedSaveData);
|
||||
parsedSaveData = JSON.parse(decodedSaveData) as {
|
||||
ctor: string;
|
||||
data: {
|
||||
PlayerSave: string;
|
||||
};
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(error); // We'll handle below
|
||||
}
|
||||
@ -632,6 +637,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
||||
if (isNaN(intExp)) intExp = 0;
|
||||
anyPlayer.exp.intelligence += intExp;
|
||||
for (const field of removePlayerFields) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete anyPlayer[field];
|
||||
}
|
||||
for (const sleeve of anyPlayer.sleeves) {
|
||||
@ -640,6 +646,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
||||
if (isNaN(intExp)) intExp = 0;
|
||||
anySleeve.exp.intelligence += intExp;
|
||||
for (const field of removeSleeveFields) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete sleeve[field];
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,10 @@ export const sanitizeTheme = (theme: IScriptEditorTheme): void => {
|
||||
return;
|
||||
}
|
||||
for (const themeKey of getRecordKeys(theme)) {
|
||||
if (typeof theme[themeKey] !== "object") delete theme[themeKey];
|
||||
if (typeof theme[themeKey] !== "object") {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete theme[themeKey];
|
||||
}
|
||||
switch (themeKey) {
|
||||
case "base":
|
||||
if (!["vs-dark", "vs"].includes(theme.base)) theme.base = "vs-dark";
|
||||
|
@ -13,6 +13,7 @@ import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
|
||||
import { IPAddress, isIPAddress } from "../Types/strings";
|
||||
|
||||
import "../Script/RunningScript"; // For reviver side-effect
|
||||
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||
|
||||
/**
|
||||
* Map of all Servers that exist in the game
|
||||
@ -63,6 +64,7 @@ export function DeleteServer(serverkey: string): void {
|
||||
for (const key of Object.keys(AllServers)) {
|
||||
const server = AllServers[key];
|
||||
if (server.ip !== serverkey && server.hostname !== serverkey) continue;
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete AllServers[key];
|
||||
break;
|
||||
}
|
||||
@ -100,6 +102,7 @@ export function AddToAllServers(server: Server | HacknetServer): void {
|
||||
|
||||
export const renameServer = (hostname: string, newName: string): void => {
|
||||
AllServers[newName] = AllServers[hostname];
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete AllServers[hostname];
|
||||
};
|
||||
|
||||
@ -188,13 +191,22 @@ export function initForeignServers(homeComputer: Server): void {
|
||||
|
||||
export function prestigeAllServers(): void {
|
||||
for (const member of Object.keys(AllServers)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete AllServers[member];
|
||||
}
|
||||
AllServers = {};
|
||||
}
|
||||
|
||||
export function loadAllServers(saveString: string): void {
|
||||
AllServers = JSON.parse(saveString, Reviver);
|
||||
const allServersData: unknown = JSON.parse(saveString, Reviver);
|
||||
objectAssert(allServersData);
|
||||
for (const [serverName, server] of Object.entries(allServersData)) {
|
||||
if (!(server instanceof Server) && !(server instanceof HacknetServer)) {
|
||||
throw new Error(`Server ${serverName} is not an instance of Server or HacknetServer.`);
|
||||
}
|
||||
}
|
||||
// We validated the data above, so it's safe to typecast here.
|
||||
AllServers = allServersData as typeof AllServers;
|
||||
}
|
||||
|
||||
export function saveAllServers(): string {
|
||||
|
@ -24,6 +24,7 @@ import lodash from "lodash";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
|
||||
import type { ScriptKey } from "../utils/helpers/scriptKey";
|
||||
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
@ -293,6 +294,7 @@ export abstract class BaseServer implements IServer {
|
||||
// RunningScripts are stored as a simple array, both for backward compatibility,
|
||||
// compactness, and ease of filtering them here.
|
||||
const result = Generic_toJSON(ctorName, this, keys);
|
||||
objectAssert(result.data);
|
||||
if (Settings.ExcludeRunningScriptsFromSave) {
|
||||
result.data.runningScripts = [];
|
||||
return result;
|
||||
@ -313,8 +315,11 @@ export abstract class BaseServer implements IServer {
|
||||
// Initializes a Server Object from a JSON save state
|
||||
// Called by subclasses, not Reviver.
|
||||
static fromJSONBase<T extends BaseServer>(value: IReviverValue, ctor: new () => T, keys: readonly (keyof T)[]): T {
|
||||
objectAssert(value.data);
|
||||
const server = Generic_fromJSON(ctor, value.data, keys);
|
||||
server.savedScripts = value.data.runningScripts;
|
||||
if (value.data.runningScripts != null && Array.isArray(value.data.runningScripts)) {
|
||||
server.savedScripts = value.data.runningScripts;
|
||||
}
|
||||
// If textFiles is not an array, we've already done the 2.3 migration to textFiles and scripts as maps + path changes.
|
||||
if (!Array.isArray(server.textFiles)) return server;
|
||||
|
||||
@ -333,7 +338,7 @@ export abstract class BaseServer implements IServer {
|
||||
// In case somehow there are previously valid filenames that can't be sanitized, they will go in a new directory with a note.
|
||||
for (const script of oldScripts) {
|
||||
// We're about to do type validation on the filename anyway.
|
||||
if (script.filename.endsWith(".ns")) script.filename = (script.filename + ".js") as any;
|
||||
if (script.filename.endsWith(".ns")) script.filename = (script.filename + ".js") as ScriptFilePath;
|
||||
let newFilePath = resolveScriptFilePath(script.filename);
|
||||
if (!newFilePath) {
|
||||
newFilePath = `${newDirectory}script${++invalidScriptCount}.js` as ScriptFilePath;
|
||||
|
@ -151,6 +151,7 @@ export function deleteStockMarket(): void {
|
||||
|
||||
export function initStockMarket(): void {
|
||||
for (const stockName of Object.getOwnPropertyNames(StockMarket)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete StockMarket[stockName];
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ export function exportScripts(pattern: string, server: BaseServer, currDir = roo
|
||||
// Return an error if no files matched, rather than an empty zip folder
|
||||
if (Object.keys(zip.files).length == 0) throw new Error(`No files match the pattern ${pattern}`);
|
||||
const filename = `bitburner${
|
||||
hasScriptExtension(pattern) ? "Scripts" : pattern.endsWith(".txt") ? "Texts" : "Files"
|
||||
hasScriptExtension(pattern) ? "Scripts" : hasTextExtension(pattern) ? "Texts" : "Files"
|
||||
}.zip`;
|
||||
zip
|
||||
.generateAsync({ type: "blob" })
|
||||
|
@ -33,6 +33,7 @@ export function ls(args: (string | number | boolean)[], server: BaseServer): voi
|
||||
}
|
||||
let flags: LSFlags;
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||
flags = libarg(
|
||||
{
|
||||
"-l": Boolean,
|
||||
|
@ -4,8 +4,11 @@ import { matchScriptPathUnanchored } from "../../utils/helpers/scriptKey";
|
||||
import libarg from "arg";
|
||||
|
||||
export function ps(args: (string | number | boolean)[], server: BaseServer): void {
|
||||
let flags;
|
||||
let flags: {
|
||||
"--grep": string;
|
||||
};
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||
flags = libarg(
|
||||
{
|
||||
"--grep": String,
|
||||
|
@ -18,8 +18,14 @@ export function runScript(path: ScriptFilePath, commandArgs: (string | number |
|
||||
if (!script) return Terminal.error(`Script ${path} does not exist on this server.`);
|
||||
|
||||
const runArgs = { "--tail": Boolean, "-t": Number, "--ram-override": Number };
|
||||
let flags;
|
||||
let flags: {
|
||||
_: ScriptArg[];
|
||||
"--tail": boolean;
|
||||
"-t": string;
|
||||
"--ram-override": string;
|
||||
};
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||
flags = libarg(runArgs, {
|
||||
permissive: true,
|
||||
argv: commandArgs,
|
||||
@ -42,7 +48,7 @@ export function runScript(path: ScriptFilePath, commandArgs: (string | number |
|
||||
if (!server.hasAdminRights) return Terminal.error("Need root access to run script");
|
||||
|
||||
// Todo: Switch out arg for something with typescript support
|
||||
const args = flags._ as ScriptArg[];
|
||||
const args = flags._;
|
||||
|
||||
const singleRamUsage = ramOverride ?? script.getRamUsage(server.scripts);
|
||||
if (!singleRamUsage) {
|
||||
|
@ -23,7 +23,10 @@ export function tail(commandArray: (string | number | boolean)[], server: BaseSe
|
||||
}
|
||||
// Just use the first one (if there are multiple with the same
|
||||
// arguments, they can't be distinguished except by pid).
|
||||
LogBoxEvents.emit(candidates.values().next().value);
|
||||
const next = candidates.values().next();
|
||||
if (!next.done) {
|
||||
LogBoxEvents.emit(next.value);
|
||||
}
|
||||
} else if (typeof commandArray[0] === "number") {
|
||||
const runningScript = findRunningScriptByPid(commandArray[0]);
|
||||
if (runningScript == null) {
|
||||
|
@ -5,7 +5,7 @@ import { GetAllServers } from "../Server/AllServers";
|
||||
import { parseCommand, parseCommands } from "./Parser";
|
||||
import { HelpTexts } from "./HelpText";
|
||||
import { compile } from "../NetscriptJSEvaluator";
|
||||
import { Flags } from "../NetscriptFunctions/Flags";
|
||||
import { Flags, type Schema } from "../NetscriptFunctions/Flags";
|
||||
import { AutocompleteData } from "@nsdefs";
|
||||
import libarg from "arg";
|
||||
import { getAllDirectories, resolveDirectory, root } from "../Paths/Directory";
|
||||
@ -280,6 +280,7 @@ export async function getTabCompletionPossibilities(terminalText: string, baseDi
|
||||
_: [],
|
||||
};
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||
flags = libarg(runArgs, {
|
||||
permissive: true,
|
||||
argv: command.slice(2),
|
||||
@ -299,9 +300,9 @@ export async function getTabCompletionPossibilities(terminalText: string, baseDi
|
||||
scripts: [...currServ.scripts.keys()],
|
||||
txts: [...currServ.textFiles.keys()],
|
||||
enums: enums,
|
||||
flags: (schema: unknown) => {
|
||||
flags: (schema: Schema) => {
|
||||
if (!Array.isArray(schema)) throw new Error("flags require an array of array");
|
||||
pos2 = schema.map((f: unknown) => {
|
||||
pos2 = schema.map((f) => {
|
||||
if (!Array.isArray(f)) throw new Error("flags require an array of array");
|
||||
if (f[0].length === 1) return "-" + f[0];
|
||||
return "--" + f[0];
|
||||
|
4
src/ThirdParty/acorn-jsx-walk.d.ts
vendored
4
src/ThirdParty/acorn-jsx-walk.d.ts
vendored
@ -1 +1,3 @@
|
||||
declare module "acorn-jsx-walk";
|
||||
declare module "acorn-jsx-walk" {
|
||||
export function extend(base: any): void;
|
||||
}
|
||||
|
1
src/ThirdParty/sprintf-js.d.ts
vendored
1
src/ThirdParty/sprintf-js.d.ts
vendored
@ -1 +0,0 @@
|
||||
declare module "sprintf-js";
|
@ -1,3 +1,4 @@
|
||||
import { arrayAssert } from "../utils/helpers/typeAssertion";
|
||||
import type { IReviverValue } from "../utils/JSONReviver";
|
||||
// Versions of js builtin classes that can be converted to and from JSON for use in save files
|
||||
|
||||
@ -6,6 +7,7 @@ export class JSONSet<T> extends Set<T> {
|
||||
return { ctor: "JSONSet", data: Array.from(this) };
|
||||
}
|
||||
static fromJSON(value: IReviverValue): JSONSet<any> {
|
||||
arrayAssert(value.data);
|
||||
return new JSONSet(value.data);
|
||||
}
|
||||
}
|
||||
@ -16,6 +18,15 @@ export class JSONMap<K, __V> extends Map<K, __V> {
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): JSONMap<any, any> {
|
||||
return new JSONMap(value.data);
|
||||
arrayAssert(value.data);
|
||||
for (const item of value.data) {
|
||||
arrayAssert(item);
|
||||
if (item.length !== 2) {
|
||||
console.error("Invalid data passed to JSONMap.fromJSON(). Value:", value);
|
||||
throw new Error(`An item is not an array with exactly 2 items. Its length is ${item.length}.`);
|
||||
}
|
||||
}
|
||||
// We validated the data above, so it's safe to typecast here.
|
||||
return new JSONMap(value.data as [unknown, unknown][]);
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,14 @@
|
||||
import { ObjectValidator, validateObject } from "./Validator";
|
||||
import { JSONMap, JSONSet } from "../Types/Jsonable";
|
||||
import { loadActionIdentifier } from "../Bladeburner/utils/loadActionIdentifier";
|
||||
import { objectAssert } from "./helpers/typeAssertion";
|
||||
|
||||
type JsonableClass = (new () => { toJSON: () => IReviverValue }) & {
|
||||
fromJSON: (value: IReviverValue) => any;
|
||||
fromJSON: (value: IReviverValue) => unknown;
|
||||
validationData?: ObjectValidator<any>;
|
||||
};
|
||||
|
||||
export interface IReviverValue<T = any> {
|
||||
export interface IReviverValue<T = unknown> {
|
||||
ctor: string;
|
||||
data: T;
|
||||
}
|
||||
@ -87,16 +88,23 @@ export function Generic_toJSON<T extends Record<string, any>>(
|
||||
* @returns The object */
|
||||
export function Generic_fromJSON<T extends Record<string, any>>(
|
||||
ctor: new () => T,
|
||||
// data can actually be anything. We're just pretending it has the right keys for T. Save data is not type validated.
|
||||
data: Record<keyof T, any>,
|
||||
data: unknown,
|
||||
keys?: readonly (keyof T)[],
|
||||
): T {
|
||||
objectAssert(data);
|
||||
const obj = new ctor();
|
||||
// If keys were provided, just load the provided keys (if they are in the data)
|
||||
if (keys) {
|
||||
for (const key of keys) {
|
||||
/**
|
||||
* The type of key is "keyof T", but the type of data is Record<string, unknown>. TypeScript won't allow us to use
|
||||
* key as the index of data, so we need to typecast here.
|
||||
*/
|
||||
for (const key of keys as string[]) {
|
||||
const val = data[key];
|
||||
if (val !== undefined) obj[key] = val;
|
||||
if (val !== undefined) {
|
||||
// @ts-expect-error -- TypeScript won't allow this action: Type 'T' is generic and can only be indexed for reading.
|
||||
obj[key] = val;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
@ -1,5 +1,15 @@
|
||||
// Various functions for asserting types.
|
||||
|
||||
export class TypeAssertionError extends Error {
|
||||
friendlyType: string;
|
||||
|
||||
constructor(message: string, friendlyType: string, options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
this.name = this.constructor.name;
|
||||
this.friendlyType = friendlyType;
|
||||
}
|
||||
}
|
||||
|
||||
/** Function for providing custom error message to throw for a type assertion.
|
||||
* @param v: Value to assert type of
|
||||
* @param assertFn: Typechecking function to use for asserting type of v.
|
||||
@ -12,31 +22,47 @@ export function assert<T>(
|
||||
try {
|
||||
assertFn(v);
|
||||
} catch (e) {
|
||||
if (e instanceof TypeAssertionError) {
|
||||
throw msgFn(e.friendlyType);
|
||||
}
|
||||
const type = typeof e === "string" ? e : "unknown";
|
||||
throw msgFn(type);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the friendlyType of v. arrays are "array" and null is "null". */
|
||||
export function getFriendlyType(v: unknown): string {
|
||||
function getFriendlyType(v: unknown): string {
|
||||
return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
||||
}
|
||||
|
||||
//All assertion functions used here should return the friendlyType of the input.
|
||||
|
||||
/** For non-objects, and for array/null, throws the friendlyType of v. */
|
||||
export function objectAssert(v: unknown): asserts v is Partial<Record<string, unknown>> {
|
||||
/** For non-objects, and for array/null, throws an error with the friendlyType of v. */
|
||||
export function objectAssert(v: unknown): asserts v is Record<string, unknown> {
|
||||
const type = getFriendlyType(v);
|
||||
if (type !== "object") throw type;
|
||||
if (type !== "object") {
|
||||
console.error("The value is not an object. Value:", v);
|
||||
throw new TypeAssertionError(
|
||||
`The value is not an object. Its type is ${type}. Its string value is ${String(v)}.`,
|
||||
type,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** For non-string, throws the friendlyType of v. */
|
||||
/** For non-string, throws an error with the friendlyType of v. */
|
||||
export function stringAssert(v: unknown): asserts v is string {
|
||||
const type = getFriendlyType(v);
|
||||
if (type !== "string") throw type;
|
||||
if (type !== "string") {
|
||||
console.error("The value is not a string. Value:", v);
|
||||
throw new TypeAssertionError(`The value is not an string. Its type is ${type}.`, type);
|
||||
}
|
||||
}
|
||||
|
||||
/** For non-array, throws the friendlyType of v. */
|
||||
/** For non-array, throws an error with the friendlyType of v. */
|
||||
export function arrayAssert(v: unknown): asserts v is unknown[] {
|
||||
if (!Array.isArray(v)) throw getFriendlyType(v);
|
||||
if (!Array.isArray(v)) {
|
||||
console.error("The value is not an array. Value:", v);
|
||||
const type = getFriendlyType(v);
|
||||
throw new TypeAssertionError(`The value is not an array. Its type is ${type}.`, type);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user