@learnpack/learnpack 2.1.46 → 2.1.48

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @learnpack/learnpack
21
21
  $ learnpack COMMAND
22
22
  running command...
23
23
  $ learnpack (-v|--version|version)
24
- @learnpack/learnpack/2.1.46 win32-x64 node-v20.10.0
24
+ @learnpack/learnpack/2.1.48 win32-x64 node-v20.10.0
25
25
  $ learnpack --help [COMMAND]
26
26
  USAGE
27
27
  $ learnpack COMMAND
@@ -74,7 +74,7 @@ DESCRIPTION
74
74
  12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
75
75
  ```
76
76
 
77
- _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.46/src\commands\audit.ts)_
77
+ _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.48/src\commands\audit.ts)_
78
78
 
79
79
  ## `learnpack clean`
80
80
 
@@ -89,7 +89,7 @@ DESCRIPTION
89
89
  Extra documentation goes here
90
90
  ```
91
91
 
92
- _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.46/src\commands\clean.ts)_
92
+ _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.48/src\commands\clean.ts)_
93
93
 
94
94
  ## `learnpack download [PACKAGE]`
95
95
 
@@ -107,7 +107,7 @@ DESCRIPTION
107
107
  Extra documentation goes here
108
108
  ```
109
109
 
110
- _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.46/src\commands\download.ts)_
110
+ _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.48/src\commands\download.ts)_
111
111
 
112
112
  ## `learnpack help [COMMAND]`
113
113
 
@@ -138,7 +138,7 @@ OPTIONS
138
138
  -h, --grading show CLI help
139
139
  ```
140
140
 
141
- _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.46/src\commands\init.ts)_
141
+ _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.48/src\commands\init.ts)_
142
142
 
143
143
  ## `learnpack login [PACKAGE]`
144
144
 
@@ -156,7 +156,7 @@ DESCRIPTION
156
156
  Extra documentation goes here
157
157
  ```
158
158
 
159
- _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.46/src\commands\login.ts)_
159
+ _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.48/src\commands\login.ts)_
160
160
 
161
161
  ## `learnpack logout [PACKAGE]`
162
162
 
@@ -174,7 +174,7 @@ DESCRIPTION
174
174
  Extra documentation goes here
175
175
  ```
176
176
 
177
- _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.46/src\commands\logout.ts)_
177
+ _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.48/src\commands\logout.ts)_
178
178
 
179
179
  ## `learnpack plugins`
180
180
 
@@ -309,7 +309,7 @@ DESCRIPTION
309
309
  Extra documentation goes here
310
310
  ```
311
311
 
312
- _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.46/src\commands\publish.ts)_
312
+ _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.48/src\commands\publish.ts)_
313
313
 
314
314
  ## `learnpack start`
315
315
 
@@ -330,7 +330,7 @@ OPTIONS
330
330
  -w, --watch Watch for file changes
331
331
  ```
332
332
 
333
- _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.46/src\commands\start.ts)_
333
+ _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.48/src\commands\start.ts)_
334
334
 
335
335
  ## `learnpack test [EXERCISESLUG]`
336
336
 
@@ -344,7 +344,7 @@ ARGUMENTS
344
344
  EXERCISESLUG The name of the exercise to test
