@learnpack/learnpack 2.1.26 → 2.1.28

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 (56) hide show
  1. package/README.md +10 -10
  2. package/lib/commands/start.js +15 -4
  3. package/lib/managers/file.d.ts +1 -0
  4. package/lib/managers/file.js +8 -1
  5. package/lib/managers/server/routes.js +48 -14
  6. package/lib/managers/session.d.ts +1 -1
  7. package/lib/managers/session.js +39 -12
  8. package/lib/managers/socket.d.ts +1 -1
  9. package/lib/managers/socket.js +57 -43
  10. package/lib/models/action.d.ts +1 -1
  11. package/lib/models/config.d.ts +1 -1
  12. package/lib/models/exercise-obj.d.ts +3 -0
  13. package/lib/models/session.d.ts +4 -1
  14. package/lib/models/socket.d.ts +7 -6
  15. package/lib/models/status.d.ts +1 -1
  16. package/lib/utils/api.d.ts +2 -0
  17. package/lib/utils/api.js +51 -6
  18. package/oclif.manifest.json +1 -1
  19. package/package.json +3 -1
  20. package/src/commands/audit.ts +113 -113
  21. package/src/commands/clean.ts +10 -10
  22. package/src/commands/download.ts +18 -18
  23. package/src/commands/init.ts +39 -39
  24. package/src/commands/login.ts +13 -13
  25. package/src/commands/logout.ts +9 -9
  26. package/src/commands/publish.ts +25 -25
  27. package/src/commands/start.ts +101 -75
  28. package/src/commands/test.ts +34 -34
  29. package/src/managers/config/allowed_files.ts +2 -2
  30. package/src/managers/config/defaults.ts +2 -2
  31. package/src/managers/config/exercise.ts +79 -79
  32. package/src/managers/config/index.ts +145 -145
  33. package/src/managers/file.ts +74 -65
  34. package/src/managers/server/index.ts +32 -31
  35. package/src/managers/server/routes.ts +139 -90
  36. package/src/managers/session.ts +53 -24
  37. package/src/managers/socket.ts +92 -79
  38. package/src/models/action.ts +8 -1
  39. package/src/models/config-manager.ts +2 -2
  40. package/src/models/config.ts +7 -2
  41. package/src/models/exercise-obj.ts +6 -3
  42. package/src/models/plugin-config.ts +2 -2
  43. package/src/models/session.ts +5 -2
  44. package/src/models/socket.ts +12 -6
  45. package/src/models/status.ts +15 -14
  46. package/src/plugin/command/compile.ts +10 -10
  47. package/src/plugin/command/test.ts +14 -14
  48. package/src/plugin/index.ts +5 -5
  49. package/src/plugin/plugin.ts +26 -26
  50. package/src/plugin/utils.ts +23 -23
  51. package/src/utils/BaseCommand.ts +16 -16
  52. package/src/utils/api.ts +143 -91
  53. package/src/utils/audit.ts +93 -96
  54. package/src/utils/exercisesQueue.ts +15 -15
  55. package/src/utils/fileQueue.ts +85 -85
  56. package/src/utils/watcher.ts +14 -14
@@ -1,12 +1,12 @@
1
- import { IAuditErrors } from "../models/audit";
2
- import { IConfigObj } from "../models/config";
3
- import { ICounter } from "../models/counter";
4
- import { IFindings } from "../models/findings";
5
- import { IExercise } from "../models/exercise-obj";
6
- import { IFrontmatter } from "../models/front-matter";
7
- import Console from "./console";
8
- import * as fs from "fs";
9
- import * as path from "path";
1
+ import { IAuditErrors } from "../models/audit"
2
+ import { IConfigObj } from "../models/config"
3
+ import { ICounter } from "../models/counter"
4
+ import { IFindings } from "../models/findings"
5
+ import { IExercise } from "../models/exercise-obj"
6
+ import { IFrontmatter } from "../models/front-matter"
7
+ import Console from "./console"
8
+ import * as fs from "fs"
9
+ import * as path from "path"
10
10
 
11
11
  // eslint-disable-next-line
12
12
  const fetch = require("node-fetch");
