@learnpack/learnpack 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +51 -398
  2. package/bin/run +14 -2
  3. package/oclif.manifest.json +1 -1
  4. package/package.json +135 -111
  5. package/src/commands/audit.ts +462 -0
  6. package/src/commands/clean.ts +29 -0
  7. package/src/commands/download.ts +62 -0
  8. package/src/commands/init.ts +169 -0
  9. package/src/commands/login.ts +42 -0
  10. package/src/commands/logout.ts +43 -0
  11. package/src/commands/publish.ts +107 -0
  12. package/src/commands/start.ts +229 -0
  13. package/src/commands/{test.js → test.ts} +19 -21
  14. package/src/index.ts +1 -0
  15. package/src/managers/config/allowed_files.ts +29 -0
  16. package/src/managers/config/defaults.ts +33 -0
  17. package/src/managers/config/exercise.ts +295 -0
  18. package/src/managers/config/index.ts +411 -0
  19. package/src/managers/file.ts +169 -0
  20. package/src/managers/gitpod.ts +84 -0
  21. package/src/managers/server/{index.js → index.ts} +26 -19
  22. package/src/managers/server/routes.ts +250 -0
  23. package/src/managers/session.ts +118 -0
  24. package/src/managers/socket.ts +239 -0
  25. package/src/managers/test.ts +83 -0
  26. package/src/models/action.ts +3 -0
  27. package/src/models/audit-errors.ts +4 -0
  28. package/src/models/config-manager.ts +23 -0
  29. package/src/models/config.ts +74 -0
  30. package/src/models/counter.ts +11 -0
  31. package/src/models/errors.ts +22 -0
  32. package/src/models/exercise-obj.ts +26 -0
  33. package/src/models/file.ts +5 -0
  34. package/src/models/findings.ts +18 -0
  35. package/src/models/flags.ts +10 -0
  36. package/src/models/front-matter.ts +11 -0
  37. package/src/models/gitpod-data.ts +19 -0
  38. package/src/models/language.ts +4 -0
  39. package/src/models/package.ts +7 -0
  40. package/src/models/plugin-config.ts +17 -0
  41. package/src/models/session.ts +26 -0
  42. package/src/models/socket.ts +48 -0
  43. package/src/models/status.ts +15 -0
  44. package/src/models/success-types.ts +1 -0
  45. package/src/plugin/command/compile.ts +17 -0
  46. package/src/plugin/command/test.ts +30 -0
  47. package/src/plugin/index.ts +6 -0
  48. package/src/plugin/plugin.ts +94 -0
  49. package/src/plugin/utils.ts +87 -0
  50. package/src/types/node-fetch.d.ts +1 -0
  51. package/src/ui/download.ts +71 -0
  52. package/src/utils/BaseCommand.ts +48 -0
  53. package/src/utils/SessionCommand.ts +48 -0
  54. package/src/utils/api.ts +194 -0
  55. package/src/utils/audit.ts +162 -0
  56. package/src/utils/console.ts +24 -0
  57. package/src/utils/errors.ts +117 -0
  58. package/src/utils/{exercisesQueue.js → exercisesQueue.ts} +12 -6
  59. package/src/utils/fileQueue.ts +198 -0
  60. package/src/utils/misc.ts +23 -0
  61. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.es.md +2 -4
  62. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.md +1 -2
  63. package/src/utils/templates/isolated/01-hello-world/README.es.md +1 -2
  64. package/src/utils/templates/isolated/01-hello-world/README.md +1 -2
  65. package/src/utils/templates/isolated/README.ejs +1 -1
  66. package/src/utils/templates/isolated/README.es.ejs +1 -1
  67. package/src/utils/validators.ts +18 -0
  68. package/src/utils/watcher.ts +27 -0
  69. package/plugin/command/compile.js +0 -17
  70. package/plugin/command/test.js +0 -29
  71. package/plugin/index.js +0 -6
  72. package/plugin/plugin.js +0 -71
  73. package/plugin/utils.js +0 -78
  74. package/src/commands/audit.js +0 -243
  75. package/src/commands/clean.js +0 -27
  76. package/src/commands/download.js +0 -52
  77. package/src/commands/hello.js +0 -20
  78. package/src/commands/init.js +0 -133
  79. package/src/commands/login.js +0 -45
  80. package/src/commands/logout.js +0 -39
  81. package/src/commands/publish.js +0 -78
  82. package/src/commands/start.js +0 -169
  83. package/src/index.js +0 -1
  84. package/src/managers/config/allowed_files.js +0 -12
  85. package/src/managers/config/defaults.js +0 -32
  86. package/src/managers/config/exercise.js +0 -212
  87. package/src/managers/config/index.js +0 -342
  88. package/src/managers/file.js +0 -137
  89. package/src/managers/server/routes.js +0 -151
  90. package/src/managers/session.js +0 -83
  91. package/src/managers/socket.js +0 -185
  92. package/src/managers/test.js +0 -77
  93. package/src/ui/download.js +0 -48
  94. package/src/utils/BaseCommand.js +0 -34
  95. package/src/utils/SessionCommand.js +0 -46
  96. package/src/utils/api.js +0 -164
  97. package/src/utils/audit.js +0 -114
  98. package/src/utils/console.js +0 -16
  99. package/src/utils/errors.js +0 -90
  100. package/src/utils/fileQueue.js +0 -194
  101. package/src/utils/misc.js +0 -26
  102. package/src/utils/validators.js +0 -15
  103. package/src/utils/watcher.js +0 -24
