@learnpack/learnpack 2.1.39 → 2.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.md +370 -35
  2. package/bin/run +17 -17
  3. package/bin/run.cmd +3 -3
  4. package/lib/commands/audit.d.ts +6 -6
  5. package/lib/commands/audit.js +342 -342
  6. package/lib/commands/clean.d.ts +8 -8
  7. package/lib/commands/clean.js +25 -25
  8. package/lib/commands/download.d.ts +13 -13
  9. package/lib/commands/download.js +55 -55
  10. package/lib/commands/init.d.ts +9 -9
  11. package/lib/commands/init.js +123 -123
  12. package/lib/commands/login.d.ts +14 -14
  13. package/lib/commands/login.js +37 -37
  14. package/lib/commands/logout.d.ts +14 -14
  15. package/lib/commands/logout.js +37 -37
  16. package/lib/commands/publish.d.ts +14 -14
  17. package/lib/commands/publish.js +82 -82
  18. package/lib/commands/start.d.ts +7 -7
  19. package/lib/commands/start.js +239 -208
  20. package/lib/commands/test.d.ts +6 -6
  21. package/lib/commands/test.js +62 -62
  22. package/lib/index.d.ts +1 -1
  23. package/lib/index.js +4 -4
  24. package/lib/managers/config/allowed_files.d.ts +5 -5
  25. package/lib/managers/config/allowed_files.js +30 -30
  26. package/lib/managers/config/defaults.d.ts +41 -41
  27. package/lib/managers/config/defaults.js +44 -44
  28. package/lib/managers/config/exercise.d.ts +36 -36
  29. package/lib/managers/config/exercise.js +236 -236
  30. package/lib/managers/config/index.d.ts +3 -3
  31. package/lib/managers/config/index.js +337 -337
  32. package/lib/managers/file.d.ts +14 -14
  33. package/lib/managers/file.js +153 -153
  34. package/lib/managers/gitpod.d.ts +3 -3
  35. package/lib/managers/gitpod.js +67 -67
  36. package/lib/managers/server/index.d.ts +6 -6
  37. package/lib/managers/server/index.js +58 -58
  38. package/lib/managers/server/routes.d.ts +4 -4
  39. package/lib/managers/server/routes.js +219 -219
  40. package/lib/managers/session.d.ts +3 -3
  41. package/lib/managers/session.js +125 -125
  42. package/lib/managers/socket.d.ts +3 -3
  43. package/lib/managers/socket.js +176 -176
  44. package/lib/managers/telemetry.d.ts +74 -74
  45. package/lib/managers/telemetry.js +206 -206
  46. package/lib/managers/test.js +84 -84
  47. package/lib/models/action.d.ts +2 -2
  48. package/lib/models/action.js +2 -2
  49. package/lib/models/audit.d.ts +15 -15
  50. package/lib/models/audit.js +2 -2
  51. package/lib/models/config-manager.d.ts +21 -21
  52. package/lib/models/config-manager.js +2 -2
  53. package/lib/models/config.d.ts +68 -67
  54. package/lib/models/config.js +2 -2
  55. package/lib/models/counter.d.ts +11 -11
  56. package/lib/models/counter.js +2 -2
  57. package/lib/models/errors.d.ts +15 -15
  58. package/lib/models/errors.js +2 -2
  59. package/lib/models/exercise-obj.d.ts +30 -30
  60. package/lib/models/exercise-obj.js +2 -2
  61. package/lib/models/file.d.ts +5 -5
  62. package/lib/models/file.js +2 -2
  63. package/lib/models/findings.d.ts +17 -17
  64. package/lib/models/findings.js +2 -2
  65. package/lib/models/flags.d.ts +10 -10
  66. package/lib/models/flags.js +2 -2
  67. package/lib/models/front-matter.d.ts +11 -11
  68. package/lib/models/front-matter.js +2 -2
  69. package/lib/models/gitpod-data.d.ts +16 -16
  70. package/lib/models/gitpod-data.js +2 -2
  71. package/lib/models/language.d.ts +4 -4
  72. package/lib/models/language.js +2 -2
  73. package/lib/models/package.d.ts +7 -7
  74. package/lib/models/package.js +2 -2
  75. package/lib/models/plugin-config.d.ts +16 -16
  76. package/lib/models/plugin-config.js +2 -2
  77. package/lib/models/session.d.ts +31 -31
  78. package/lib/models/session.js +2 -2
  79. package/lib/models/socket.d.ts +36 -32
  80. package/lib/models/socket.js +2 -2
  81. package/lib/models/status.d.ts +1 -1
  82. package/lib/models/status.js +2 -2
  83. package/lib/models/success-types.d.ts +1 -1
  84. package/lib/models/success-types.js +2 -2
  85. package/lib/plugin/command/compile.d.ts +6 -6
  86. package/lib/plugin/command/compile.js +18 -18
  87. package/lib/plugin/command/test.d.ts +6 -6
  88. package/lib/plugin/command/test.js +25 -25
  89. package/lib/plugin/index.d.ts +27 -27
  90. package/lib/plugin/index.js +7 -7
  91. package/lib/plugin/plugin.d.ts +8 -8
  92. package/lib/plugin/plugin.js +68 -68
  93. package/lib/plugin/utils.d.ts +16 -16
  94. package/lib/plugin/utils.js +58 -58
  95. package/lib/ui/download.d.ts +5 -5
  96. package/lib/ui/download.js +61 -61
  97. package/lib/utils/BaseCommand.d.ts +8 -8
  98. package/lib/utils/BaseCommand.js +41 -41
  99. package/lib/utils/SessionCommand.d.ts +10 -10
  100. package/lib/utils/SessionCommand.js +43 -43
  101. package/lib/utils/api.d.ts +14 -14
  102. package/lib/utils/api.js +255 -255
  103. package/lib/utils/audit.d.ts +16 -16
  104. package/lib/utils/audit.js +303 -303
  105. package/lib/utils/checkNotInstalled.d.ts +2 -0
  106. package/lib/utils/checkNotInstalled.js +36 -0
  107. package/lib/utils/console.d.ts +12 -12
  108. package/lib/utils/console.js +19 -19
  109. package/lib/utils/errors.d.ts +17 -17
  110. package/lib/utils/errors.js +100 -100
  111. package/lib/utils/exercisesQueue.d.ts +9 -9
  112. package/lib/utils/exercisesQueue.js +38 -38
  113. package/lib/utils/fileQueue.d.ts +40 -40
  114. package/lib/utils/fileQueue.js +168 -168
  115. package/lib/utils/misc.d.ts +1 -1
  116. package/lib/utils/misc.js +23 -23
  117. package/lib/utils/osOperations.d.ts +5 -5
  118. package/lib/utils/osOperations.js +72 -72
  119. package/lib/utils/validators.d.ts +5 -5
  120. package/lib/utils/validators.js +17 -17
  121. package/lib/utils/watcher.d.ts +2 -2
  122. package/lib/utils/watcher.js +25 -25
  123. package/oclif.manifest.json +1 -1
  124. package/package.json +139 -139
  125. package/src/commands/audit.ts +443 -443
  126. package/src/commands/clean.ts +29 -29
  127. package/src/commands/download.ts +61 -61
  128. package/src/commands/init.ts +170 -170
  129. package/src/commands/login.ts +42 -42
  130. package/src/commands/logout.ts +43 -43
  131. package/src/commands/publish.ts +107 -107
  132. package/src/commands/start.ts +53 -23
  133. package/src/commands/test.ts +85 -85
  134. package/src/index.ts +1 -1
  135. package/src/managers/config/allowed_files.ts +29 -29
  136. package/src/managers/config/defaults.ts +42 -42
  137. package/src/managers/config/exercise.ts +311 -311
  138. package/src/managers/config/index.ts +455 -455
  139. package/src/managers/file.ts +196 -196
  140. package/src/managers/gitpod.ts +84 -84
  141. package/src/managers/server/index.ts +78 -78
  142. package/src/managers/server/routes.ts +330 -330
  143. package/src/managers/session.ts +145 -145
  144. package/src/managers/socket.ts +250 -250
  145. package/src/managers/telemetry.ts +346 -346
  146. package/src/managers/test.ts +83 -83
  147. package/src/models/action.ts +10 -10
  148. package/src/models/audit.ts +16 -16
  149. package/src/models/config-manager.ts +23 -23
  150. package/src/models/config.ts +5 -3
  151. package/src/models/counter.ts +11 -11
  152. package/src/models/errors.ts +22 -22
  153. package/src/models/exercise-obj.ts +29 -29
  154. package/src/models/file.ts +5 -5
  155. package/src/models/findings.ts +18 -18
  156. package/src/models/flags.ts +10 -10
  157. package/src/models/front-matter.ts +11 -11
  158. package/src/models/gitpod-data.ts +19 -19
  159. package/src/models/language.ts +4 -4
  160. package/src/models/package.ts +7 -7
  161. package/src/models/plugin-config.ts +17 -17
  162. package/src/models/session.ts +34 -34
  163. package/src/models/socket.ts +5 -0
  164. package/src/models/status.ts +16 -16
  165. package/src/models/success-types.ts +1 -1
  166. package/src/plugin/command/compile.ts +17 -17
  167. package/src/plugin/command/test.ts +30 -30
  168. package/src/plugin/index.ts +6 -6
  169. package/src/plugin/plugin.ts +94 -94
  170. package/src/plugin/utils.ts +87 -87
  171. package/src/types/node-fetch.d.ts +1 -1
  172. package/src/ui/download.ts +71 -71
  173. package/src/utils/BaseCommand.ts +48 -48
  174. package/src/utils/SessionCommand.ts +43 -43
  175. package/src/utils/api.ts +303 -303
  176. package/src/utils/audit.ts +393 -393
  177. package/src/utils/checkNotInstalled.ts +46 -0
  178. package/src/utils/console.ts +24 -24
  179. package/src/utils/errors.ts +117 -117
  180. package/src/utils/exercisesQueue.ts +51 -51
  181. package/src/utils/fileQueue.ts +198 -198
  182. package/src/utils/misc.ts +23 -23
  183. package/src/utils/osOperations.ts +79 -79
  184. package/src/utils/templates/gitignore.txt +19 -19
  185. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.es.md +24 -24
  186. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.md +24 -24
  187. package/src/utils/templates/incremental/README.ejs +4 -4
  188. package/src/utils/templates/incremental/README.es.ejs +4 -4
  189. package/src/utils/templates/isolated/01-hello-world/README.es.md +26 -26
  190. package/src/utils/templates/isolated/01-hello-world/README.md +26 -26
  191. package/src/utils/templates/isolated/README.ejs +4 -4
  192. package/src/utils/templates/isolated/README.es.ejs +4 -4
  193. package/src/utils/templates/no-grading/README.ejs +4 -4
  194. package/src/utils/templates/no-grading/README.es.ejs +4 -4
  195. package/src/utils/validators.ts +18 -18
  196. package/src/utils/watcher.ts +27 -27
