@learnpack/learnpack 5.0.275 → 5.0.277

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 (95) hide show
  1. package/README.md +409 -409
  2. package/lib/commands/audit.js +15 -15
  3. package/lib/commands/breakToken.js +19 -19
  4. package/lib/commands/clean.js +3 -3
  5. package/lib/commands/init.js +41 -41
  6. package/lib/commands/logout.js +3 -3
  7. package/lib/commands/publish.js +5 -10
  8. package/lib/commands/serve.js +55 -2
  9. package/lib/creatorDist/assets/index-BfLyIQVh.js +10343 -10224
  10. package/lib/managers/config/index.js +77 -77
  11. package/lib/utils/api.d.ts +1 -1
  12. package/lib/utils/api.js +12 -9
  13. package/lib/utils/creatorUtilities.js +14 -14
  14. package/lib/utils/export/epub.d.ts +2 -0
  15. package/lib/utils/export/epub.js +298 -0
  16. package/lib/utils/export/index.d.ts +3 -0
  17. package/lib/utils/export/index.js +7 -0
  18. package/lib/utils/export/scorm.d.ts +2 -0
  19. package/lib/utils/export/scorm.js +84 -0
  20. package/lib/utils/export/shared.d.ts +4 -0
  21. package/lib/utils/export/shared.js +61 -0
  22. package/lib/utils/export/types.d.ts +15 -0
  23. package/lib/utils/export/types.js +2 -0
  24. package/package.json +2 -1
  25. package/src/commands/audit.ts +487 -487
  26. package/src/commands/breakToken.ts +67 -67
  27. package/src/commands/clean.ts +30 -30
  28. package/src/commands/init.ts +650 -650
  29. package/src/commands/logout.ts +38 -38
  30. package/src/commands/publish.ts +20 -25
  31. package/src/commands/serve.ts +69 -4
  32. package/src/commands/start.ts +333 -333
  33. package/src/commands/translate.ts +123 -123
  34. package/src/creator/README.md +54 -54
  35. package/src/creator/eslint.config.js +7 -7
  36. package/src/creator/src/components/syllabus/ContentIndex.tsx +312 -312
  37. package/src/creator/src/i18n.ts +28 -28
  38. package/src/creator/src/index.css +217 -217
  39. package/src/creator/src/locales/en.json +126 -126
  40. package/src/creator/src/locales/es.json +126 -126
  41. package/src/creator/src/utils/configTypes.ts +122 -122
  42. package/src/creator/src/utils/constants.ts +13 -13
  43. package/src/creator/src/utils/creatorUtils.ts +46 -46
  44. package/src/creator/src/utils/eventBus.ts +2 -2
  45. package/src/creator/src/utils/lib.ts +468 -468
  46. package/src/creator/src/utils/socket.ts +61 -61
  47. package/src/creator/src/utils/store.ts +222 -222
  48. package/src/creator/src/vite-env.d.ts +1 -1
  49. package/src/creator/vite.config.ts +13 -13
  50. package/src/creatorDist/assets/index-BfLyIQVh.js +10343 -10224
  51. package/src/managers/config/defaults.ts +49 -49
  52. package/src/managers/config/exercise.ts +364 -364
  53. package/src/managers/config/index.ts +775 -775
  54. package/src/managers/file.ts +236 -236
  55. package/src/managers/server/routes.ts +554 -554
  56. package/src/managers/session.ts +182 -182
  57. package/src/managers/telemetry.ts +188 -188
  58. package/src/models/action.ts +13 -13
  59. package/src/models/config-manager.ts +28 -28
  60. package/src/models/config.ts +106 -106
  61. package/src/models/creator.ts +47 -47
  62. package/src/models/exercise-obj.ts +30 -30
  63. package/src/models/session.ts +39 -39
  64. package/src/models/socket.ts +61 -61
  65. package/src/models/status.ts +16 -16
  66. package/src/ui/_app/app.css +1 -1
  67. package/src/ui/_app/app.js +400 -397
  68. package/src/ui/app.tar.gz +0 -0
  69. package/src/utils/BaseCommand.ts +56 -56
  70. package/src/utils/api.ts +53 -39
  71. package/src/utils/audit.ts +392 -392
  72. package/src/utils/checkNotInstalled.ts +267 -267
  73. package/src/utils/configBuilder.ts +82 -82
  74. package/src/utils/convertCreds.js +34 -34
  75. package/src/utils/creatorUtilities.ts +504 -504
  76. package/src/utils/export/README.md +178 -0
  77. package/src/utils/export/epub.ts +400 -0
  78. package/src/utils/export/index.ts +3 -0
  79. package/src/utils/export/scorm.ts +121 -0
  80. package/src/utils/export/shared.ts +61 -0
  81. package/src/utils/export/types.ts +17 -0
  82. package/src/utils/incrementVersion.js +74 -74
  83. package/src/utils/misc.ts +58 -58
  84. package/src/utils/rigoActions.ts +500 -500
  85. package/src/utils/sidebarGenerator.ts +195 -195
  86. package/src/utils/templates/epub/epub.css +133 -0
  87. package/src/utils/templates/isolated/exercises/01-hello-world/README.es.md +26 -26
  88. package/src/utils/templates/isolated/exercises/01-hello-world/README.md +26 -26
  89. package/src/utils/templates/scorm/adlcp_rootv1p2.xsd +110 -0
  90. package/src/utils/templates/scorm/config/api.js +175 -0
  91. package/src/utils/templates/scorm/config/index.html +210 -0
  92. package/src/utils/templates/scorm/ims_xml.xsd +1 -0
  93. package/src/utils/templates/scorm/imscp_rootv1p1p2.xsd +345 -0
  94. package/src/utils/templates/scorm/imsmanifest.xml +38 -0
  95. package/src/utils/templates/scorm/imsmd_rootv1p2p1.xsd +573 -0