@@ -19,39 +19,39 @@ const isUrl = async (
19
19
  errors: IAuditErrors[],
20
20
  counter: ICounter
21
21
  ) => {
22
- const regexUrl = /(https?:\/\/[\w./-]+)/gm;
23
- counter.links.total++;
22
+ const regexUrl = /(https?:\/\/[\w./-]+)/gm
23
+ counter.links.total++
24
24
  if (!regexUrl.test(url)) {
25
- counter.links.error++;
25
+ counter.links.error++
26
26
  errors.push({
27
27
  exercise: undefined,
28
28
  msg: `The repository value of the configuration file is not a link: ${url}`,
29
- });
30
- return false;
29
+ })
30
+ return false
31
31
  }
32
32
 
33
- const res = await fetch(url, { method: "HEAD" });
33
+ const res = await fetch(url, { method: "HEAD" })
34
34
  if (!res.ok) {
35
- counter.links.error++;
35
+ counter.links.error++
36
36
  errors.push({
37
37
  exercise: undefined,
38
38
  msg: `The link of the repository is broken: ${url}`,
39
- });
39
+ })
40
40
  }
41
41
 
42
- return true;
43
- };
42
+ return true
43
+ }
44
44
 
45
45
  const checkForEmptySpaces = (str: string) => {
46
- const isEmpty = true;
46
+ const isEmpty = true
47
47
  for (const letter of str) {
48
48
  if (letter !== " ") {
49
- return false;
49
+ return false
50
50
  }
51
51
  }
52
52
 
53
- return isEmpty;
54
- };
53
+ return isEmpty
54
+ }
55
55
 
56
56
  const checkLearnpackClean = (configObj: IConfigObj, errors: IAuditErrors[]) => {
57
57
  if (
@@ -67,9 +67,9 @@ const checkLearnpackClean = (configObj: IConfigObj, errors: IAuditErrors[]) => {
67
67
  errors.push({
68
68
  exercise: undefined,
69
69
  msg: "You have to run learnpack clean command",
70
- });
70
+ })
71
71
  }
72
- };
72
+ }
73
73
 
74
74
  const findInFile = (types: string[], content: string) => {
75
75
  const regex: any = {
@@ -79,13 +79,13 @@ const findInFile = (types: string[], content: string) => {
79
79
  markdownLinks: /(\s)+\[.*]\((https?:\/(\/[^)/]+)+\/?)\)/gm,
80
80
  url: /(https?:\/\/[\w./-]+)/gm,
81
81
  uploadcare: /https:\/\/ucarecdn.com\/(?:.*\/)*([\w./-]+)/gm,
82
- };
82
+ }
83
83
 
84
- const validTypes = Object.keys(regex);
84
+ const validTypes = Object.keys(regex)
85
85
  if (!Array.isArray(types))
86
- types = [types];
86
+ types = [types]
87
87
 
88
- const findings: IFindings = {};
88
+ const findings: IFindings = {}
89
89
  type findingsType =
90
90
  | "relativeImages"
91
91
  | "externalImages"
@@ -95,17 +95,17 @@ types = [types];
95
95
 
96
96
  for (const type of types) {
97
97
  if (!validTypes.includes(type))
98
- throw new Error("Invalid type: " + type);
98
+ throw new Error("Invalid type: " + type)
99
99
  else
100
- findings[type as findingsType] = {};
100
+ findings[type as findingsType] = {}
101
101
  }
102
102
 
103
103
  for (const type of types) {
104
- let m: RegExpExecArray;
104
+ let m: RegExpExecArray
105
105
  while ((m = regex[type].exec(content)) !== null) {
106
106
  // This is necessary to avoid infinite loops with zero-width matches
107
107
  if (m.index === regex.lastIndex) {
108
- regex.lastIndex++;
108
+ regex.lastIndex++
109
109
  }
110
110
 
111
111
  // The result can be accessed through the `m`-variable.
@@ -116,12 +116,12 @@ findings[type as findingsType] = {};
116
116
  absUrl: m[1],
117
117
  mdUrl: m[2],
118
118
  relUrl: m[6],
119
- };
119
+ }
120
120
  }
121
121
  }
122
122
 
123
- return findings;
124
- };
123
+ return findings
124
+ }
125
125
 
126
126
  // This function checks that each of the url's are working.
