@learnpack/learnpack 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. package/README.md +51 -398
  2. package/bin/run +14 -2
  3. package/oclif.manifest.json +1 -1
  4. package/package.json +135 -111
  5. package/src/commands/audit.ts +462 -0
  6. package/src/commands/clean.ts +29 -0
  7. package/src/commands/download.ts +62 -0
  8. package/src/commands/init.ts +169 -0
  9. package/src/commands/login.ts +42 -0
  10. package/src/commands/logout.ts +43 -0
  11. package/src/commands/publish.ts +107 -0
  12. package/src/commands/start.ts +229 -0
  13. package/src/commands/{test.js → test.ts} +19 -21
  14. package/src/index.ts +1 -0
  15. package/src/managers/config/allowed_files.ts +29 -0
  16. package/src/managers/config/defaults.ts +33 -0
  17. package/src/managers/config/exercise.ts +295 -0
  18. package/src/managers/config/index.ts +411 -0
  19. package/src/managers/file.ts +169 -0
  20. package/src/managers/gitpod.ts +84 -0
  21. package/src/managers/server/{index.js → index.ts} +26 -19
  22. package/src/managers/server/routes.ts +250 -0
  23. package/src/managers/session.ts +118 -0
  24. package/src/managers/socket.ts +239 -0
  25. package/src/managers/test.ts +83 -0
  26. package/src/models/action.ts +3 -0
  27. package/src/models/audit-errors.ts +4 -0
  28. package/src/models/config-manager.ts +23 -0
  29. package/src/models/config.ts +74 -0
  30. package/src/models/counter.ts +11 -0
  31. package/src/models/errors.ts +22 -0
  32. package/src/models/exercise-obj.ts +26 -0
  33. package/src/models/file.ts +5 -0
  34. package/src/models/findings.ts +18 -0
  35. package/src/models/flags.ts +10 -0
  36. package/src/models/front-matter.ts +11 -0
  37. package/src/models/gitpod-data.ts +19 -0
  38. package/src/models/language.ts +4 -0
  39. package/src/models/package.ts +7 -0
  40. package/src/models/plugin-config.ts +17 -0
  41. package/src/models/session.ts +26 -0
  42. package/src/models/socket.ts +48 -0
  43. package/src/models/status.ts +15 -0
  44. package/src/models/success-types.ts +1 -0
  45. package/src/plugin/command/compile.ts +17 -0
  46. package/src/plugin/command/test.ts +30 -0
  47. package/src/plugin/index.ts +6 -0
  48. package/src/plugin/plugin.ts +94 -0
  49. package/src/plugin/utils.ts +87 -0
  50. package/src/types/node-fetch.d.ts +1 -0
  51. package/src/ui/download.ts +71 -0
  52. package/src/utils/BaseCommand.ts +48 -0
  53. package/src/utils/SessionCommand.ts +48 -0
  54. package/src/utils/api.ts +194 -0
  55. package/src/utils/audit.ts +162 -0
  56. package/src/utils/console.ts +24 -0
  57. package/src/utils/errors.ts +117 -0
  58. package/src/utils/{exercisesQueue.js → exercisesQueue.ts} +12 -6
  59. package/src/utils/fileQueue.ts +198 -0
  60. package/src/utils/misc.ts +23 -0
  61. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.es.md +2 -4
  62. package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.md +1 -2
  63. package/src/utils/templates/isolated/01-hello-world/README.es.md +1 -2
  64. package/src/utils/templates/isolated/01-hello-world/README.md +1 -2
  65. package/src/utils/templates/isolated/README.ejs +1 -1
  66. package/src/utils/templates/isolated/README.es.ejs +1 -1
  67. package/src/utils/validators.ts +18 -0
  68. package/src/utils/watcher.ts +27 -0
  69. package/plugin/command/compile.js +0 -17
  70. package/plugin/command/test.js +0 -29
  71. package/plugin/index.js +0 -6
  72. package/plugin/plugin.js +0 -71
  73. package/plugin/utils.js +0 -78
  74. package/src/commands/audit.js +0 -243
  75. package/src/commands/clean.js +0 -27
  76. package/src/commands/download.js +0 -52
  77. package/src/commands/hello.js +0 -20
  78. package/src/commands/init.js +0 -133
  79. package/src/commands/login.js +0 -45
  80. package/src/commands/logout.js +0 -39
  81. package/src/commands/publish.js +0 -78
  82. package/src/commands/start.js +0 -169
  83. package/src/index.js +0 -1
  84. package/src/managers/config/allowed_files.js +0 -12
  85. package/src/managers/config/defaults.js +0 -32
  86. package/src/managers/config/exercise.js +0 -212
  87. package/src/managers/config/index.js +0 -342
  88. package/src/managers/file.js +0 -137
  89. package/src/managers/server/routes.js +0 -151
  90. package/src/managers/session.js +0 -83
  91. package/src/managers/socket.js +0 -185
  92. package/src/managers/test.js +0 -77
  93. package/src/ui/download.js +0 -48
  94. package/src/utils/BaseCommand.js +0 -34
  95. package/src/utils/SessionCommand.js +0 -46
  96. package/src/utils/api.js +0 -164
  97. package/src/utils/audit.js +0 -114
  98. package/src/utils/console.js +0 -16
  99. package/src/utils/errors.js +0 -90
  100. package/src/utils/fileQueue.js +0 -194
  101. package/src/utils/misc.js +0 -26
  102. package/src/utils/validators.js +0 -15
  103. package/src/utils/watcher.js +0 -24