345
345
  ```
346
346
 
347
- _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.46/src\commands\test.ts)_
347
+ _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.48/src\commands\test.ts)_
348
348
  <!-- commandsstop -->
349
349
 
350
350
  > > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
@@ -90,14 +90,20 @@ class StartCommand extends SessionCommand_1.default {
90
90
  socket_1.default.on("open_window", (data) => {
91
91
  console_1.default.debug("Opening window: ", data);
92
92
  // cli.open(data.url); This uses XDG under the ground
93
- if (config.editor.agent === "os" &&
94
- ((config.os === "linux" && hasXDG) || config.os !== "linux")) {
93
+ if (config.os !== "linux" || (config.os === "linux" && hasXDG)) {
95
94
  osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data);
96
95
  }
97
96
  else {
98
97
  dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data);
99
98
  }
100
- socket_1.default.log("open_window", `URL ${data.url} opened in the browser`);
99
+ socket_1.default.log("open_window", "", undefined, data.url);
100
+ });
101
+ socket_1.default.on("open_terminal", (exercise) => {
102
+ console_1.default.debug("Opening terminal: ", exercise);
103
+ // eventManager.enqueue(dispatcher.events.OPEN_TERMINAL, exercise);
104
+ if (config.editor.agent === "vscode") {
105
+ dispatcher.enqueue(dispatcher.events.OPEN_TERMINAL, exercise);
106
+ }
101
107
  });
102
108
  socket_1.default.on("reset", (exercise) => {
103
109
  var _a;
@@ -18,23 +18,30 @@ exports.checkNotInstalledPlugins = async (exercises, installedPlugins, command)
18
18
  // See the extensions of the files that are not tests files
19
19
  for (const e of exercises) {
20
20
  const notReadmeFiles = e.files.filter(f => !f.name.toLowerCase().includes("readme"));
21
- if (!someExerciseWithOnlyTestFile) {
22
- someExerciseWithOnlyTestFile = notReadmeFiles.every(f => f.name.includes("test"));
21
+ if (!someExerciseWithOnlyTestFile && notReadmeFiles.length > 0) {
22
+ someExerciseWithOnlyTestFile = notReadmeFiles.every(f => {
23
+ return f.name.includes("test");
24
+ });
25
+ if (someExerciseWithOnlyTestFile) {
26
+ console_1.default.debug("Some exercises have only test files, I will install the @learnpack/node plugin to run the tests.");
27
+ }
23
28
  }
24
29
  for (const f of notReadmeFiles) {
25
- // There are some courses with only test files in js and grading: incremental
26
- // TODO: We should know in the incremental exercises which compilers are needed
27
30
  const ext = f.name.split(".").pop();
31
+ if (!ext)
32
+ continue;
28
33
  if (f.name.includes("test")) {
29
- testingExtensions.push(ext);
34
+ !testingExtensions.includes(ext) && testingExtensions.push(ext);
30
35
  continue;
31
36
  }
32
- if (ext && usefulExtensions.has(ext) && !foundExtensions.includes(ext)) {
37
+ if (usefulExtensions.has(ext) && !foundExtensions.includes(ext)) {
33
38
  foundExtensions.push(ext);
34
39
  }
35
40
  }
36
41
  }
37
- if (foundExtensions.length === 0)
42
+ console_1.default.debug("foundExtensions", foundExtensions);
43
+ console_1.default.debug("testingExtensions", testingExtensions);
44
+ if (foundExtensions.length === 0 && testingExtensions.length === 0)
38
45
  return {
39
46
  needed: [],
40
47
  notInstalled: [],
@@ -8,6 +8,7 @@ declare const _default: {
8
8
  OPEN_FILES: string;
9
9
  OPEN_WINDOW: string;
10
10
  INSTRUCTIONS_CLOSED: string;
11
+ OPEN_TERMINAL: string;
11
12
  };
12
13
  dispatcher: (opts?: any) => {
13
14
  enqueue: (name: string, data?: any) => void;
@@ -20,6 +21,7 @@ declare const _default: {
20
21
  OPEN_FILES: string;
21
22
  OPEN_WINDOW: string;
22
23
  INSTRUCTIONS_CLOSED: string;
24
+ OPEN_TERMINAL: string;
23
25
  };
24
26
  };
25
27
  listener: (opts?: any) => {
@@ -34,6 +36,7 @@ declare const _default: {
34
36
  OPEN_FILES: string;
35
37
  OPEN_WINDOW: string;
36
38
  INSTRUCTIONS_CLOSED: string;
39
+ OPEN_TERMINAL: string;
37
40
  };
38
41
  };
39
42
  };
@@ -14,6 +14,7 @@ const events = {
14
14
  OPEN_FILES: "open_files",
15
15
  OPEN_WINDOW: "open_window",
16
16
  INSTRUCTIONS_CLOSED: "instructions_closed",
17
+ OPEN_TERMINAL: "open_terminal",
17
18
  };
18
19
  let options = {
19
20
  path: null,
@@ -1 +1 @@
1
- {"version":"2.1.46","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]}}}
1
+ {"version":"2.1.48","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@learnpack/learnpack",
3
3
  "description": "Create, sell or download and take learning amazing learning packages",
4
- "version": "2.1.46",
4
+ "version": "2.1.48",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "bin": {
7
7
  "learnpack": "bin/run"
@@ -100,7 +100,8 @@
100
100
  "plugins": [
101
101
  "@oclif/plugin-help",
102
102
  "@oclif/plugin-plugins",
103
- "@oclif/plugin-warn-if-update-available"
103
+ "@oclif/plugin-warn-if-update-available",
104
+ "@learnpack/dom"
104
105
  ],
105
106
  "permanent_plugins": [
106
107
  "@oclif/plugin-help",
@@ -187,16 +187,21 @@ export default class StartCommand extends SessionCommand {
187
187
  socket.on("open_window", (data: TOpenWindowData) => {
188
188
  Console.debug("Opening window: ", data)
189
189
  // cli.open(data.url); This uses XDG under the ground
190
- if (
191
- config.editor.agent === "os" &&
192
- ((config.os === "linux" && hasXDG) || config.os !== "linux")
193
- ) {
190
+ if (config.os !== "linux" || (config.os === "linux" && hasXDG)) {
194
191
  eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
195
192
  } else {
196
193
  dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data)
197
194
  }
198
195
 
199
- socket.log("open_window", `URL ${data.url} opened in the browser`)
196
+ socket.log("open_window", "", undefined, data.url)
197
+ })
198
+
199
+ socket.on("open_terminal", (exercise: IExerciseData) => {
200
+ Console.debug("Opening terminal: ", exercise)
201
+ // eventManager.enqueue(dispatcher.events.OPEN_TERMINAL, exercise);
202
+ if (config.editor.agent === "vscode") {
203
+ dispatcher.enqueue(dispatcher.events.OPEN_TERMINAL, exercise)
204
+ }
200
205
  })
201
206
 
202
207
  socket.on("reset", (exercise: IExerciseData) => {
@@ -21,7 +21,7 @@ export const checkNotInstalledPlugins = async (
21
21
  Console.info("Checking needed plugins...")
22
22
  const usefulExtensions = new Set(["py", "js", "jsx", "html"])
23
23
  const foundExtensions: string[] = []
24
- const testingExtensions = []
24
+ const testingExtensions: string[] = []
25
25
  const neededPlugins: string[] = []
26
26
  let someExerciseWithOnlyTestFile = false // I will suppose that there are not exercises with only readme and test files
27
27
 
@@ -31,29 +31,38 @@ export const checkNotInstalledPlugins = async (
31
31
  f => !f.name.toLowerCase().includes("readme")
32
32
  )
33
33
 
34
- if (!someExerciseWithOnlyTestFile) {
35
- someExerciseWithOnlyTestFile = notReadmeFiles.every(f =>
36
- f.name.includes("test")
37
- )
34
+ if (!someExerciseWithOnlyTestFile && notReadmeFiles.length > 0) {
35
+ someExerciseWithOnlyTestFile = notReadmeFiles.every(f => {
36
+ return f.name.includes("test")
37
+ })
38
+
39
+ if (someExerciseWithOnlyTestFile) {
40
+ Console.debug(
41
+ "Some exercises have only test files, I will install the @learnpack/node plugin to run the tests."
42
+ )
43
+ }
38
44
  }
39
45
 
40
46
  for (const f of notReadmeFiles) {
41
- // There are some courses with only test files in js and grading: incremental
42
- // TODO: We should know in the incremental exercises which compilers are needed
43
-
44
47
  const ext = f.name.split(".").pop()
48
+ if (!ext)
49
+ continue
50
+
45
51
  if (f.name.includes("test")) {
46
- testingExtensions.push(ext)
52
+ !testingExtensions.includes(ext) && testingExtensions.push(ext)
47
53
  continue
48
54
  }
49
55
 
50
- if (ext && usefulExtensions.has(ext) && !foundExtensions.includes(ext)) {
56
+ if (usefulExtensions.has(ext) && !foundExtensions.includes(ext)) {
51
57
  foundExtensions.push(ext)
52
58
  }
53
59
  }
54
60
  }
55
61
 
56
- if (foundExtensions.length === 0)
62
+ Console.debug("foundExtensions", foundExtensions)
63
+ Console.debug("testingExtensions", testingExtensions)
64
+
65
+ if (foundExtensions.length === 0 && testingExtensions.length === 0)
57
66
  return {
58
67
  needed: [],
59
68
  notInstalled: [],
@@ -1,198 +1,199 @@
1
- import logger from "../utils/console"
2
- import * as fs from "fs"
3
- // import em from "events"
4
- import * as XXH from "xxhashjs"
5
-
6
- // possible events to dispatch
7
- const events = {
8
- START_EXERCISE: "start_exercise",
9
- INIT: "initializing",
10
- RUNNING: "configuration_loaded",
11
- END: "connection_ended",
12
- RESET_EXERCISE: "reset_exercise",
13
- OPEN_FILES: "open_files",
14
- OPEN_WINDOW: "open_window",
15
- INSTRUCTIONS_CLOSED: "instructions_closed",
16
- }
17
-
18
- let options = {
19
- path: null,
20
- create: false,
21
- }
22
- let lastHash: any = null
23
- let watcher: any = null // subscribe to file and listen to changes
24
- let actions: any = null // action queue
25
-
26
- const loadDispatcher = (opts: any) => {
27
- actions = [{ name: "initializing", time: now() }]
28
- logger.debug(`Loading from ${opts.path}`)
29
-
30
- let exists = fs.existsSync(opts.path)
31
- if (opts.create) {
32
- if (exists)
33
- actions.push({ name: "reset", time: now() })
34
- fs.writeFileSync(opts.path, JSON.stringify(actions), { flag: "w" })
35
- exists = true
36
- }
37
-
38
- if (!exists)
39
- throw new Error(`Invalid queue path, missing file at: ${opts.path}`)
40
-
41
- let incomingActions = []
42
- try {
43
- const content = fs.readFileSync(opts.path, "utf-8")
44
- incomingActions = JSON.parse(content)
45
- if (!Array.isArray(incomingActions))
46
- incomingActions = []
47
- } catch {
48
- incomingActions = []
49
- logger.debug("Error loading VSCode Actions file")
50
- }
51
-
52
- logger.debug("Actions load ", incomingActions)
53
- return incomingActions
54
- }
55
-
56
- // eslint-disable-next-line
57
- const enqueue = (name: string, data: any | undefined = undefined) => {
58
- if (!Object.values(events).includes(name)) {
59
- logger.debug(`Invalid event ${name}`)
60
- throw new Error(`Invalid action ${name}`)
61
- }
62
-
63
- if (!actions)
64
- actions = []
65
-
66
- actions.push({ name, time: now(), data: data })
67
- logger.debug(`EMIT -> ${name}:Exporting changes to ${options.path}`)
68
-
69
- return fs.writeFileSync(options.path || "", JSON.stringify(actions))
70
- }
71
-
72
- const now = () => {
73
- const hrTime = process.hrtime()
74
- // eslint-disable-next-line
75
- const htTime0 = hrTime[0] * 1000000;
76
- return (htTime0 + hrTime[1]) / 1000
77
- }
78
-
79
- const loadFile = (filePath: string) => {
80
- if (!fs.existsSync(filePath))
81
- throw new Error(`No queue.json file to load on ${filePath}`)
82
-
83
- const content = fs.readFileSync(filePath, "utf8")
84
- const newHash = XXH.h32(content, 0xAB_CD).toString(16)
85
- const isUpdated = lastHash !== newHash
86
- lastHash = newHash
87
- const incomingActions = JSON.parse(content)
88
- return { isUpdated, incomingActions }
89
- }
90
-
91
- const dequeue = () => {
92
- // first time dequeue loads
93
- if (!actions)
94
- actions = []
95
-
96
- const { isUpdated, incomingActions } = loadFile(options.path || "")
97
-
98
- if (!isUpdated) {
99
- /**
100
- * make sure no tasks are executed from the queue by matching both
101
- * queues (the incoming with current one)
102
- */
103
- actions = incomingActions
104
- logger.debug(
105
- `No new actions to process: ${actions.length}/${incomingActions.length}`
106
- )
107
- return null
108
- }
109
-
110
- // do i need to reset actions to zero?
111
- if (actions.length > 0 && actions[0].time !== incomingActions[0].time) {
112
- actions = []
113
- }
114
-
115
- const action = incomingActions[incomingActions.length - 1]
116
- logger.debug("Dequeing action ", action)
117
- actions.push(action)
118
- return action
119
- }
120
-
121
- const pull = (callback: (T: any) => any) => {
122
- logger.debug("Pulling actions")
123
- let incoming = dequeue()
124
- while (incoming) {
125
- callback(incoming)
126
- incoming = dequeue()
127
- }
128
- }
129
-
130
- const reset = (callback: (T?: any) => any) => {
131
- logger.debug("Queue reseted")
132
- actions = []
133
- if (fs.existsSync(options.path || "")) {
134
- fs.writeFileSync(options.path || "", "[]")
135
- callback()
136
- }
137
- }
138
-
139
- const onPull = (callback: (T?: any) => any) => {
140
- // eslint-disable-next-line
141
- const chokidar = require("chokidar");
142
-
143
- logger.debug("Starting to listen...")
144
- try {
145
- loadFile(options.path || "")
146
- } catch {
147
- logger.debug("No previoues queue file, waiting for it to be created...")
148
- }
149
-
150
- if (!watcher) {
151
- logger.debug(`Watching ${options.path}`)
152
- watcher = chokidar.watch(`${options.path}`, {
153
- persistent: true,
154
- })
155
- } else
156
- logger.debug("Already watching queue path")
157
-
158
- watcher.on("add", () => pull(callback)).on("change", () => pull(callback))
159
-
160
- return true
161
- }
162
-
163
- const onReset = (callback: (T?: any) => any) => {
164
- // eslint-disable-next-line
165
- const chokidar = require("chokidar");
166
-
167
- if (!watcher) {
168
- logger.debug(`Watching ${options.path}`)
169
- watcher = chokidar.watch(`${options.path}`, {
170
- persistent: true,
171
- })
172
- }
173
-
174
- watcher.on("unlink", () => reset(callback))
175
-
176
- return true
177
- }
178
-
179
- export default {
180
- events,
181
- dispatcher: (opts: any = {}) => {
182
- if (!actions) {
183
- options = { ...options, ...opts }
184
- logger.debug("Initializing queue dispatcher", options)
185
- actions = loadDispatcher(options)
186
- }
187
-
188
- return { enqueue, events }
189
- },
190
- listener: (opts: any = {}) => {
191
- if (!actions) {
192
- options = { ...options, ...opts }
193
- logger.debug("Initializing queue listener", options)
194
- }
195
-
196
- return { onPull, onReset, events }
197
- },
198
- }
1
+ import logger from "../utils/console"
2
+ import * as fs from "fs"
3
+ // import em from "events"
4
+ import * as XXH from "xxhashjs"
5
+
6
+ // possible events to dispatch
7
+ const events = {
8
+ START_EXERCISE: "start_exercise",
9
+ INIT: "initializing",
10
+ RUNNING: "configuration_loaded",
11
+ END: "connection_ended",
12
+ RESET_EXERCISE: "reset_exercise",
13
+ OPEN_FILES: "open_files",
14
+ OPEN_WINDOW: "open_window",
15
+ INSTRUCTIONS_CLOSED: "instructions_closed",
16
+ OPEN_TERMINAL: "open_terminal",
17
+ }
18
+
19
+ let options = {
20
+ path: null,
21
+ create: false,
22
+ }
23
+ let lastHash: any = null
24
+ let watcher: any = null // subscribe to file and listen to changes
25
+ let actions: any = null // action queue
26
+
27
+ const loadDispatcher = (opts: any) => {
28
+ actions = [{ name: "initializing", time: now() }]
29
+ logger.debug(`Loading from ${opts.path}`)
30
+
31
+ let exists = fs.existsSync(opts.path)
32
+ if (opts.create) {
33
+ if (exists)
34
+ actions.push({ name: "reset", time: now() })
35
+ fs.writeFileSync(opts.path, JSON.stringify(actions), { flag: "w" })
36
+ exists = true
37
+ }
38
+
39
+ if (!exists)
40
+ throw new Error(`Invalid queue path, missing file at: ${opts.path}`)
41
+
42
+ let incomingActions = []
43
+ try {
44
+ const content = fs.readFileSync(opts.path, "utf-8")
45
+ incomingActions = JSON.parse(content)
46
+ if (!Array.isArray(incomingActions))
47
+ incomingActions = []
48
+ } catch {
49
+ incomingActions = []
50
+ logger.debug("Error loading VSCode Actions file")
51
+ }
52
+
53
+ logger.debug("Actions load ", incomingActions)
54
+ return incomingActions
55
+ }
56
+
57
+ // eslint-disable-next-line
58
+ const enqueue = (name: string, data: any | undefined = undefined) => {
59
+ if (!Object.values(events).includes(name)) {
60
+ logger.debug(`Invalid event ${name}`)
61
+ throw new Error(`Invalid action ${name}`)
62
+ }
63
+
64
+ if (!actions)
65
+ actions = []
66
+
67
+ actions.push({ name, time: now(), data: data })
68
+ logger.debug(`EMIT -> ${name}:Exporting changes to ${options.path}`)
69
+
70
+ return fs.writeFileSync(options.path || "", JSON.stringify(actions))
71
+ }
72
+
73
+ const now = () => {
74
+ const hrTime = process.hrtime()
75
+ // eslint-disable-next-line
76
+ const htTime0 = hrTime[0] * 1000000;
77
+ return (htTime0 + hrTime[1]) / 1000
78
+ }
79
+
80
+ const loadFile = (filePath: string) => {
81
+ if (!fs.existsSync(filePath))
82
+ throw new Error(`No queue.json file to load on ${filePath}`)
83
+
84
+ const content = fs.readFileSync(filePath, "utf8")
85
+ const newHash = XXH.h32(content, 0xAB_CD).toString(16)
86
+ const isUpdated = lastHash !== newHash
87
+ lastHash = newHash
88
+ const incomingActions = JSON.parse(content)
89
+ return { isUpdated, incomingActions }
90
+ }
91
+
92
+ const dequeue = () => {
93
+ // first time dequeue loads
94
+ if (!actions)
95
+ actions = []
96
+
97
+ const { isUpdated, incomingActions } = loadFile(options.path || "")
98
+
99
+ if (!isUpdated) {
100
+ /**
101
+ * make sure no tasks are executed from the queue by matching both
102
+ * queues (the incoming with current one)
103
+ */
104
+ actions = incomingActions
105
+ logger.debug(
106
+ `No new actions to process: ${actions.length}/${incomingActions.length}`
107
+ )
108
+ return null
109
+ }
110
+
111
+ // do i need to reset actions to zero?
112
+ if (actions.length > 0 && actions[0].time !== incomingActions[0].time) {
113
+ actions = []
114
+ }
115
+
116
+ const action = incomingActions[incomingActions.length - 1]
117
+ logger.debug("Dequeing action ", action)
118
+ actions.push(action)
119
+ return action
120
+ }
121
+
122
+ const pull = (callback: (T: any) => any) => {
123
+ logger.debug("Pulling actions")
124
+ let incoming = dequeue()
125
+ while (incoming) {
126
+ callback(incoming)
127
+ incoming = dequeue()
128
+ }
129
+ }
130
+
131
+ const reset = (callback: (T?: any) => any) => {
132
+ logger.debug("Queue reseted")
133
+ actions = []
134
+ if (fs.existsSync(options.path || "")) {
135
+ fs.writeFileSync(options.path || "", "[]")
136
+ callback()
137
+ }
138
+ }
139
+
140
+ const onPull = (callback: (T?: any) => any) => {
141
+ // eslint-disable-next-line
142
+ const chokidar = require("chokidar");
143
+
144
+ logger.debug("Starting to listen...")
145
+ try {
146
+ loadFile(options.path || "")
147
+ } catch {
148
+ logger.debug("No previoues queue file, waiting for it to be created...")
149
+ }
150
+
151
+ if (!watcher) {
152
+ logger.debug(`Watching ${options.path}`)
153
+ watcher = chokidar.watch(`${options.path}`, {
154
+ persistent: true,
155
+ })
156
+ } else
157
+ logger.debug("Already watching queue path")
158
+
159
+ watcher.on("add", () => pull(callback)).on("change", () => pull(callback))
160
+
161
+ return true
162
+ }
163
+
164
+ const onReset = (callback: (T?: any) => any) => {
165
+ // eslint-disable-next-line
166
+ const chokidar = require("chokidar");
167
+
168
+ if (!watcher) {
169
+ logger.debug(`Watching ${options.path}`)
170
+ watcher = chokidar.watch(`${options.path}`, {
171
+ persistent: true,
172
+ })
173
+ }
174
+
175
+ watcher.on("unlink", () => reset(callback))
176
+
177
+ return true
178
+ }
179
+
180
+ export default {
181
+ events,
182
+ dispatcher: (opts: any = {}) => {
183
+ if (!actions) {
184
+ options = { ...options, ...opts }
185
+ logger.debug("Initializing queue dispatcher", options)
186
+ actions = loadDispatcher(options)
187
+ }
188
+
189
+ return { enqueue, events }
190
+ },
191
+ listener: (opts: any = {}) => {
192
+ if (!actions) {
193
+ options = { ...options, ...opts }
194
+ logger.debug("Initializing queue listener", options)
195
+ }
196
+
197
+ return { onPull, onReset, events }
198
+ },
199
+ }