@learnpack/learnpack 2.1.38 → 2.1.40

Sign up to get free protection for your applications and to get access to all the features.
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 -205
  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 -336
  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 -4
  118. package/lib/utils/osOperations.js +72 -56
  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 -140
  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 +54 -17
  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 -63
  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,336 +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
- }
115
- else if (configObj.config && Boolean(codespaces_workspace)) {
116
- console_1.default.debug("Codespaces detected: ", codespaces_workspace);
117
- configObj.address = `https://${codespaces_workspace}.github.dev`;
118
- configObj.config.publicUrl = `https://${codespaces_workspace}-${configObj.config.port}.app.github.dev`;
119
- // TODO: Why is needed to have an agent?
120
- }
121
- else {
122
- console_1.default.debug("Neither Gitpod nor Codespaces detected, using localhost.");
123
- configObj.address = `http://localhost:${configObj.config.port}`;
124
- configObj.config.publicUrl = `http://localhost:${configObj.config.port}`;
125
- }
126
- if (!configObj.config.editor.agent) {
127
- configObj.config.editor.agent = shell.which("code") ? "vscode" : "os";
128
- }
129
- // TODO: We need to set the correct EventListener if the agent is not vscode
130
- if (configObj.config && !configObj.config.publicUrl)
131
- configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`;
132
- if (configObj.config && !configObj.config.editor.mode && mode) {
133
- configObj.config.editor.mode = mode;
134
- }
135
- if (!configObj.config.editor.mode) {
136
- configObj.config.editor.mode =
137
- configObj.config.editor.agent === "vscode" ? "extension" : "preview";
138
- }
139
- if (version)
140
- configObj.config.editor.version = version;
141
- else if (configObj.config.editor.version === null) {
142
- console_1.default.debug("Config version not found, downloading default.");
143
- const resp = await fetch("https://raw.githubusercontent.com/learnpack/ide/master/package.json");
144
- const packageJSON = await resp.json();
145
- configObj.config.editor.version = packageJSON.version || "3.1.22";
146
- }
147
- configObj.config.dirPath = "./" + confPath.base;
148
- configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./";
149
- return {
150
- validLanguages: {},
151
- get: () => configObj,
152
- validateEngine: function (language, server, socket) {
153
- // eslint-disable-next-line
154
- const alias = (_l) => {
155
- const map = {
156
- python3: "python",
157
- };
158
- if (map[_l])
159
- return map[_l];
160
- return _l;
161
- };
162
- // decode aliases
163
- language = alias(language);
164
- if (this.validLanguages[language])
165
- return true;
166
- console_1.default.debug(`Validating engine for ${language} compilation`);
167
- let result = shell.exec("learnpack plugins", { silent: true });
168
- if (result.code === 0 &&
169
- result.stdout.includes(`learnpack-${language}`)) {
170
- this.validLanguages[language] = true;
171
- return true;
172
- }
173
- console_1.default.info(`Language engine for ${language} not found, installing...`);
174
- // Install the compiler in their new versions
175
- result = shell.exec(`learnpack plugins:install learnpack-${language}`, {
176
- silent: true,
177
- });
178
- if (result.code === 0) {
179
- socket.log("compiling", "Installing the python compiler, you will have to reset the exercises after installation by writing on your terminal: $ learnpack run");
180
- 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 `);
181
- server.terminate();
182
- return false;
183
- }
184
- this.validLanguages[language] = false;
185
- socket.error(`Error installing ${language} exercise engine`);
186
- console_1.default.error(`Error installing ${language} exercise engine`);
187
- console_1.default.log(result.stdout);
188
- throw errors_1.InternalError(`Error installing ${language} exercise engine`);
189
- },
190
- clean: () => {
191
- if (configObj.config) {
192
- if (configObj.config.outputPath) {
193
- file_1.rmSync(configObj.config.outputPath);
194
- }
195
- file_1.rmSync(configObj.config.dirPath + "/_app");
196
- file_1.rmSync(configObj.config.dirPath + "/reports");
197
- file_1.rmSync(configObj.config.dirPath + "/.session");
198
- file_1.rmSync(configObj.config.dirPath + "/resets");
199
- // clean tag gz
200
- if (fs.existsSync(configObj.config.dirPath + "/app.tar.gz"))
201
- fs.unlinkSync(configObj.config.dirPath + "/app.tar.gz");
202
- if (fs.existsSync(configObj.config.dirPath + "/config.json"))
203
- fs.unlinkSync(configObj.config.dirPath + "/config.json");
204
- if (fs.existsSync(configObj.config.dirPath + "/telemetry.json"))
205
- fs.unlinkSync(configObj.config.dirPath + "/telemetry.json");
206
- if (fs.existsSync(configObj.config.dirPath + "/vscode_queue.json"))
207
- fs.unlinkSync(configObj.config.dirPath + "/vscode_queue.json");
208
- }
209
- },
210
- getExercise: slug => {
211
- console_1.default.debug("ExercisePath Slug", slug);
212
- const exercise = (configObj.exercises || []).find(ex => ex.slug === slug);
213
- if (!exercise)
214
- throw errors_1.ValidationError(`Exercise ${slug} not found`);
215
- return exercise;
216
- },
217
- getAllExercises: () => {
218
- return configObj.exercises;
219
- },
220
- startExercise: function (slug) {
221
- const exercise = this.getExercise(slug);
222
- // set config.json with current exercise
223
- configObj.currentExercise = exercise.slug;
224
- this.save();
225
- // eslint-disable-next-line
226
- exercise.files.forEach((f) => {
227
- if (configObj.config) {
228
- const _path = configObj.config.outputPath + "/" + f.name;
229
- if (f.hidden === false && fs.existsSync(_path))
230
- fs.unlinkSync(_path);
231
- }
232
- });
233
- return exercise;
234
- },
235
- noCurrentExercise: function () {
236
- configObj.currentExercise = null;
237
- this.save();
238
- },
239
- reset: slug => {
240
- var _a;
241
- if (configObj.config &&
242
- !fs.existsSync(`${configObj.config.dirPath}/resets/` + slug))
243
- throw errors_1.ValidationError("Could not find the original files for " + slug);
244
- const exercise = (configObj.exercises || []).find(ex => ex.slug === slug);
245
- if (!exercise)
246
- throw errors_1.ValidationError(`Exercise ${slug} not found on the configuration`);
247
- if (configObj.config) {
248
- for (const fileName of fs.readdirSync(`${configObj.config.dirPath}/resets/${slug}/`)) {
249
- const content = fs.readFileSync(`${(_a = configObj.config) === null || _a === void 0 ? void 0 : _a.dirPath}/resets/${slug}/${fileName}`);
250
- fs.writeFileSync(`${exercise.path}/${fileName}`, content);
251
- }
252
- }
253
- },
254
- buildIndex: function () {
255
- var _a, _b;
256
- console_1.default.info("Building the exercise index...");
257
- const isDirectory = (source) => {
258
- var _a;
259
- const name = path.basename(source);
260
- if (name === path.basename(((_a = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _a === void 0 ? void 0 : _a.dirPath) || ""))
261
- return false;
262
- // ignore folders that start with a dot
263
- if (name.charAt(0) === "." || name.charAt(0) === "_")
264
- return false;
265
- return fs.lstatSync(source).isDirectory();
266
- };
267
- const getDirectories = (source) => fs
268
- .readdirSync(source)
269
- .map(name => path.join(source, name))
270
- .filter(isDirectory);
271
- // add the .learn folder
272
- if (!fs.existsSync(confPath.base))
273
- fs.mkdirSync(confPath.base);
274
- // add the outout folder where webpack will publish the the html/css/js files
275
- if (configObj.config &&
276
- configObj.config.outputPath &&
277
- !fs.existsSync(configObj.config.outputPath))
278
- fs.mkdirSync(configObj.config.outputPath);
279
- // TODO: we could use npm library front-mater to read the title of the exercises from the README.md
280
- const grupedByDirectory = getDirectories(((_a = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _a === void 0 ? void 0 : _a.exercisesPath) || "");
281
- configObj.exercises =
282
- grupedByDirectory.length > 0 ?
283
- grupedByDirectory.map((path, position) => exercise_1.exercise(path, position, configObj)) :
284
- [exercise_1.exercise(((_b = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _b === void 0 ? void 0 : _b.exercisesPath) || "", 0, configObj)];
285
- this.save();
286
- },
287
- watchIndex: function (onChange) {
288
- var _a;
289
- if (configObj.config && !configObj.config.exercisesPath)
290
- throw errors_1.ValidationError("No exercises directory to watch: " + configObj.config.exercisesPath);
291
- this.buildIndex();
292
- watcher_1.default(((_a = configObj === null || configObj === void 0 ? void 0 : configObj.config) === null || _a === void 0 ? void 0 : _a.exercisesPath) || "", onChange)
293
- .then(() => {
294
- console_1.default.debug("Changes detected on your exercises");
295
- this.buildIndex();
296
- // if (onChange) onChange(filename);
297
- })
298
- .catch(error => {
299
- throw error;
300
- });
301
- },
302
- save: () => {
303
- // Console.debug("Saving configuration with: ", configObj)
304
- // remove the duplicates form the actions array
305
- // configObj.config.actions = [...new Set(configObj.config.actions)];
306
- if (configObj.config) {
307
- configObj.config.translations = [
308
- ...new Set(configObj.config.translations),
309
- ];
310
- fs.writeFileSync(configObj.config.dirPath + "/config.json", JSON.stringify(configObj, null, 4));
311
- }
312
- },
313
- };
314
- };
315
- function deepMerge(...sources) {
316
- let acc = {};
317
- for (const source of sources) {
318
- if (Array.isArray(source)) {
319
- if (!Array.isArray(acc)) {
320
- acc = [];
321
- }
322
- acc = [...source];
323
- }
324
- else if (source instanceof Object) {
325
- // eslint-disable-next-line
326
- for (let [key, value] of Object.entries(source)) {
327
- if (value instanceof Object && key in acc) {
328
- value = deepMerge(acc[key], value);
329
- }
330
- if (value !== undefined)
331
- acc = Object.assign(Object.assign({}, acc), { [key]: value });
332
- }
333
- }
334
- }
335
- return acc;
336
- }
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
+ }