@learnpack/learnpack 2.1.39 → 2.1.40

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 (196) hide show
  1. package/README.md +370 -35
  2. package/bin/run +17 -17
  3. package/bin/run.cmd +3 -3
  4. package/lib/commands/audit.d.ts +6 -6
  5. package/lib/commands/audit.js +342 -342
  6. package/lib/commands/clean.d.ts +8 -8
  7. package/lib/commands/clean.js +25 -25
  8. package/lib/commands/download.d.ts +13 -13
  9. package/lib/commands/download.js +55 -55
  10. package/lib/commands/init.d.ts +9 -9
  11. package/lib/commands/init.js +123 -123
  12. package/lib/commands/login.d.ts +14 -14
  13. package/lib/commands/login.js +37 -37
  14. package/lib/commands/logout.d.ts +14 -14
  15. package/lib/commands/logout.js +37 -37
  16. package/lib/commands/publish.d.ts +14 -14
  17. package/lib/commands/publish.js +82 -82
  18. package/lib/commands/start.d.ts +7 -7
  19. package/lib/commands/start.js +239 -208
  20. package/lib/commands/test.d.ts +6 -6
  21. package/lib/commands/test.js +62 -62
  22. package/lib/index.d.ts +1 -1
  23. package/lib/index.js +4 -4
  24. package/lib/managers/config/allowed_files.d.ts +5 -5
  25. package/lib/managers/config/allowed_files.js +30 -30
  26. package/lib/managers/config/defaults.d.ts +41 -41
  27. package/lib/managers/config/defaults.js +44 -44
  28. package/lib/managers/config/exercise.d.ts +36 -36
  29. package/lib/managers/config/exercise.js +236 -236
  30. package/lib/managers/config/index.d.ts +3 -3
  31. package/lib/managers/config/index.js +337 -337
  32. package/lib/managers/file.d.ts +14 -14
  33. package/lib/managers/file.js +153 -153
  34. package/lib/managers/gitpod.d.ts +3 -3
  35. package/lib/managers/gitpod.js +67 -67
  36. package/lib/managers/server/index.d.ts +6 -6
  37. package/lib/managers/server/index.js +58 -58
  38. package/lib/managers/server/routes.d.ts +4 -4
  39. package/lib/managers/server/routes.js +219 -219
  40. package/lib/managers/session.d.ts +3 -3
  41. package/lib/managers/session.js +125 -125
  42. package/lib/managers/socket.d.ts +3 -3
  43. package/lib/managers/socket.js +176 -176
  44. package/lib/managers/telemetry.d.ts +74 -74
  45. package/lib/managers/telemetry.js +206 -206
  46. package/lib/managers/test.js +84 -84
  47. package/lib/models/action.d.ts +2 -2
  48. package/lib/models/action.js +2 -2
  49. package/lib/models/audit.d.ts +15 -15
  50. package/lib/models/audit.js +2 -2
  51. package/lib/models/config-manager.d.ts +21 -21
  52. package/lib/models/config-manager.js +2 -2
  53. package/lib/models/config.d.ts +68 -67
  54. package/lib/models/config.js +2 -2
  55. package/lib/models/counter.d.ts +11 -11
  56. package/lib/models/counter.js +2 -2
  57. package/lib/models/errors.d.ts +15 -15
  58. package/lib/models/errors.js +2 -2
  59. package/lib/models/exercise-obj.d.ts +30 -30
  60. package/lib/models/exercise-obj.js +2 -2
  61. package/lib/models/file.d.ts +5 -5
  62. package/lib/models/file.js +2 -2
  63. package/lib/models/findings.d.ts +17 -17
  64. package/lib/models/findings.js +2 -2
  65. package/lib/models/flags.d.ts +10 -10
  66. package/lib/models/flags.js +2 -2
  67. package/lib/models/front-matter.d.ts +11 -11
  68. package/lib/models/front-matter.js +2 -2
  69. package/lib/models/gitpod-data.d.ts +16 -16
  70. package/lib/models/gitpod-data.js +2 -2
  71. package/lib/models/language.d.ts +4 -4
  72. package/lib/models/language.js +2 -2
  73. package/lib/models/package.d.ts +7 -7
  74. package/lib/models/package.js +2 -2
  75. package/lib/models/plugin-config.d.ts +16 -16
  76. package/lib/models/plugin-config.js +2 -2
  77. package/lib/models/session.d.ts +31 -31
  78. package/lib/models/session.js +2 -2
  79. package/lib/models/socket.d.ts +36 -32
  80. package/lib/models/socket.js +2 -2
  81. package/lib/models/status.d.ts +1 -1
  82. package/lib/models/status.js +2 -2
  83. package/lib/models/success-types.d.ts +1 -1
  84. package/lib/models/success-types.js +2 -2
  85. package/lib/plugin/command/compile.d.ts +6 -6
  86. package/lib/plugin/command/compile.js +18 -18
  87. package/lib/plugin/command/test.d.ts +6 -6
  88. package/lib/plugin/command/test.js +25 -25
  89. package/lib/plugin/index.d.ts +27 -27
  90. package/lib/plugin/index.js +7 -7
  91. package/lib/plugin/plugin.d.ts +8 -8
  92. package/lib/plugin/plugin.js +68 -68
  93. package/lib/plugin/utils.d.ts +16 -16
  94. package/lib/plugin/utils.js +58 -58
  95. package/lib/ui/download.d.ts +5 -5
  96. package/lib/ui/download.js +61 -61
  97. package/lib/utils/BaseCommand.d.ts +8 -8
  98. package/lib/utils/BaseCommand.js +41 -41
  99. package/lib/utils/SessionCommand.d.ts +10 -10
  100. package/lib/utils/SessionCommand.js +43 -43
  101. package/lib/utils/api.d.ts +14 -14
  102. package/lib/utils/api.js +255 -255
  103. package/lib/utils/audit.d.ts +16 -16
  104. package/lib/utils/audit.js +303 -303
  105. package/lib/utils/checkNotInstalled.d.ts +2 -0
  106. package/lib/utils/checkNotInstalled.js +36 -0
  107. package/lib/utils/console.d.ts +12 -12
  108. package/lib/utils/console.js +19 -19
  109. package/lib/utils/errors.d.ts +17 -17
  110. package/lib/utils/errors.js +100 -100
  111. package/lib/utils/exercisesQueue.d.ts +9 -9
  112. package/lib/utils/exercisesQueue.js +38 -38
  113. package/lib/utils/fileQueue.d.ts +40 -40
  114. package/lib/utils/fileQueue.js +168 -168
  115. package/lib/utils/misc.d.ts +1 -1
  116. package/lib/utils/misc.js +23 -23
  117. package/lib/utils/osOperations.d.ts +5 -5
  118. package/lib/utils/osOperations.js +72 -72
  119. package/lib/utils/validators.d.ts +5 -5
  120. package/lib/utils/validators.js +17 -17
  121. package/lib/utils/watcher.d.ts +2 -2
  122. package/lib/utils/watcher.js +25 -25
  123. package/oclif.manifest.json +1 -1
  124. package/package.json +139 -139
  125. package/src/commands/audit.ts +443 -443
  126. package/src/commands/clean.ts +29 -29
  127. package/src/commands/download.ts +61 -61
  128. package/src/commands/init.ts +170 -170
  129. package/src/commands/login.ts +42 -42
  130. package/src/commands/logout.ts +43 -43
  131. package/src/commands/publish.ts +107 -107
  132. package/src/commands/start.ts +53 -23
  133. package/src/commands/test.ts +85 -85
  134. package/src/index.ts +1 -1
  135. package/src/managers/config/allowed_files.ts +29 -29
  136. package/src/managers/config/defaults.ts +42 -42
  137. package/src/managers/config/exercise.ts +311 -311
  138. package/src/managers/config/index.ts +455 -455
  139. package/src/managers/file.ts +196 -196
  140. package/src/managers/gitpod.ts +84 -84
  141. package/src/managers/server/index.ts +78 -78
  142. package/src/managers/server/routes.ts +330 -330
  143. package/src/managers/session.ts +145 -145
  144. package/src/managers/socket.ts +250 -250
  145. package/src/managers/telemetry.ts +346 -346
  146. package/src/managers/test.ts +83 -83
  147. package/src/models/action.ts +10 -10
  148. package/src/models/audit.ts +16 -16
  149. package/src/models/config-manager.ts +23 -23
  150. package/src/models/config.ts +5 -3
  151. package/src/models/counter.ts +11 -11
  152. package/src/models/errors.ts +22 -22
  153. package/src/models/exercise-obj.ts +29 -29
  154. package/src/models/file.ts +5 -5
  155. package/src/models/findings.ts +18 -18
  156. package/src/models/flags.ts +10 -10
  157. package/src/models/front-matter.ts +11 -11
  158. package/src/models/gitpod-data.ts +19 -19
  159. package/src/models/language.ts +4 -4
  160. package/src/models/package.ts +7 -7
  161. package/src/models/plugin-config.ts +17 -17
  162. package/src/models/session.ts +34 -34
  163. package/src/models/socket.ts +5 -0
  164. package/src/models/status.ts +16 -16
  165. package/src/models/success-types.ts +1 -1
  166. package/src/plugin/command/compile.ts +17 -17
  167. package/src/plugin/command/test.ts +30 -30
  168. package/src/plugin/index.ts +6 -6
  169. package/src/plugin/plugin.ts +94 -94
  170. package/src/plugin/utils.ts +87 -87
  171. package/src/types/node-fetch.d.ts +1 -1
  172. package/src/ui/download.ts +71 -71
  173. package/src/utils/BaseCommand.ts +48 -48
  174. package/src/utils/SessionCommand.ts +43 -43
  175. package/src/utils/api.ts +303 -303
  176. package/src/utils/audit.ts +393 -393
  177. package/src/utils/checkNotInstalled.ts +46 -0
  178. package/src/utils/console.ts +24 -24
  179. package/src/utils/errors.ts +117 -117
  180. package/src/utils/exercisesQueue.ts +51 -51
  181. package/src/utils/fileQueue.ts +198 -198
  182. package/src/utils/misc.ts +23 -23
  183. package/src/utils/osOperations.ts +79 -79
  184. package/src/utils/templates/gitignore.txt +19 -19
  185. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.es.md +24 -24
  186. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.md +24 -24
  187. package/src/utils/templates/incremental/README.ejs +4 -4
  188. package/src/utils/templates/incremental/README.es.ejs +4 -4
  189. package/src/utils/templates/isolated/01-hello-world/README.es.md +26 -26
  190. package/src/utils/templates/isolated/01-hello-world/README.md +26 -26
  191. package/src/utils/templates/isolated/README.ejs +4 -4
  192. package/src/utils/templates/isolated/README.es.ejs +4 -4
  193. package/src/utils/templates/no-grading/README.ejs +4 -4
  194. package/src/utils/templates/no-grading/README.es.ejs +4 -4
  195. package/src/utils/validators.ts +18 -18
  196. package/src/utils/watcher.ts +27 -27
