@learnpack/learnpack 1.0.0 → 2.0.3

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 +106 -118
  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 +172 -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 +234 -0
  13. package/src/commands/{test.js → test.ts} +21 -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 +302 -0
  18. package/src/managers/config/index.ts +412 -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 +255 -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
package/plugin/utils.js DELETED
@@ -1,78 +0,0 @@
1
- const chalk = require("chalk")
2
-
3
- const getMatches = (reg, content) => {
4
- let inputs = [];
5
- let m;
6
- while ((m = reg.exec(content)) !== null) {
7
- // This is necessary to avoid infinite loops with zero-width matches
8
- if (m.index === reg.lastIndex) reg.lastIndex++;
9
-
10
- // The result can be accessed through the `m`-variable.
11
- inputs.push(m[1] || null);
12
- }
13
- return inputs;
14
- }
15
-
16
- const cleanStdout = (buffer, inputs) => {
17
- if(Array.isArray(inputs))
18
- for(let i = 0; i < inputs.length; i++)
19
- if(inputs[i]) buffer = buffer.replace(inputs[i],'');
20
-
21
- return buffer;
22
- }
23
-
24
- const indent = (string, count = 1, options) => {
25
- options = {
26
- indent: ' ',
27
- includeEmptyLines: false,
28
- ...options
29
- };
30
-
31
- if (typeof string !== 'string') {
32
- throw new TypeError(
33
- `Expected \`input\` to be a \`string\`, got \`${typeof string}\``
34
- );
35
- }
36
-
37
- if (typeof count !== 'number') {
38
- throw new TypeError(
39
- `Expected \`count\` to be a \`number\`, got \`${typeof count}\``
40
- );
41
- }
42
-
43
- if (count < 0) {
44
- throw new RangeError(
45
- `Expected \`count\` to be at least 0, got \`${count}\``
46
- );
47
- }
48
-
49
- if (typeof options.indent !== 'string') {
50
- throw new TypeError(
51
- `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\``
52
- );
53
- }
54
-
55
- if (count === 0) {
56
- return string;
57
- }
58
-
59
- const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm;
60
-
61
- return string.replace(regex, options.indent.repeat(count));
62
- };
63
-
64
- const Console = {
65
- // _debug: true,
66
- _debug: process.env.DEBUG == 'true',
67
- startDebug: function(){ this._debug = true; },
68
- log: (msg, ...args) => console.log(chalk.gray(msg), ...args),
69
- error: (msg, ...args) => console.log(chalk.red('⨉ '+msg), ...args),
70
- success: (msg, ...args) => console.log(chalk.green('✓ '+msg), ...args),
71
- info: (msg, ...args) => console.log(chalk.blue('ⓘ '+msg), ...args),
72
- help: (msg) => console.log(`${chalk.white.bold('⚠ help:')} ${chalk.white(msg)}`),
73
- debug(...args){
74
- this._debug && console.log(chalk.magentaBright(`⚠ debug: `), args)
75
- }
76
- }
77
-
78
- module.exports = { getMatches, cleanStdout, indent, Console };
@@ -1,243 +0,0 @@
1
- const fs = require('fs')
2
- const fetch = require('node-fetch')
3
- const {validateExerciseDirectoryName} = require('../managers/config/exercise.js')
4
- const { flags } = require('@oclif/command')
5
- const Console = require('../utils/console')
6
- const { isUrl, findInFile, checkLearnpackClean, checkForEmptySpaces, showErrors, showWarnings } = require('../utils/audit')
7
- const SessionCommand = require('../utils/SessionCommand');
8
- const fm = require("front-matter")
9
- const path = require('path')
10
-
11
- class AuditCommand extends SessionCommand {
12
- async init() {
13
- const { flags } = this.parse(AuditCommand)
14
- await this.initSession(flags)
15
- }
16
- async run() {
17
- const { flags } = this.parse(AuditCommand)
18
-
19
- Console.log("Running command audit...")
20
-
21
- // Get configuration object.
22
- const config = this.configManager.get();
23
-
24
- let errors = []
25
- let warnings = []
26
- let counter = {
27
- images: {
28
- error: 0,
29
- total: 0,
30
- },
31
- links: {
32
- error: 0,
33
- total: 0,
34
- },
35
- exercises: 0,
36
- readmeFiles: 0
37
- }
38
-
39
- // Checks if learnpack clean has been run
40
- checkLearnpackClean(config, errors)
41
-
42
- // Build exercises if they are not built yet.
43
- if (!this.configManager.get().exercises) this.configManager.buildIndex()
44
-
45
- // Check if the exercises folder has some files within any ./exercise
46
- const exercisesPath = config.config.exercisesPath
47
- fs.readdir(exercisesPath, (err, files) => {
48
- if (err) {
49
- return console.log('Unable to scan directory: ' + err);
50
- }
51
- //listing all files using forEach
52
- files.forEach(function (file) {
53
- // Do whatever you want to do with the file
54
- let filePath = path.join(exercisesPath, file)
55
- if (fs.statSync(filePath).isFile()) warnings.push({ exercise: file, msg: `This file is not inside any exercise folder.` })
56
- });
57
- })
58
-
59
- // This function checks that each of the url's are working.
60
- const checkUrl = async (file, exercise) => {
61
- if (!fs.existsSync(file.path)) return false
62
- const content = fs.readFileSync(file.path).toString();
63
- let isEmpty = checkForEmptySpaces(content);
64
- if (isEmpty === true || content == false) errors.push({ exercise: exercise.title, msg: `This file (${file.name}) doesn't have any content inside.` })
65
- const frontmatter = fm(content).attributes
66
- for (const attribute in frontmatter) {
67
- if (attribute === "intro" || attribute === "tutorial") {
68
- counter.links.total++
69
- try {
70
- let res = await fetch(frontmatter[attribute], { method: "HEAD" });
71
- if (!res.ok) {
72
- counter.links.error++;
73
- errors.push({ exercise: exercise.title, msg: `This link is broken (${res.ok}): ${frontmatter[attribute]}` })
74
- }
75
- }
76
- catch (error) {
77
- counter.links.error++;
78
- errors.push({ exercise: exercise.title, msg: `This link is broken: ${frontmatter[attribute]}` })
79
- }
80
- }
81
- }
82
-
83
- // Check url's of each README file.
84
- const findings = findInFile(["relative_images", "external_images", "markdown_links"], content);
85
- for (const finding in findings) {
86
- let obj = findings[finding];
87
- // Valdites all the relative path images.
88
- if (finding === "relative_images" && Object.keys(obj).length > 0) {
89
- for (const img in obj) {
90
- // Validates if the image is in the assets folder.
91
- counter.images.total++
92
- let relativePath = path.relative(exercise.path.replace(/\\/gm, "/"), `${config.config.dirPath}/assets/${obj[img].relUrl}`).replace(/\\/gm, "/")
93
- if (relativePath != obj[img].absUrl.split("?").shift()) {
94
- counter.images.error++;
95
- errors.push({ exercise: exercise.title, msg: `This relative path (${obj[img].relUrl}) is not pointing to the assets folder.` })
96
- }
97
- if (!fs.existsSync(`${config.config.dirPath}/assets/${obj[img].relUrl}`)) {
98
- counter.images.error++;
99
- errors.push({ exercise: exercise.title, msg: `The file ${obj[img].relUrl} doesn't exist in the assets folder.` })
100
- }
101
- }
102
- } else if (finding === "external_images" && Object.keys(obj).length > 0) {
103
- // Valdites all the aboslute path images.
104
- for (const img in obj) {
105
- counter.images.total++
106
- try {
107
- let res = await fetch(obj[img].absUrl, { method: "HEAD" });
108
- if (!res.ok) {
109
- counter.images.error++;
110
- errors.push({ exercise: exercise.title, msg: `This link is broken: ${obj[img].absUrl}` })
111
- }
112
- }
113
- catch (error) {
114
- counter.images.error++;
115
- errors.push({ exercise: exercise.title, msg: `This link is broken: ${obj[img].absUrl}` })
116
- }
117
- }
118
- } else if (finding === "markdown_links" && Object.keys(obj).length > 0) {
119
- for (const link in obj) {
120
- counter.links.total++
121
- try {
122
- let res = await fetch(obj[link].mdUrl, { method: "HEAD" });
123
- if (res.status > 399 && res.status < 200) {
124
- counter.links.error++;
125
- errors.push({ exercise: exercise.title, msg: `This link is broken: ${obj[link].mdUrl}` })
126
- }
127
- }
128
- catch (error) {
129
- counter.links.error++;
130
- errors.push({ exercise: exercise.title, msg: `This link is broken: ${obj[link].mdUrl}` })
131
- }
132
- }
133
- }
134
- }
135
- return true
136
- }
137
-
138
- // This function is being created because the find method doesn't work with promises.
139
- const find = async (file, lang, exercise) => {
140
- if (file.name === lang) {
141
- await checkUrl(file, exercise)
142
- return true
143
- }
144
- return false
145
- }
146
-
147
- Console.info(' Checking if the config file is fine...')
148
- // These two lines check if the 'slug' property is inside the configuration object.
149
- Console.debug("Checking if the slug property is inside the configuration object...")
150
- if (!config.config.slug) errors.push({ exercise: null, msg: "The slug property is not in the configuration object" })
151
-
152
- // These two lines check if the 'repository' property is inside the configuration object.
153
- Console.debug("Checking if the repository property is inside the configuration object...")
154
- if (!config.config.repository) errors.push({ exercise: null, msg: "The repository property is not in the configuration object" })
155
- else isUrl(config.config.repository, errors, counter)
156
-
157
- // These two lines check if the 'description' property is inside the configuration object.
158
- Console.debug("Checking if the description property is inside the configuration object...")
159
- if (!config.config.description) errors.push({ exercise: null, msg: "The description property is not in the configuration object" })
160
-
161
- if (errors.length == 0) Console.log("The config file is ok")
162
-
163
- // Validates if images and links are working at every README file.
164
- const exercises = config.exercises
165
- let readmeFiles = []
166
-
167
- if (exercises.length > 0) {
168
- Console.info(' Checking if the images are working...')
169
- for (const index in exercises) {
170
- let exercise = exercises[index]
171
- if (!validateExerciseDirectoryName(exercise.title)) errors.push({exercise: exercise.title, msg: `The exercise ${exercise.title} has an invalid name.`})
172
- let readmeFilesCount = { exercise: exercise.title, count: 0 };
173
- if (Object.keys(exercise.translations).length == 0) errors.push({ exercise: exercise.title, msg: `The exercise ${exercise.title} doesn't have a README.md file.` })
174
-
175
- if (exercise.language == "python3" || exercise.language == "python") {
176
- exercise.files.map(f => f).find(f => {
177
- if (f.path.includes('test.py') || f.path.includes('tests.py')) {
178
- const content = fs.readFileSync(f.path).toString();
179
- let isEmpty = checkForEmptySpaces(content);
180
- if (isEmpty === true || content == false) errors.push({ exercise: exercise.title, msg: `This file (${f.name}) doesn't have any content inside.` })
181
- }
182
- });
183
- }
184
- else {
185
- exercise.files.map(f => f).find(f => {
186
- if (f.path.includes('test.js') || f.path.includes('tests.js')) {
187
- const content = fs.readFileSync(f.path).toString();
188
- let isEmpty = checkForEmptySpaces(content);
189
- if (isEmpty === true || content == false) errors.push({ exercise: exercise.title, msg: `This file (${f.name}) doesn't have any content inside.` })
190
- }
191
- });
192
- }
193
-
194
- for (const lang in exercise.translations) {
195
- let files = []
196
- for (const file of exercise.files) {
197
- let found = await find(file, exercise.translations[lang], exercise)
198
- if (found == true) readmeFilesCount = { ...readmeFilesCount, count: readmeFilesCount.count + 1 }
199
- files.push(found)
200
- }
201
- if (!files.includes(true)) errors.push({ exercise: exercise.title, msg: `This exercise doesn't have a README.md file.` })
202
-
203
- }
204
- readmeFiles.push(readmeFilesCount)
205
- }
206
- } else errors.push({ exercise: null, msg: "The exercises array is empty." })
207
-
208
- Console.log(`${counter.images.total - counter.images.error} images ok from ${counter.images.total}`)
209
-
210
- Console.info(" Checking if important files are missing... (README's, translations, gitignore...)")
211
- // Check if all the exercises has the same ammount of README's, this way we can check if they have the same ammount of translations.
212
- let files = [];
213
- readmeFiles.map((item, i, arr) => {
214
- if (item.count !== arr[0].count) files.push(` ${item.exercise}`)
215
- })
216
- if (files.length > 0) {
217
- files = files.join()
218
- warnings.push({ exercise: null, msg: `These exercises are missing translations: ${files}` })
219
- }
220
-
221
- // Checks if the .gitignore file exists.
222
- if (!fs.existsSync(`.gitignore`)) warnings.push(".gitignore file doesn't exist")
223
-
224
- counter.exercises = exercises.length;
225
- readmeFiles.forEach((readme) => {
226
- counter.readmeFiles += readme.count
227
- })
228
-
229
- await showWarnings(warnings)
230
- await showErrors(errors, counter)
231
- }
232
- }
233
-
234
- AuditCommand.description = `Check if the configuration object has slug, description and repository property
235
- ...
236
- Extra documentation goes here
237
- `
238
-
239
- AuditCommand.flags = {
240
- // name: flags.string({char: 'n', description: 'name to print'}),
241
- }
242
-
243
- module.exports = AuditCommand
@@ -1,27 +0,0 @@
1
- const {flags} = require('@oclif/command')
2
- const Console = require('../utils/console')
3
- const SessionCommand = require('../utils/SessionCommand')
4
- class CleanCommand extends SessionCommand {
5
- async init() {
6
- const {flags} = this.parse(CleanCommand)
7
- await this.initSession(flags)
8
- }
9
- async run() {
10
- const {flags} = this.parse(CleanCommand)
11
-
12
- this.configManager.clean()
13
-
14
- Console.success("Package cleaned successfully, ready to publish")
15
- }
16
- }
17
-
18
- CleanCommand.description = `Clean the configuration object
19
- ...
20
- Extra documentation goes here
21
- `
22
-
23
- CleanCommand.flags = {
24
- // name: flags.string({char: 'n', description: 'name to print'}),
25
- }
26
-
27
- module.exports = CleanCommand
@@ -1,52 +0,0 @@
1
- const {Command, flags} = require('@oclif/command')
2
- const fetch = require('node-fetch');
3
- const { clone } = require('../managers/file.js')
4
- const Console = require('../utils/console')
5
- const api = require('../utils/api')
6
- const getRepoInfo = require('git-repo-info')
7
- const { askPackage } = require('../ui/download')
8
-
9
- class DownloadCommand extends Command {
10
- // async init() {
11
- // const {flags} = this.parse(DownloadCommand)
12
- // await this.initSession(flags)
13
- // }
14
- async run() {
15
- const {flags, args} = this.parse(DownloadCommand)
16
- // start watching for file changes
17
- let _package = args.package
18
- if(!_package) _package = await askPackage()
19
-
20
- if(!_package) return null
21
-
22
- try{
23
- _package = _package.pack;
24
- clone(_package.repository)
25
- .then(result => {
26
- Console.success(`Successfully downloaded`)
27
- const folder = _package.repository.substring(_package.repository.lastIndexOf('/') + 1).split(".")[0];
28
- Console.info(`You can now CD into the folder like this: $ cd ${folder}`)
29
- })
30
- .catch(error => Console.error(error.message || error))
31
- }
32
- catch(error){}
33
- }
34
- }
35
-
36
- DownloadCommand.description = `Describe the command here
37
- ...
38
- Extra documentation goes here
39
- `
40
- DownloadCommand.flags = {
41
- // name: flags.string({char: 'n', description: 'name to print'}),
42
- }
43
- DownloadCommand.args =[
44
- {
45
- name: 'package', // name of arg to show in help and reference with args[name]
46
- required: false, // make the arg required with `required: true`
47
- description: 'The unique string that identifies this package on learnpack', // help description
48
- hidden: false // hide this arg from help
49
- }
50
- ]
51
-
52
- module.exports = DownloadCommand
@@ -1,20 +0,0 @@
1
- const {Command, flags} = require('@oclif/command')
2
-
3
- class HelloCommand extends Command {
4
- async run() {
5
- const {flags} = this.parse(HelloCommand)
6
- const name = flags.name || 'world'
7
- this.log(`hello ${name} from ./src/commands/hello.js`)
8
- }
9
- }
10
-
11
- HelloCommand.description = `Describe the command here
12
- ...
13
- Extra documentation goes here
14
- `
15
-
16
- HelloCommand.flags = {
17
- name: flags.string({char: 'n', description: 'name to print'}),
18
- }
19
-
20
- module.exports = HelloCommand
@@ -1,133 +0,0 @@
1
- const {flags} = require('@oclif/command')
2
- const BaseCommand = require('../utils/BaseCommand')
3
-
4
- const fs = require('fs-extra')
5
- const prompts = require('prompts')
6
- const cli = require("cli-ux").default
7
- const eta = require("eta")
8
-
9
- const Console = require('../utils/console')
10
- const { ValidationError } = require('../utils/errors')
11
- let defaults = require('../managers/config/defaults.js')
12
-
13
- const path = require('path')
14
- const { resolve } = require('path')
15
-
16
- class InitComand extends BaseCommand {
17
- async run() {
18
- const {flags} = this.parse(InitComand)
19
-
20
- try{
21
- // if the folder/file .learn or .breathecode aleady exists
22
- await alreadyInitialized();
23
- }
24
- catch(error){
25
- Console.error(error.message)
26
- return false
27
- }
28
-
29
- let choices = await prompts([
30
- {
31
- type: 'select',
32
- name: 'grading',
33
- message: 'Is the auto-grading going to be isolated or incremental?',
34
- choices: [
35
- { title: 'Incremental: Build on top of each other like a tutorial', value: 'incremental' },
36
- { title: 'Isolated: Small isolated exercises', value: 'isolated' },
37
- { title: 'No grading: No feedback or testing whatsoever', value: null },
38
- ],
39
- },{
40
- type: 'text',
41
- name: 'title',
42
- initial: 'My Interactive Tutorial',
43
- message: 'Title for your tutorial? Press enter to leave as it is'
44
- },{
45
- type: 'text',
46
- name: 'description',
47
- initial: '',
48
- message: 'Description for your tutorial? Press enter to leave blank'
49
- },{
50
- type: 'select',
51
- name: 'difficulty',
52
- message: 'How difficulty will be to complete the tutorial?',
53
- choices: [
54
- { title: 'Begginer (no previous experience)', value: 'beginner' },
55
- { title: 'Easy (just a bit of experience required)', value: 'easy' },
56
- { title: 'Intermediate (you need experience)', value: 'intermediate' },
57
- { title: 'Hard (master the topic)', value: 'hard' },
58
- ],
59
- },{
60
- type: 'text',
61
- name: 'duration',
62
- initial: "1",
63
- message: 'How many hours avg it takes to complete (number)?',
64
- validate: value => {
65
- var n = Math.floor(Number(value))
66
- return n !== Infinity && String(n) === value && n >= 0
67
- }
68
- }
69
- ])
70
-
71
- const packageInfo = {
72
- ...defaults.config,
73
- grading: choices.grading,
74
- difficulty: choices.difficulty,
75
- duration: parseInt(choices.duration),
76
- description: choices.description,
77
- title: choices.title,
78
- slug: choices.title.toLowerCase().replace(/ /g,'-').replace(/[^\w-]+/g,'')
79
- }
80
-
81
- cli.action.start('Initializing package')
82
-
83
- const languages = ['en', 'es']
84
-
85
- try{
86
- const templatesDir = path.resolve(__dirname,"../utils/templates/"+choices.grading || "no-grading")
87
- if(!fs.existsSync(templatesDir)) throw ValidationError(`Template ${templatesDir} does not exists`)
88
- await fs.copySync(templatesDir, './')
89
-
90
- // Creating README files
91
- languages.forEach((language) => {
92
- const readmeFilename = `README${language !== 'en' ? `.${language}` : ''}`
93
- fs.writeFileSync(`./${readmeFilename}.md`, eta.render(fs.readFileSync(path.resolve(__dirname,`${templatesDir}/${readmeFilename}.ejs`),'utf-8'), packageInfo))
94
- if(fs.existsSync(`./${readmeFilename}.ejs`)) fs.removeSync(`./${readmeFilename}.ejs`)
95
- })
96
-
97
- if(!fs.existsSync('./.gitignore')) fs.copyFile(path.resolve(__dirname,'../utils/templates/gitignore.txt'), './.gitignore')
98
- fs.writeFileSync('./learn.json', JSON.stringify(packageInfo, null, 2))
99
- }
100
- catch(error){
101
- Console.error(error.message || error)
102
- return false
103
- }
104
-
105
- cli.action.stop()
106
- Console.success(`😋 Package initialized successfully`)
107
- Console.help(`Start the exercises by running the following command on your terminal: $ learnpack start`)
108
-
109
- }
110
- }
111
-
112
- InitComand.description = 'Create a new learning package: Book, Tutorial or Exercise'
113
- InitComand.flags = {
114
- ...BaseCommand.flags,
115
- grading: flags.help({char:'h'}),
116
- }
117
-
118
- const alreadyInitialized = () => new Promise((resolve, reject) => {
119
- fs.readdir('./', function(err, files) {
120
- files = files.filter(f => ['.learn', 'learn.json', 'bc.json', '.breathecode', '.gitignore'].includes(f))
121
- if (err) {
122
- reject(ValidationError(err.message))
123
- return true
124
- } else if (files.length > 0){
125
- reject(ValidationError("It seems the package is already initialized because we've found the following files: "+files.join(',')))
126
- return true
127
- }
128
-
129
- resolve(false)
130
- })
131
- })
132
-
133
- module.exports = InitComand
@@ -1,45 +0,0 @@
1
- const {Command, flags} = require('@oclif/command')
2
- const { prompt } = require("enquirer")
3
- const fetch = require('node-fetch');
4
- const SessionCommand = require('../utils/SessionCommand')
5
- const SessionManager = require('../managers/session.js')
6
- const Console = require('../utils/console');
7
- const { replace } = require('node-emoji');
8
- const { validURL } = require("../utils/validators")
9
- // const BaseCommand = require('../utils/BaseCommand');
10
-
11
- class PublishCommand extends SessionCommand {
12
- async init() {
13
- const {flags} = this.parse(PublishCommand)
14
- await this.initSession(flags)
15
- }
16
- async run() {
17
- const {flags, args} = this.parse(PublishCommand)
18
-
19
- try{
20
- await SessionManager.login();
21
- }
22
- catch(error){
23
- Console.error("Error trying to authenticate")
24
- Console.error(error.message || error)
25
- }
26
- }
27
- }
28
-
29
- PublishCommand.description = `Describe the command here
30
- ...
31
- Extra documentation goes here
32
- `
33
- PublishCommand.flags = {
34
- // name: flags.string({char: 'n', description: 'name to print'}),
35
- }
36
- PublishCommand.args =[
37
- {
38
- name: 'package', // name of arg to show in help and reference with args[name]
39
- required: false, // make the arg required with `required: true`
40
- description: 'The unique string that identifies this package on learnpack', // help description
41
- hidden: false // hide this arg from help
42
- }
43
- ]
44
-
45
- module.exports = PublishCommand
@@ -1,39 +0,0 @@
1
- const {Command, flags} = require('@oclif/command')
2
- const { prompt } = require("enquirer")
3
- const fetch = require('node-fetch');
4
- const SessionCommand = require('../utils/SessionCommand')
5
- const SessionManager = require('../managers/session.js')
6
- const Console = require('../utils/console');
7
- const { replace } = require('node-emoji');
8
- const { validURL } = require("../utils/validators")
9
- // const BaseCommand = require('../utils/BaseCommand');
10
-
11
- class PublishCommand extends SessionCommand {
12
- async init() {
13
- const {flags} = this.parse(PublishCommand)
14
- await this.initSession(flags)
15
- }
16
- async run() {
17
- const {flags, args} = this.parse(PublishCommand)
18
-
19
- SessionManager.destroy();
20
- }
21
- }
22
-
23
- PublishCommand.description = `Describe the command here
24
- ...
25
- Extra documentation goes here
26
- `
27
- PublishCommand.flags = {
28
- // name: flags.string({char: 'n', description: 'name to print'}),
29
- }
30
- PublishCommand.args =[
31
- {
32
- name: 'package', // name of arg to show in help and reference with args[name]
33
- required: false, // make the arg required with `required: true`
34
- description: 'The unique string that identifies this package on learnpack', // help description
35
- hidden: false // hide this arg from help
36
- }
37
- ]
38
-
39
- module.exports = PublishCommand