@@ -1,303 +1,303 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const console_1 = require("./console");
4
- const fs = require("fs");
5
- const path = require("path");
6
- // eslint-disable-next-line
7
- const fetch = require("node-fetch");
8
- // eslint-disable-next-line
9
- const fm = require("front-matter");
10
- // This function checks if a url is valid.
11
- const isUrl = async (url, errors, counter) => {
12
- const regexUrl = /(https?:\/\/[\w./-]+)/gm;
13
- counter.links.total++;
14
- if (!regexUrl.test(url)) {
15
- counter.links.error++;
16
- errors.push({
17
- exercise: undefined,
18
- msg: `The repository value of the configuration file is not a link: ${url}`,
19
- });
20
- return false;
21
- }
22
- const res = await fetch(url, { method: "HEAD" });
23
- if (!res.ok) {
24
- counter.links.error++;
25
- errors.push({
26
- exercise: undefined,
27
- msg: `The link of the repository is broken: ${url}`,
28
- });
29
- }
30
- return true;
31
- };
32
- const checkForEmptySpaces = (str) => {
33
- const isEmpty = true;
34
- for (const letter of str) {
35
- if (letter !== " ") {
36
- return false;
37
- }
38
- }
39
- return isEmpty;
40
- };
41
- const checkLearnpackClean = (configObj, errors) => {
42
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
43
- if ((((_a = configObj.config) === null || _a === void 0 ? void 0 : _a.outputPath) &&
44
- fs.existsSync((_b = configObj.config) === null || _b === void 0 ? void 0 : _b.outputPath)) ||
45
- fs.existsSync(`${(_c = configObj.config) === null || _c === void 0 ? void 0 : _c.dirPath}/_app`) ||
46
- fs.existsSync(`${(_d = configObj.config) === null || _d === void 0 ? void 0 : _d.dirPath}/reports`) ||
47
- fs.existsSync(`${(_e = configObj.config) === null || _e === void 0 ? void 0 : _e.dirPath}/resets`) ||
48
- fs.existsSync(`${(_f = configObj.config) === null || _f === void 0 ? void 0 : _f.dirPath}/app.tar.gz`) ||
49
- fs.existsSync(`${(_g = configObj.config) === null || _g === void 0 ? void 0 : _g.dirPath}/config.json`) ||
50
- fs.existsSync(`${(_h = configObj.config) === null || _h === void 0 ? void 0 : _h.dirPath}/vscode_queue.json`) ||
51
- fs.existsSync(`${(_j = configObj.config) === null || _j === void 0 ? void 0 : _j.dirPath}/telemetry.json`)) {
52
- errors.push({
53
- exercise: undefined,
54
- msg: "You have to run learnpack clean command",
55
- });
56
- }
57
- };
58
- const findInFile = (types, content) => {
59
- const regex = {
60
- relativeImages: /!\[.*]\s*\((((\.\/)?(\.{2}\/){1,5})(.*\/)*(.[^\s/]*\.[A-Za-z]{2,4})\S*)\)/gm,
61
- externalImages: /!\[.*]\((https?:\/(\/[^)/]+)+\/?)\)/gm,
62
- markdownLinks: /(\s)+\[.*]\((https?:\/(\/[^)/]+)+\/?)\)/gm,
63
- url: /(https?:\/\/[\w./-]+)/gm,
64
- uploadcare: /https:\/\/ucarecdn.com\/(?:.*\/)*([\w./-]+)/gm,
65
- };
66
- const validTypes = Object.keys(regex);
67
- if (!Array.isArray(types))
68
- types = [types];
69
- const findings = {};
70
- for (const type of types) {
71
- if (!validTypes.includes(type))
72
- throw new Error("Invalid type: " + type);
73
- else
74
- findings[type] = {};
75
- }
76
- for (const type of types) {
77
- let m;
78
- while ((m = regex[type].exec(content)) !== null) {
79
- // This is necessary to avoid infinite loops with zero-width matches
80
- if (m.index === regex.lastIndex) {
81
- regex.lastIndex++;
82
- }
83
- // The result can be accessed through the `m`-variable.
84
- // m.forEach((match, groupIndex) => values.push(match));
85
- findings[type][m[0]] = {
86
- content: m[0],
87
- absUrl: m[1],
88
- mdUrl: m[2],
89
- relUrl: m[6],
90
- };
91
- }
92
- }
93
- return findings;
94
- };
95
- // This function checks that each of the url's are working.
96
- const checkUrl = async (config, filePath, fileName, exercise, errors, warnings, counter) => {
97
- var _a, _b, _c, _d;
98
- if (!fs.existsSync(filePath))
99
- return false;
100
- const content = fs.readFileSync(filePath).toString();
101
- const isEmpty = checkForEmptySpaces(content);
102
- if (isEmpty || !content)
103
- errors.push({
104
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
105
- msg: `This file (${fileName}) doesn't have any content inside.`,
106
- });
107
- const frontmatter = fm(content);
108
- for (const attribute in frontmatter.attributes) {
109
- if (Object.prototype.hasOwnProperty.call(frontmatter.attributes, attribute) &&
110
- (attribute === "intro" || attribute === "tutorial")) {
111
- counter && counter.links.total++;
112
- try {
113
- // eslint-disable-next-line
114
- let res = await fetch(frontmatter.attributes[attribute], {
115
- method: "HEAD",
116
- });
117
- if (!res.ok) {
118
- counter && counter.links.error++;
119
- errors.push({
120
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
121
- msg: `This link is broken (${res.ok}): ${frontmatter.attributes[attribute]}`,
122
- });
123
- }
124
- }
125
- catch (_e) {
126
- counter && counter.links.error++;
127
- errors.push({
128
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
129
- msg: `This link is broken: ${frontmatter.attributes[attribute]}`,
130
- });
131
- }
132
- }
133
- }
134
- // Check url's of each README file.
135
- const findings = findInFile(["relativeImages", "externalImages", "markdownLinks"], content);
136
- for (const finding in findings) {
137
- if (Object.prototype.hasOwnProperty.call(findings, finding)) {
138
- const obj = findings[finding];
139
- // Valdites all the relative path images.
140
- if (finding === "relativeImages" && Object.keys(obj).length > 0) {
141
- for (const img in obj) {
142
- if (Object.prototype.hasOwnProperty.call(obj, img)) {
143
- // Validates if the image is in the assets folder.
144
- counter && counter.images.total++;
145
- const relativePath = path
146
- .relative(exercise ? exercise.path.replace(/\\/gm, "/") : "./", `${(_a = config.config) === null || _a === void 0 ? void 0 : _a.dirPath}/assets/${obj[img].relUrl}`)
147
- .replace(/\\/gm, "/");
148
- if (relativePath !== obj[img].absUrl.split("?").shift()) {
149
- counter && counter.images.error++;
150
- errors.push({
151
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
152
- msg: `This relative path (${obj[img].relUrl}) is not pointing to the assets folder.`,
153
- });
154
- }
155
- if (!fs.existsSync(`${(_b = config.config) === null || _b === void 0 ? void 0 : _b.dirPath}/assets/${obj[img].relUrl}`)) {
156
- counter && counter.images.error++;
157
- errors.push({
158
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
159
- msg: `The file ${obj[img].relUrl} doesn't exist in the assets folder.`,
160
- });
161
- }
162
- }
163
- }
164
- }
165
- else if (finding === "externalImages" && Object.keys(obj).length > 0) {
166
- // Valdites all the aboslute path images.
167
- for (const img in obj) {
168
- if (Object.prototype.hasOwnProperty.call(obj, img)) {
169
- counter && counter.images.total++;
170
- if (fs.existsSync(`${(_c = config.config) === null || _c === void 0 ? void 0 : _c.dirPath}/assets${obj[img].mdUrl
171
- .split("?")
172
- .shift()}`)) {
173
- const relativePath = path
174
- .relative(exercise ? exercise.path.replace(/\\/gm, "/") : "./", `${(_d = config.config) === null || _d === void 0 ? void 0 : _d.dirPath}/assets/${obj[img].mdUrl}`)
175
- .replace(/\\/gm, "/");
176
- warnings.push({
177
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
178
- msg: `On this exercise you have an image with an absolute path "${obj[img].absUrl}". We recommend you to replace it by the relative path: "${relativePath}".`,
179
- });
180
- }
181
- try {
182
- // eslint-disable-next-line
183
- let res = await fetch(obj[img].absUrl, {
184
- method: "HEAD",
185
- });
186
- if (!res.ok) {
187
- counter && counter.images.error++;
188
- errors.push({
189
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
190
- msg: `This link is broken: ${obj[img].absUrl}`,
191
- });
192
- }
193
- }
194
- catch (_f) {
195
- counter && counter.images.error++;
196
- errors.push({
197
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
198
- msg: `This link is broken: ${obj[img].absUrl}`,
199
- });
200
- }
201
- }
202
- }
203
- }
204
- else if (finding === "markdownLinks" && Object.keys(obj).length > 0) {
205
- for (const link in obj) {
206
- if (Object.prototype.hasOwnProperty.call(obj, link)) {
207
- counter && counter.links.total++;
208
- if (!obj[link].mdUrl.includes("twitter")) {
209
- try {
210
- // eslint-disable-next-line
211
- let res = await fetch(obj[link].mdUrl, {
212
- method: "HEAD",
213
- });
214
- if (res.status > 399 && res.status < 500) {
215
- counter && counter.links.error++;
216
- errors.push({
217
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
218
- msg: `This link is broken: ${obj[link].mdUrl}`,
219
- });
220
- }
221
- }
222
- catch (_g) {
223
- counter && counter.links.error++;
224
- errors.push({
225
- exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
226
- msg: `This link is broken: ${obj[link].mdUrl}`,
227
- });
228
- }
229
- }
230
- }
231
- }
232
- }
233
- }
234
- }
235
- return true;
236
- };
237
- // This function writes a given file with the given content.
238
- const writeFile = async (content, filePath) => {
239
- try {
240
- await fs.promises.writeFile(filePath, content);
241
- }
242
- catch (error) {
243
- if (error)
244
- console_1.default.error(`We weren't able to write the file in this path "${filePath}".`, error);
245
- }
246
- };
247
- // This function checks if there are errors, and show them in the console at the end.
248
- const showErrors = (errors, counter) => {
249
- return new Promise((resolve, reject) => {
250
- if (errors) {
251
- if (errors.length > 0) {
252
- console_1.default.log("Checking for errors...");
253
- for (const [i, error] of errors.entries())
254
- console_1.default.error(`${i + 1}) ${error.msg} ${error.exercise ? `(Exercise: ${error.exercise})` : ""}`);
255
- if (counter) {
256
- console_1.default.error(` We found ${errors.length} error${errors.length > 1 ? "s" : ""} among ${counter.images.total} images, ${counter.links.total} link, ${counter.readmeFiles} README files and ${counter.exercises} exercises.`);
257
- }
258
- else {
259
- console_1.default.error(` We found ${errors.length} error${errors.length > 1 ? "s" : ""} related with the project integrity.`);
260
- }
261
- process.exit(1);
262
- }
263
- else {
264
- if (counter) {
265
- console_1.default.success(`We didn't find any errors in this repository among ${counter.images.total} images, ${counter.links.total} link, ${counter.readmeFiles} README files and ${counter.exercises} exercises.`);
266
- }
267
- else {
268
- console_1.default.success(`We didn't find any errors in this repository.`);
269
- }
270
- process.exit(0);
271
- }
272
- }
273
- else {
274
- reject("Failed");
275
- }
276
- });
277
- };
278
- // This function checks if there are warnings, and show them in the console at the end.
279
- const showWarnings = (warnings) => {
280
- return new Promise((resolve, reject) => {
281
- if (warnings) {
282
- if (warnings.length > 0) {
283
- console_1.default.log("Checking for warnings...");
284
- for (const [i, warning] of warnings.entries())
285
- console_1.default.warning(`${i + 1}) ${warning.msg} ${warning.exercise ? `File: ${warning.exercise}` : ""}`);
286
- }
287
- resolve("SUCCESS");
288
- }
289
- else {
290
- reject("Failed");
291
- }
292
- });
293
- };
294
- exports.default = {
295
- isUrl,
296
- checkForEmptySpaces,
297
- checkLearnpackClean,
298
- findInFile,
299
- checkUrl,
300
- writeFile,
301
- showErrors,
302
- showWarnings,
303
- };
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const console_1 = require("./console");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ // eslint-disable-next-line
7
+ const fetch = require("node-fetch");
8
+ // eslint-disable-next-line
9
+ const fm = require("front-matter");
10
+ // This function checks if a url is valid.
11
+ const isUrl = async (url, errors, counter) => {
12
+ const regexUrl = /(https?:\/\/[\w./-]+)/gm;
13
+ counter.links.total++;
14
+ if (!regexUrl.test(url)) {
15
+ counter.links.error++;
16
+ errors.push({
17
+ exercise: undefined,
18
+ msg: `The repository value of the configuration file is not a link: ${url}`,
19
+ });
20
+ return false;
21
+ }
22
+ const res = await fetch(url, { method: "HEAD" });
23
+ if (!res.ok) {
24
+ counter.links.error++;
25
+ errors.push({
26
+ exercise: undefined,
27
+ msg: `The link of the repository is broken: ${url}`,
28
+ });
29
+ }
30
+ return true;
31
+ };
32
+ const checkForEmptySpaces = (str) => {
33
+ const isEmpty = true;
34
+ for (const letter of str) {
35
+ if (letter !== " ") {
36
+ return false;
37
+ }
38
+ }
39
+ return isEmpty;
40
+ };
41
+ const checkLearnpackClean = (configObj, errors) => {
42
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
43
+ if ((((_a = configObj.config) === null || _a === void 0 ? void 0 : _a.outputPath) &&
44
+ fs.existsSync((_b = configObj.config) === null || _b === void 0 ? void 0 : _b.outputPath)) ||
45
+ fs.existsSync(`${(_c = configObj.config) === null || _c === void 0 ? void 0 : _c.dirPath}/_app`) ||
46
+ fs.existsSync(`${(_d = configObj.config) === null || _d === void 0 ? void 0 : _d.dirPath}/reports`) ||
47
+ fs.existsSync(`${(_e = configObj.config) === null || _e === void 0 ? void 0 : _e.dirPath}/resets`) ||
48
+ fs.existsSync(`${(_f = configObj.config) === null || _f === void 0 ? void 0 : _f.dirPath}/app.tar.gz`) ||
49
+ fs.existsSync(`${(_g = configObj.config) === null || _g === void 0 ? void 0 : _g.dirPath}/config.json`) ||
50
+ fs.existsSync(`${(_h = configObj.config) === null || _h === void 0 ? void 0 : _h.dirPath}/vscode_queue.json`) ||
51
+ fs.existsSync(`${(_j = configObj.config) === null || _j === void 0 ? void 0 : _j.dirPath}/telemetry.json`)) {
52
+ errors.push({
53
+ exercise: undefined,
54
+ msg: "You have to run learnpack clean command",
55
+ });
56
+ }
57
+ };
58
+ const findInFile = (types, content) => {
59
+ const regex = {
60
+ relativeImages: /!\[.*]\s*\((((\.\/)?(\.{2}\/){1,5})(.*\/)*(.[^\s/]*\.[A-Za-z]{2,4})\S*)\)/gm,
61
+ externalImages: /!\[.*]\((https?:\/(\/[^)/]+)+\/?)\)/gm,
62
+ markdownLinks: /(\s)+\[.*]\((https?:\/(\/[^)/]+)+\/?)\)/gm,
63
+ url: /(https?:\/\/[\w./-]+)/gm,
64
+ uploadcare: /https:\/\/ucarecdn.com\/(?:.*\/)*([\w./-]+)/gm,
65
+ };
66
+ const validTypes = Object.keys(regex);
67
+ if (!Array.isArray(types))
68
+ types = [types];
69
+ const findings = {};
70
+ for (const type of types) {
71
+ if (!validTypes.includes(type))
72
+ throw new Error("Invalid type: " + type);
73
+ else
74
+ findings[type] = {};
75
+ }
76
+ for (const type of types) {
77
+ let m;
78
+ while ((m = regex[type].exec(content)) !== null) {
79
+ // This is necessary to avoid infinite loops with zero-width matches
80
+ if (m.index === regex.lastIndex) {
81
+ regex.lastIndex++;
82
+ }
83
+ // The result can be accessed through the `m`-variable.
84
+ // m.forEach((match, groupIndex) => values.push(match));
85
+ findings[type][m[0]] = {
86
+ content: m[0],
87
+ absUrl: m[1],
88
+ mdUrl: m[2],
89
+ relUrl: m[6],
90
+ };
91
+ }
92
+ }
93
+ return findings;
94
+ };
95
+ // This function checks that each of the url's are working.
96
+ const checkUrl = async (config, filePath, fileName, exercise, errors, warnings, counter) => {
97
+ var _a, _b, _c, _d;
98
+ if (!fs.existsSync(filePath))
99
+ return false;
100
+ const content = fs.readFileSync(filePath).toString();
101
+ const isEmpty = checkForEmptySpaces(content);
102
+ if (isEmpty || !content)
103
+ errors.push({
104
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
105
+ msg: `This file (${fileName}) doesn't have any content inside.`,
106
+ });
107
+ const frontmatter = fm(content);
108
+ for (const attribute in frontmatter.attributes) {
109
+ if (Object.prototype.hasOwnProperty.call(frontmatter.attributes, attribute) &&
110
+ (attribute === "intro" || attribute === "tutorial")) {
111
+ counter && counter.links.total++;
112
+ try {
113
+ // eslint-disable-next-line
114
+ let res = await fetch(frontmatter.attributes[attribute], {
115
+ method: "HEAD",
116
+ });
117
+ if (!res.ok) {
118
+ counter && counter.links.error++;
119
+ errors.push({
120
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
121
+ msg: `This link is broken (${res.ok}): ${frontmatter.attributes[attribute]}`,
122
+ });
123
+ }
124
+ }
125
+ catch (_e) {
126
+ counter && counter.links.error++;
127
+ errors.push({
128
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
129
+ msg: `This link is broken: ${frontmatter.attributes[attribute]}`,
130
+ });
131
+ }
132
+ }
133
+ }
134
+ // Check url's of each README file.
135
+ const findings = findInFile(["relativeImages", "externalImages", "markdownLinks"], content);
136
+ for (const finding in findings) {
137
+ if (Object.prototype.hasOwnProperty.call(findings, finding)) {
138
+ const obj = findings[finding];
139
+ // Valdites all the relative path images.
140
+ if (finding === "relativeImages" && Object.keys(obj).length > 0) {
141
+ for (const img in obj) {
142
+ if (Object.prototype.hasOwnProperty.call(obj, img)) {
143
+ // Validates if the image is in the assets folder.
144
+ counter && counter.images.total++;
145
+ const relativePath = path
146
+ .relative(exercise ? exercise.path.replace(/\\/gm, "/") : "./", `${(_a = config.config) === null || _a === void 0 ? void 0 : _a.dirPath}/assets/${obj[img].relUrl}`)
147
+ .replace(/\\/gm, "/");
148
+ if (relativePath !== obj[img].absUrl.split("?").shift()) {
149
+ counter && counter.images.error++;
150
+ errors.push({
151
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
152
+ msg: `This relative path (${obj[img].relUrl}) is not pointing to the assets folder.`,
153
+ });
154
+ }
155
+ if (!fs.existsSync(`${(_b = config.config) === null || _b === void 0 ? void 0 : _b.dirPath}/assets/${obj[img].relUrl}`)) {
156
+ counter && counter.images.error++;
157
+ errors.push({
158
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
159
+ msg: `The file ${obj[img].relUrl} doesn't exist in the assets folder.`,
160
+ });
161
+ }
162
+ }
163
+ }
164
+ }
165
+ else if (finding === "externalImages" && Object.keys(obj).length > 0) {
166
+ // Valdites all the aboslute path images.
167
+ for (const img in obj) {
168
+ if (Object.prototype.hasOwnProperty.call(obj, img)) {
169
+ counter && counter.images.total++;
170
+ if (fs.existsSync(`${(_c = config.config) === null || _c === void 0 ? void 0 : _c.dirPath}/assets${obj[img].mdUrl
171
+ .split("?")
172
+ .shift()}`)) {
173
+ const relativePath = path
174
+ .relative(exercise ? exercise.path.replace(/\\/gm, "/") : "./", `${(_d = config.config) === null || _d === void 0 ? void 0 : _d.dirPath}/assets/${obj[img].mdUrl}`)
175
+ .replace(/\\/gm, "/");
176
+ warnings.push({
177
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
178
+ msg: `On this exercise you have an image with an absolute path "${obj[img].absUrl}". We recommend you to replace it by the relative path: "${relativePath}".`,
179
+ });
180
+ }
181
+ try {
182
+ // eslint-disable-next-line
183
+ let res = await fetch(obj[img].absUrl, {
184
+ method: "HEAD",
185
+ });
186
+ if (!res.ok) {
187
+ counter && counter.images.error++;
188
+ errors.push({
189
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
190
+ msg: `This link is broken: ${obj[img].absUrl}`,
191
+ });
192
+ }
193
+ }
194
+ catch (_f) {
195
+ counter && counter.images.error++;
196
+ errors.push({
197
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
198
+ msg: `This link is broken: ${obj[img].absUrl}`,
199
+ });
200
+ }
201
+ }
202
+ }
203
+ }
204
+ else if (finding === "markdownLinks" && Object.keys(obj).length > 0) {
205
+ for (const link in obj) {
206
+ if (Object.prototype.hasOwnProperty.call(obj, link)) {
207
+ counter && counter.links.total++;
208
+ if (!obj[link].mdUrl.includes("twitter")) {
209
+ try {
210
+ // eslint-disable-next-line
211
+ let res = await fetch(obj[link].mdUrl, {
212
+ method: "HEAD",
213
+ });
214
+ if (res.status > 399 && res.status < 500) {
215
+ counter && counter.links.error++;
216
+ errors.push({
217
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
218
+ msg: `This link is broken: ${obj[link].mdUrl}`,
219
+ });
220
+ }
221
+ }
222
+ catch (_g) {
223
+ counter && counter.links.error++;
224
+ errors.push({
225
+ exercise: exercise === null || exercise === void 0 ? void 0 : exercise.title,
226
+ msg: `This link is broken: ${obj[link].mdUrl}`,
227
+ });
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+ }
235
+ return true;
236
+ };
237
+ // This function writes a given file with the given content.
238
+ const writeFile = async (content, filePath) => {
239
+ try {
240
+ await fs.promises.writeFile(filePath, content);
241
+ }
242
+ catch (error) {
243
+ if (error)
244
+ console_1.default.error(`We weren't able to write the file in this path "${filePath}".`, error);
245
+ }
246
+ };
247
+ // This function checks if there are errors, and show them in the console at the end.
248
+ const showErrors = (errors, counter) => {
249
+ return new Promise((resolve, reject) => {
250
+ if (errors) {
251
+ if (errors.length > 0) {
252
+ console_1.default.log("Checking for errors...");
253
+ for (const [i, error] of errors.entries())
254
+ console_1.default.error(`${i + 1}) ${error.msg} ${error.exercise ? `(Exercise: ${error.exercise})` : ""}`);
255
+ if (counter) {
256
+ console_1.default.error(` We found ${errors.length} error${errors.length > 1 ? "s" : ""} among ${counter.images.total} images, ${counter.links.total} link, ${counter.readmeFiles} README files and ${counter.exercises} exercises.`);
257
+ }
258
+ else {
259
+ console_1.default.error(` We found ${errors.length} error${errors.length > 1 ? "s" : ""} related with the project integrity.`);
260
+ }
261
+ process.exit(1);
262
+ }
263
+ else {
264
+ if (counter) {
265
+ console_1.default.success(`We didn't find any errors in this repository among ${counter.images.total} images, ${counter.links.total} link, ${counter.readmeFiles} README files and ${counter.exercises} exercises.`);
266
+ }
267
+ else {
268
+ console_1.default.success(`We didn't find any errors in this repository.`);
269
+ }
270
+ process.exit(0);
271
+ }
272
+ }
273
+ else {
274
+ reject("Failed");
275
+ }
276
+ });
277
+ };
278
+ // This function checks if there are warnings, and show them in the console at the end.
279
+ const showWarnings = (warnings) => {
280
+ return new Promise((resolve, reject) => {
281
+ if (warnings) {
282
+ if (warnings.length > 0) {
283
+ console_1.default.log("Checking for warnings...");
284
+ for (const [i, warning] of warnings.entries())
285
+ console_1.default.warning(`${i + 1}) ${warning.msg} ${warning.exercise ? `File: ${warning.exercise}` : ""}`);
286
+ }
287
+ resolve("SUCCESS");
288
+ }
289
+ else {
290
+ reject("Failed");
291
+ }
292
+ });
293
+ };
294
+ exports.default = {
295
+ isUrl,
296
+ checkForEmptySpaces,
297
+ checkLearnpackClean,
298
+ findInFile,
299
+ checkUrl,
300
+ writeFile,
301
+ showErrors,
302
+ showWarnings,
303
+ };
@@ -0,0 +1,2 @@
1
+ import { IExercise } from "../models/exercise-obj";
2
+ export declare const checkNotInstalledPlugins: (exercises: IExercise[], installedPlugins: string[]) => string[];
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkNotInstalledPlugins = void 0;
4
+ exports.checkNotInstalledPlugins = (exercises, installedPlugins) => {
5
+ const usefulExtensions = new Set(["py", "js", "jsx", "html"]);
6
+ const foundExtensions = [];
7
+ for (const e of exercises) {
8
+ for (const f of e.files) {
9
+ const ext = f.name.split(".").pop();
10
+ if (ext &&
11
+ usefulExtensions.has(ext) &&
12
+ !foundExtensions.includes(ext)) {
13
+ foundExtensions.push(ext);
14
+ }
15
+ }
16
+ }
17
+ const neededPlugins = [];
18
+ if (foundExtensions.length > 0) {
19
+ if (foundExtensions.includes("html") && foundExtensions.includes("js")) {
20
+ neededPlugins.push("@learnpack/dom");
21
+ }
22
+ if (foundExtensions.includes("html") && !foundExtensions.includes("js")) {
23
+ neededPlugins.push("@learnpack/html");
24
+ }
25
+ if (foundExtensions.includes("jsx")) {
26
+ neededPlugins.push("@learnpack/react");
27
+ }
28
+ if (foundExtensions.includes("py")) {
29
+ neededPlugins.push("@learnpack/python");
30
+ }
31
+ if (foundExtensions.includes("js")) {
32
+ neededPlugins.push("@learnpack/javascript");
33
+ }
34
+ }
35
+ return neededPlugins.filter(item => !installedPlugins.includes(item));
36
+ };