@@ -1,337 +1,337 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const path = require("path");
4
- const fs = require("fs");
5
- const shell = require("shelljs");
6
- const console_1 = require("../../utils/console");
7
- const watcher_1 = require("../../utils/watcher");
8
- const errors_1 = require("../../utils/errors");
9
- const defaults_1 = require("./defaults");
10
- const exercise_1 = require("./exercise");
11
- const file_1 = require("../file");
12
- /* exercise folder name standard */
13
- // eslint-disable-next-line
14
- const fetch = require("node-fetch");
15
- // eslint-disable-next-line
16
- const chalk = require("chalk");
17
- /* exercise folder name standard */
18
- /**
19
- * Retrieves the configuration path for the learnpack package.
20
- *
21
- * @returns An object containing the configuration file path and the base directory.
22
- * @throws NotFoundError if the learn.json file is not found in the current folder.
23
- */
24
- const getConfigPath = () => {
25
- const possibleFileNames = ["learn.json", ".learn/learn.json"];
26
- const config = possibleFileNames.find(file => fs.existsSync(file)) || null;
27
- if (config && fs.existsSync(".breathecode"))
28
- return { config, base: ".breathecode" };
29
- if (config === null)
30
- throw errors_1.NotFoundError("learn.json file not found on current folder, is this a learnpack package?");
31
- return { config, base: ".learn" };
32
- };
33
- const getExercisesPath = (base) => {
34
- const possibleFileNames = ["./exercises", base + "/exercises", "./"];
35
- return possibleFileNames.find(file => fs.existsSync(file)) || null;
36
- };
37
- const getGitpodAddress = () => {
38
- if (shell.exec("gp -h", { silent: true }).code === 0) {
39
- return shell
40
- .exec("gp url", { silent: true })
41
- .stdout.replace(/(\r\n|\n|\r)/gm, "");
42
- }
43
- console_1.default.debug("Gitpod command line tool not found");
44
- return "http://localhost";
45
- };
46
- const getCodespacesNamespace = () => {
47
- // Example: https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
48
- const codespace_name = shell
49
- .exec("echo $CODESPACE_NAME", { silent: true })
50
- .stdout.replace(/(\r\n|\n|\r)/gm, "");
51
- if (!codespace_name ||
52
- codespace_name === "" ||
53
- codespace_name === undefined ||
54
- // ! I added this line
55
- codespace_name === "$CODESPACE_NAME") {
56
- return null;
57
- }
58
- return codespace_name;
59
- };
60
- exports.default = async ({ grading, mode, disableGrading, version, }) => {
61
- var _a, _b, _c, _d;
62
- const confPath = getConfigPath();
63
- console_1.default.debug("This is the config path: ", confPath);
64
- let configObj = {};
65
- if (confPath) {
66
- const learnJsonContent = fs.readFileSync(confPath.config);
67
- let hiddenBcContent = {};
68
- if (fs.existsSync(confPath.base + "/config.json")) {
69
- hiddenBcContent = fs.readFileSync(confPath.base + "/config.json");
70
- hiddenBcContent = JSON.parse(hiddenBcContent);
71
- if (!hiddenBcContent)
72
- throw new Error(`Invalid ${confPath.base}/config.json syntax: Unable to parse.`);
73
- }
74
- const jsonConfig = JSON.parse(`${learnJsonContent}`);
75
- if (!jsonConfig)
76
- throw new Error(`Invalid ${confPath.config} syntax: Unable to parse.`);
77
- let session;
78
- // add using id to the installation
79
- if (!jsonConfig.session) {
80
- session = Math.floor(Math.random() * 10000000000000000000);
81
- }
82
- else {
83
- session = jsonConfig.session;
84
- delete jsonConfig.session;
85
- }
86
- configObj = deepMerge(hiddenBcContent, {
87
- config: jsonConfig,
88
- session: session,
89
- });
90
- }
91
- else {
92
- throw errors_1.ValidationError("No learn.json file has been found, make sure you are in the correct directory. To start a new LearnPack package run: $ learnpack init my-course-name");
93
- }
94
- configObj = deepMerge(defaults_1.default || {}, configObj, {
95
- config: {
96
- grading: grading || ((_a = configObj.config) === null || _a === void 0 ? void 0 : _a.grading),
97
- configPath: confPath.config,
98
- },
99
- });
100
- if (!configObj.config)
101
- throw errors_1.InternalError("Config object not found");
102
- configObj.config.outputPath = confPath.base + "/dist";
103
- console_1.default.debug("This is your configuration object: ", Object.assign(Object.assign({}, configObj), { exercises: configObj.exercises ?
104
- configObj.exercises.map(e => e.slug) :
105
- [] }));
106
- // auto detect agent (if possible)
107
- const codespaces_workspace = getCodespacesNamespace();
108
- console_1.default.debug("This is the codespace namespace: ", codespaces_workspace);
109
- console_1.default.debug("This is the agent, and this should be null an the beginning: ", (_c = (_b = configObj.config) === null || _b === void 0 ? void 0 : _b.editor) === null || _c === void 0 ? void 0 : _c.agent);
110
- if (shell.which("gp") && configObj && configObj.config) {
111
- console_1.default.debug("Gitpod detected");
112
- configObj.address = getGitpodAddress();
113
- configObj.config.publicUrl = `https://${configObj.config.port}-${(_d = configObj.address) === null || _d === void 0 ? void 0 : _d.slice(8)}`;
114
- configObj.config.editor.agent = "vscode";
115
- }
116
- else if (configObj.config && Boolean(codespaces_workspace)) {
117
- console_1.default.debug("Codespaces detected: ", codespaces_workspace);
118
- configObj.address = `https://${codespaces_workspace}.github.dev`;
119
- configObj.config.publicUrl = `https://${codespaces_workspace}-${configObj.config.port}.app.github.dev`;
120
- configObj.config.editor.agent = "vscode";
121
- }
122
- else {
123
- console_1.default.debug("Neither Gitpod nor Codespaces detected, using localhost.");
124
- configObj.address = `http://localhost:${configObj.config.port}`;
125
- configObj.config.publicUrl = `http://localhost:${configObj.config.port}`;
126
- }
127
- if (!configObj.config.editor.agent) {
128
- configObj.config.editor.agent =
129
- process.env.TERM_PROGRAM === "vscode" ? "vscode" : "os";
130
- }
131
- if (configObj.config && !configObj.config.publicUrl)
132
- configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`;
133
- if (configObj.config && !configObj.config.editor.mode && mode) {
134
- configObj.config.editor.mode = mode;
135
- }
136
- if (!configObj.config.editor.mode) {
137
- configObj.config.editor.mode =
138
- configObj.config.editor.agent === "vscode" ? "extension" : "preview";
139
- }
140
- if (version)
141
- configObj.config.editor.version = version;
142
- else if (configObj.config.editor.version === null) {
143
- console_1.default.debug("Config version not found, downloading default.");
144
- const resp = await fetch("https://raw.githubusercontent.com/learnpack/ide/master/package.json");
145
- const packageJSON = await resp.json();
146
- configObj.config.editor.version = packageJSON.version || "3.1.22";
147
- }
148
- configObj.config.dirPath = "./" + confPath.base;
149
- configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./";
150
- return {
151
- validLanguages: {},
152
- get: () => configObj,
153
- validateEngine: function (language, server, socket) {
154
- // eslint-disable-next-line
155
- const alias = (_l) => {
156
- const map = {
157
- python3: "python",
158
- };
159
- if (map[_l])
160
- return map[_l];
161
- return _l;
162
- };
163
- // decode aliases
164
- language = alias(language);
165
- if (this.validLanguages[language])
166
- return true;
167
- console_1.default.debug(`Validating engine for ${language} compilation`);
168
- let result = shell.exec("learnpack plugins", { silent: true });
169
- if (result.code === 0 &&
170
- result.stdout.includes(`learnpack-${language}`)) {
171
- this.validLanguages[language] = true;
172
- return true;
173
- }
174
- console_1.default.info(`Language engine for ${language} not found, installing...`);
175
- // Install the compiler in their new versions
176
- result = shell.exec(`learnpack plugins:install learnpack-${language}`, {
177
- silent: true,
178
- });
179
- if (result.code === 0) {
180
- socket.log("compiling", "Installing the python compiler, you will have to reset the exercises after installation by writing on your terminal: $ learnpack run");
181
- console_1.default.info(`Successfully installed the ${language} exercise engine, \n please start learnpack again by running the following command: \n ${chalk.white("$ learnpack start")}\n\n `);
182
- server.terminate();
183
- return false;
184
- }
185
- this.validLanguages[language] = false;
186
- socket.error(`Error installing ${language} exercise engine`);
187
- console_1.default.error(`Error installing ${language} exercise engine`);
188
- console_1.default.log(result.stdout);
189
- throw errors_1.InternalError(`Error installing ${language} exercise engine`);
190
- },
191
- clean: () => {
192
- if (configObj.config) {
193
- if (configObj.config.outputPath) {
194
- file_1.rmSync(configObj.config.outputPath);
195
- }
196
- file_1.rmSync(configObj.config.dirPath + "/_app");
197
- file_1.rmSync(configObj.config.dirPath + "/reports");
198
- file_1.rmSync(configObj.config.dirPath + "/.session");
199
- file_1.rmSync(configObj.config.dirPath + "/resets");
200
- // clean tag gz
201
- if (fs.existsSync(configObj.config.dirPath + "/app.tar.gz"))
202
- fs.unlinkSync(configObj.config.dirPath + "/app.tar.gz");
203
- if (fs.existsSync(configObj.config.dirPath + "/config.json"))
204
- fs.unlinkSync(configObj.config.dirPath + "/config.json");
205
- if (fs.existsSync(configObj.config.dirPath + "/telemetry.json"))
206
- fs.unlinkSync(configObj.config.dirPath + "/telemetry.json");
207
- if (fs.existsSync(configObj.config.dirPath + "/vscode_queue.json"))
208
- fs.unlinkSync(configObj.config.dirPath + "/vscode_queue.json");
209
- }
210
- },
211
- getExercise: slug => {
212
- console_1.default.debug("ExercisePath Slug", slug);
213
- const exercise = (configObj.exercises || []).find(ex => ex.slug === slug);
214
- if (!exercise)
215
- throw errors_1.ValidationError(`Exercise ${slug} not found`);
216
- return exercise;
217
- },
218
- getAllExercises: () => {
219
- return configObj.exercises;
220
- },
221
- startExercise: function (slug) {
222
- const exercise = this.getExercise(slug);
223
- // set config.json with current exercise
224
- configObj.currentExercise = exercise.slug;
225
- this.save();
226
- // eslint-disable-next-line
227
- exercise.files.forEach((f) => {
228
- if (configObj.config) {
229
- const _path = configObj.config.outputPath + "/" + f.name;
230
- if (f.hidden === false && fs.existsSync(_path))
231
- fs.unlinkSync(_path);
232
- }
233
- });
234
- return exercise;
235
- },
236
- noCurrentExercise: function () {
237
- configObj.currentExercise = null;
238
- this.save();
239
- },
240
- reset: slug => {
241
- var _a;
242
- if (configObj.config &&
243
- !fs.existsSync(`${configObj.config.dirPath}/resets/` + slug))
244
- throw errors_1.ValidationError("Could not find the original files for " + slug);
245
- const exercise = (configObj.exercises || []).find(ex => ex.slug === slug);
246
- if (!exercise)
247
- throw errors_1.ValidationError(`Exercise ${slug} not found on the configuration`);
248
- if (configObj.config) {
249
- for (const fileName of fs.readdirSync(`${configObj.config.dirPath}/resets/${slug}/`)) {
250
- const content = fs.readFileSync(`${(_a = configObj.config) === null || _a === void 0 ? void 0 : _a.dirPath}/resets/${slug}/${fileName}`);
251
- fs.writeFileSync(`${exercise.path}/${fileName}`, content);
252
- }
253
- }
254
- },
255
- buildIndex: function () {
256
- var _a, _b;
257
- console_1.default.info("Building the exercise index...");
258
- const isDirectory = (source) => {
259
- var _a;
260
- const name = path.basename(source);
261
- if (name === path.basename(((_a = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _a === void 0 ? void 0 : _a.dirPath) || ""))
262
- return false;
263
- // ignore folders that start with a dot
264
- if (name.charAt(0) === "." || name.charAt(0) === "_")
265
- return false;
266
- return fs.lstatSync(source).isDirectory();
267
- };
268
- const getDirectories = (source) => fs
269
- .readdirSync(source)
270
- .map(name => path.join(source, name))
271
- .filter(isDirectory);
272
- // add the .learn folder
273
- if (!fs.existsSync(confPath.base))
274
- fs.mkdirSync(confPath.base);
275
- // add the outout folder where webpack will publish the the html/css/js files
276
- if (configObj.config &&
277
- configObj.config.outputPath &&
278
- !fs.existsSync(configObj.config.outputPath))
279
- fs.mkdirSync(configObj.config.outputPath);
280
- // TODO: we could use npm library front-mater to read the title of the exercises from the README.md
281
- const grupedByDirectory = getDirectories(((_a = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _a === void 0 ? void 0 : _a.exercisesPath) || "");
282
- configObj.exercises =
283
- grupedByDirectory.length > 0 ?
284
- grupedByDirectory.map((path, position) => exercise_1.exercise(path, position, configObj)) :
285
- [exercise_1.exercise(((_b = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _b === void 0 ? void 0 : _b.exercisesPath) || "", 0, configObj)];
286
- this.save();
287
- },
288
- watchIndex: function (onChange) {
289
- var _a;
290
- if (configObj.config && !configObj.config.exercisesPath)
291
- throw errors_1.ValidationError("No exercises directory to watch: " + configObj.config.exercisesPath);
292
- this.buildIndex();
293
- watcher_1.default(((_a = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _a === void 0 ? void 0 : _a.exercisesPath) || "", onChange)
294
- .then(() => {
295
- console_1.default.debug("Changes detected on your exercises");
296
- this.buildIndex();
297
- // if (onChange) onChange(filename);
298
- })
299
- .catch(error => {
300
- throw error;
301
- });
302
- },
303
- save: () => {
304
- // Console.debug("Saving configuration with: ", configObj)
305
- // remove the duplicates form the actions array
306
- // configObj.config.actions = [...new Set(configObj.config.actions)];
307
- if (configObj.config) {
308
- configObj.config.translations = [
309
- ...new Set(configObj.config.translations),
310
- ];
311
- fs.writeFileSync(configObj.config.dirPath + "/config.json", JSON.stringify(configObj, null, 4));
312
- }
313
- },
314
- };
315
- };
316
- function deepMerge(...sources) {
317
- let acc = {};
318
- for (const source of sources) {
319
- if (Array.isArray(source)) {
320
- if (!Array.isArray(acc)) {
321
- acc = [];
322
- }
323
- acc = [...source];
324
- }
325
- else if (source instanceof Object) {
326
- // eslint-disable-next-line
327
- for (let [key, value] of Object.entries(source)) {
328
- if (value instanceof Object && key in acc) {
329
- value = deepMerge(acc[key], value);
330
- }
331
- if (value !== undefined)
332
- acc = Object.assign(Object.assign({}, acc), { [key]: value });
333
- }
334
- }
335
- }
336
- return acc;
337
- }
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const path = require("path");
4
+ const fs = require("fs");
5
+ const shell = require("shelljs");
6
+ const console_1 = require("../../utils/console");
7
+ const watcher_1 = require("../../utils/watcher");
8
+ const errors_1 = require("../../utils/errors");
9
+ const defaults_1 = require("./defaults");
10
+ const exercise_1 = require("./exercise");
11
+ const file_1 = require("../file");
12
+ /* exercise folder name standard */
13
+ // eslint-disable-next-line
14
+ const fetch = require("node-fetch");
15
+ // eslint-disable-next-line
16
+ const chalk = require("chalk");
17
+ /* exercise folder name standard */
18
+ /**
19
+ * Retrieves the configuration path for the learnpack package.
20
+ *
21
+ * @returns An object containing the configuration file path and the base directory.
22
+ * @throws NotFoundError if the learn.json file is not found in the current folder.
23
+ */
24
+ const getConfigPath = () => {
25
+ const possibleFileNames = ["learn.json", ".learn/learn.json"];
26
+ const config = possibleFileNames.find(file => fs.existsSync(file)) || null;
27
+ if (config && fs.existsSync(".breathecode"))
28
+ return { config, base: ".breathecode" };
29
+ if (config === null)
30
+ throw errors_1.NotFoundError("learn.json file not found on current folder, is this a learnpack package?");
31
+ return { config, base: ".learn" };
32
+ };
33
+ const getExercisesPath = (base) => {
34
+ const possibleFileNames = ["./exercises", base + "/exercises", "./"];
35
+ return possibleFileNames.find(file => fs.existsSync(file)) || null;
36
+ };
37
+ const getGitpodAddress = () => {
38
+ if (shell.exec("gp -h", { silent: true }).code === 0) {
39
+ return shell
40
+ .exec("gp url", { silent: true })
41
+ .stdout.replace(/(\r\n|\n|\r)/gm, "");
42
+ }
43
+ console_1.default.debug("Gitpod command line tool not found");
44
+ return "http://localhost";
45
+ };
46
+ const getCodespacesNamespace = () => {
47
+ // Example: https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
48
+ const codespace_name = shell
49
+ .exec("echo $CODESPACE_NAME", { silent: true })
50
+ .stdout.replace(/(\r\n|\n|\r)/gm, "");
51
+ if (!codespace_name ||
52
+ codespace_name === "" ||
53
+ codespace_name === undefined ||
54
+ // ! I added this line
55
+ codespace_name === "$CODESPACE_NAME") {
56
+ return null;
57
+ }
58
+ return codespace_name;
59
+ };
60
+ exports.default = async ({ grading, mode, disableGrading, version, }) => {
61
+ var _a, _b, _c, _d;
62
+ const confPath = getConfigPath();
63
+ console_1.default.debug("This is the config path: ", confPath);
64
+ let configObj = {};
65
+ if (confPath) {
66
+ const learnJsonContent = fs.readFileSync(confPath.config);
67
+ let hiddenBcContent = {};
68
+ if (fs.existsSync(confPath.base + "/config.json")) {
69
+ hiddenBcContent = fs.readFileSync(confPath.base + "/config.json");
70
+ hiddenBcContent = JSON.parse(hiddenBcContent);
71
+ if (!hiddenBcContent)
72
+ throw new Error(`Invalid ${confPath.base}/config.json syntax: Unable to parse.`);
73
+ }
74
+ const jsonConfig = JSON.parse(`${learnJsonContent}`);
75
+ if (!jsonConfig)
76
+ throw new Error(`Invalid ${confPath.config} syntax: Unable to parse.`);
77
+ let session;
78
+ // add using id to the installation
79
+ if (!jsonConfig.session) {
80
+ session = Math.floor(Math.random() * 10000000000000000000);
81
+ }
82
+ else {
83
+ session = jsonConfig.session;
84
+ delete jsonConfig.session;
85
+ }
86
+ configObj = deepMerge(hiddenBcContent, {
87
+ config: jsonConfig,
88
+ session: session,
89
+ });
90
+ }
91
+ else {
92
+ throw errors_1.ValidationError("No learn.json file has been found, make sure you are in the correct directory. To start a new LearnPack package run: $ learnpack init my-course-name");
93
+ }
94
+ configObj = deepMerge(defaults_1.default || {}, configObj, {
95
+ config: {
96
+ grading: grading || ((_a = configObj.config) === null || _a === void 0 ? void 0 : _a.grading),
97
+ configPath: confPath.config,
98
+ },
99
+ });
100
+ if (!configObj.config)
101
+ throw errors_1.InternalError("Config object not found");
102
+ configObj.config.outputPath = confPath.base + "/dist";
103
+ console_1.default.debug("This is your configuration object: ", Object.assign(Object.assign({}, configObj), { exercises: configObj.exercises ?
104
+ configObj.exercises.map(e => e.slug) :
105
+ [] }));
106
+ // auto detect agent (if possible)
107
+ const codespaces_workspace = getCodespacesNamespace();
108
+ console_1.default.debug("This is the codespace namespace: ", codespaces_workspace);
109
+ console_1.default.debug("This is the agent, and this should be null an the beginning: ", (_c = (_b = configObj.config) === null || _b === void 0 ? void 0 : _b.editor) === null || _c === void 0 ? void 0 : _c.agent);
110
+ if (shell.which("gp") && configObj && configObj.config) {
111
+ console_1.default.debug("Gitpod detected");
112
+ configObj.address = getGitpodAddress();
113
+ configObj.config.publicUrl = `https://${configObj.config.port}-${(_d = configObj.address) === null || _d === void 0 ? void 0 : _d.slice(8)}`;
114
+ configObj.config.editor.agent = "vscode";
115
+ }
116
+ else if (configObj.config && Boolean(codespaces_workspace)) {
117
+ console_1.default.debug("Codespaces detected: ", codespaces_workspace);
118
+ configObj.address = `https://${codespaces_workspace}.github.dev`;
119
+ configObj.config.publicUrl = `https://${codespaces_workspace}-${configObj.config.port}.app.github.dev`;
120
+ configObj.config.editor.agent = "vscode";
121
+ }
122
+ else {
123
+ console_1.default.debug("Neither Gitpod nor Codespaces detected, using localhost.");
124
+ configObj.address = `http://localhost:${configObj.config.port}`;
125
+ configObj.config.publicUrl = `http://localhost:${configObj.config.port}`;
126
+ }
127
+ if (!configObj.config.editor.agent) {
128
+ configObj.config.editor.agent =
129
+ process.env.TERM_PROGRAM === "vscode" ? "vscode" : "os";
130
+ }
131
+ if (configObj.config && !configObj.config.publicUrl)
132
+ configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`;
133
+ if (configObj.config && !configObj.config.editor.mode && mode) {
134
+ configObj.config.editor.mode = mode;
135
+ }
136
+ if (!configObj.config.editor.mode) {
137
+ configObj.config.editor.mode =
138
+ configObj.config.editor.agent === "vscode" ? "extension" : "preview";
139
+ }
140
+ if (version)
141
+ configObj.config.editor.version = version;
142
+ else if (configObj.config.editor.version === null) {
143
+ console_1.default.debug("Config version not found, downloading default.");
144
+ const resp = await fetch("https://raw.githubusercontent.com/learnpack/ide/master/package.json");
145
+ const packageJSON = await resp.json();
146
+ configObj.config.editor.version = packageJSON.version || "3.1.22";
147
+ }
148
+ configObj.config.dirPath = "./" + confPath.base;
149
+ configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./";
150
+ return {
151
+ validLanguages: {},
152
+ get: () => configObj,
153
+ validateEngine: function (language, server, socket) {
154
+ // eslint-disable-next-line
155
+ const alias = (_l) => {
156
+ const map = {
157
+ python3: "python",
158
+ };
159
+ if (map[_l])
160
+ return map[_l];
161
+ return _l;
162
+ };
163
+ // decode aliases
164
+ language = alias(language);
165
+ if (this.validLanguages[language])
166
+ return true;
167
+ console_1.default.debug(`Validating engine for ${language} compilation`);
168
+ let result = shell.exec("learnpack plugins", { silent: true });
169
+ if (result.code === 0 &&
170
+ result.stdout.includes(`learnpack-${language}`)) {
171
+ this.validLanguages[language] = true;
172
+ return true;
173
+ }
174
+ console_1.default.info(`Language engine for ${language} not found, installing...`);
175
+ // Install the compiler in their new versions
176
+ result = shell.exec(`learnpack plugins:install learnpack-${language}`, {
177
+ silent: true,
178
+ });
179
+ if (result.code === 0) {
180
+ socket.log("compiling", "Installing the python compiler, you will have to reset the exercises after installation by writing on your terminal: $ learnpack run");
181
+ console_1.default.info(`Successfully installed the ${language} exercise engine, \n please start learnpack again by running the following command: \n ${chalk.white("$ learnpack start")}\n\n `);
182
+ server.terminate();
183
+ return false;
184
+ }
185
+ this.validLanguages[language] = false;
186
+ socket.error(`Error installing ${language} exercise engine`);
187
+ console_1.default.error(`Error installing ${language} exercise engine`);
188
+ console_1.default.log(result.stdout);
189
+ throw errors_1.InternalError(`Error installing ${language} exercise engine`);
190
+ },
191
+ clean: () => {
192
+ if (configObj.config) {
193
+ if (configObj.config.outputPath) {
194
+ file_1.rmSync(configObj.config.outputPath);
195
+ }
196
+ file_1.rmSync(configObj.config.dirPath + "/_app");
197
+ file_1.rmSync(configObj.config.dirPath + "/reports");
198
+ file_1.rmSync(configObj.config.dirPath + "/.session");
199
+ file_1.rmSync(configObj.config.dirPath + "/resets");
200
+ // clean tag gz
201
+ if (fs.existsSync(configObj.config.dirPath + "/app.tar.gz"))
202
+ fs.unlinkSync(configObj.config.dirPath + "/app.tar.gz");
203
+ if (fs.existsSync(configObj.config.dirPath + "/config.json"))
204
+ fs.unlinkSync(configObj.config.dirPath + "/config.json");
205
+ if (fs.existsSync(configObj.config.dirPath + "/telemetry.json"))
206
+ fs.unlinkSync(configObj.config.dirPath + "/telemetry.json");
207
+ if (fs.existsSync(configObj.config.dirPath + "/vscode_queue.json"))
208
+ fs.unlinkSync(configObj.config.dirPath + "/vscode_queue.json");
209
+ }
210
+ },
211
+ getExercise: slug => {
212
+ console_1.default.debug("ExercisePath Slug", slug);
213
+ const exercise = (configObj.exercises || []).find(ex => ex.slug === slug);
214
+ if (!exercise)
215
+ throw errors_1.ValidationError(`Exercise ${slug} not found`);
216
+ return exercise;
217
+ },
218
+ getAllExercises: () => {
219
+ return configObj.exercises;
220
+ },
221
+ startExercise: function (slug) {
222
+ const exercise = this.getExercise(slug);
223
+ // set config.json with current exercise
224
+ configObj.currentExercise = exercise.slug;
225
+ this.save();
226
+ // eslint-disable-next-line
227
+ exercise.files.forEach((f) => {
228
+ if (configObj.config) {
229
+ const _path = configObj.config.outputPath + "/" + f.name;
230
+ if (f.hidden === false && fs.existsSync(_path))
231
+ fs.unlinkSync(_path);
232
+ }
233
+ });
234
+ return exercise;
235
+ },
236
+ noCurrentExercise: function () {
237
+ configObj.currentExercise = null;
238
+ this.save();
239
+ },
240
+ reset: slug => {
241
+ var _a;
242
+ if (configObj.config &&
243
+ !fs.existsSync(`${configObj.config.dirPath}/resets/` + slug))
244
+ throw errors_1.ValidationError("Could not find the original files for " + slug);
245
+ const exercise = (configObj.exercises || []).find(ex => ex.slug === slug);
246
+ if (!exercise)
247
+ throw errors_1.ValidationError(`Exercise ${slug} not found on the configuration`);
248
+ if (configObj.config) {
249
+ for (const fileName of fs.readdirSync(`${configObj.config.dirPath}/resets/${slug}/`)) {
250
+ const content = fs.readFileSync(`${(_a = configObj.config) === null || _a === void 0 ? void 0 : _a.dirPath}/resets/${slug}/${fileName}`);
251
+ fs.writeFileSync(`${exercise.path}/${fileName}`, content);
252
+ }
253
+ }
254
+ },
255
+ buildIndex: function () {
256
+ var _a, _b;
257
+ console_1.default.info("Building the exercise index...");
258
+ const isDirectory = (source) => {
259
+ var _a;
260
+ const name = path.basename(source);
261
+ if (name === path.basename(((_a = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _a === void 0 ? void 0 : _a.dirPath) || ""))
262
+ return false;
263
+ // ignore folders that start with a dot
264
+ if (name.charAt(0) === "." || name.charAt(0) === "_")
265
+ return false;
266
+ return fs.lstatSync(source).isDirectory();
267
+ };
268
+ const getDirectories = (source) => fs
269
+ .readdirSync(source)
270
+ .map(name => path.join(source, name))
271
+ .filter(isDirectory);
272
+ // add the .learn folder
273
+ if (!fs.existsSync(confPath.base))
274
+ fs.mkdirSync(confPath.base);
275
+ // add the outout folder where webpack will publish the the html/css/js files
276
+ if (configObj.config &&
277
+ configObj.config.outputPath &&
278
+ !fs.existsSync(configObj.config.outputPath))
279
+ fs.mkdirSync(configObj.config.outputPath);
280
+ // TODO: we could use npm library front-mater to read the title of the exercises from the README.md
281
+ const grupedByDirectory = getDirectories(((_a = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _a === void 0 ? void 0 : _a.exercisesPath) || "");
282
+ configObj.exercises =
283
+ grupedByDirectory.length > 0 ?
284
+ grupedByDirectory.map((path, position) => exercise_1.exercise(path, position, configObj)) :
285
+ [exercise_1.exercise(((_b = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _b === void 0 ? void 0 : _b.exercisesPath) || "", 0, configObj)];
286
+ this.save();
287
+ },
288
+ watchIndex: function (onChange) {
289
+ var _a;
290
+ if (configObj.config && !configObj.config.exercisesPath)
291
+ throw errors_1.ValidationError("No exercises directory to watch: " + configObj.config.exercisesPath);
292
+ this.buildIndex();
293
+ watcher_1.default(((_a = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _a === void 0 ? void 0 : _a.exercisesPath) || "", onChange)
294
+ .then(() => {
295
+ console_1.default.debug("Changes detected on your exercises");
296
+ this.buildIndex();
297
+ // if (onChange) onChange(filename);
298
+ })
299
+ .catch(error => {
300
+ throw error;
301
+ });
302
+ },
303
+ save: () => {
304
+ // Console.debug("Saving configuration with: ", configObj)
305
+ // remove the duplicates form the actions array
306
+ // configObj.config.actions = [...new Set(configObj.config.actions)];
307
+ if (configObj.config) {
308
+ configObj.config.translations = [
309
+ ...new Set(configObj.config.translations),
310
+ ];
311
+ fs.writeFileSync(configObj.config.dirPath + "/config.json", JSON.stringify(configObj, null, 4));
312
+ }
313
+ },
314
+ };
315
+ };
316
+ function deepMerge(...sources) {
317
+ let acc = {};
318
+ for (const source of sources) {
319
+ if (Array.isArray(source)) {
320
+ if (!Array.isArray(acc)) {
321
+ acc = [];
322
+ }
323
+ acc = [...source];
324
+ }
325
+ else if (source instanceof Object) {
326
+ // eslint-disable-next-line
327
+ for (let [key, value] of Object.entries(source)) {
328
+ if (value instanceof Object && key in acc) {
329
+ value = deepMerge(acc[key], value);
330
+ }
331
+ if (value !== undefined)
332
+ acc = Object.assign(Object.assign({}, acc), { [key]: value });
333
+ }
334
+ }
335
+ }
336
+ return acc;
337
+ }