@learnpack/learnpack 2.1.26 → 2.1.28

Sign up to get free protection for your applications and to get access to all the features.
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