127
127
  const checkUrl = async (
@@ -134,40 +134,40 @@ const checkUrl = async (
134
134
  counter: ICounter | undefined
135
135
  ) => {
136
136
  if (!fs.existsSync(filePath))
137
- return false;
138
- const content: string = fs.readFileSync(filePath).toString();
139
- const isEmpty = checkForEmptySpaces(content);
137
+ return false
138
+ const content: string = fs.readFileSync(filePath).toString()
139
+ const isEmpty = checkForEmptySpaces(content)
140
140
  if (isEmpty || !content)
141
141
  errors.push({
142
142
  exercise: exercise?.title!,
143
143
  msg: `This file (${fileName}) doesn't have any content inside.`,
144
- });
144
+ })
145
145
 
146
- const frontmatter: IFrontmatter = fm(content);
146
+ const frontmatter: IFrontmatter = fm(content)
147
147
  for (const attribute in frontmatter.attributes) {
148
148
  if (
149
149
  Object.prototype.hasOwnProperty.call(frontmatter.attributes, attribute) &&
150
150
  (attribute === "intro" || attribute === "tutorial")
151
151
  ) {
152
- counter && counter.links.total++;
152
+ counter && counter.links.total++
153
153
  try {
154
154
  // eslint-disable-next-line
155
155
  let res = await fetch(frontmatter.attributes[attribute], {
156
156
  method: "HEAD",
157
- });
157
+ })
158
158
  if (!res.ok) {
159
- counter && counter.links.error++;
159
+ counter && counter.links.error++
160
160
  errors.push({
161
161
  exercise: exercise?.title!,
162
162
  msg: `This link is broken (${res.ok}): ${frontmatter.attributes[attribute]}`,
163
- });
163
+ })
164
164
  }
165
165
  } catch {
166
- counter && counter.links.error++;
166
+ counter && counter.links.error++
167
167
  errors.push({
168
168
  exercise: exercise?.title,
169
169
  msg: `This link is broken: ${frontmatter.attributes[attribute]}`,
170
- });
170
+ })
171
171
  }
172
172
  }
173
173
  }
@@ -176,7 +176,7 @@ return false;
176
176
  const findings: IFindings = findInFile(
177
177
  ["relativeImages", "externalImages", "markdownLinks"],
178
178
  content
179
- );
179
+ )
180
180
  type findingsType =
181
181
  | "relativeImages"
182
182
  | "externalImages"
@@ -185,25 +185,25 @@ return false;
185
185
  | "uploadcare";
186
186
  for (const finding in findings) {
187
187
  if (Object.prototype.hasOwnProperty.call(findings, finding)) {
188
- const obj = findings[finding as findingsType];
188
+ const obj = findings[finding as findingsType]
189
189
  // Valdites all the relative path images.
190
190
  if (finding === "relativeImages" && Object.keys(obj!).length > 0) {
191
191
  for (const img in obj) {
192
192
  if (Object.prototype.hasOwnProperty.call(obj, img)) {
193
193
  // Validates if the image is in the assets folder.
194
- counter && counter.images.total++;
194
+ counter && counter.images.total++
195
195
  const relativePath = path
196
196
  .relative(
197
197
  exercise ? exercise.path.replace(/\\/gm, "/") : "./",
198
198
  `${config!.config?.dirPath}/assets/${obj[img].relUrl}`
199
199
  )
200
- .replace(/\\/gm, "/");
200
+ .replace(/\\/gm, "/")
201
201
  if (relativePath !== obj[img].absUrl.split("?").shift()) {
202
- counter && counter.images.error++;
202
+ counter && counter.images.error++
203
203
  errors.push({
204
204
  exercise: exercise?.title,
205
205
  msg: `This relative path (${obj[img].relUrl}) is not pointing to the assets folder.`,
206
- });
206
+ })
207
207
  }
208
208
 
209
209
  if (
@@ -211,11 +211,11 @@ return false;
211
211
  `${config!.config?.dirPath}/assets/${obj[img].relUrl}`
212
212
  )
213
213
  ) {
214
- counter && counter.images.error++;
214
+ counter && counter.images.error++
215
215
  errors.push({
216
216
  exercise: exercise?.title,
217
217
  msg: `The file ${obj[img].relUrl} doesn't exist in the assets folder.`,
218
- });
218
+ })
219
219
  }
220
220
  }
221
221
  }
@@ -223,7 +223,7 @@ return false;
223
223
  // Valdites all the aboslute path images.