@@ -1,114 +0,0 @@
1
- const Console = require('./console')
2
- const fetch = require('node-fetch')
3
- const fs = require('fs')
4
-
5
- module.exports = {
6
- // This function checks if a url is valid.
7
- isUrl: async (url, errors, counter) => {
8
- let regex_url = /(https?:\/\/[a-zA-Z_\-.\/0-9]+)/gm
9
- counter.links.total++
10
- if (!regex_url.exec(url)) {
11
- counter.links.error++
12
- errors.push({ exercise: null, msg: `The repository value of the configuration file is not a link: ${url}` })
13
- return false;
14
- }
15
- let res = await fetch(url, { method: "HEAD" });
16
- if (!res.ok) {
17
- counter.links.error++
18
- errors.push({ exercise: null, msg: `The link of the repository is broken: ${url}` })
19
- }
20
- return true;
21
- },
22
- checkForEmptySpaces: (str) => {
23
- let isEmpty = true;
24
- for(let letter of str){
25
- if (letter !== " ") {
26
- isEmpty = false;
27
- return isEmpty;
28
- }
29
- }
30
- return isEmpty;
31
- },
32
- checkLearnpackClean: (configObj, errors) => {
33
- if(fs.existsSync(configObj.config.outputPath) || fs.existsSync(configObj.config.dirPath + "/_app") || fs.existsSync(configObj.config.dirPath + "/reports") || fs.existsSync(configObj.config.dirPath + "/resets") || fs.existsSync(configObj.config.dirPath + "/app.tar.gz") || fs.existsSync(configObj.config.dirPath + "/config.json") || fs.existsSync(configObj.config.dirPath + "/vscode_queue.json")) {
34
- errors.push({ exercise: null, msg: `You have to run learnpack clean command` })
35
- }
36
- },
37
- findInFile: (types, content) => {
38
- const regex = {
39
- relative_images: /!\[.*\]\s*\((((\.\/)?(\.{2}\/){1,5})(.*\/)*(.[^\/\s]*\.[a-zA-Z]{2,4})[^\s]*)\)/gm,
40
- external_images: /!\[.*\]\((https?:\/(\/{1}[^/)]+)+\/?)\)/gm,
41
- markdown_links: /(\s)+\[.*\]\((https?:\/(\/{1}[^/)]+)+\/?)\)/gm,
42
- url: /(https?:\/\/[a-zA-Z_\-.\/0-9]+)/gm,
43
- uploadcare: /https:\/\/ucarecdn.com\/(?:.*\/)*([a-zA-Z_\-.\/0-9]+)/gm
44
- }
45
-
46
- const validTypes = Object.keys(regex);
47
- if (!Array.isArray(types)) types = [types];
48
-
49
- let findings = {}
50
-
51
- types.forEach(type => {
52
- if (!validTypes.includes(type)) throw Error("Invalid type: " + type)
53
- else findings[type] = {};
54
- });
55
-
56
- types.forEach(type => {
57
-
58
- let count = 0;
59
- let m;
60
- while ((m = regex[type].exec(content)) !== null) {
61
- // This is necessary to avoid infinite loops with zero-width matches
62
- if (m.index === regex.lastIndex) {
63
- regex.lastIndex++;
64
- }
65
-
66
- // The result can be accessed through the `m`-variable.
67
- // m.forEach((match, groupIndex) => values.push(match));
68
- count++;
69
-
70
- findings[type][m[0]] = {
71
- content: m[0],
72
- absUrl: m[1],
73
- mdUrl: m[2],
74
- relUrl: m[6]
75
- }
76
- }
77
- })
78
-
79
- return findings;
80
- },
81
- // This function checks if there are errors, and show them in the console at the end.
82
- showErrors: (errors, counter) => {
83
- return new Promise((resolve, reject) => {
84
- if (errors) {
85
- if (errors.length > 0) {
86
- Console.log("Checking for errors...")
87
- errors.forEach((error, i) => Console.error(`${i + 1}) ${error.msg} ${error.exercise != null ? `(Exercise: ${error.exercise})` : ""}`))
88
- Console.error(` We found ${errors.length} errors among ${counter.images.total} images, ${counter.links.total} link, ${counter.readmeFiles} README files and ${counter.exercises} exercises.`)
89
- process.exit(1)
90
- } else {
91
- Console.success(`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.`)
92
- process.exit(0)
93
- }
94
- resolve("SUCCESS")
95
- } else {
96
- reject("Failed")
97
- }
98
- })
99
- },
100
- // This function checks if there are warnings, and show them in the console at the end.
101
- showWarnings: (warnings) => {
102
- return new Promise((resolve, reject) => {
103
- if (warnings) {
104
- if (warnings.length > 0) {
105
- Console.log("Checking for warnings...")
106
- warnings.forEach((warning, i) => Console.warning(`${i + 1}) ${warning.msg} ${warning.exercise ? `File: ${warning.exercise}` : ""}`))
107
- }
108
- resolve("SUCCESS")
109
- } else {
110
- reject("Failed")
111
- }
112
- })
113
- }
114
- }
@@ -1,16 +0,0 @@
1
- const chalk = require("chalk")
2
-
3
- module.exports = {
4
- // _debug: true,
5
- _debug: process.env.DEBUG == 'true',
6
- startDebug: function(){ this._debug = true; },
7
- log: (msg, ...args) => console.log(chalk.gray(msg), ...args),
8
- error: (msg, ...args) => console.log(chalk.red('⨉ '+msg), ...args),
9
- success: (msg, ...args) => console.log(chalk.green('✓ '+msg), ...args),
10
- info: (msg, ...args) => console.log(chalk.blue('ⓘ '+msg), ...args),
11
- help: (msg) => console.log(`${chalk.white.bold('⚠ help:')} ${chalk.white(msg)}`),
12
- warning: (msg) => console.log(`${chalk.yellow('⚠ warning:')} ${chalk.yellow(msg)}`),
13
- debug(...args){
14
- this._debug && console.log(chalk.magentaBright(`⚠ debug: `), args)
15
- }
16
- }
@@ -1,90 +0,0 @@
1
- const fetch = require("node-fetch");
2
- const Console = require("./console");
3
- let solutions = null;
4
- const uknown = {
5
- video: "https://www.youtube.com/watch?v=gD1Sa99GiE4",
6
- message: "Uknown internal error",
7
- slug: "uknown",
8
- gif: "https://github.com/breatheco-de/breathecode-cli/blob/master/docs/errors/uknown.gif?raw=true"
9
- };
10
-
11
- const getSolution = (slug=null) => {
12
-
13
- if(!slug) Console.debug(`Getting solution templates from the learnpack repository`);
14
- else Console.debug(`Getting solution for ${slug}`, solutions);
15
-
16
-
17
- if(!solutions){
18
- Console.debug("Fetching for errors.json on github");
19
- fetch('https://raw.githubusercontent.com/breatheco-de/breathecode-cli/master/docs/errors/errors.json')
20
- .then(r => r.json()).then(_s => solutions = _s);
21
- return uknown;
22
- }
23
- if(typeof solutions[slug] === "undefined" || !slug) return uknown;
24
- else return solutions[slug];
25
- }
26
- const ValidationError = (error) => {
27
- const message = error.message || error;
28
- const _err = new Error(message);
29
- _err.status = 400;
30
- _err.type = 'validation-error';
31
-
32
- const sol = getSolution(error.slug);
33
- _err.video = sol.video;
34
- _err.gif = sol.gif;
35
- _err.message = typeof message === "string" ? message : sol.message;
36
- return _err;
37
- }
38
- const NotFoundError = (error) => {
39
- const message = error.message || error;
40
- const _err = new Error(message);
41
- _err.status = 400;
42
- _err.type = 'not-found-error';
43
-
44
- const sol = getSolution(error.slug);
45
- _err.video = sol.video;
46
- _err.gif = sol.gif;
47
- _err.message = typeof message === "string" ? message : sol.message;
48
- return _err;
49
- }
50
- const CompilerError = (error) => {
51
- const message = error.message || error;
52
- const _err = new Error(message);
53
- _err.status = 400;
54
- _err.type = 'compiler-error';
55
-
56
- const sol = getSolution(error.slug);
57
- _err.video = sol.video;
58
- _err.gif = sol.gif;
59
- _err.message = typeof message === "string" ? message : sol.message;
60
- return _err;
61
- }
62
- const TestingError = (error) => {
63
- const message = error.message || error;
64
- const _err = new Error(message);
65
- _err.status = 400;
66
- _err.type = 'testing-error';
67
- return _err;
68
- }
69
- const AuthError = (error) => {
70
- const message = error.message || error;
71
- const _err = new Error(message);
72
- _err.status = 403;
73
- _err.type = 'auth-error';
74
- return _err;
75
- }
76
- const InternalError = (error) => {
77
- const message = error.message || error;
78
- const _err = new Error(message);
79
- _err.status = 500;
80
- _err.type = 'internal-error';
81
-
82
- const sol = getSolution(error.slug);
83
- _err.video = sol.video;
84
- _err.gif = sol.gif;
85
- _err.message = typeof message === "string" ? message : sol.message;
86
- return _err;
87
- }
88
-
89
- getSolution();
90
- module.exports = { ValidationError, CompilerError, TestingError, NotFoundError, InternalError, AuthError };
@@ -1,194 +0,0 @@
1
- const logger = require('../utils/console')
2
- const fs = require("fs")
3
- const em = require('events')
4
- const XXH = require('xxhashjs')
5
-
6
- // possible events to dispatch
7
- let 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 = null
23
- let watcher = null // subscribe to file and listen to changes
24
- let actions = null // action queue
25
-
26
- const loadDispatcher = (opts) => {
27
-
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) actions.push({ name: "reset", time: now() })
34
- fs.writeFileSync(opts.path, JSON.stringify(actions), { flag: "w"})
35
- exists = true
36
- }
37
-
38
- if(!exists) throw Error(`Invalid queue path, missing file at: ${opts.path}`)
39
-
40
- let incomingActions = []
41
- try{
42
- const content = fs.readFileSync(opts.path, 'utf-8')
43
- incomingActions = JSON.parse(content)
44
- if(!Array.isArray(incomingActions)) incomingActions = []
45
- }
46
- catch(error){
47
- incomingActions = []
48
- logger.debug(`Error loading VSCode Actions file`)
49
- }
50
-
51
- logger.debug(`Actions load `, incomingActions)
52
- return incomingActions
53
- }
54
-
55
-
56
- const enqueue = (name, data) => {
57
-
58
-
59
- if(!Object.values(events).includes(name)){
60
- logger.debug(`Invalid event ${name}`)
61
- throw Error(`Invalid action ${name}`)
62
- }
63
-
64
- if(!actions) 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
- const now = () => {
72
- const hrTime = process.hrtime()
73
- return hrTime[0] * 1000000 + hrTime[1] / 1000
74
- }
75
- const loadFile = (filePath) => {
76
-
77
- if(!fs.existsSync(filePath)) throw Error(`No queue.json file to load on ${filePath}`);
78
-
79
- const content = fs.readFileSync(filePath, 'utf8')
80
- const newHash = XXH.h32( content, 0xABCD ).toString(16);
81
- const isUpdated = lastHash != newHash
82
- lastHash = newHash
83
- const incomingActions = JSON.parse(content)
84
- return { isUpdated, incomingActions }
85
- }
86
-
87
- const dequeue = () => {
88
-
89
- // first time dequeue loads
90
- if(!actions) actions = []
91
-
92
- const { isUpdated, incomingActions } = loadFile(options.path, 'utf8')
93
-
94
- if(!isUpdated){
95
-
96
- /**
97
- * make sure no tasks are executed from the queue by matching both
98
- * queues (the incoming with current one)
99
- */
100
- actions = incomingActions
101
- logger.debug(`No new actions to process: ${actions.length}/${incomingActions.length}`)
102
- return null
103
- }
104
-
105
- // do i need to reset actions to zero?
106
- if(actions.length > 0 && actions[0].time != incomingActions[0].time){
107
- actions = []
108
- }
109
-
110
- let action = incomingActions[actions.length]
111
- logger.debug("Dequeing action ", action)
112
- actions.push(action)
113
- return action
114
- }
115
-
116
- const pull = (callback) => {
117
- logger.debug("Pulling actions")
118
- let incoming = dequeue()
119
- while(incoming){
120
- callback(incoming)
121
- incoming = dequeue()
122
- }
123
- }
124
-
125
- const reset = (callback) => {
126
- logger.debug("Queue reseted")
127
- actions = []
128
- if(fs.existsSync(options.path)){
129
- const success = fs.writeFileSync(options.path, "[]")
130
- if(success) callback()
131
- }
132
- }
133
-
134
- const onPull = (callback) => {
135
-
136
- const chokidar = require('chokidar')
137
-
138
- logger.debug("Starting to listen...")
139
- try{
140
- loadFile(options.path)
141
- }catch{
142
- logger.debug("No previeues queue file, waiting for it to be created...")
143
- }
144
-
145
- if(!watcher){
146
- logger.debug(`Watching ${options.path}`)
147
- watcher = chokidar.watch(`${options.path}`, {
148
- persistent: true
149
- })
150
- }
151
- else logger.debug("Already watching queue path")
152
-
153
- watcher
154
- .on('add', path => pull(callback))
155
- .on('change', path => pull(callback))
156
-
157
- return true
158
- }
159
-
160
- const onReset = (callback) => {
161
-
162
- const chokidar = require('chokidar')
163
-
164
- if(!watcher){
165
- logger.debug(`Watching ${options.path}`)
166
- watcher = chokidar.watch(`${options.path}`, {
167
- persistent: true
168
- })
169
- }
170
-
171
- watcher.on('unlink', path => reset(callback))
172
-
173
- return true
174
- }
175
-
176
-
177
- module.exports = {
178
- events,
179
- dispatcher: (opts = {}) => {
180
- if(!actions){
181
- options = { ...options, ...opts }
182
- logger.debug("Initializing queue dispatcher", options)
183
- actions = loadDispatcher(options)
184
- }
185
- return { enqueue, events }
186
- },
187
- listener: (opts = {}) => {
188
- if(!actions){
189
- options = { ...options, ...opts }
190
- logger.debug("Initializing queue listener", options)
191
- }
192
- return { onPull, onReset, events }
193
- }
194
- }
package/src/utils/misc.js DELETED
@@ -1,26 +0,0 @@
1
- const prioritizeHTMLFile = (entryFiles) => {
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 (let i = 0; i < entryFiles.length; i++) {
12
- if (i !== index) {
13
- files.push(entryFiles[i]);
14
- }
15
- }
16
- files.push(entryFiles[index]);
17
- } else {
18
- files = entryFiles;
19
- }
20
-
21
- return files;
22
- };
23
-
24
- module.exports = {
25
- prioritizeHTMLFile,
26
- };
@@ -1,15 +0,0 @@
1
-
2
- function validURL(str) {
3
-
4
- if(!str || !str.includes("github.com")) return false;
5
-
6
- var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
7
- '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
8
- '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
9
- '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
10
- '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
11
- '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
12
- return !!pattern.test(str);
13
- }
14
-
15
- module.exports = { validURL }
@@ -1,24 +0,0 @@
1
- const chokidar = require('chokidar');
2
- const debounce = require('debounce');
3
-
4
- module.exports = (path) => new Promise((resolve, reject) => {
5
- const watcher = chokidar.watch(path, {
6
- ignored: (_path, _stats) => {
7
- return _stats && !_stats.isDirectory();
8
- },
9
- persistent: true,
10
- depth: 1,
11
- ignoreInitial: true
12
- });
13
-
14
- const onChange = (eventname, filename) =>{
15
- resolve(eventname, filename);
16
- }
17
- watcher.on('all', debounce(onChange, 500, true));
18
- // watcher.on('all', onChange);
19
-
20
- process.on('SIGINT', function() {
21
- watcher.close();
22
- process.exit();
23
- });
24
- });