diff --git a/Gruntfile.js b/Gruntfile.js
index 255b8e7..c93c3b4 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -17,6 +17,18 @@ module.exports = grunt => {
files: [{expand: true, cwd: "src/main/", src: "**/*.css", dest: "build/"}]
}
},
+ replace: {
+ default: {
+ src: "./build/bundle.js",
+ replacements: [
+ {
+ from: "%%VERSION_NUMBER%%",
+ to: "<%= pkg.version %>"
+ }
+ ],
+ overwrite: true
+ }
+ },
webpack: {
options: {
entry: "./src/main/js/Main.ts",
@@ -49,6 +61,7 @@ module.exports = grunt => {
grunt.loadNpmTasks("grunt-contrib-clean");
grunt.loadNpmTasks("grunt-contrib-copy");
+ grunt.loadNpmTasks("grunt-text-replace");
grunt.loadNpmTasks("grunt-webpack");
grunt.registerTask("dev", [
@@ -59,7 +72,9 @@ module.exports = grunt => {
"copy:html",
"copy:css",
// Compile
- "webpack:dev"
+ "webpack:dev",
+ // Post
+ "replace"
]);
grunt.registerTask("deploy", [
// Pre
@@ -69,7 +84,9 @@ module.exports = grunt => {
"copy:html",
"copy:css",
// Compile JS
- "webpack:deploy"
+ "webpack:deploy",
+ // Post
+ "replace"
]);
grunt.registerTask("default", ["dev"]);
diff --git a/package-lock.json b/package-lock.json
index 1a799ac..e0b1d1f 100644
Binary files a/package-lock.json and b/package-lock.json differ
diff --git a/package.json b/package.json
index de01d3a..4e6ed1d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "fwdekker.com",
- "version": "1.0.0",
+ "version": "1.6.4",
"description": "The source code of [my personal website](https://fwdekker.com/).",
"author": "Felix W. Dekker",
"repository": {
@@ -26,6 +26,7 @@
"grunt-cli": "^1.3.2",
"grunt-contrib-clean": "^2.0.0",
"grunt-contrib-copy": "^1.0.0",
+ "grunt-text-replace": "^0.4.0",
"grunt-webpack": "^3.1.3",
"mocha": "^6.2.2",
"ts-loader": "^6.2.1",
diff --git a/src/main/js/Commands.ts b/src/main/js/Commands.ts
index 6f0fe34..60c60e6 100644
--- a/src/main/js/Commands.ts
+++ b/src/main/js/Commands.ts
@@ -1,7 +1,7 @@
-import * as Cookies from "js-cookie";
import "./Extensions"
import {Environment} from "./Environment";
import {Directory, File, FileSystem, Path} from "./FileSystem"
+import {Persistence} from "./Persistence";
import {IllegalArgumentError, IllegalStateError} from "./Shared";
import {InputArgs} from "./Shell";
import {EscapeCharacters} from "./Terminal";
@@ -229,8 +229,7 @@ export class Commands {
*/
execute(input: InputArgs): string {
if (input.command === "factory-reset") {
- Cookies.remove("files");
- Cookies.remove("env");
+ Persistence.reset();
location.reload();
throw new Error("Goodbye");
}
@@ -369,7 +368,7 @@ export class Commands {
List of commands
${commandEntries.join("\n")}
- Write "help [COMMAND]" or click a command in the list above for more information on a command.\\\
+ Write "help [COMMAND]" or click a command in the list above for more information.\\\
`.trimMultiLines();
}
}
@@ -476,12 +475,9 @@ export class Commands {
if (userName === "")
throw new IllegalStateError("Cannot execute `poweroff` while not logged in.");
- Cookies.set("poweroff", "true", {
- "expires": new Date(new Date().setSeconds(new Date().getSeconds() + 30)),
- "path": "/"
- });
-
+ Persistence.setPoweroff(true);
setTimeout(() => location.reload(), 2000);
+
return `Shutdown NOW!
*** FINAL System shutdown message from ${userName}@fwdekker.com ***
diff --git a/src/main/js/Main.ts b/src/main/js/Main.ts
index 8910a16..95d4944 100644
--- a/src/main/js/Main.ts
+++ b/src/main/js/Main.ts
@@ -29,5 +29,5 @@ addOnLoad(() => {
// @ts-ignore: Ugly hack to execute it anyway
if (window.terminal.shell.environment.get("user") !== "")
- window.terminal.processInput("ls");
+ window.execute("ls");
});
diff --git a/src/main/js/Persistence.ts b/src/main/js/Persistence.ts
new file mode 100644
index 0000000..bfb3e93
--- /dev/null
+++ b/src/main/js/Persistence.ts
@@ -0,0 +1,115 @@
+import * as Cookies from "js-cookie";
+import {Environment} from "./Environment";
+import {Directory, FileSystem, Node, Path} from "./FileSystem";
+import {UserList} from "./UserList";
+
+
+/**
+ * Manages persistence of state.
+ */
+export class Persistence {
+ /**
+ * Deserializes a file system from persistent storage, or returns the default file system if the deserialization
+ * failed.
+ */
+ static getFileSystem(): FileSystem {
+ const filesString = Cookies.get("files");
+
+ let files: Directory | undefined = undefined;
+ if (filesString !== undefined) {
+ try {
+ const parsedFiles = Node.deserialize(filesString);
+ if (parsedFiles instanceof Directory)
+ files = parsedFiles;
+ else
+ console.warn("`files` cookie contains non-directory.");
+ } catch (error) {
+ console.warn("Failed to deserialize `files` cookie.", error);
+ }
+ }
+
+ return new FileSystem(files);
+ }
+
+ /**
+ * Persists the given file system.
+ *
+ * @param fileSystem the file system to persist
+ */
+ static setFileSystem(fileSystem: FileSystem) {
+ Cookies.set("files", fileSystem.root, {
+ "expires": new Date(new Date().setFullYear(new Date().getFullYear() + 25)),
+ "path": "/"
+ });
+ }
+
+ /**
+ * Deserializes an environment from persistent storage, or returns the default environment if the deserialization
+ * failed.
+ *
+ * @param fileSystem the file system used to validate the `cwd` environment variable
+ * @param userList the list of users used to validate the `user` environment variable
+ */
+ static getEnvironment(fileSystem: FileSystem, userList: UserList): Environment {
+ const environmentString = Cookies.get("env") ?? "{}";
+
+ let environment: Environment;
+ try {
+ environment = new Environment(["cwd", "home", "user"], JSON.parse(environmentString));
+ } catch (error) {
+ console.warn("Failed to set environment from cookie.");
+ environment = new Environment(["cwd", "home", "user"]);
+ }
+
+ // Check user in environment
+ if (!environment.has("user")) {
+ environment.set("user", "felix");
+ } else if (environment.get("user") !== "" && !userList.has(environment.get("user"))) {
+ console.warn(`Invalid user '${environment.get("user")}' in environment.`);
+ environment.set("user", "felix");
+ }
+
+ // Set home directory
+ environment.set("home", userList.get(environment.get("user"))?.home ?? "/");
+
+ // Check cwd in environment
+ if (!environment.has("cwd")) {
+ environment.set("cwd", environment.get("home"));
+ } else if (!fileSystem.has(new Path(environment.get("cwd")))) {
+ console.warn(`Invalid cwd '${environment.get("cwd")}' in environment.`);
+ environment.set("cwd", environment.get("home"));
+ }
+
+ return environment;
+ }
+
+ /**
+ * Persists the given environment.
+ *
+ * @param environment the environment to persist
+ */
+ static setEnvironment(environment: Environment) {
+ Cookies.set("env", environment.variables, {"path": "/"});
+ }
+
+ /**
+ * Persists the "power off" setting.
+ *
+ * @param value the value to persist for the "power off" setting
+ */
+ static setPoweroff(value: boolean) {
+ Cookies.set("poweroff", `${value}`, {
+ "expires": new Date(new Date().setSeconds(new Date().getSeconds() + 30)),
+ "path": "/"
+ });
+ }
+
+ /**
+ * Removes all persistent storage.
+ */
+ static reset() {
+ Cookies.remove("files");
+ Cookies.remove("env");
+ Cookies.remove("poweroff");
+ }
+}
diff --git a/src/main/js/Shell.ts b/src/main/js/Shell.ts
index 399f1c5..0b50d33 100644
--- a/src/main/js/Shell.ts
+++ b/src/main/js/Shell.ts
@@ -1,8 +1,8 @@
-import * as Cookies from "js-cookie";
import {Commands} from "./Commands";
import {Environment} from "./Environment";
-import {Directory, File, FileSystem, Node, Path} from "./FileSystem";
+import {Directory, File, FileSystem, Path} from "./FileSystem";
import {InputParser} from "./InputParser";
+import {Persistence} from "./Persistence";
import {asciiHeaderHtml, IllegalStateError, stripHtmlTags} from "./Shared";
import {EscapeCharacters, InputHistory} from "./Terminal";
import {UserList} from "./UserList";
@@ -48,8 +48,8 @@ export class Shell {
constructor(inputHistory: InputHistory) {
this.inputHistory = inputHistory;
this.userList = new UserList();
- this.fileSystem = Shell.loadFileSystem();
- this.environment = Shell.loadEnvironment(this.fileSystem, this.userList);
+ this.fileSystem = Persistence.getFileSystem();
+ this.environment = Persistence.getEnvironment(this.fileSystem, this.userList);
this.commands = new Commands(this.environment, this.userList, this.fileSystem);
this.saveState();
@@ -71,6 +71,7 @@ export class Shell {
Type "help" for help.
+ Welcome to josh v%%VERSION_NUMBER%%, the javascript online shell.
`.trimLines();
}
@@ -205,74 +206,13 @@ export class Shell {
/**
- * Saves the shell's state in cookies.
+ * Persists the shell's state.
+ *
+ * @see Persistence
*/
private saveState() {
- Cookies.set("files", this.fileSystem.root, {
- "expires": new Date(new Date().setFullYear(new Date().getFullYear() + 25)),
- "path": "/"
- });
- Cookies.set("env", this.environment.variables, {"path": "/"});
- }
-
- /**
- * Returns the file system loaded from a cookie, or the default file system if no cookie is present or the cookie
- * is invalid.
- */
- private static loadFileSystem(): FileSystem {
- let files: Directory | undefined = undefined;
- const filesString = Cookies.get("files");
- if (filesString !== undefined) {
- try {
- const parsedFiles = Node.deserialize(filesString);
- if (parsedFiles instanceof Directory)
- files = parsedFiles;
- else
- console.warn("`files` cookie contains non-directory.");
- } catch (error) {
- console.warn("Failed to deserialize `files` cookie.", error);
- }
- }
- return new FileSystem(files);
- }
-
- /**
- * Returns the environment loaded from a cookie, or the default environment if no cookie is present or the cookie
- * is invalid.
- *
- * @param fileSystem the file system used to validate the `cwd` environment variable
- * @param userList the list of users used to validate the `user` environment variable
- */
- private static loadEnvironment(fileSystem: FileSystem, userList: UserList): Environment {
- const environmentString = Cookies.get("env") ?? "{}";
- let environment: Environment;
- try {
- environment = new Environment(["cwd", "home", "user"], JSON.parse(environmentString));
- } catch (error) {
- console.warn("Failed to set environment from cookie.");
- environment = new Environment(["cwd", "home", "user"]);
- }
-
- // Check user in environment
- if (!environment.has("user")) {
- environment.set("user", "felix");
- } else if (environment.get("user") !== "" && !userList.has(environment.get("user"))) {
- console.warn(`Invalid user '${environment.get("user")}' in environment.`);
- environment.set("user", "felix");
- }
-
- // Set home directory
- environment.set("home", userList.get(environment.get("user"))?.home ?? "/");
-
- // Check cwd in environment
- if (!environment.has("cwd")) {
- environment.set("cwd", environment.get("home"));
- } else if (!fileSystem.has(new Path(environment.get("cwd")))) {
- console.warn(`Invalid cwd '${environment.get("cwd")}' in environment.`);
- environment.set("cwd", environment.get("home"));
- }
-
- return environment;
+ Persistence.setFileSystem(this.fileSystem);
+ Persistence.setEnvironment(this.environment);
}
}