@learnpack/learnpack 2.1.25 → 2.1.26

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