@learnpack/learnpack 2.1.43 → 2.1.44

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.
@@ -1,339 +1,339 @@
1
- // import path from "path";
2
- import { flags } from "@oclif/command"
3
-
4
- import SessionCommand from "../utils/SessionCommand"
5
- import Console from "../utils/console"
6
- import socket from "../managers/socket"
7
- import TelemetryManager, { TStep } from "../managers/telemetry"
8
- import createServer from "../managers/server"
9
- import queue from "../utils/fileQueue"
10
- import {
11
- decompress,
12
- downloadEditor,
13
- checkIfDirectoryExists,
14
- } from "../managers/file"
15
- import { prioritizeHTMLFile } from "../utils/misc"
16
-
17
- import { IGitpodData } from "../models/gitpod-data"
18
- import { IExercise, IExerciseData } from "../models/exercise-obj"
19
- import { eventManager } from "../utils/osOperations"
20
- import { cli } from "cli-ux"
21
- import { TOpenWindowData } from "../models/socket"
22
- import { checkNotInstalledPlugins } from "../utils/checkNotInstalled"
23
-
24
- export default class StartCommand extends SessionCommand {
25
- static description = "Runs a small server with all the exercise instructions"
26
-
27
- static flags = {
28
- ...SessionCommand.flags,
29
- port: flags.string({ char: "p", description: "server port" }),
30
- host: flags.string({ char: "h", description: "server host" }),
31
- disableGrading: flags.boolean({
32
- char: "D",
33
- description: "disble grading functionality",
34
- default: false,
35
- }),
36
- // disableGrading: flags.boolean({char: 'dg', description: 'disble grading functionality', default: false }),
37
- watch: flags.boolean({
38
- char: "w",
39
- description: "Watch for file changes",
40
- default: false,
41
- }),
42
- editor: flags.string({
43
- char: "e",
44
- description: "[preview, extension]",
45
- options: ["extension", "preview"],
46
- }),
47
- version: flags.string({
48
- char: "v",
49
- description: "E.g: 1.0.1",
50
- default: undefined,
51
- }),
52
- grading: flags.string({
53
- char: "g",
54
- description: "[isolated, incremental]",
55
- options: ["isolated", "incremental"],
56
- }),
57
- debug: flags.boolean({
58
- char: "d",
59
- description: "debugger mode for more verbage",
60
- default: false,
61
- }),
62
- }
63
-
64
- // 🛑 IMPORTANT
65
- // Every command that will use the configManager needs this init method
66
- async init() {
67
- const { flags } = this.parse(StartCommand)
68
- await this.initSession(flags)
69
- }
70
-
71
- async run() {
72
- // get configuration object
73
- const configObject = this.configManager?.get()
74
-
75
- // TODO: console.log(this.config.platform); get the platfrom from Oclif
76
- const hasXDG = await eventManager.checkXDGInstalled()
77
-
78
- const installedPlugins = this.config.plugins.map(plugin => {
79
- return `${plugin.pjson.name}`
80
- })
81
-
82
- const neededToInstallPlugins = checkNotInstalledPlugins(
83
- configObject?.exercises || [],
84
- installedPlugins
85
- )
86
- if (neededToInstallPlugins.length > 0) {
87
- Console.error(
88
- "These plugins are not installed but required: ",
89
- neededToInstallPlugins
90
- )
91
- const confirmInstall = await cli.confirm(
92
- "Do you want to install the needed plugins? (y/n)"
93
- )
94
- if (confirmInstall) {
95
- Console.info("Installing the needed plugins...")
96
- for await (const p of neededToInstallPlugins) {
97
- await this.config.runCommand(`plugins:install`, [p])
98
- Console.log(`Plugin ${p} installed`)
99
- }
100
-
101
- Console.log(
102
- "All needed plugins installed, please restart LearnPack to load them."
103
- )
104
- Console.info("Run: $ learnpack start")
105
- this.exit(0)
106
- } else {
107
- Console.error(
108
- "You need to install the plugins to complete this exercise"
109
- )
110
- Console.info(
111
- "To install the plugins run each of the following commands: "
112
- )
113
- for (const p of neededToInstallPlugins) {
114
- Console.log(`learnpack plugins:install ${p}`)
115
- }
116
-
117
- this.exit(1)
118
- }
119
- }
120
-
121
- if (configObject) {
122
- const { config } = configObject
123
-
124
- // build exerises
125
- this.configManager?.buildIndex()
126
-
127
- Console.debug(
128
- `Grading: ${config?.grading} ${
129
- config?.disabledActions?.includes("test") ? "(disabled)" : ""
130
- }, editor: ${config?.editor.mode} ${config?.editor.version}, for ${
131
- Array.isArray(configObject?.exercises) ?
132
- configObject?.exercises.length :
133
- 0
134
- } exercises found`
135
- )
136
-
137
- const appAlreadyExists = checkIfDirectoryExists(
138
- `${config?.dirPath}/_app`
139
- )
140
-
141
- if (!appAlreadyExists) {
142
- // download app and decompress
143
- await downloadEditor(
144
- config?.editor.version,
145
- `${config?.dirPath}/app.tar.gz`
146
- )
147
-
148
- Console.info("Decompressing LearnPack UI, this may take a minute...")
149
- await decompress(
150
- `${config?.dirPath}/app.tar.gz`,
151
- `${config?.dirPath}/_app/`
152
- )
153
- }
154
-
155
- // listen to socket commands
156
- if (config && this.configManager) {
157
- const server = await createServer(
158
- configObject,
159
- this.configManager,
160
- process.env.NODE_ENV === "test"
161
- )
162
- server.setMaxListeners(25)
163
-
164
- // I should call a method to get the EventListener
165
- const dispatcher = queue.dispatcher({
166
- create: true,
167
- path: `${config.dirPath}/vscode_queue.json`,
168
- })
169
-
170
- if (configObject.exercises) {
171
- const agent = configObject.config?.editor.agent || ""
172
- const path = configObject.config?.dirPath || ""
173
- const tutorialSlug = configObject.config?.slug || ""
174
-
175
- const steps = configObject.exercises.map(
176
- (e: IExercise, index): TStep => ({
177
- slug: e.slug,
178
- position: e.position || index,
179
- files: e.files,
180
- ai_interactions: [],
181
- compilations: [],
182
- tests: [],
183
- is_testeable: e.graded || false,
184
- })
185
- )
186
- if (path && steps.length > 0) {
187
- TelemetryManager.start(agent, steps, path, tutorialSlug)
188
- }
189
-
190
- if (config.telemetry) {
191
- TelemetryManager.urls = config.telemetry
192
- }
193
- }
194
-
195
- socket.start(config, server, false)
196
-
197
- socket.on("open", (data: IGitpodData) => {
198
- Console.debug("Opening these files: ", data)
199
-
200
- const files = prioritizeHTMLFile(data.files)
201
-
202
- if (config.editor.agent === "os") {
203
- eventManager.enqueue(dispatcher.events.OPEN_FILES, files)
204
- } else {
205
- dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
206
- }
207
-
208
- socket.ready("Ready to compile...")
209
- })
210
-
211
- socket.on("open_window", (data: TOpenWindowData) => {
212
- Console.debug("Opening window: ", data)
213
- // cli.open(data.url); This uses XDG under the ground
214
- if (
215
- config.editor.agent === "os" &&
216
- ((config.os === "linux" && hasXDG) || config.os !== "linux")
217
- ) {
218
- eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
219
- } else {
220
- dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data)
221
- }
222
-
223
- socket.log("open_window", `URL ${data.url} opened in the browser`)
224
- })
225
-
226
- socket.on("reset", (exercise: IExerciseData) => {
227
- try {
228
- this.configManager?.reset(exercise.exerciseSlug)
229
- socket.ready("Ready to compile...")
230
- } catch (error) {
231
- socket.error(
232
- "compiler-error",
233
- (error as TypeError).message ||
234
- "There was an error reseting the exercise"
235
- )
236
- setTimeout(() => socket.ready("Ready to compile..."), 2000)
237
- }
238
- })
239
-
240
- socket.on("build", async (data: IExerciseData) => {
241
- const exercise = this.configManager?.getExercise(data.exerciseSlug)
242
-
243
- if (!exercise?.language) {
244
- socket.error(
245
- "compiler-error",
246
- "Impossible to detect language to build for " +
247
- data.exerciseSlug +
248
- "..."
249
- )
250
- return
251
- }
252
-
253
- socket.log(
254
- "compiling",
255
- "Building exercise " +
256
- data.exerciseSlug +
257
- " with " +
258
- exercise.language +
259
- "..."
260
- )
261
- await this.config.runHook("action", {
262
- action: "compile",
263
- socket,
264
- configuration: config,
265
- exercise,
266
- telemetry: TelemetryManager,
267
- })
268
- })
269
-
270
- socket.on("ai_interaction", (data: any) => {
271
- const { stepPosition, event, eventData } = data
272
- TelemetryManager.registerStepEvent(stepPosition, event, eventData)
273
- })
274
-
275
- socket.on("test", async (data: IExerciseData) => {
276
- const exercise = this.configManager?.getExercise(data.exerciseSlug)
277
-
278
- if (!exercise?.language) {
279
- socket.error(
280
- "compiler-error",
281
- "Impossible to detect engine language for testing for " +
282
- data.exerciseSlug +
283
- "..."
284
- )
285
- return
286
- }
287
-
288
- if (
289
- config?.disabledActions!.includes("test") ||
290
- config?.disableGrading
291
- ) {
292
- socket.ready("Grading is disabled on configuration")
293
- return true
294
- }
295
-
296
- socket.log(
297
- "testing",
298
- "Testing your exercise using the " + exercise.language + " engine."
299
- )
300
-
301
- await this.config.runHook("action", {
302
- action: "test",
303
- socket,
304
- configuration: config,
305
- exercise,
306
- telemetry: TelemetryManager,
307
- })
308
-
309
- this.configManager?.save()
310
-
311
- return true
312
- })
313
-
314
- const terminate = async () => {
315
- Console.error("Terminating Learnpack...")
316
- await TelemetryManager.submit()
317
- this.configManager?.noCurrentExercise()
318
- dispatcher.enqueue(dispatcher.events.END)
319
- process.exit()
320
- }
321
-
322
- server.on("close", terminate)
323
- process.on("SIGINT", terminate)
324
- process.on("SIGTERM", terminate)
325
- process.on("SIGHUP", terminate)
326
-
327
- // finish the server startup
328
- setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000)
329
-
330
- // start watching for file changes
331
- if (StartCommand.flags.watch)
332
- this.configManager.watchIndex(_filename => {
333
- // Instead of reloading with socket.reload(), I just notify the frontend for the file change
334
- socket.emit("file_change", "ready", _filename)
335
- })
336
- }
337
- }
338
- }
339
- }
1
+ // import path from "path";
2
+ import { flags } from "@oclif/command"
3
+
4
+ import SessionCommand from "../utils/SessionCommand"
5
+ import Console from "../utils/console"
6
+ import socket from "../managers/socket"
7
+ import TelemetryManager, { TStep } from "../managers/telemetry"
8
+ import createServer from "../managers/server"
9
+ import queue from "../utils/fileQueue"
10
+ import {
11
+ decompress,
12
+ downloadEditor,
13
+ checkIfDirectoryExists,
14
+ } from "../managers/file"
15
+ import { prioritizeHTMLFile } from "../utils/misc"
16
+
17
+ import { IGitpodData } from "../models/gitpod-data"
18
+ import { IExercise, IExerciseData } from "../models/exercise-obj"
19
+ import { eventManager } from "../utils/osOperations"
20
+ import { cli } from "cli-ux"
21
+ import { TOpenWindowData } from "../models/socket"
22
+ import { checkNotInstalledPlugins } from "../utils/checkNotInstalled"
23
+
24
+ export default class StartCommand extends SessionCommand {
25
+ static description = "Runs a small server with all the exercise instructions"
26
+
27
+ static flags = {
28
+ ...SessionCommand.flags,
29
+ port: flags.string({ char: "p", description: "server port" }),
30
+ host: flags.string({ char: "h", description: "server host" }),
31
+ disableGrading: flags.boolean({
32
+ char: "D",
33
+ description: "disble grading functionality",
34
+ default: false,
35
+ }),
36
+ // disableGrading: flags.boolean({char: 'dg', description: 'disble grading functionality', default: false }),
37
+ watch: flags.boolean({
38
+ char: "w",
39
+ description: "Watch for file changes",
40
+ default: false,
41
+ }),
42
+ editor: flags.string({
43
+ char: "e",
44
+ description: "[preview, extension]",
45
+ options: ["extension", "preview"],
46
+ }),
47
+ version: flags.string({
48
+ char: "v",
49
+ description: "E.g: 1.0.1",
50
+ default: undefined,
51
+ }),
52
+ grading: flags.string({
53
+ char: "g",
54
+ description: "[isolated, incremental]",
55
+ options: ["isolated", "incremental"],
56
+ }),
57
+ debug: flags.boolean({
58
+ char: "d",
59
+ description: "debugger mode for more verbage",
60
+ default: false,
61
+ }),
62
+ }
63
+
64
+ // 🛑 IMPORTANT
65
+ // Every command that will use the configManager needs this init method
66
+ async init() {
67
+ const { flags } = this.parse(StartCommand)
68
+ await this.initSession(flags)
69
+ }
70
+
71
+ async run() {
72
+ // get configuration object
73
+ const configObject = this.configManager?.get()
74
+
75
+ // TODO: console.log(this.config.platform); get the platfrom from Oclif
76
+ const hasXDG = await eventManager.checkXDGInstalled()
77
+
78
+ const installedPlugins = this.config.plugins.map(plugin => {
79
+ return `${plugin.pjson.name}`
80
+ })
81
+
82
+ const neededToInstallPlugins = checkNotInstalledPlugins(
83
+ configObject?.exercises || [],
84
+ installedPlugins
85
+ )
86
+ if (neededToInstallPlugins.length > 0) {
87
+ Console.error(
88
+ "These plugins are not installed but required: ",
89
+ neededToInstallPlugins
90
+ )
91
+ const confirmInstall = await cli.confirm(
92
+ "Do you want to install the needed plugins? (y/n)"
93
+ )
94
+ if (confirmInstall) {
95
+ Console.info("Installing the needed plugins...")
96
+ for await (const p of neededToInstallPlugins) {
97
+ await this.config.runCommand(`plugins:install`, [p])
98
+ Console.log(`Plugin ${p} installed`)
99
+ }
100
+
101
+ Console.log(
102
+ "All needed plugins installed, please restart LearnPack to load them."
103
+ )
104
+ Console.info("Run: $ learnpack start")
105
+ this.exit(0)
106
+ } else {
107
+ Console.error(
108
+ "You need to install the plugins to complete this exercise"
109
+ )
110
+ Console.info(
111
+ "To install the plugins run each of the following commands: "
112
+ )
113
+ for (const p of neededToInstallPlugins) {
114
+ Console.log(`learnpack plugins:install ${p}`)
115
+ }
116
+
117
+ this.exit(1)
118
+ }
119
+ }
120
+
121
+ if (configObject) {
122
+ const { config } = configObject
123
+
124
+ // build exerises
125
+ this.configManager?.buildIndex()
126
+
127
+ Console.debug(
128
+ `Grading: ${config?.grading} ${
129
+ config?.disabledActions?.includes("test") ? "(disabled)" : ""
130
+ }, editor: ${config?.editor.mode} ${config?.editor.version}, for ${
131
+ Array.isArray(configObject?.exercises) ?
132
+ configObject?.exercises.length :
133
+ 0
134
+ } exercises found`
135
+ )
136
+
137
+ const appAlreadyExists = checkIfDirectoryExists(
138
+ `${config?.dirPath}/_app`
139
+ )
140
+
141
+ if (!appAlreadyExists) {
142
+ // download app and decompress
143
+ await downloadEditor(
144
+ config?.editor.version,
145
+ `${config?.dirPath}/app.tar.gz`
146
+ )
147
+
148
+ Console.info("Decompressing LearnPack UI, this may take a minute...")
149
+ await decompress(
150
+ `${config?.dirPath}/app.tar.gz`,
151
+ `${config?.dirPath}/_app/`
152
+ )
153
+ }
154
+
155
+ // listen to socket commands
156
+ if (config && this.configManager) {
157
+ const server = await createServer(
158
+ configObject,
159
+ this.configManager,
160
+ process.env.NODE_ENV === "test"
161
+ )
162
+ server.setMaxListeners(25)
163
+
164
+ // I should call a method to get the EventListener
165
+ const dispatcher = queue.dispatcher({
166
+ create: true,
167
+ path: `${config.dirPath}/vscode_queue.json`,
168
+ })
169
+
170
+ if (configObject.exercises) {
171
+ const agent = configObject.config?.editor.agent || ""
172
+ const path = configObject.config?.dirPath || ""
173
+ const tutorialSlug = configObject.config?.slug || ""
174
+
175
+ const steps = configObject.exercises.map(
176
+ (e: IExercise, index): TStep => ({
177
+ slug: e.slug,
178
+ position: e.position || index,
179
+ files: e.files,
180
+ ai_interactions: [],
181
+ compilations: [],
182
+ tests: [],
183
+ is_testeable: e.graded || false,
184
+ })
185
+ )
186
+ if (path && steps.length > 0) {
187
+ TelemetryManager.start(agent, steps, path, tutorialSlug)
188
+ }
189
+
190
+ if (config.telemetry) {
191
+ TelemetryManager.urls = config.telemetry
192
+ }
193
+ }
194
+
195
+ socket.start(config, server, false)
196
+
197
+ socket.on("open", (data: IGitpodData) => {
198
+ Console.debug("Opening these files: ", data)
199
+
200
+ const files = prioritizeHTMLFile(data.files)
201
+
202
+ if (config.editor.agent === "os") {
203
+ eventManager.enqueue(dispatcher.events.OPEN_FILES, files)
204
+ } else {
205
+ dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
206
+ }
207
+
208
+ socket.ready("Ready to compile...")
209
+ })
210
+
211
+ socket.on("open_window", (data: TOpenWindowData) => {
212
+ Console.debug("Opening window: ", data)
213
+ // cli.open(data.url); This uses XDG under the ground
214
+ if (
215
+ config.editor.agent === "os" &&
216
+ ((config.os === "linux" && hasXDG) || config.os !== "linux")
217
+ ) {
218
+ eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
219
+ } else {
220
+ dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data)
221
+ }
222
+
223
+ socket.log("open_window", `URL ${data.url} opened in the browser`)
224
+ })
225
+
226
+ socket.on("reset", (exercise: IExerciseData) => {
227
+ try {
228
+ this.configManager?.reset(exercise.exerciseSlug)
229
+ socket.ready("Ready to compile...")
230
+ } catch (error) {
231
+ socket.error(
232
+ "compiler-error",
233
+ (error as TypeError).message ||
234
+ "There was an error reseting the exercise"
235
+ )
236
+ setTimeout(() => socket.ready("Ready to compile..."), 2000)
237
+ }
238
+ })
239
+
240
+ socket.on("build", async (data: IExerciseData) => {
241
+ const exercise = this.configManager?.getExercise(data.exerciseSlug)
242
+
243
+ if (!exercise?.language) {
244
+ socket.error(
245
+ "compiler-error",
246
+ "Impossible to detect language to build for " +
247
+ data.exerciseSlug +
248
+ "..."
249
+ )
250
+ return
251
+ }
252
+
253
+ socket.log(
254
+ "compiling",
255
+ "Building exercise " +
256
+ data.exerciseSlug +
257
+ " with " +
258
+ exercise.language +
259
+ "..."
260
+ )
261
+ await this.config.runHook("action", {
262
+ action: "compile",
263
+ socket,
264
+ configuration: config,
265
+ exercise,
266
+ telemetry: TelemetryManager,
267
+ })
268
+ })
269
+
270
+ socket.on("ai_interaction", (data: any) => {
271
+ const { stepPosition, event, eventData } = data
272
+ TelemetryManager.registerStepEvent(stepPosition, event, eventData)
273
+ })
274
+
275
+ socket.on("test", async (data: IExerciseData) => {
276
+ const exercise = this.configManager?.getExercise(data.exerciseSlug)
277
+
278
+ if (!exercise?.language) {
279
+ socket.error(
280
+ "compiler-error",
281
+ "Impossible to detect engine language for testing for " +
282
+ data.exerciseSlug +
283
+ "..."
284
+ )
285
+ return
286
+ }
287
+
288
+ if (
289
+ config?.disabledActions!.includes("test") ||
290
+ config?.disableGrading
291
+ ) {
292
+ socket.ready("Grading is disabled on configuration")
293
+ return true
294
+ }
295
+
296
+ socket.log(
297
+ "testing",
298
+ "Testing your exercise using the " + exercise.language + " engine."
299
+ )
300
+
301
+ await this.config.runHook("action", {
302
+ action: "test",
303
+ socket,
304
+ configuration: config,
305
+ exercise,
306
+ telemetry: TelemetryManager,
307
+ })
308
+
309
+ this.configManager?.save()
310
+
311
+ return true
312
+ })
313
+
314
+ const terminate = async () => {
315
+ Console.error("Terminating Learnpack...")
316
+ await TelemetryManager.submit()
317
+ this.configManager?.noCurrentExercise()
318
+ dispatcher.enqueue(dispatcher.events.END)
319
+ process.exit()
320
+ }
321
+
322
+ server.on("close", terminate)
323
+ process.on("SIGINT", terminate)
324
+ process.on("SIGTERM", terminate)
325
+ process.on("SIGHUP", terminate)
326
+
327
+ // finish the server startup
328
+ setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000)
329
+
330
+ // start watching for file changes
331
+ if (StartCommand.flags.watch)
332
+ this.configManager.watchIndex(_filename => {
333
+ // Instead of reloading with socket.reload(), I just notify the frontend for the file change
334
+ socket.emit("file_change", "ready", _filename)
335
+ })
336
+ }
337
+ }
338
+ }
339
+ }