@learnpack/learnpack 5.0.7 → 5.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. package/README.md +17 -17
  2. package/bin/run +17 -17
  3. package/bin/run.cmd +3 -3
  4. package/lib/commands/audit.js +30 -21
  5. package/lib/commands/clean.js +3 -3
  6. package/lib/commands/download.js +3 -3
  7. package/lib/commands/login.js +3 -3
  8. package/lib/commands/logout.js +3 -3
  9. package/oclif.manifest.json +1 -1
  10. package/package.json +152 -152
  11. package/src/commands/audit.ts +449 -443
  12. package/src/commands/clean.ts +29 -29
  13. package/src/commands/download.ts +61 -61
  14. package/src/commands/login.ts +42 -42
  15. package/src/commands/logout.ts +43 -43
  16. package/src/commands/publish.ts +249 -249
  17. package/src/commands/test.ts +85 -85
  18. package/src/index.ts +1 -1
  19. package/src/managers/config/allowed_files.ts +29 -29
  20. package/src/managers/gitpod.ts +84 -84
  21. package/src/managers/server/index.ts +78 -78
  22. package/src/managers/telemetry.ts +353 -353
  23. package/src/managers/test.ts +83 -83
  24. package/src/models/audit.ts +16 -16
  25. package/src/models/config-manager.ts +23 -23
  26. package/src/models/counter.ts +11 -11
  27. package/src/models/errors.ts +22 -22
  28. package/src/models/exercise-obj.ts +29 -29
  29. package/src/models/file.ts +5 -5
  30. package/src/models/findings.ts +18 -18
  31. package/src/models/flags.ts +10 -10
  32. package/src/models/front-matter.ts +11 -11
  33. package/src/models/gitpod-data.ts +19 -19
  34. package/src/models/language.ts +4 -4
  35. package/src/models/package.ts +7 -7
  36. package/src/models/plugin-config.ts +17 -17
  37. package/src/models/success-types.ts +1 -1
  38. package/src/plugin/command/compile.ts +17 -17
  39. package/src/plugin/command/test.ts +30 -30
  40. package/src/plugin/index.ts +6 -6
  41. package/src/plugin/plugin.ts +94 -94
  42. package/src/plugin/utils.ts +87 -87
  43. package/src/types/node-fetch.d.ts +1 -1
  44. package/src/ui/download.ts +71 -71
  45. package/src/utils/BaseCommand.ts +48 -48
  46. package/src/utils/SessionCommand.ts +43 -43
  47. package/src/utils/audit.ts +393 -393
  48. package/src/utils/errors.ts +117 -117
  49. package/src/utils/exercisesQueue.ts +51 -51
  50. package/src/utils/fileQueue.ts +199 -199
  51. package/src/utils/misc.ts +23 -23
  52. package/src/utils/osOperations.ts +79 -79
  53. package/src/utils/templates/gitignore.txt +19 -19
  54. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.es.md +24 -24
  55. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.md +24 -24
  56. package/src/utils/templates/incremental/.vscode/schema.json +121 -121
  57. package/src/utils/templates/incremental/.vscode/settings.json +13 -13
  58. package/src/utils/templates/incremental/README.ejs +4 -4
  59. package/src/utils/templates/incremental/README.es.ejs +4 -4
  60. package/src/utils/templates/isolated/.vscode/schema.json +121 -121
  61. package/src/utils/templates/isolated/.vscode/settings.json +13 -13
  62. package/src/utils/templates/isolated/README.ejs +4 -4
  63. package/src/utils/templates/isolated/README.es.ejs +4 -4
  64. package/src/utils/templates/no-grading/README.ejs +4 -4
  65. package/src/utils/templates/no-grading/README.es.ejs +4 -4
  66. package/src/utils/validators.ts +18 -18
  67. package/src/utils/watcher.ts +27 -27