224
224
  for (const img in obj) {
225
225
  if (Object.prototype.hasOwnProperty.call(obj, img)) {
226
- counter && counter.images.total++;
226
+ counter && counter.images.total++
227
227
  if (
228
228
  fs.existsSync(
229
229
  `${config!.config?.dirPath}/assets${obj[img].mdUrl
@@ -236,57 +236,57 @@ return false;
236
236
  exercise ? exercise.path.replace(/\\/gm, "/") : "./",
237
237
  `${config!.config?.dirPath}/assets/${obj[img].mdUrl}`
238
238
  )
239
- .replace(/\\/gm, "/");
239
+ .replace(/\\/gm, "/")
240
240
  warnings.push({
241
241
  exercise: exercise?.title,
242
242
  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}".`,
243
- });
243
+ })
244
244
  }
245
245
 
246
246
  try {
247
247
  // eslint-disable-next-line
248
248
  let res = await fetch(obj[img].absUrl, {
249
249
  method: "HEAD",
250
- });
250
+ })
251
251
  if (!res.ok) {
252
- counter && counter.images.error++;
252
+ counter && counter.images.error++
253
253
  errors.push({
254
254
  exercise: exercise?.title,
255
255
  msg: `This link is broken: ${obj[img].absUrl}`,
256
- });
256
+ })
257
257
  }
258
258
  } catch {
259
- counter && counter.images.error++;
259
+ counter && counter.images.error++
260
260
  errors.push({
261
261
  exercise: exercise?.title,
262
262
  msg: `This link is broken: ${obj[img].absUrl}`,
263
- });
263
+ })
264
264
  }
265
265
  }
266
266
  }
267
267
  } else if (finding === "markdownLinks" && Object.keys(obj!).length > 0) {
268
268
  for (const link in obj) {
269
269
  if (Object.prototype.hasOwnProperty.call(obj, link)) {
270
- counter && counter.links.total++;
270
+ counter && counter.links.total++
271
271
  if (!obj[link].mdUrl.includes("twitter")) {
272
272
  try {
273
273
  // eslint-disable-next-line
274
274
  let res = await fetch(obj[link].mdUrl, {
275
275
  method: "HEAD",
276
- });
276
+ })
277
277
  if (res.status > 399 && res.status < 500) {
278
- counter && counter.links.error++;
278
+ counter && counter.links.error++
279
279
  errors.push({
280
280
  exercise: exercise?.title,
281
281
  msg: `This link is broken: ${obj[link].mdUrl}`,
282
- });
282
+ })
283
283
  }
284
284
  } catch {
285
- counter && counter.links.error++;
285
+ counter && counter.links.error++
286
286
  errors.push({
287
287
  exercise: exercise?.title,
288
288
  msg: `This link is broken: ${obj[link].mdUrl}`,
289
- });
289
+ })
290
290
  }
291
291
  }
292
292
  }
@@ -295,37 +295,34 @@ return false;
295
295
  }
296
296
  }
297
297
 
298
- return true;
299
- };
298
+ return true
299
+ }
300
300
 
301
301
  // This function writes a given file with the given content.
302
302
  const writeFile = async (content: string, filePath: string) => {
303
303
  try {
304
- await fs.promises.writeFile(filePath, content);
304
+ await fs.promises.writeFile(filePath, content)
305
305
  } catch (error) {
306
306
  if (error)
307
307
  Console.error(
308
308
  `We weren't able to write the file in this path "${filePath}".`,
309
309
  error
310
- );
310
+ )
311
311
  }
312
- };
312
+ }
313
313
 
314
314
  // This function checks if there are errors, and show them in the console at the end.
