@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 * as p from "path";
1
+ import * as p from "path"
2
2
  // import frontMatter from 'front-matter'
3
- import * as fs from "fs";
4
- import Console from "../../utils/console";
5
- import allowed from "./allowed_files";
3
+ import * as fs from "fs"
4
+ import Console from "../../utils/console"
5
+ import allowed from "./allowed_files"
6
6
 
7
- import { IConfigObj } from "../../models/config";
8
- import { IFile } from "../../models/file";
9
- import { IExercise } from "../../models/exercise-obj";
7
+ import { IConfigObj } from "../../models/config"
8
+ import { IFile } from "../../models/file"
9
+ import { IExercise } from "../../models/exercise-obj"
10
10
 
11
11
  // eslint-disable-next-line
12
12
  const frontMatter = require("front-matter");
@@ -16,17 +16,17 @@ export const exercise = (
16
16
  position: number,
17
17
  configObject: IConfigObj
18
18
  ): IExercise => {
19
- const { config, exercises } = configObject;
20
- let slug = p.basename(path);
19
+ const { config, exercises } = configObject
20
+ let slug = p.basename(path)
21
21
 
22
22
  if (!validateExerciseDirectoryName(slug)) {
23
23
  Console.error(
24
24
  `Exercise directory ${slug} has an invalid name, it has to start with two or three digits followed by words separated by underscors or hyphen (no white spaces). e.g: 01.12-hello-world`
25
- );
25
+ )
26
26
  }
27
27
 
28
28
  // get all the files
29
- const files = fs.readdirSync(path);
29
+ const files = fs.readdirSync(path)
30
30
 
31
31
  /**
32
32
  * build the translation array like:
@@ -35,23 +35,23 @@ export const exercise = (
35
35
  "es": "path/to/Readme.es.md"
36
36
  }
37
37
  */
38
- const translations: { [key: string]: string } = {};
38
+ const translations: { [key: string]: string } = {}
39
39
  for (const file of files.filter(file =>
40
40
  file.toLowerCase().includes("readme")
41
41
  )) {
42
- const parts = file.split(".");
42
+ const parts = file.split(".")
43
43
 
44
44
  if (parts.length === 3)
45
- translations[parts[1]] = file;
45
+ translations[parts[1]] = file
46
46
  else
47
- translations.us = file;
47
+ translations.us = file
48
48
  }
49
49
 
50
50
  // if the slug is a dot, it means there is not "exercises" folder, and its just a single README.md
51
51
  if (slug === ".")
52
- slug = "default-index";
52
+ slug = "default-index"
53
53
 
54
- const detected = detect(configObject, files);
54
+ const detected = detect(configObject, files)
55
55
 
56
56
  const exerciseObj: IExercise = {
57
57
  position,
@@ -77,52 +77,52 @@ slug = "default-index";
77
77
  false,
78
78
  getReadme: function (lang = null) {
79
79
  if (lang === "us")
80
- lang = null; // <-- english is default, no need to append it to the file name
80
+ lang = null // <-- english is default, no need to append it to the file name
81
81
 
82
82
  if (!fs.existsSync(`${this.path}/README${lang ? "." + lang : ""}.md`)) {
83
83
  Console.error(
84
84
  `Language ${lang} not found for exercise ${slug}, switching to default language`
85
- );
85
+ )
86
86
 
87
87
  if (lang)
88
- lang = null;
88
+ lang = null
89
89
 
90
90
  if (!fs.existsSync(`${this.path}/README${lang ? "." + lang : ""}.md`))
91
91
  throw new Error(
92
92
  "Readme file not found for exercise: " + this.path + "/README.md"
93
- );
93
+ )
94
94
  }
95
95
 
96
96
  const content = fs.readFileSync(
97
97
  `${this.path}/README${lang ? "." + lang : ""}.md`,
98
98
  "utf8"
99
- );
100
- const attr = frontMatter(content);
101
- return attr;
99
+ )
100
+ const attr = frontMatter(content)
101
+ return attr
102
102
  },
103
103
  getFile: function (name: string) {
104
104
  const file: IFile | undefined = this.files.find(
105
105
  (f: IFile) => f.name === name
106
- );
106
+ )
107
107
 
