forked from tools/josh
Enable strict type checking
This commit is contained in:
parent
c736981c20
commit
511b1a1e24
|
@ -6,7 +6,7 @@ import {Terminal} from "./terminal.js";
|
|||
export class Commands {
|
||||
private readonly terminal: Terminal;
|
||||
private readonly fileSystem: FileSystem;
|
||||
private readonly commands: object;
|
||||
private readonly commands: { [key: string]: Command };
|
||||
|
||||
|
||||
constructor(terminal: Terminal, fileSystem: FileSystem) {
|
||||
|
@ -134,7 +134,7 @@ export class Commands {
|
|||
}
|
||||
|
||||
|
||||
parse(input: string): string {
|
||||
execute(input: string): string {
|
||||
const args = new InputArgs(input);
|
||||
|
||||
if (args.command.trim() === "")
|
||||
|
@ -296,7 +296,7 @@ class Command {
|
|||
|
||||
class InputArgs {
|
||||
readonly command: string;
|
||||
private readonly _options: object;
|
||||
private readonly _options: { [key: string]: string };
|
||||
private readonly _args: string[];
|
||||
|
||||
|
||||
|
@ -353,7 +353,7 @@ class InputArgs {
|
|||
}
|
||||
|
||||
|
||||
getArg(index: number, def: string = undefined): string {
|
||||
getArg(index: number, def: string | undefined = undefined): string {
|
||||
return (def === undefined)
|
||||
? this._args[index]
|
||||
: (this.hasArg(index) ? this._args[index] : def);
|
||||
|
@ -364,7 +364,7 @@ class InputArgs {
|
|||
}
|
||||
|
||||
|
||||
getOption(key: string, def: string = undefined) {
|
||||
getOption(key: string, def: string | undefined = undefined) {
|
||||
return (def === undefined)
|
||||
? this._options[key]
|
||||
: (this.hasOption(key) ? this._options[key] : def);
|
||||
|
|
|
@ -3,5 +3,5 @@ interface String {
|
|||
}
|
||||
|
||||
interface Array<T> {
|
||||
sortAlphabetically(transform: (element: T) => string);
|
||||
sortAlphabetically(transform: (element: T) => string): T[];
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ String.prototype.trimLines = function (): string {
|
|||
};
|
||||
|
||||
String.prototype.replaceAll = function (regex, replacement) {
|
||||
let string = this;
|
||||
let string = this.toString();
|
||||
|
||||
while (regex.test(string))
|
||||
string = string.replace(regex, replacement);
|
||||
|
@ -19,10 +19,10 @@ String.prototype.replaceAll = function (regex, replacement) {
|
|||
|
||||
|
||||
interface Array<T> {
|
||||
sortAlphabetically(transform: (element: T) => string);
|
||||
sortAlphabetically(transform: (element: T) => string): T[];
|
||||
}
|
||||
|
||||
Array.prototype.sortAlphabetically = function (transform = (x) => x) {
|
||||
Array.prototype.sortAlphabetically = function (transform: (_: any) => string = (it) => it) {
|
||||
return this.sort((a, b) => {
|
||||
const aName = transform(a).toLowerCase();
|
||||
const bName = transform(b).toLowerCase();
|
||||
|
|
41
js/fs.ts
41
js/fs.ts
|
@ -28,6 +28,7 @@ export class FileSystem {
|
|||
}),
|
||||
"resume.pdf": new UrlFile("https://static.fwdekker.com/misc/resume.pdf")
|
||||
});
|
||||
this.files = this.root;
|
||||
}
|
||||
|
||||
|
||||
|
@ -62,8 +63,8 @@ export class FileSystem {
|
|||
}
|
||||
|
||||
|
||||
private executeForEach(inputs: string[], fun: (string) => string): string {
|
||||
const outputs = [];
|
||||
private executeForEach(inputs: string[], fun: (_: string) => string): string {
|
||||
const outputs: string[] = [];
|
||||
|
||||
inputs.forEach(input => {
|
||||
const output = fun(input);
|
||||
|
@ -161,9 +162,15 @@ export class FileSystem {
|
|||
let targetNode;
|
||||
let targetName;
|
||||
if (destinationTailNode === undefined) {
|
||||
if (!(destinationHeadNode instanceof Directory))
|
||||
return `The path '${destinationPath.head}' does not point to a directory`;
|
||||
|
||||
targetNode = destinationHeadNode;
|
||||
targetName = destinationPath.tail;
|
||||
} else {
|
||||
if (!(destinationTailNode instanceof Directory))
|
||||
return `The path '${destinationPath.tail}' does not point to a directory`;
|
||||
|
||||
targetNode = destinationTailNode;
|
||||
targetName = sourcePath.tail;
|
||||
}
|
||||
|
@ -192,7 +199,7 @@ export class FileSystem {
|
|||
return `'${path.path}' is not a directory`;
|
||||
|
||||
const dirList = [new Directory({}).nameString("."), new Directory({}).nameString("..")];
|
||||
const fileList = [];
|
||||
const fileList: string[] = [];
|
||||
|
||||
const nodes = node.nodes;
|
||||
Object.keys(nodes)
|
||||
|
@ -272,9 +279,15 @@ export class FileSystem {
|
|||
let targetNode;
|
||||
let targetName;
|
||||
if (destinationTailNode === undefined) {
|
||||
if (!(destinationHeadNode instanceof Directory))
|
||||
return `The path '${destinationPath.head}' does not point to a directory`;
|
||||
|
||||
targetNode = destinationHeadNode;
|
||||
targetName = destinationPath.tail;
|
||||
} else {
|
||||
if (!(destinationTailNode instanceof Directory))
|
||||
return `The path '${destinationPath.tail}' does not point to a directory`;
|
||||
|
||||
targetNode = destinationTailNode;
|
||||
targetName = sourcePath.tail;
|
||||
}
|
||||
|
@ -403,7 +416,7 @@ export class Path {
|
|||
readonly tail: string;
|
||||
|
||||
|
||||
constructor(currentPath: string, relativePath: string = undefined) {
|
||||
constructor(currentPath: string, relativePath: string | undefined = undefined) {
|
||||
let path;
|
||||
if (relativePath === undefined)
|
||||
path = currentPath;
|
||||
|
@ -435,26 +448,29 @@ export abstract class Node {
|
|||
|
||||
abstract nameString(name: string): string;
|
||||
|
||||
abstract visit(fun: (node: Node) => void, pre: (node: Node) => void, post: (node: Node) => void);
|
||||
abstract visit(fun: (node: Node) => void, pre: (node: Node) => void, post: (node: Node) => void): void;
|
||||
}
|
||||
|
||||
export class Directory extends Node {
|
||||
private readonly _nodes: object;
|
||||
private readonly _nodes: { [key: string]: Node };
|
||||
// noinspection TypeScriptFieldCanBeMadeReadonly: False positive
|
||||
private _parent: Directory;
|
||||
|
||||
|
||||
constructor(nodes: object = {}) {
|
||||
constructor(nodes: { [key: string]: Node } = {}) {
|
||||
super();
|
||||
|
||||
this._parent = this;
|
||||
this._nodes = nodes;
|
||||
|
||||
Object.keys(this._nodes).forEach(name => this._nodes[name]._parent = this);
|
||||
Object.values(this._nodes)
|
||||
.forEach(node => {
|
||||
if (node instanceof Directory) node._parent = this;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
get nodes(): object {
|
||||
get nodes(): { [key: string]: Node } {
|
||||
return Object.assign({}, this._nodes);
|
||||
}
|
||||
|
||||
|
@ -488,6 +504,9 @@ export class Directory extends Node {
|
|||
removeNode(nodeOrName: Node | string) {
|
||||
if (nodeOrName instanceof Node) {
|
||||
const name = Object.keys(this._nodes).find(key => this._nodes[key] === nodeOrName);
|
||||
if (name === undefined)
|
||||
throw `Could not remove node '${nodeOrName}'.`;
|
||||
|
||||
delete this._nodes[name];
|
||||
} else {
|
||||
delete this._nodes[name];
|
||||
|
@ -496,9 +515,7 @@ export class Directory extends Node {
|
|||
|
||||
|
||||
copy(): Directory {
|
||||
const copy = new Directory(this.nodes);
|
||||
copy._parent = undefined;
|
||||
return copy;
|
||||
return new Directory(this.nodes);
|
||||
}
|
||||
|
||||
nameString(name: string): string {
|
||||
|
|
12
js/shared.ts
12
js/shared.ts
|
@ -26,10 +26,16 @@ export function moveCaretToEndOf(element: Node) {
|
|||
range.collapse(false);
|
||||
|
||||
const selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
if (selection !== null) {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
export function q(query: string): HTMLElement {
|
||||
return document.querySelector(query);
|
||||
const element = document.querySelector(query);
|
||||
if (!(element instanceof HTMLElement))
|
||||
throw "Could not find element `query`.";
|
||||
|
||||
return element;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ export class Terminal {
|
|||
private readonly fileSystem: FileSystem;
|
||||
private readonly commands: Commands;
|
||||
|
||||
private _currentUser: string;
|
||||
private _currentUser: string | undefined;
|
||||
private isLoggedIn: boolean;
|
||||
|
||||
|
||||
|
@ -64,7 +64,7 @@ export class Terminal {
|
|||
this.prefixDiv.innerHTML = prefixText;
|
||||
}
|
||||
|
||||
get currentUser(): string {
|
||||
get currentUser(): string | undefined {
|
||||
return this._currentUser;
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ export class Terminal {
|
|||
this.outputText += `${this.prefixText}${input}\n`;
|
||||
this.inputHistory.addEntry(input);
|
||||
|
||||
const output = this.commands.parse(input.trim());
|
||||
const output = this.commands.execute(input.trim());
|
||||
if (output !== "")
|
||||
this.outputText += output + `\n`;
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ export class Terminal {
|
|||
this.input.focus();
|
||||
}
|
||||
|
||||
private onkeypress(event) {
|
||||
private onkeypress(event: KeyboardEvent) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
case "enter":
|
||||
this.processInput(this.inputText.replaceAll(/ /, " "));
|
||||
|
@ -171,7 +171,7 @@ export class Terminal {
|
|||
}
|
||||
}
|
||||
|
||||
private onkeydown(event) {
|
||||
private onkeydown(event: KeyboardEvent) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
case "arrowup":
|
||||
this.inputText = this.inputHistory.previousEntry();
|
||||
|
@ -200,7 +200,8 @@ class InputHistory {
|
|||
|
||||
|
||||
constructor() {
|
||||
this.clear();
|
||||
this.history = [];
|
||||
this.index = -1;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"module": "esnext",
|
||||
"target": "es2019",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"outDir": "./build/js"
|
||||
},
|
||||
"files": [
|
||||
|
|
Loading…
Reference in New Issue