forked from tools/josh
1
0
Fork 0

Update to TypeScript 3.7

And clear empty lines and consistify error usage.
This commit is contained in:
Florine W. Dekker 2019-11-06 14:47:14 +01:00
parent f5126065bf
commit 67898842ba
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
8 changed files with 81 additions and 98 deletions

BIN
package-lock.json generated

Binary file not shown.

View File

@ -30,7 +30,7 @@
"mocha": "^6.2.2",
"ts-loader": "^6.2.1",
"ts-node": "^8.4.1",
"typescript": "^3.6.4",
"typescript": "^3.7.2",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10"
}

View File

@ -2,7 +2,7 @@ import * as Cookies from "js-cookie";
import "./Extensions"
import {Environment} from "./Environment";
import {Directory, File, FileSystem, Path} from "./FileSystem"
import {IllegalStateError} from "./Shared";
import {IllegalArgumentError, IllegalStateError} from "./Shared";
import {InputArgs} from "./Shell";
import {EscapeCharacters} from "./Terminal";
import {UserList} from "./UserList";
@ -72,10 +72,10 @@ export class Commands {
`In its first form, the file or directory at SOURCE is copied to DESTINATION.
If DESTINATION is an existing directory, SOURCE is copied into that directory, retaining the file name from SOURCE.
If DESTINATION does not exist, SOURCE is copied to the exact location of DESTINATION.
In its second form, all files and directories at SOURCES are copied to DESTINATION.
DESTINATION must be a pre-existing directory, and all SOURCES are copied into DESTINATION retaining the file names from SOURCES.
In both forms, sources are not copied if they are directories unless the -R options is given.`.trimLines(),
new InputValidator({minArgs: 2})
),
@ -124,7 +124,7 @@ export class Commands {
`make directories`,
`mkdir [-p] DIRECTORY ...`,
`Creates the directories given by DIRECTORY.
If more than one directory is given, the directories are created in the order they are given in.
If the -p option is given, parent directories that do not exist are created as well.`.trimLines(),
new InputValidator({minArgs: 1})
@ -137,7 +137,7 @@ export class Commands {
`In its first form, the file or directory at SOURCE is moved to DESTINATION.
If DESTINATION is an existing directory, SOURCE is moved into that directory, retaining the file name from SOURCE.
If DESTINATION does not exist, SOURCE is moved to the exact location of DESTINATION.
In its second form, all files and directories at SOURCES are moved to DESTINATION.
DESTINATION must be a pre-existing directory, and all SOURCES are moved into DESTINATION retaining the file names from SOURCES.`.trimLines(),
new InputValidator({minArgs: 2})
@ -147,7 +147,7 @@ export class Commands {
`open web page`,
`open [-b | --blank] FILE`,
`Opens the web page linked to by FILE in this browser window.
If -b or --blank is set, the web page is opened in a new tab.`.trimLines(),
new InputValidator({minArgs: 1, maxArgs: 1})
),
@ -170,13 +170,13 @@ export class Commands {
`remove file`,
`rm [-f | --force] [-r | -R | --recursive] [--no-preserve-root] FILE...`,
`Removes the files given by FILE.
If more than one file is given, the files are removed in the order they are given in.
If -f or --force is set, no warning is given if a file could not be removed.
If -r, -R, or --recursive is set, files and directories are removed recursively.
Unless --no-preserve-root is set, the root directory cannot be removed.`.trimLines(),
new InputValidator({minArgs: 1})
),
@ -185,7 +185,7 @@ export class Commands {
`remove directories`,
`rmdir DIRECTORY...`,
`Removes the directories given by DIRECTORY.
If more than one directory is given, the directories are removed in the order they are given in.`.trimLines(),
new InputValidator({minArgs: 1})
),
@ -202,7 +202,7 @@ export class Commands {
`change file timestamps`,
`touch FILE...`,
`Update the access and modification times of each FILE to the current time.
If a file does not exist, it is created.`.trimLines(),
new InputValidator({minArgs: 1})
),
@ -252,13 +252,12 @@ export class Commands {
private createUsageErrorOutput(commandName: string, errorMessage: string | undefined): string {
const command = this.commands[commandName];
if (command === undefined)
throw new Error(`Unknown command \`${commandName}\`.`);
throw new IllegalArgumentError(`Unknown command \`${commandName}\`.`);
return "" +
`Invalid usage of ${commandName}.${errorMessage === undefined ? "" : ` ${errorMessage}`}
<b>Usage</b>
${command.usage}`.trimLines();
return `Invalid usage of ${commandName}. ${errorMessage ?? ""}
<b>Usage</b>
${command.usage}`.trimLines();
}
@ -337,18 +336,17 @@ export class Commands {
const commandName = it.toLowerCase();
const command = this.commands[commandName];
return "" +
`<b>Name</b>
${commandName}
<b>Summary</b>
${command.summary}
return `<b>Name</b>
${commandName}
<b>Usage</b>
${command.usage}
<b>Summary</b>
${command.summary}
<b>Description</b>
${command.desc}`.trimLines();
<b>Usage</b>
${command.usage}
<b>Description</b>
${command.desc}`.trimLines();
})
.join("\n\n\n");
} else {
@ -360,13 +358,12 @@ export class Commands {
const commandEntries = commandNames
.map((it, i) => `${commandLinks[i]}${this.commands[it].summary}`);
return "" +
`The source code of this website is <a href="https://git.fwdekker.com/FWDekker/fwdekker.com">available on git</a>.
return `The source code of this website is <a href="https://git.fwdekker.com/FWDekker/fwdekker.com">available on git</a>.
<b>List of commands</b>
${commandEntries.join("\n")}
<b>List of commands</b>
${commandEntries.join("\n")}
Write "help [COMMAND]" or click a command in the list above for more information on a command.`.trimLines();
Write "help [COMMAND]" or click a command in the list above for more information on a command.`.trimLines();
}
}
@ -478,15 +475,14 @@ export class Commands {
});
setTimeout(() => location.reload(), 2000);
return "" +
`Shutdown NOW!
*** FINAL System shutdown message from ${userName}@fwdekker.com ***
System going down IMMEDIATELY
System shutdown time has arrived`.trimLines();
return `Shutdown NOW!
*** FINAL System shutdown message from ${userName}@fwdekker.com ***
System going down IMMEDIATELY
System shutdown time has arrived`.trimLines();
}
private pwd(): string {
@ -578,19 +574,19 @@ export class Commands {
// Move into directory
if (!(this.fileSystem.get(destination) instanceof Directory)) {
if (sources.length === 1)
throw new Error(`'${destination}' already exists.`);
throw new IllegalArgumentError(`'${destination}' already exists.`);
else
throw new Error(`'${destination}' is not a directory.`);
throw new IllegalArgumentError(`'${destination}' is not a directory.`);
}
mappings = sources.map(source => [source, destination.getChild(source.fileName)]);
} else {
// Move to exact location
if (sources.length !== 1)
throw new Error(`'${destination}' is not a directory.`);
throw new IllegalArgumentError(`'${destination}' is not a directory.`);
if (!(this.fileSystem.get(destination.parent) instanceof Directory))
throw new Error(`'${destination.parent}' is not a directory.`);
throw new IllegalArgumentError(`'${destination.parent}' is not a directory.`);
mappings = sources.map(path => [path, destination]);
}

View File

@ -94,10 +94,7 @@ export class Environment {
* @param def the default value to return in case there is no environment variable with the given key
*/
getOrDefault(key: string, def: string): string {
const value = this._variables[key];
if (value === undefined)
return def;
return value;
return this._variables[key] ?? def;
}
/**

View File

@ -7,7 +7,7 @@ interface String {
/**
* Returns this string with all leading and trailing whitespace removed from each line.
*/
String.prototype.trimLines = function (): string {
String.prototype.trimLines = function(): string {
return this.split("\n").map(it => it.trim()).join("\n");
};
@ -17,7 +17,7 @@ String.prototype.trimLines = function (): string {
* @param regex the regex to find matches with
* @param replacement the string to replace matches with
*/
String.prototype.replaceAll = function (regex: RegExp, replacement: string): string {
String.prototype.replaceAll = function(regex: RegExp, replacement: string): string {
let string = this.toString();
while (regex.test(string))
@ -37,8 +37,8 @@ interface Array<T> {
* @param transform transforms elements of the array into a string that is used for comparing
* @param caseSensitive `true` if and only if the comparator should be sensitive to the case of characters
*/
Array.prototype.sortAlphabetically = function (transform: (_: any) => string = (it) => it,
caseSensitive: boolean = true) {
Array.prototype.sortAlphabetically = function(transform: (_: any) => string = (it) => it,
caseSensitive: boolean = true) {
return this.sort((a, b) => {
const aName = caseSensitive ? transform(a) : transform(a).toLowerCase();
const bName = caseSensitive ? transform(b) : transform(b).toLowerCase();

View File

@ -56,14 +56,14 @@ export class FileSystem {
if (createParents)
this.add(target.parent, new Directory(), true);
else
throw new Error(`The directory '${target.parent}' does not exist.`);
throw new IllegalArgumentError(`The directory '${target.parent}' does not exist.`);
}
const parent = this.get(target.parent);
if (!(parent instanceof Directory))
throw new Error(`'${target.parent}' is not a directory.`);
throw new IllegalArgumentError(`'${target.parent}' is not a directory.`);
if (parent.hasNode(target.fileName))
throw new Error(`A file or directory already exists at '${target}'.`);
throw new IllegalArgumentError(`A file or directory already exists at '${target}'.`);
parent.addNode(target.fileName, node);
}
@ -79,13 +79,13 @@ export class FileSystem {
*/
copy(source: Path, destination: Path, isRecursive: boolean): void {
if (destination.ancestors.indexOf(source) >= 0)
throw new Error("Cannot move directory into itself.");
throw new IllegalArgumentError("Cannot move directory into itself.");
const sourceNode = this.get(source);
if (sourceNode === undefined)
throw new Error(`File or directory '${source}' does not exist.`);
throw new IllegalArgumentError(`File or directory '${source}' does not exist.`);
if (sourceNode instanceof Directory && !isRecursive)
throw new Error(`'${source}' is a directory.`);
throw new IllegalArgumentError(`'${source}' is a directory.`);
this.add(destination, sourceNode.copy(), false);
}
@ -116,7 +116,7 @@ export class FileSystem {
return true;
const parent = this.get(target.parent);
if (parent === undefined || !(parent instanceof Directory))
if (!(parent instanceof Directory))
return false;
return parent.hasNode(target.fileName);
@ -132,11 +132,11 @@ export class FileSystem {
*/
move(source: Path, destination: Path): void {
if (destination.ancestors.indexOf(source) >= 0)
throw new Error("Cannot move directory into itself.");
throw new IllegalArgumentError("Cannot move directory into itself.");
const sourceNode = this.get(source);
if (sourceNode === undefined)
throw new Error(`File or directory '${source}' does not exist.`);
throw new IllegalArgumentError(`File or directory '${source}' does not exist.`);
this.add(destination, sourceNode, false);
this.remove(source, true, true, false);
@ -157,7 +157,7 @@ export class FileSystem {
if (force)
return;
else
throw new Error(`The file or directory '${targetPath}' does not exist.`);
throw new IllegalArgumentError(`The file or directory '${targetPath}' does not exist.`);
}
const parent = this.get(targetPath.parent);
@ -166,9 +166,9 @@ export class FileSystem {
if (target instanceof Directory) {
if (targetPath.toString() === "/" && !noPreserveRoot)
throw new Error(`Cannot remove root directory.`);
throw new IllegalArgumentError(`Cannot remove root directory.`);
if (!recursive)
throw new Error(`'${targetPath}' is a directory.`);
throw new IllegalArgumentError(`'${targetPath}' is a directory.`);
}
parent.removeNode(targetPath.fileName);
@ -393,7 +393,7 @@ export class Directory extends Node {
*/
getNode(name: string): Node {
if (!this.hasNode(name))
throw new Error(`Directory does not have a node with name '${name}'.`);
throw new IllegalArgumentError(`Directory does not have a node with name '${name}'.`);
return this._nodes[name];
}

View File

@ -78,7 +78,7 @@ export function parseCssPixels(string: string | null): number {
return 0;
} else {
if (!string.endsWith("px"))
throw new Error("CSS string is not expressed in pixels.");
throw new IllegalArgumentError("CSS string is not expressed in pixels.");
return parseFloat(string);
}

View File

@ -2,7 +2,7 @@ import * as Cookies from "js-cookie";
import {Commands} from "./Commands";
import {Environment} from "./Environment";
import {Directory, File, FileSystem, Node, Path} from "./FileSystem";
import {asciiHeaderHtml, IllegalStateError, stripHtmlTags} from "./Shared";
import {asciiHeaderHtml, IllegalArgumentError, IllegalStateError, stripHtmlTags} from "./Shared";
import {EscapeCharacters, InputHistory} from "./Terminal";
import {UserList} from "./UserList";
@ -62,16 +62,15 @@ export class Shell {
if (this.environment.get("user") === "")
return "";
return "" +
`${asciiHeaderHtml}
return `${asciiHeaderHtml}
Student MSc Computer Science <span class="smallScreenOnly">
</span>@ <a href="https://www.tudelft.nl/en/">TU Delft</a>, the Netherlands
<span class="wideScreenOnly">${(new Date()).toISOString()}
</span>
Type "<a href="#" onclick="execute('help');">help</a>" for help.
Student MSc Computer Science <span class="smallScreenOnly">
</span>@ <a href="https://www.tudelft.nl/en/">TU Delft</a>, the Netherlands
<span class="wideScreenOnly">${(new Date()).toISOString()}
</span>
Type "<a href="#" onclick="execute('help');">help</a>" for help.
`.trimLines();
`.trimLines();
}
/**
@ -80,24 +79,15 @@ export class Shell {
generatePrefix(): string {
const userName = this.environment.get("user");
if (userName === "") {
if (this.attemptUser === undefined)
return "login as: ";
else
return `Password for ${this.attemptUser}@fwdekker.com: `;
return this.attemptUser === undefined
? "login as: "
: `Password for ${this.attemptUser}@fwdekker.com: `;
}
const cwd = new Path(this.environment.get("cwd"));
const parts = cwd.ancestors.reverse();
parts.push(cwd);
const link = parts
.map(part => {
const node = this.fileSystem.get(part);
if (node === undefined)
throw new IllegalStateError(`Ancestor '${part}' of cwd does not exist.`);
return node.nameString(part.fileName + "/", part);
})
.join("");
const link = cwd.ancestors.reverse()
.concat(cwd)
.map(part => this.fileSystem.get(part)?.nameString(part.fileName + "/", part)).join("");
return `${userName}@fwdekker.com <span class="prefixPath">${link}</span>&gt; `;
}
@ -178,7 +168,7 @@ export class Shell {
const target = this.fileSystem.get(path);
if (!(target instanceof File))
throw new Error("File unexpectedly disappeared since last check.");
throw new IllegalStateError("File unexpectedly disappeared since last check.");
if (append)
target.contents += data;
@ -427,7 +417,7 @@ export class InputParser {
switch (char) {
case "\\":
if (i === input.length - 1)
throw new Error("Unexpected end of input. `\\` was used but there was nothing to escape.");
throw new IllegalArgumentError("Unexpected end of input. `\\` was used but there was nothing to escape.");
const nextChar = input[i + 1];
if (isInSingleQuotes || isInDoubleQuotes)
@ -485,7 +475,7 @@ export class InputParser {
}
if (isInSingleQuotes || isInDoubleQuotes)
throw new Error("Unexpected end of input. Missing closing quotation mark.");
throw new IllegalArgumentError("Unexpected end of input. Missing closing quotation mark.");
return [token, ""];
}
@ -544,7 +534,7 @@ export class InputParser {
const argsParts = arg.split(/=(.*)/, 2);
if (argsParts.length === 0 || argsParts.length > 2)
throw new Error("Unexpected number of parts.");
throw new IllegalArgumentError("Unexpected number of parts.");
if (argsParts[0].indexOf(' ') >= 0)
break;
@ -565,7 +555,7 @@ export class InputParser {
options[keys] = value;
} else {
if (value !== null)
throw new Error("Cannot assign value to multiple short options.");
throw new IllegalArgumentError("Cannot assign value to multiple short options.");
for (const key of keys)
options[key] = value;