@learnpack/learnpack 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
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