@learnpack/learnpack 2.1.24 → 2.1.26

Sign up to get free protection for your applications and to get access to all the features.
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 +342 -317
  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 +443 -418
  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
+ }