315
- const showErrors = (
316
- errors: IAuditErrors[],
317
- counter: ICounter | undefined
318
- ) => {
315
+ const showErrors = (errors: IAuditErrors[], counter: ICounter | undefined) => {
319
316
  return new Promise((resolve, reject) => {
320
317
  if (errors) {
321
318
  if (errors.length > 0) {
322
- Console.log("Checking for errors...");
319
+ Console.log("Checking for errors...")
323
320
  for (const [i, error] of errors.entries())
324
321
  Console.error(
325
322
  `${i + 1}) ${error.msg} ${
326
323
  error.exercise ? `(Exercise: ${error.exercise})` : ""
327
324
  }`
328
- );
325
+ )
329
326
  if (counter) {
330
327
  Console.error(
331
328
  ` We found ${errors.length} error${
@@ -335,53 +332,53 @@ const showErrors = (
335
332
  } link, ${counter.readmeFiles} README files and ${
336
333
  counter.exercises
337
334
  } exercises.`
338
- );
335
+ )
339
336
  } else {
340
337
  Console.error(
341
338
  ` We found ${errors.length} error${
342
339
  errors.length > 1 ? "s" : ""
343
340
  } related with the project integrity.`
344
- );
341
+ )
345
342
  }
346
343
 
347
- process.exit(1);
344
+ process.exit(1)
348
345
  } else {
349
346
  if (counter) {
350
347
  Console.success(
351
348
  `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.`
352
- );
349
+ )
353
350
  } else {
354
- Console.success(`We didn't find any errors in this repository.`);
351
+ Console.success(`We didn't find any errors in this repository.`)
355
352
  }
356
353
 
357
- process.exit(0);
354
+ process.exit(0)
358
355
  }
359
356
  } else {
360
- reject("Failed");
357
+ reject("Failed")
361
358
  }
362
- });
363
- };
359
+ })
360
+ }
364
361
 
365
362
  // This function checks if there are warnings, and show them in the console at the end.
366
363
  const showWarnings = (warnings: IAuditErrors[]) => {
367
364
  return new Promise((resolve, reject) => {
368
365
  if (warnings) {
369
366
  if (warnings.length > 0) {
370
- Console.log("Checking for warnings...");
367
+ Console.log("Checking for warnings...")
371
368
  for (const [i, warning] of warnings.entries())
372
369
  Console.warning(
373
370
  `${i + 1}) ${warning.msg} ${
374
371
  warning.exercise ? `File: ${warning.exercise}` : ""
375
372
  }`
376
- );
373
+ )
377
374
  }
378
375
 
379
- resolve("SUCCESS");
376
+ resolve("SUCCESS")
380
377
  } else {
381
- reject("Failed");
378
+ reject("Failed")
382
379
  }
383
- });
384
- };
380
+ })
381
+ }
385
382
 
386
383
  export default {
387
384
  isUrl,
@@ -392,4 +389,4 @@ export default {
392
389
  writeFile,
393
390
  showErrors,
394
391
  showWarnings,
395
- };
392
+ }
@@ -1,11 +1,11 @@
1
- import { IConfig } from "../models/config";
2
- import { IExercise } from "../models/exercise-obj";
3
- import { ISocket } from "../models/socket";
1
+ import { IConfig } from "../models/config"
2
+ import { IExercise } from "../models/exercise-obj"
3
+ import { ISocket } from "../models/socket"
4
4
 
5
5
  class Exercise {
6
- exercise: IExercise;
6
+ exercise: IExercise
7
7
  constructor(exercise: IExercise) {
8
- this.exercise = exercise;
8
+ this.exercise = exercise
9
9
  }
10
10
 
11
11
  test(sessionConfig: IConfig, config: IConfig, socket: ISocket) {
@@ -13,39 +13,39 @@ class Exercise {
13
13
  socket.log(
14
14
  "testing",
15
15
  `Testing exercise ${this.exercise.slug} using ${this.exercise.language} engine`
16
- );
16
+ )
17
17
 
18
18
  sessionConfig.runHook("action", {
19
19
  action: "test",
20
20
  socket,
21
21
  configuration: config,
22
22
  exercise: this.exercise,
23
- });
23
+ })
24
24
  } else {
25
- socket.onTestingFinished({ result: "success" });
25
+ socket.onTestingFinished({ result: "success" })
26
26
  }
27
27
  }
28
28
  }
29
29
 
30
30
  class ExercisesQueue {
31
- exercises: IExercise[];
31
+ exercises: IExercise[]
32
32
  constructor(exercises: any) {
33
33
  this.exercises = exercises.map((exercise: IExercise) => {
34
- return new Exercise(exercise);
35
- });
34
+ return new Exercise(exercise)
35
+ })
36
36
  }
37
37
 
38
38
  pop() {
39
- return this.exercises.shift();
39
+ return this.exercises.shift()
40
40
  }
41
41
 
42
42
  isEmpty() {
43
- return this.size() === 0;
43
+ return this.size() === 0
44
44
  }
45
45
 
46
46
  size() {
47
- return this.exercises.length;
47
+ return this.exercises.length
48
48
  }
49
49
  }
50
50
 
51
- export default ExercisesQueue;
51
+ export default ExercisesQueue