108
108
  if (!file || !fs.existsSync(file.path)) {
109
- throw new Error(`File not found: + ${file?.path}`);
109
+ throw new Error(`File not found: + ${file?.path}`)
110
110
  } else if (fs.lstatSync(file.path).isDirectory()) {
111
111
  return (
112
112
  "Error: This is not a file to be read, but a directory: " + file.path
113
- );
113
+ )
114
114
  }
115
115
 
116
116
  // get file content
117
- const content = fs.readFileSync(file.path);
117
+ const content = fs.readFileSync(file.path)
118
118
 
119
119
  // create reset folder
120
120
  if (!fs.existsSync(`${config?.dirPath}/resets`))
121
- fs.mkdirSync(`${config?.dirPath}/resets`);
121
+ fs.mkdirSync(`${config?.dirPath}/resets`)
122
122
  if (!fs.existsSync(`${config?.dirPath}/resets/` + this.slug)) {
123
- fs.mkdirSync(`${config?.dirPath}/resets/` + this.slug);
123
+ fs.mkdirSync(`${config?.dirPath}/resets/` + this.slug)
124
124
  for (const _file of this.files) {
125
- const fileContent = fs.readFileSync(_file.path);
125
+ const fileContent = fs.readFileSync(_file.path)
126
126
  if (
127
127
  !fs.existsSync(
128
128
  `${config?.dirPath}/resets/${this.slug}/${_file.name}`
@@ -131,57 +131,57 @@ lang = null;
131
131
  fs.writeFileSync(
132
132
  `${config?.dirPath}/resets/${this.slug}/${_file.name}`,
133
133
  fileContent
134
- );
134
+ )
135
135
  }
136
136
  }
137
137
  }
138
138
 
139
- return content;
139
+ return content
140
140
  },
141
141
  saveFile: function (name: string, content: string) {
142
142
  const file: IFile | undefined = this.files.find(
143
143
  (f: IFile) => f.name === name
144
- );
144
+ )
145
145
 
146
146
  if (file) {
147
147
  if (!fs.existsSync(file.path)) {
148
- throw new Error("File not found: " + file.path);
148
+ throw new Error("File not found: " + file.path)
149
149
  }
150
150
 
151
- return fs.writeFileSync(file.path, content, "utf8");
151
+ return fs.writeFileSync(file.path, content, "utf8")
152
152
  }
153
153
  },
154
154
  getTestReport: function () {
155
- const _path = `${configObject?.confPath?.base}/reports/${this.slug}.json`;
155
+ const _path = `${configObject?.confPath?.base}/reports/${this.slug}.json`
156
156
 
157
157
  if (!fs.existsSync(_path))
158
- return {};
158
+ return {}
159
159
 
160
- const content = fs.readFileSync(_path);
161
- const data = JSON.parse(`${content}`);
162
- return data;
160
+ const content = fs.readFileSync(_path)
161
+ const data = JSON.parse(`${content}`)
162
+ return data
163
163
  },
164
- };
164
+ }
165
165
 
166
- return exerciseObj;
167
- };
166
+ return exerciseObj
167
+ }
168
168
 
169
169
  export const validateExerciseDirectoryName = (str: string) => {
170
170
  if (str === "./")
171
- return true;
171
+ return true
172
172
  // TODO: Add nameValidationREgex from the config
173
- const regex = /^(\d{2,3}(\.\d{1,2})?-([\dA-Za-z]+(-|_)?)+)$/;
174
- return regex.test(str);
175
- };
173
+ const regex = /^(\d{2,3}(\.\d{1,2})?-([\dA-Za-z]+(-|_)?)+)$/
174
+ return regex.test(str)
175
+ }
176
176
 
177
177
  export const isCodable = (str: string) => {
178
- const extension = p.extname(str);
179
- return allowed.extensions.includes(extension.slice(1).toLowerCase());
180
- };
178
+ const extension = p.extname(str)
179
+ return allowed.extensions.includes(extension.slice(1).toLowerCase())
180
+ }
181
181
 
182
182
  const isNotConfiguration = (str: string) => {
183
- return !allowed.names.includes(str);
184
- };
183
+ return !allowed.names.includes(str)
184
+ }
185
185
 
186
186
  export const shouldBeVisible = function (file: IFile) {
187
187
  return (
@@ -201,56 +201,56 @@ export const shouldBeVisible = function (file: IFile) {
201
201
  !file.name.toLowerCase().includes("readme.") &&
202
202
  !isDirectory(file.path) &&
203
203
  file.name.charAt(0) !== "_"
204
- );
205
- };
204
+ )
205
+ }
206
206
 