@@ -1,333 +1,333 @@
1
- // import path from "path";
2
- import { flags } from "@oclif/command"
3
- import * as fs from "fs"
4
- import * as path from "path"
5
-
6
- import SessionCommand from "../utils/SessionCommand"
7
- import Console from "../utils/console"
8
- import socket from "../managers/socket"
9
- import TelemetryManager from "../managers/telemetry"
10
- import createServer from "../managers/server"
11
- import queue from "../utils/fileQueue"
12
- import {
13
- decompress,
14
- downloadEditor,
15
- checkIfDirectoryExists,
16
- } from "../managers/file"
17
- import { prioritizeHTMLFile } from "../utils/misc"
18
-
19
- import { IGitpodData } from "../models/gitpod-data"
20
- import { IExerciseData } from "../models/exercise-obj"
21
- import { eventManager } from "../utils/osOperations"
22
- // import { cli } from "cli-ux"
23
- import { TOpenWindowData } from "../models/socket"
24
- import {
25
- checkNotInstalledPlugins,
26
- checkNotInstalledDependencies,
27
- } from "../utils/checkNotInstalled"
28
- import { generateSidebar } from "../utils/sidebarGenerator"
29
-
30
- export default class StartCommand extends SessionCommand {
31
- static description = "Runs a small server with all the exercise instructions"
32
-
33
- static flags = {
34
- ...SessionCommand.flags,
35
- port: flags.string({ char: "p", description: "server port" }),
36
- host: flags.string({ char: "h", description: "server host" }),
37
- disableGrading: flags.boolean({
38
- char: "D",
39
- description: "disble grading functionality",
40
- default: false,
41
- }),
42
- // disableGrading: flags.boolean({char: 'dg', description: 'disble grading functionality', default: false }),
43
- watch: flags.boolean({
44
- char: "w",
45
- description: "Watch for file changes",
46
- default: false,
47
- }),
48
- editor: flags.string({
49
- char: "e",
50
- description: "[preview, extension]",
51
- options: ["extension", "preview"],
52
- }),
53
- version: flags.string({
54
- char: "v",
55
- description: "E.g: 1.0.1",
56
- default: undefined,
57
- }),
58
- grading: flags.string({
59
- char: "g",
60
- description: "[isolated, incremental]",
61
- options: ["isolated", "incremental"],
62
- }),
63
- debug: flags.boolean({
64
- char: "d",
65
- description: "debugger mode for more verbage",
66
- default: false,
67
- }),
68
- }
69
-
70
- // 🛑 IMPORTANT
71
- // Every command that will use the configManager needs this init method
72
- async init() {
73
- const { flags } = this.parse(StartCommand)
74
- await this.initSession(flags)
75
- }
76
-
77
- async run() {
78
- // get configuration object
79
- const configObject = this.configManager?.get()
80
-
81
- const hasXDG = await eventManager.checkXDGInstalled()
82
-
83
- const installedPlugins = this.config.plugins.map(plugin => {
84
- return `${plugin.pjson.name}`
85
- })
86
-
87
- if (configObject) {
88
- const { config } = configObject
89
-
90
- // build exerises
91
- this.configManager?.buildIndex()
92
-
93
- Console.debug(
94
- `Grading: ${config?.grading} ${
95
- config?.disabledActions?.includes("test") ? "(disabled)" : ""
96
- }, editor: ${config?.editor.mode} ${config?.editor.version}, for ${
97
- Array.isArray(configObject?.exercises) ?
98
- configObject?.exercises.length :
99
- 0
100
- } exercises found`
101
- )
102
-
103
- generateSidebar(configObject?.exercises || [], config?.dirPath || "")
104
-
105
- const neededPlugins = await checkNotInstalledPlugins(
106
- configObject?.exercises || [],
107
- installedPlugins,
108
- this
109
- )
110
-
111
- const allDepsInstalled = await checkNotInstalledDependencies(
112
- neededPlugins.needed
113
- )
114
- if (!allDepsInstalled) {
115
- this.exit(1)
116
- }
117
-
118
- const appAlreadyExists = checkIfDirectoryExists(`${config?.dirPath}/_app`)
119
-
120
- if (!appAlreadyExists) {
121
- // download app and decompress
122
- await downloadEditor(
123
- config?.editor.version,
124
- `${config?.dirPath}/app.tar.gz`
125
- )
126
-
127
- Console.info("Decompressing LearnPack UI, this may take a minute...")
128
- await decompress(
129
- `${config?.dirPath}/app.tar.gz`,
130
- `${config?.dirPath}/_app/`
131
- )
132
- }
133
-
134
- // listen to socket commands
135
- if (config && this.configManager) {
136
- const server = await createServer(
137
- configObject,
138
- this.configManager,
139
- process.env.NODE_ENV === "test"
140
- )
141
- server.setMaxListeners(30)
142
-
143
- // I should call a method to get the EventListener
144
- const dispatcher = queue.dispatcher({
145
- create: true,
146
- path: `${config.dirPath}/vscode_queue.json`,
147
- })
148
-
149
- socket.start(config, server, false)
150
- const telemetryPath = configObject.config?.dirPath || ""
151
- TelemetryManager.start(telemetryPath, socket)
152
-
153
- socket.on("open", (data: IGitpodData) => {
154
- Console.debug("Opening these files: ", data)
155
-
156
- const files = prioritizeHTMLFile(data.files)
157
-
158
- if (config.editor.agent !== "os") {
159
- // Console.info("Opening files for vscode agent")
160
- eventManager.enqueue(dispatcher.events.OPEN_FILES, files)
161
- } else {
162
- // dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
163
- Console.debug("Ignoring files for os agent")
164
- }
165
-
166
- socket.ready("Ready to compile...")
167
- })
168
-
169
- socket.on("open_window", (data: TOpenWindowData) => {
170
- Console.debug("Opening window: ", data)
171
-
172
- // cli.open(data.url); This uses XDG under the ground
173
- if (config.os !== "linux" || (config.os === "linux" && hasXDG)) {
174
- eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
175
- } else {
176
- dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data)
177
- }
178
-
179
- socket.log("open_window", "", undefined, data.url)
180
- })
181
-
182
- socket.on("open_terminal", (exercise: IExerciseData) => {
183
- Console.debug("Opening terminal: ", exercise)
184
- // eventManager.enqueue(dispatcher.events.OPEN_TERMINAL, exercise);
185
- if (config.editor.agent === "vscode") {
186
- dispatcher.enqueue(dispatcher.events.OPEN_TERMINAL, exercise)
187
- }
188
- })
189
-
190
- socket.on("reset", (exercise: IExerciseData) => {
191
- try {
192
- this.configManager?.reset(exercise.exerciseSlug)
193
- socket.ready("Ready to compile...")
194
- } catch (error) {
195
- socket.error(
196
- "compiler-error",
197
- (error as TypeError).message ||
198
- "There was an error reseting the exercise"
199
- )
200
- setTimeout(() => socket.ready("Ready to compile..."), 2000)
201
- }
202
- })
203
-
204
- socket.on("build", async (data: IExerciseData) => {
205
- const exercise = this.configManager?.getExercise(data.exerciseSlug)
206
-
207
- if (!exercise?.language) {
208
- socket.error(
209
- "compiler-error",
210
- "Impossible to detect language to build for " +
211
- data.exerciseSlug +
212
- "..."
213
- )
214
- return
215
- }
216
-
217
- socket.log(
218
- "compiling",
219
- "Building exercise " +
220
- data.exerciseSlug +
221
- " with " +
222
- exercise.language +
223
- "..."
224
- )
225
- await this.config.runHook("action", {
226
- action: "compile",
227
- socket,
228
- configuration: config,
229
- exercise,
230
- telemetry: TelemetryManager,
231
- })
232
- })
233
-
234
- socket.on("test", async (data: IExerciseData) => {
235
- const exercise = this.configManager?.getExercise(data.exerciseSlug)
236
-
237
- if (!exercise?.language) {
238
- socket.error(
239
- "compiler-error",
240
- "Impossible to detect engine language for testing for " +
241
- data.exerciseSlug +
242
- "..."
243
- )
244
- return
245
- }
246
-
247
- if (
248
- config?.disabledActions!.includes("test") ||
249
- config?.disableGrading
250
- ) {
251
- socket.ready("Grading is disabled on configuration")
252
- return true
253
- }
254
-
255
- socket.log(
256
- "testing",
257
- "Testing your exercise using the " + exercise.language + " engine."
258
- )
259
-
260
- await this.config.runHook("action", {
261
- action: "test",
262
- socket,
263
- configuration: config,
264
- exercise,
265
- telemetry: TelemetryManager,
266
- })
267
-
268
- try {
269
- if (!configObject.config) {
270
- return
271
- }
272
-
273
- const getReportPath = (ext: string) => {
274
- if (!configObject.config?.dirPath) {
275
- throw new Error("No directory path found in config")
276
- }
277
-
278
- return path.join(
279
- configObject.config.dirPath,
280
- "reports",
281
- `${exercise.slug}`,
282
- `report.${ext}`
283
- )
284
- }
285
-
286
- const markdownReportPath = getReportPath("md")
287
- const textReportPath = getReportPath("txt")
288
-
289
- if (fs.existsSync(markdownReportPath)) {
290
- let reportContent = ""
291
- reportContent = fs.readFileSync(markdownReportPath, "utf8")
292
- socket.dialog(reportContent)
293
- }
294
-
295
- if (fs.existsSync(textReportPath)) {
296
- let reportContent = ""
297
- reportContent = fs.readFileSync(textReportPath, "utf8")
298
- socket.dialog(reportContent)
299
- }
300
- } catch (error) {
301
- Console.debug("Error finding report for exercise.slug", error)
302
- }
303
-
304
- this.configManager?.save()
305
-
306
- return true
307
- })
308
-
309
- const terminate = async () => {
310
- Console.error("Terminating Learnpack...")
311
- this.configManager?.noCurrentExercise()
312
- dispatcher.enqueue(dispatcher.events.END)
313
- process.exit()
314
- }
315
-
316
- server.on("close", terminate)
317
- process.on("SIGINT", terminate)
318
- process.on("SIGTERM", terminate)
319
- process.on("SIGHUP", terminate)
320
-
321
- // finish the server startup
322
- setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000)
323
-
324
- // start watching for file changes
325
- if (StartCommand.flags.watch)
326
- this.configManager.watchIndex((_filename, _fileContent) => {
327
- // Instead of reloading with socket.reload(), I just notify the frontend for the file change
328
- socket.emit("file_change", "ready", [_filename, _fileContent])
329
- })
330
- }
331
- }
332
- }
333
- }
1
+ // import path from "path";
2
+ import { flags } from "@oclif/command"
3
+ import * as fs from "fs"
4
+ import * as path from "path"
5
+
6
+ import SessionCommand from "../utils/SessionCommand"
7
+ import Console from "../utils/console"
8
+ import socket from "../managers/socket"
9
+ import TelemetryManager from "../managers/telemetry"
10
+ import createServer from "../managers/server"
11
+ import queue from "../utils/fileQueue"
12
+ import {
13
+ decompress,
14
+ downloadEditor,
15
+ checkIfDirectoryExists,
16
+ } from "../managers/file"
17
+ import { prioritizeHTMLFile } from "../utils/misc"
18
+
19
+ import { IGitpodData } from "../models/gitpod-data"
20
+ import { IExerciseData } from "../models/exercise-obj"
21
+ import { eventManager } from "../utils/osOperations"
22
+ // import { cli } from "cli-ux"
23
+ import { TOpenWindowData } from "../models/socket"
24
+ import {
25
+ checkNotInstalledPlugins,
26
+ checkNotInstalledDependencies,
27
+ } from "../utils/checkNotInstalled"
28
+ import { generateSidebar } from "../utils/sidebarGenerator"
29
+
30
+ export default class StartCommand extends SessionCommand {
31
+ static description = "Runs a small server with all the exercise instructions"
32
+
33
+ static flags = {
34
+ ...SessionCommand.flags,
35
+ port: flags.string({ char: "p", description: "server port" }),
36
+ host: flags.string({ char: "h", description: "server host" }),
37
+ disableGrading: flags.boolean({
38
+ char: "D",
39
+ description: "disble grading functionality",
40
+ default: false,
41
+ }),
42
+ // disableGrading: flags.boolean({char: 'dg', description: 'disble grading functionality', default: false }),
43
+ watch: flags.boolean({
44
+ char: "w",
45
+ description: "Watch for file changes",
46
+ default: false,
47
+ }),
48
+ editor: flags.string({
49
+ char: "e",
50
+ description: "[preview, extension]",
51
+ options: ["extension", "preview"],
52
+ }),
53
+ version: flags.string({
54
+ char: "v",
55
+ description: "E.g: 1.0.1",
56
+ default: undefined,
57
+ }),
58
+ grading: flags.string({
59
+ char: "g",
60
+ description: "[isolated, incremental]",
61
+ options: ["isolated", "incremental"],
62
+ }),
63
+ debug: flags.boolean({
64
+ char: "d",
65
+ description: "debugger mode for more verbage",
66
+ default: false,
67
+ }),
68
+ }
69
+
70
+ // 🛑 IMPORTANT
71
+ // Every command that will use the configManager needs this init method
72
+ async init() {
73
+ const { flags } = this.parse(StartCommand)
74
+ await this.initSession(flags)
75
+ }
76
+
77
+ async run() {
78
+ // get configuration object
79
+ const configObject = this.configManager?.get()
80
+
81
+ const hasXDG = await eventManager.checkXDGInstalled()
82
+
83
+ const installedPlugins = this.config.plugins.map(plugin => {
84
+ return `${plugin.pjson.name}`
85
+ })
86
+
87
+ if (configObject) {
88
+ const { config } = configObject
89
+
90
+ // build exerises
91
+ this.configManager?.buildIndex()
92
+
93
+ Console.debug(
94
+ `Grading: ${config?.grading} ${
95
+ config?.disabledActions?.includes("test") ? "(disabled)" : ""
96
+ }, editor: ${config?.editor.mode} ${config?.editor.version}, for ${
97
+ Array.isArray(configObject?.exercises) ?
98
+ configObject?.exercises.length :
99
+ 0
100
+ } exercises found`
101
+ )
102
+
103
+ generateSidebar(configObject?.exercises || [], config?.dirPath || "")
104
+
105
+ const neededPlugins = await checkNotInstalledPlugins(
106
+ configObject?.exercises || [],
107
+ installedPlugins,
108
+ this
109
+ )
110
+
111
+ const allDepsInstalled = await checkNotInstalledDependencies(
112
+ neededPlugins.needed
113
+ )
114
+ if (!allDepsInstalled) {
115
+ this.exit(1)
116
+ }
117
+
118
+ const appAlreadyExists = checkIfDirectoryExists(`${config?.dirPath}/_app`)
119
+
120
+ if (!appAlreadyExists) {
121
+ // download app and decompress
122
+ await downloadEditor(
123
+ config?.editor.version,
124
+ `${config?.dirPath}/app.tar.gz`
125
+ )
126
+
127
+ Console.info("Decompressing LearnPack UI, this may take a minute...")
128
+ await decompress(
129
+ `${config?.dirPath}/app.tar.gz`,
130
+ `${config?.dirPath}/_app/`
131
+ )
132
+ }
133
+
134
+ // listen to socket commands
135
+ if (config && this.configManager) {
136
+ const server = await createServer(
137
+ configObject,
138
+ this.configManager,
139
+ process.env.NODE_ENV === "test"
140
+ )
141
+ server.setMaxListeners(30)
142
+
143
+ // I should call a method to get the EventListener
144
+ const dispatcher = queue.dispatcher({
145
+ create: true,
146
+ path: `${config.dirPath}/vscode_queue.json`,
147
+ })
148
+
149
+ socket.start(config, server, false)
150
+ const telemetryPath = configObject.config?.dirPath || ""
151
+ TelemetryManager.start(telemetryPath, socket)
152
+
153
+ socket.on("open", (data: IGitpodData) => {
154
+ Console.debug("Opening these files: ", data)
155
+
156
+ const files = prioritizeHTMLFile(data.files)
157
+
158
+ if (config.editor.agent !== "os") {
159
+ // Console.info("Opening files for vscode agent")
160
+ eventManager.enqueue(dispatcher.events.OPEN_FILES, files)
161
+ } else {
162
+ // dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
163
+ Console.debug("Ignoring files for os agent")
164
+ }
165
+
166
+ socket.ready("Ready to compile...")
167
+ })
168
+
169
+ socket.on("open_window", (data: TOpenWindowData) => {
170
+ Console.debug("Opening window: ", data)
171
+
172
+ // cli.open(data.url); This uses XDG under the ground
173
+ if (config.os !== "linux" || (config.os === "linux" && hasXDG)) {
174
+ eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
175
+ } else {
176
+ dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data)
177
+ }
178
+
179
+ socket.log("open_window", "", undefined, data.url)
180
+ })
181
+
182
+ socket.on("open_terminal", (exercise: IExerciseData) => {
183
+ Console.debug("Opening terminal: ", exercise)
184
+ // eventManager.enqueue(dispatcher.events.OPEN_TERMINAL, exercise);
185
+ if (config.editor.agent === "vscode") {
186
+ dispatcher.enqueue(dispatcher.events.OPEN_TERMINAL, exercise)
187
+ }
188
+ })
189
+
190
+ socket.on("reset", (exercise: IExerciseData) => {
191
+ try {
192
+ this.configManager?.reset(exercise.exerciseSlug)
193
+ socket.ready("Ready to compile...")
194
+ } catch (error) {
195
+ socket.error(
196
+ "compiler-error",
197
+ (error as TypeError).message ||
198
+ "There was an error reseting the exercise"
199
+ )
200
+ setTimeout(() => socket.ready("Ready to compile..."), 2000)
201
+ }
202
+ })
203
+
204
+ socket.on("build", async (data: IExerciseData) => {
205
+ const exercise = this.configManager?.getExercise(data.exerciseSlug)
206
+
207
+ if (!exercise?.language) {
208
+ socket.error(
209
+ "compiler-error",
210
+ "Impossible to detect language to build for " +
211
+ data.exerciseSlug +
212
+ "..."
213
+ )
214
+ return
215
+ }
216
+
217
+ socket.log(
218
+ "compiling",
219
+ "Building exercise " +
220
+ data.exerciseSlug +
221
+ " with " +
222
+ exercise.language +
223
+ "..."
224
+ )
225
+ await this.config.runHook("action", {
226
+ action: "compile",
227
+ socket,
228
+ configuration: config,
229
+ exercise,
230
+ telemetry: TelemetryManager,
231
+ })
232
+ })
233
+
234
+ socket.on("test", async (data: IExerciseData) => {
235
+ const exercise = this.configManager?.getExercise(data.exerciseSlug)
236
+
237
+ if (!exercise?.language) {
238
+ socket.error(
239
+ "compiler-error",
240
+ "Impossible to detect engine language for testing for " +
241
+ data.exerciseSlug +
242
+ "..."
243
+ )
244
+ return
245
+ }
246
+
247
+ if (
248
+ config?.disabledActions!.includes("test") ||
249
+ config?.disableGrading
250
+ ) {
251
+ socket.ready("Grading is disabled on configuration")
252
+ return true
253
+ }
254
+
255
+ socket.log(
256
+ "testing",
257
+ "Testing your exercise using the " + exercise.language + " engine."
258
+ )
259
+
260
+ await this.config.runHook("action", {
261
+ action: "test",
262
+ socket,
263
+ configuration: config,
264
+ exercise,
265
+ telemetry: TelemetryManager,
266
+ })
267
+
268
+ try {
269
+ if (!configObject.config) {
270
+ return
271
+ }
272
+
273
+ const getReportPath = (ext: string) => {
274
+ if (!configObject.config?.dirPath) {
275
+ throw new Error("No directory path found in config")
276
+ }
277
+
278
+ return path.join(
279
+ configObject.config.dirPath,
280
+ "reports",
281
+ `${exercise.slug}`,
282
+ `report.${ext}`
283
+ )
284
+ }
285
+
286
+ const markdownReportPath = getReportPath("md")
287
+ const textReportPath = getReportPath("txt")
288
+
289
+ if (fs.existsSync(markdownReportPath)) {
290
+ let reportContent = ""
291
+ reportContent = fs.readFileSync(markdownReportPath, "utf8")
292
+ socket.dialog(reportContent)
293
+ }
294
+
295
+ if (fs.existsSync(textReportPath)) {
296
+ let reportContent = ""
297
+ reportContent = fs.readFileSync(textReportPath, "utf8")
298
+ socket.dialog(reportContent)
299
+ }
300
+ } catch (error) {
301
+ Console.debug("Error finding report for exercise.slug", error)
302
+ }
303
+
304
+ this.configManager?.save()
305
+
306
+ return true
307
+ })
308
+
309
+ const terminate = async () => {
310
+ Console.error("Terminating Learnpack...")
311
+ this.configManager?.noCurrentExercise()
312
+ dispatcher.enqueue(dispatcher.events.END)
313
+ process.exit()
314
+ }
315
+
316
+ server.on("close", terminate)
317
+ process.on("SIGINT", terminate)
318
+ process.on("SIGTERM", terminate)
319
+ process.on("SIGHUP", terminate)
320
+
321
+ // finish the server startup
322
+ setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000)
323
+
324
+ // start watching for file changes
325
+ if (StartCommand.flags.watch)
326
+ this.configManager.watchIndex((_filename, _fileContent) => {
327
+ // Instead of reloading with socket.reload(), I just notify the frontend for the file change
328
+ socket.emit("file_change", "ready", [_filename, _fileContent])
329
+ })
330
+ }
331
+ }
332
+ }
333
+ }