@@ -0,0 +1,169 @@
1
+ import { flags } from "@oclif/command";
2
+ import BaseCommand from "../utils/BaseCommand";
3
+
4
+ // eslint-disable-next-line
5
+ import * as fs from "fs-extra";
6
+ import * as prompts from "prompts";
7
+ import cli from "cli-ux";
8
+ import * as eta from "eta";
9
+
10
+ import Console from "../utils/console";
11
+ import { ValidationError } from "../utils/errors";
12
+ import defaults from "../managers/config/defaults";
13
+
14
+ import * as path from "path";
15
+
16
+ class InitComand extends BaseCommand {
17
+ static description =
18
+ "Create a new learning package: Book, Tutorial or Exercise";
19
+
20
+ static flags = {
21
+ ...BaseCommand.flags,
22
+ grading: flags.help({ char: "h" }),
23
+ };
24
+
25
+ async run() {
26
+ const { flags } = this.parse(InitComand);
27
+
28
+ // if the folder/file .learn or .breathecode aleady exists
29
+ await alreadyInitialized();
30
+
31
+ const choices = await prompts([
32
+ {
33
+ type: "select",
34
+ name: "grading",
35
+ message: "Is the auto-grading going to be isolated or incremental?",
36
+ choices: [
37
+ {
38
+ title: "Incremental: Build on top of each other like a tutorial",
39
+ value: "incremental",
40
+ },
41
+ { title: "Isolated: Small separated exercises", value: "isolated" },
42
+ {
43
+ title: "No grading: No feedback or testing whatsoever",
44
+ value: null,
45
+ },
46
+ ],
47
+ },
48
+ {
49
+ type: "text",
50
+ name: "title",
51
+ initial: "My Interactive Tutorial",
52
+ message: "Title for your tutorial? Press enter to leave as it is",
53
+ },
54
+ {
55
+ type: "text",
56
+ name: "description",
57
+ initial: "",
58
+ message: "Description for your tutorial? Press enter to leave blank",
59
+ },
60
+ {
61
+ type: "select",
62
+ name: "difficulty",
63
+ message: "How difficulty will be to complete the tutorial?",
64
+ choices: [
65
+ { title: "Begginer (no previous experience)", value: "beginner" },
66
+ { title: "Easy (just a bit of experience required)", value: "easy" },
67
+ {
68
+ title: "Intermediate (you need experience)",
69
+ value: "intermediate",
70
+ },
71
+ { title: "Hard (master the topic)", value: "hard" },
72
+ ],
73
+ },
74
+ {
75
+ type: "text",
76
+ name: "duration",
77
+ initial: "1",
78
+ message: "How many hours avg it takes to complete (number)?",
79
+ validate: (value: string) => {
80
+ const n = Math.floor(Number(value));
81
+ return (
82
+ n !== Number.POSITIVE_INFINITY && String(n) === value && n >= 0
83
+ );
84
+ },
85
+ },
86
+ ]);
87
+
88
+ const packageInfo = {
89
+ ...defaults.config,
90
+ grading: choices.grading,
91
+ difficulty: choices.difficulty,
92
+ duration: parseInt(choices.duration),
93
+ description: choices.description,
94
+ title: choices.title,
95
+ slug: choices.title.toLowerCase().replace(/ /g, "-").replace(/[^\w-]+/g, ""),
96
+ };
97
+
98
+ cli.action.start("Initializing package");
99
+
100
+ const languages = ["en", "es"];
101
+
102
+ const templatesDir = path.resolve(
103
+ __dirname,
104
+ "../utils/templates/" + choices.grading || "no-grading"
105
+ );
106
+ if (!fs.existsSync(templatesDir))
107
+ throw ValidationError(`Template ${templatesDir} does not exists`);
108
+ await fs.copySync(templatesDir, "./");
109
+
110
+ // Creating README files
111
+ // eslint-disable-next-line
112
+ languages.forEach((language) => {
113
+ const readmeFilename = `README${language !== "en" ? `.${language}` : ""}`;
114
+ fs.writeFileSync(
115
+ `./${readmeFilename}.md`,
116
+ eta.render(
117
+ fs.readFileSync(
118
+ path.resolve(__dirname, `${templatesDir}/${readmeFilename}.ejs`),
119
+ "utf-8"
120
+ ),
121
+ packageInfo
122
+ )
123
+ );
124
+ if (fs.existsSync(`./${readmeFilename}.ejs`))
125
+ fs.removeSync(`./${readmeFilename}.ejs`);
126
+ });
127
+
128
+ if (!fs.existsSync("./.gitignore"))
129
+ fs.copyFile(
130
+ path.resolve(__dirname, "../utils/templates/gitignore.txt"),
131
+ "./.gitignore"
132
+ );
133
+ fs.writeFileSync("./learn.json", JSON.stringify(packageInfo, null, 2));
134
+
135
+ cli.action.stop();
136
+ Console.success(`😋 Package initialized successfully`);
137
+ Console.help(
138
+ `Start the exercises by running the following command on your terminal: $ learnpack start`
139
+ );
140
+ }
141
+ }
142
+
143
+ const alreadyInitialized = () =>
144
+ new Promise((resolve, reject) => {
145
+ fs.readdir("./", function (err: any, files: any) {
146
+ files = files.filter((f: any) =>
147
+ [".learn", "learn.json", "bc.json", ".breathecode"].includes(f)
148
+ );
149
+ if (err) {
150
+ reject(ValidationError(err.message));
151
+ throw ValidationError(err.message);
152
+ } else if (files.length > 0) {
153
+ reject(
154
+ ValidationError(
155
+ "It seems the package is already initialized because we've found the following files: " +
156
+ files.join(",")
157
+ )
158
+ );
159
+ throw ValidationError(
160
+ "It seems the package is already initialized because we've found the following files: " +
161
+ files.join(",")
162
+ );
163
+ }
164
+
165
+ resolve(false);
166
+ });
167
+ });
168
+
169
+ export default InitComand;
@@ -0,0 +1,42 @@
1
+ import SessionCommand from "../utils/SessionCommand";
2
+ import SessionManager from "../managers/session";
3
+ import Console from "../utils/console";
4
+
5
+ class LoginCommand extends SessionCommand {
6
+ static description = `Describe the command here
7
+ ...
8
+ Extra documentation goes here
9
+ `;
10
+
11
+ static flags: any = {
12
+ // name: flags.string({char: 'n', description: 'name to print'}),
13
+ };
14
+
15
+ static args = [
16
+ {
17
+ name: "package", // name of arg to show in help and reference with args[name]
18
+ required: false, // make the arg required with `required: true`
19
+ description:
20
+ "The unique string that identifies this package on learnpack", // help description
21
+ hidden: false, // hide this arg from help
22
+ },
23
+ ];
24
+
25
+ async init() {
26
+ const { flags } = this.parse(LoginCommand);
27
+ await this.initSession(flags);
28
+ }
29
+
30
+ async run() {
31
+ /* const {flags, args} = */ this.parse(LoginCommand);
32
+
33
+ try {
34
+ await SessionManager.login();
35
+ } catch (error) {
36
+ Console.error("Error trying to authenticate");
37
+ Console.error((error as TypeError).message || (error as string));
38
+ }
39
+ }
40
+ }
41
+
42
+ export default LoginCommand;
@@ -0,0 +1,43 @@
1
+ // import {Command, flags} from '@oclif/command'
2
+ // import { prompt } from "enquirer"
3
+ // import fetch from 'node-fetch'
4
+ import SessionCommand from '../utils/SessionCommand'
5
+ import SessionManager from '../managers/session'
6
+ // import Console from '../utils/console'
7
+ // import { replace } from 'node-emoji'
8
+ // import { validURL } from "../utils/validators"
9
+ // const BaseCommand from '../utils/BaseCommand');
10
+
11
+ class LogoutCommand extends SessionCommand {
12
+ static description = `Describe the command here
13
+ ...
14
+ Extra documentation goes here
15
+ `
16
+
17
+ static flags: any = {
18
+ // name: flags.string({char: 'n', description: 'name to print'}),
19
+ }
20
+
21
+ static args = [
22
+ {
23
+ name: 'package', // name of arg to show in help and reference with args[name]
24
+ required: false, // make the arg required with `required: true`
25
+ description:
26
+ 'The unique string that identifies this package on learnpack', // help description
27
+ hidden: false, // hide this arg from help
28
+ },
29
+ ]
30
+
31
+ async init() {
32
+ const {flags} = this.parse(LogoutCommand)
33
+ await this.initSession(flags)
34
+ }
35
+
36
+ async run() {
37
+ // const {flags, args} = this.parse(LogoutCommand)
38
+
39
+ SessionManager.destroy()
40
+ }
41
+ }
42
+
43
+ export default LogoutCommand
@@ -0,0 +1,107 @@
1
+ import { prompt } from "enquirer";
2
+ import SessionCommand from "../utils/SessionCommand";
3
+ import Console from "../utils/console";
4
+ import api from "../utils/api";
5
+ import { validURL } from "../utils/validators";
6
+
7
+ // eslint-disable-next-line
8
+ const fetch = require("node-fetch");
9
+
10
+ class PublishCommand extends SessionCommand {
11
+ static description = `Describe the command here
12
+ ...
13
+ Extra documentation goes here
14
+ `;
15
+
16
+ static flags: any = {
17
+ // name: flags.string({char: 'n', description: 'name to print'}),
18
+ };
19
+
20
+ static args = [
21
+ {
22
+ name: "package", // name of arg to show in help and reference with args[name]
23
+ required: false, // make the arg required with `required: true`
24
+ description:
25
+ "The unique string that identifies this package on learnpack", // help description
26
+ hidden: false, // hide this arg from help
27
+ },
28
+ ];
29
+
30
+ async init() {
31
+ const { flags } = this.parse(PublishCommand);
32
+ await this.initSession(flags, true);
33
+ }
34
+
35
+ async run() {
36
+ const { flags, args } = this.parse(PublishCommand);
37
+
38
+ // avoid annonymus sessions
39
+ // eslint-disable-next-line
40
+ if (!this.session) return;
41
+
42
+ Console.info(
43
+ `Session found for ${this.session.payload.email}, publishing the package...`
44
+ );
45
+
46
+ const configObject = this.configManager?.get();
47
+ if (
48
+ configObject?.config?.slug === undefined ||
49
+ !configObject.config?.slug
50
+ ) {
51
+ throw new Error(
52
+ "The package is missing a slug (unique name identifier), please check your learn.json file and make sure it has a 'slug'"
53
+ );
54
+ }
55
+
56
+ if (!validURL(configObject?.config?.repository ?? "")) {
57
+ throw new Error(
58
+ "The package has a missing or invalid 'repository' on the configuration file, it needs to be a Github URL"
59
+ );
60
+ } else {
61
+ const validateResp = await fetch(configObject.config?.repository, {
62
+ method: "HEAD",
63
+ });
64
+ if (!validateResp.ok || validateResp.status !== 200) {
65
+ throw new Error(
66
+ `The specified repository URL on the configuration file does not exist or its private, only public repositories are allowed at the moment: ${configObject.config?.repository}`
67
+ );
68
+ }
69
+ }
70
+
71
+ // start watching for file changes
72
+ try {
73
+ await api.publish({
74
+ ...configObject,
75
+ author: this.session.payload.user_id,
76
+ });
77
+ Console.success(
78
+ `Package updated and published successfully: ${configObject.config?.slug}`
79
+ );
80
+ } catch (error) {
81
+ if ((error as any).status === 404) {
82
+ const answer = await prompt([
83
+ {
84
+ type: "confirm",
85
+ name: "create",
86
+ message: `Package with slug ${configObject.config?.slug} does not exist, do you want to create it?`,
87
+ },
88
+ ]);
89
+ if (answer) {
90
+ await api.update({
91
+ ...configObject,
92
+ author: this.session.payload.user_id,
93
+ });
94
+ Console.success(
95
+ `Package created and published successfully: ${configObject.config?.slug}`
96
+ );
97
+ } else {
98
+ Console.error("No answer from server");
99
+ }
100
+ } else {
101
+ Console.error((error as TypeError).message);
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ export default PublishCommand;
@@ -0,0 +1,229 @@
1
+ // import path from "path";
2
+ import { flags } from "@oclif/command";
3
+ import SessionCommand from "../utils/SessionCommand";
4
+ import Console from "../utils/console";
5
+ import socket from "../managers/socket";
6
+ import queue from "../utils/fileQueue";
7
+ import { decompress, downloadEditor } from "../managers/file";
8
+ import { prioritizeHTMLFile } from "../utils/misc";
9
+
10
+ import createServer from "../managers/server";
11
+
12
+ import { IGitpodData } from "../models/gitpod-data";
13
+ import { IExercise, IExerciseData } from "../models/exercise-obj";
14
+
15
+ export default class StartCommand extends SessionCommand {
16
+ static description = "Runs a small server with all the exercise instructions";
17
+
18
+ static flags = {
19
+ ...SessionCommand.flags,
20
+ port: flags.string({ char: "p", description: "server port" }),
21
+ host: flags.string({ char: "h", description: "server host" }),
22
+ disableGrading: flags.boolean({
23
+ char: "D",
24
+ description: "disble grading functionality",
25
+ default: false,
26
+ }),
27
+ // disableGrading: flags.boolean({char: 'dg', description: 'disble grading functionality', default: false }),
28
+ watch: flags.boolean({
29
+ char: "w",
30
+ description: "Watch for file changes",
31
+ default: false,
32
+ }),
33
+ editor: flags.string({
34
+ char: "e",
35
+ description: "[standalone, gitpod]",
36
+ options: ["standalone", "gitpod"],
37
+ }),
38
+ version: flags.string({
39
+ char: "v",
40
+ description: "E.g: 1.0.1",
41
+ default: undefined,
42
+ }),
43
+ grading: flags.string({
44
+ char: "g",
45
+ description: "[isolated, incremental]",
46
+ options: ["isolated", "incremental"],
47
+ }),
48
+ debug: flags.boolean({
49
+ char: "d",
50
+ description: "debugger mode for more verbage",
51
+ default: false,
52
+ }),
53
+ };
54
+
55
+ // 🛑 IMPORTANT
56
+ // Every command that will use the configManager needs this init method
57
+ async init() {
58
+ const { flags } = this.parse(StartCommand);
59
+ await this.initSession(flags);
60
+ }
61
+
62
+ async run() {
63
+ // get configuration object
64
+ const configObject = this.configManager?.get();
65
+ const config = configObject?.config;
66
+
67
+ if (configObject) {
68
+ const { config } = configObject;
69
+
70
+ // build exerises
71
+ this.configManager?.buildIndex();
72
+
73
+ Console.debug(
74
+ `Grading: ${config?.grading} ${
75
+ config?.disabledActions?.includes("test") ? "(disabled)" : ""
76
+ }, editor: ${config?.editor.mode} ${config?.editor.version}, for ${
77
+ Array.isArray(configObject?.exercises) ? configObject?.exercises.length : 0
78
+ } exercises found`
79
+ );
80
+
81
+ // download app and decompress
82
+ await downloadEditor(
83
+ config?.editor.version,
84
+ `${config?.dirPath}/app.tar.gz`
85
+ );
86
+
87
+ Console.info("Decompressing LearnPack UI, this may take a minute...");
88
+ await decompress(
89
+ `${config?.dirPath}/app.tar.gz`,
90
+ `${config?.dirPath}/_app/`
91
+ );
92
+
93
+ // listen to socket commands
94
+ if (config && this.configManager) {
95
+ const server = await createServer(configObject, this.configManager);
96
+
97
+ const dispatcher = queue.dispatcher({
98
+ create: true,
99
+ path: `${config.dirPath}/vscode_queue.json`,
100
+ });
101
+
102
+ socket.start(config, server, false);
103
+
104
+ socket.on("open", (data: IGitpodData) => {
105
+ Console.debug("Opening these files: ", data);
106
+ const files = prioritizeHTMLFile(data.files);
107
+ dispatcher.enqueue(dispatcher.events.OPEN_FILES, files);
108
+ socket.ready("Ready to compile...");
109
+ });
110
+
111
+ socket.on("open_window", (data: IGitpodData) => {
112
+ Console.debug("Opening window: ", data);
113
+ dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data);
114
+ socket.ready("Ready to compile...");
115
+ });
116
+
117
+ socket.on("reset", (exercise: IExerciseData) => {
118
+ try {
119
+ this.configManager?.reset(exercise.exerciseSlug);
120
+ dispatcher.enqueue(
121
+ dispatcher.events.RESET_EXERCISE,
122
+ exercise.exerciseSlug
123
+ );
124
+ socket.ready("Ready to compile...");
125
+ } catch (error) {
126
+ socket.error(
127
+ "compiler-error",
128
+ (error as TypeError).message ||
129
+ "There was an error reseting the exercise"
130
+ );
131
+ setTimeout(() => socket.ready("Ready to compile..."), 2000);
132
+ }
133
+ });
134
+ // socket.on("preview", (data) => {
135
+ // Console.debug("Preview triggered, removing the 'preview' action ")
136
+ // socket.removeAllowed("preview")
137
+ // socket.log('ready',['Ready to compile...'])
138
+ // })
139
+
140
+ socket.on("build", async (data: IExerciseData) => {
141
+ const exercise = this.configManager?.getExercise(data.exerciseSlug);
142
+
143
+ if (!exercise?.language) {
144
+ socket.error(
145
+ "compiler-error",
146
+ "Impossible to detect language to build for " +
147
+ data.exerciseSlug +
148
+ "..."
149
+ );
150
+ return;
151
+ }
152
+
153
+ socket.log(
154
+ "compiling",
155
+ "Building exercise " +
156
+ data.exerciseSlug +
157
+ " with " +
158
+ exercise.language +
159
+ "..."
160
+ );
161
+ await this.config.runHook("action", {
162
+ action: "compile",
163
+ socket,
164
+ configuration: config,
165
+ exercise,
166
+ });
167
+ });
168
+
169
+ socket.on("test", async (data: IExerciseData) => {
170
+ const exercise = this.configManager?.getExercise(data.exerciseSlug);
171
+
172
+ if (!exercise?.language) {
173
+ socket.error(
174
+ "compiler-error",
175
+ "Impossible to detect engine language for testing for " +
176
+ data.exerciseSlug +
177
+ "..."
178
+ );
179
+ return;
180
+ }
181
+
182
+ if (
183
+ config?.disabledActions!.includes("test") ||
184
+ config?.disableGrading
185
+ ) {
186
+ socket.ready("Grading is disabled on configuration");
187
+ return true;
188
+ }
189
+
190
+ socket.log(
191
+ "testing",
192
+ "Testing your exercise using the " + exercise.language + " engine."
193
+ );
194
+
195
+ await this.config.runHook("action", {
196
+ action: "test",
197
+ socket,
198
+ configuration: config,
199
+ exercise,
200
+ });
201
+ this.configManager?.save();
202
+
203
+ return true;
204
+ });
205
+
206
+ const terminate = () => {
207
+ Console.debug("Terminating Learnpack...");
208
+ server.terminate(() => {
209
+ this.configManager?.noCurrentExercise();
210
+ dispatcher.enqueue(dispatcher.events.END);
211
+ process.exit();
212
+ });
213
+ };
214
+
215
+ server.on("close", terminate);
216
+ process.on("SIGINT", terminate);
217
+ process.on("SIGTERM", terminate);
218
+ process.on("SIGHUP", terminate);
219
+
220
+ // finish the server startup
221
+ setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000);
222
+
223
+ // start watching for file changes
224
+ // eslint-disable-next-line
225
+ if (StartCommand.flags.watch) this.configManager.watchIndex(_exercises => socket.reload(null, _exercises));
226
+ }
227
+ }
228
+ }
229
+ }
@@ -1,42 +1,40 @@
1
- const Console = require("../utils/console");
2
- const SessionCommand = require("../utils/SessionCommand");
3
- const socket = require("../managers/socket.js");
1
+ import Console from "../utils/console";
2
+ import SessionCommand from "../utils/SessionCommand";
3
+ import socket from "../managers/socket";
4
4
 
5
- const createServer = require("../managers/server");
6
- const ExercisesQueue = require("../utils/exercisesQueue");
5
+ import createServer from "../managers/server";
6
+ import ExercisesQueue from "../utils/exercisesQueue";
7
+ import { IExercise } from "../models/exercise-obj";
7
8
 
8
9
  class TestCommand extends SessionCommand {
9
10
  async init() {
10
11
  const { flags } = this.parse(TestCommand);
11
12
  await this.initSession(flags);
12
13
  }
14
+
13
15
  async run() {
14
16
  const {
15
17
  args: { exerciseSlug },
16
18
  } = this.parse(TestCommand);
17
19
 
18
20
  // Build exercises index
19
- this.configManager.buildIndex();
21
+ this.configManager?.buildIndex();
20
22
 
21
- let exercises = [];
23
+ let exercises: IExercise[] | undefined = [];
22
24
 
23
25
  // test all exercises
24
- if (!exerciseSlug) {
25
- exercises = this.configManager.getAllExercises();
26
- } else {
27
- exercises = [this.configManager.getExercise(exerciseSlug)];
28
- }
26
+ !exerciseSlug ? exercises = this.configManager?.getAllExercises() : exercises = [this.configManager!.getExercise(exerciseSlug)];
29
27
 
30
28
  const exercisesQueue = new ExercisesQueue(exercises);
31
29
 
32
- const configObject = this.configManager.get();
30
+ const configObject = this.configManager?.get();
33
31
 
34
32
  let hasFailed = false;
35
33
  let failedTestsCount = 0;
36
34
  let successTestsCount = 0;
37
- let testsToRunCount = exercisesQueue.size();
35
+ const testsToRunCount = exercisesQueue.size();
38
36
 
39
- configObject.config.testingFinishedCallback = ({ result }) => {
37
+ configObject!.config!.testingFinishedCallback = ({ result }) => {
40
38
  if (result === "failed") {
41
39
  hasFailed = true;
42
40
  failedTestsCount++;
@@ -57,17 +55,17 @@ class TestCommand extends SessionCommand {
57
55
 
58
56
  process.exit(hasFailed ? 1 : 0);
59
57
  } else {
60
- exercisesQueue.pop().test(this.config, config, socket);
58
+ exercisesQueue.pop()!.test!(this.config, config!, socket);
61
59
  }
62
60
  };
63
61
 
64
- const { config } = configObject;
62
+ const config = configObject?.config;
65
63
 
66
- const server = await createServer(configObject, this.configManager, true);
64
+ const server = await createServer(configObject!, this.configManager!, true);
67
65
 
68
- socket.start(config, server, true);
66
+ socket.start(config!, server, true);
69
67
 
70
- exercisesQueue.pop().test(this.config, config, socket);
68
+ exercisesQueue.pop()!.test!(this.config, config!, socket);
71
69
  }
72
70
  }
73
71
 
@@ -82,4 +80,4 @@ TestCommand.args = [
82
80
  },
83
81
  ];
84
82
 
85
- module.exports = TestCommand;
83
+ export default TestCommand;
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from '@oclif/command'