@@ -1,199 +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
- 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
- }
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
+ }
package/src/utils/misc.ts CHANGED
@@ -1,23 +1,23 @@
1
- export const prioritizeHTMLFile = (entryFiles: string[]) => {
2
- let files = []
3
-
4
- // Find the html file and put it as latest in the files array
5
- // in order to keep the html file opened in vscode plugin
6
- const index = entryFiles.findIndex(file => {
7
- return /.*\.html$/.test(file)
8
- })
9
-
10
- if (index !== -1) {
11
- for (const [i, entryFile] of entryFiles.entries()) {
12
- if (i !== index) {
13
- files.push(entryFile)
14
- }
15
- }
16
-
17
- files.push(entryFiles[index])
18
- } else {
19
- files = entryFiles
20
- }
21
-
22
- return files
23
- }
1
+ export const prioritizeHTMLFile = (entryFiles: string[]) => {
2
+ let files = []
3
+
4
+ // Find the html file and put it as latest in the files array
5
+ // in order to keep the html file opened in vscode plugin
6
+ const index = entryFiles.findIndex(file => {
7
+ return /.*\.html$/.test(file)
8
+ })
9
+
10
+ if (index !== -1) {
11
+ for (const [i, entryFile] of entryFiles.entries()) {
12
+ if (i !== index) {
13
+ files.push(entryFile)
14
+ }
15
+ }
16
+
17
+ files.push(entryFiles[index])
18
+ } else {
19
+ files = entryFiles
20
+ }
21
+
22
+ return files
23
+ }
@@ -1,79 +1,79 @@
1
- import { exec } from "child_process"
2
- import * as os from "os"
3
- import * as path from "path"
4
- import cli from "cli-ux"
5
-
6
- const checkXDGInstalled = (): Promise<boolean> => {
7
- return new Promise((resolve, reject) => {
8
- exec("which xdg-open", (error, stdout, stderr) => {
9
- if (error) {
10
- resolve(false)
11
- }
12
-
13
- if (stdout) {
14
- resolve(true)
15
- } else {
16
- resolve(false)
17
- }
18
- })
19
- })
20
- }
21
-
22
- const openFile = (filePath: string): void => {
23
- const fullPath = path.join(process.cwd(), filePath)
24
- let command: string
25
-
26
- switch (os.platform()) {
27
- case "darwin": // macOS
28
- command = `open ${fullPath}`
29
- break
30
- case "win32": // Windows
31
- command = `start ${fullPath}`
32
- break
33
- case "linux": // Linux
34
- command = `xdg-open ${fullPath}`
35
- break
36
- default:
37
- throw new Error("Unsupported OS")
38
- }
39
-
40
- exec(command, (error, stdout, stderr) => {
41
- if (error) {
42
- console.error(`exec error: ${error}`)
43
- }
44
- })
45
- }
46
-
47
- const eventManager = {
48
- enqueue: (event: string, data: any) => {
49
- if (event === "start_exercise") {
50
- const exercise = data
51
- const filesToOpen = exercise.files
52
- .filter((file: any) => !file.hidden)
53
- .map((file: any) => {
54
- return file.path.replace("\\", "/")
55
- })
56
-
57
- for (const file of filesToOpen) {
58
- openFile(file)
59
- }
60
- }
61
-
62
- if (event === "open_files") {
63
- const files = data
64
-
65
- for (const file of files) {
66
- const correctedPath = file.replace("\\", "/")
67
- openFile(correctedPath)
68
- }
69
- }
70
-
71
- if (event === "open_window") {
72
- const url = data.url
73
- cli.open(url)
74
- }
75
- },
76
- checkXDGInstalled,
77
- }
78
-
79
- export { eventManager }
1
+ import { exec } from "child_process"
2
+ import * as os from "os"
3
+ import * as path from "path"
4
+ import cli from "cli-ux"
5
+
6
+ const checkXDGInstalled = (): Promise<boolean> => {
7
+ return new Promise((resolve, reject) => {
8
+ exec("which xdg-open", (error, stdout, stderr) => {
9
+ if (error) {
10
+ resolve(false)
11
+ }
12
+
13
+ if (stdout) {
14
+ resolve(true)
15
+ } else {
16
+ resolve(false)
17
+ }
18
+ })
19
+ })
20
+ }
21
+
22
+ const openFile = (filePath: string): void => {
23
+ const fullPath = path.join(process.cwd(), filePath)
24
+ let command: string
25
+
26
+ switch (os.platform()) {
27
+ case "darwin": // macOS
28
+ command = `open ${fullPath}`
29
+ break
30
+ case "win32": // Windows
31
+ command = `start ${fullPath}`
32
+ break
33
+ case "linux": // Linux
34
+ command = `xdg-open ${fullPath}`
35
+ break
36
+ default:
37
+ throw new Error("Unsupported OS")
38
+ }
39
+
40
+ exec(command, (error, stdout, stderr) => {
41
+ if (error) {
42
+ console.error(`exec error: ${error}`)
43
+ }
44
+ })
45
+ }
46
+
47
+ const eventManager = {
48
+ enqueue: (event: string, data: any) => {
49
+ if (event === "start_exercise") {
50
+ const exercise = data
51
+ const filesToOpen = exercise.files
52
+ .filter((file: any) => !file.hidden)
53
+ .map((file: any) => {
54
+ return file.path.replace("\\", "/")
55
+ })
56
+
57
+ for (const file of filesToOpen) {
58
+ openFile(file)
59
+ }
60
+ }
61
+
62
+ if (event === "open_files") {
63
+ const files = data
64
+
65
+ for (const file of files) {
66
+ const correctedPath = file.replace("\\", "/")
67
+ openFile(correctedPath)
68
+ }
69
+ }
70
+
71
+ if (event === "open_window") {
72
+ const url = data.url
73
+ cli.open(url)
74
+ }
75
+ },
76
+ checkXDGInstalled,
77
+ }
78
+
79
+ export { eventManager }
@@ -1,20 +1,20 @@
1
- # configuration and readme
2
- !.gitignore
3
- !.gitpod.yml
4
- !.gitpod.Dockerfile
5
- !learn.json
6
- !README.md
7
-
8
- # exercises
9
- !.learn/
10
- !.learn/*
11
- .learn/_app
12
- .learn/.session
13
- .learn/dist
14
- .learn/app.tar.gz
15
- .learn/config.json
16
-
17
- # python compiled files
18
- *.pyc
19
- __pycache__/
1
+ # configuration and readme
2
+ !.gitignore
3
+ !.gitpod.yml
4
+ !.gitpod.Dockerfile
5
+ !learn.json
6
+ !README.md
7
+
8
+ # exercises
9
+ !.learn/
10
+ !.learn/*
11
+ .learn/_app
12
+ .learn/.session
13
+ .learn/dist
14
+ .learn/app.tar.gz
15
+ .learn/config.json
16
+
17
+ # python compiled files
18
+ *.pyc
19
+ __pycache__/
20
20
  .pytest_cache/