207
207
  export const isDirectory = (source: string) => {
208
208
  // if(path.basename(source) === path.basename(config.dirPath)) return false
209
- return fs.lstatSync(source).isDirectory();
210
- };
209
+ return fs.lstatSync(source).isDirectory()
210
+ }
211
211
 
212
212
  export const detect = (
213
213
  configObject: IConfigObj | undefined,
214
214
  files: Array<string>
215
215
  ) => {
216
216
  if (!configObject) {
217
- return;
217
+ return
218
218
  }
219
219
 
220
- const { config } = configObject;
220
+ const { config } = configObject
221
221
 
222
222
  if (!config)
223
- throw new Error("No configuration found during the engine detection");
223
+ throw new Error("No configuration found during the engine detection")
224
224
 
225
225
  if (!config.entries)
226
226
  throw new Error(
227
227
  "No configuration found for entries, please add a 'entries' object with the default file name for your exercise entry file that is going to be used while compiling, for example: index.html for html, app.py for python3, etc."
228
- );
228
+ )
229
229
  // A language was found on the config object, but this language will only be used as last resort, learnpack will try to guess each exercise language independently based on file extension (js, jsx, html, etc.)
230
230
 
231
- let hasFiles = files.filter(f => f.includes(".py"));
231
+ let hasFiles = files.filter(f => f.includes(".py"))
232
232
  if (hasFiles.length > 0)
233
233
  return {
234
234
  language: "python3",
235
235
  entry: hasFiles.find(f => config.entries.python3 === f),
236
- };
236
+ }
237
237
 
238
- hasFiles = files.filter(f => f.includes(".java"));
238
+ hasFiles = files.filter(f => f.includes(".java"))
239
239
  if (hasFiles.length > 0)
240
240
  return {
241
241
  language: "java",
242
242
  entry: hasFiles.find(f => config.entries.java === f),
243
- };
243
+ }
244
244
 
245
- hasFiles = files.filter(f => f.includes(".jsx"));
245
+ hasFiles = files.filter(f => f.includes(".jsx"))
246
246
  if (hasFiles.length > 0)
247
247
  return {
248
248
  language: "react",
249
249
  entry: hasFiles.find(f => config.entries.react === f),
250
- };
251
- const hasHTML = files.filter(f => f.includes("index.html"));
252
- const hasIndexJS = files.find(f => f.includes("index.js"));
253
- const hasJS = files.filter(f => f.includes(".js"));
250
+ }
251
+ const hasHTML = files.filter(f => f.includes("index.html"))
252
+ const hasIndexJS = files.find(f => f.includes("index.js"))
253
+ const hasJS = files.filter(f => f.includes(".js"))
254
254
  // angular, vue, vanillajs needs to have at least 2 files (html,css,js),
255
255
  // the test.js and the entry file in js
256
256
  // if not its just another HTML
@@ -259,23 +259,23 @@ export const detect = (
259
259
  return {
260
260
  language: "vanillajs",
261
261
  entry: hasIndexJS,
262
- };
262
+ }
263
263
  if (hasHTML.length > 0)
264
264
  return {
265
265
  language: "html",
266
266
  entry: hasHTML.find(f => config.entries.html === f),
267
- };
267
+ }
268
268
  if (hasJS.length > 0)
269
269
  return {
270
270
  language: "node",
271
271
  entry: hasJS.find(f => config.entries.node === f),
272
- };
272
+ }
273
273
 
274
274
  return {
275
275
  language: null,
276
276
  entry: null,
277
- };
278
- };
277
+ }
278
+ }
279
279
 
280
280
  export const filterFiles = (files: Array<string>, basePath = ".") =>
281
281
  files
@@ -298,12 +298,12 @@ export const filterFiles = (files: Array<string>, basePath = ".") =>
298
298
  "index.css": 2,
299
299
  "index.scss": 2,
300
300
  "index.js": 3,
301
- };
302
- return score[f1.name] < score[f2.name] ? -1 : 1;
303
- });
301
+ }
302
+ return score[f1.name] < score[f2.name] ? -1 : 1
303
+ })
304
304
 
305
305
  export default {
306
306
  exercise,
307
307
  detect,
308
308
  filterFiles,
309
- };
309
+ }