@learnpack/learnpack 5.0.38 → 5.0.40

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.
package/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @learnpack/learnpack
21
21
  $ learnpack COMMAND
22
22
  running command...
23
23
  $ learnpack (-v|--version|version)
24
- @learnpack/learnpack/5.0.38 win32-x64 node-v22.14.0
24
+ @learnpack/learnpack/5.0.40 win32-x64 node-v22.14.0
25
25
  $ learnpack --help [COMMAND]
26
26
  USAGE
27
27
  $ learnpack COMMAND
@@ -58,6 +58,9 @@ learnpack audit is the command in charge of creating an auditory of the reposito
58
58
  USAGE
59
59
  $ learnpack audit
60
60
 
61
+ OPTIONS
62
+ -s, --strict strict mode
63
+
61
64
  DESCRIPTION
62
65
  ...
63
66
  learnpack audit checks for the following information in a repository:
@@ -76,7 +79,7 @@ DESCRIPTION
76
79
  12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
77
80
  ```
78
81
 
79
- _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\audit.ts)_
82
+ _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\audit.ts)_
80
83
 
81
84
  ## `learnpack breakToken`
82
85
 
@@ -91,7 +94,7 @@ OPTIONS
91
94
  -y, --yes Skip all prompts and initialize an empty project
92
95
  ```
93
96
 
94
- _See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\breakToken.ts)_
97
+ _See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\breakToken.ts)_
95
98
 
96
99
  ## `learnpack clean`
97
100
 
@@ -106,7 +109,7 @@ DESCRIPTION
106
109
  Extra documentation goes here
107
110
  ```
108
111
 
109
- _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\clean.ts)_
112
+ _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\clean.ts)_
110
113
 
111
114
  ## `learnpack download [PACKAGE]`
112
115
 
@@ -124,7 +127,7 @@ DESCRIPTION
124
127
  Extra documentation goes here
125
128
  ```
126
129
 
127
- _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\download.ts)_
130
+ _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\download.ts)_
128
131
 
129
132
  ## `learnpack help [COMMAND]`
130
133
 
@@ -156,7 +159,7 @@ OPTIONS
156
159
  -y, --yes Skip all prompts and initialize an empty project
157
160
  ```
158
161
 
159
- _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\init.ts)_
162
+ _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\init.ts)_
160
163
 
161
164
  ## `learnpack login [PACKAGE]`
162
165
 
@@ -174,7 +177,7 @@ DESCRIPTION
174
177
  Extra documentation goes here
175
178
  ```
176
179
 
177
- _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\login.ts)_
180
+ _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\login.ts)_
178
181
 
179
182
  ## `learnpack logout [PACKAGE]`
180
183
 
@@ -192,7 +195,7 @@ DESCRIPTION
192
195
  Extra documentation goes here
193
196
  ```
194
197
 
195
- _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\logout.ts)_
198
+ _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\logout.ts)_
196
199
 
197
200
  ## `learnpack plugins`
198
201
 
@@ -320,10 +323,11 @@ USAGE
320
323
  $ learnpack publish
321
324
 
322
325
  OPTIONS
323
- -h, --help show CLI help
326
+ -h, --help show CLI help
327
+ -s, --strict strict mode
324
328
  ```
325
329
 
326
- _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\publish.ts)_
330
+ _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\publish.ts)_
327
331
 
328
332
  ## `learnpack start`
329
333
 
@@ -345,7 +349,7 @@ OPTIONS
345
349
  -y, --yes Skip all prompts and initialize an empty project
346
350
  ```
347
351
 
348
- _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\start.ts)_
352
+ _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\start.ts)_
349
353
 
350
354
  ## `learnpack test [EXERCISESLUG]`
351
355
 
@@ -362,7 +366,7 @@ OPTIONS
362
366
  -y, --yes Skip all prompts and initialize an empty project
363
367
  ```
364
368
 
365
- _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\test.ts)_
369
+ _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\test.ts)_
366
370
 
367
371
  ## `learnpack translate`
368
372
 
@@ -376,7 +380,7 @@ OPTIONS
376
380
  -y, --yes Skip all prompts and initialize an empty project
377
381
  ```
378
382
 
379
- _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\translate.ts)_
383
+ _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.40/src\commands\translate.ts)_
380
384
  <!-- commandsstop -->
381
385
 
382
386
  > > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
@@ -6,6 +6,7 @@ const console_1 = require("../utils/console");
6
6
  const audit_1 = require("../utils/audit");
7
7
  const SessionCommand_1 = require("../utils/SessionCommand");
8
8
  const path = require("path");
9
+ const command_1 = require("@oclif/command");
9
10
  // eslint-disable-next-line
10
11
  const fetch = require("node-fetch");
11
12
  class AuditCommand extends SessionCommand_1.default {
@@ -14,7 +15,9 @@ class AuditCommand extends SessionCommand_1.default {
14
15
  await this.initSession(flags);
15
16
  }
16
17
  async run() {
17
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
18
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
19
+ const { flags } = this.parse(AuditCommand);
20
+ const strict = flags.strict;
18
21
  console_1.default.log("Running command audit...");
19
22
  // Get configuration object.
20
23
  let config = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.get();
@@ -74,50 +77,70 @@ class AuditCommand extends SessionCommand_1.default {
74
77
  exercise: undefined,
75
78
  msg: "The slug property is not in the configuration object",
76
79
  });
80
+ if (((_f = config.config) === null || _f === void 0 ? void 0 : _f.slug) && !audit_1.default.checkSlug((_g = config.config) === null || _g === void 0 ? void 0 : _g.slug)) {
81
+ errors.push({
82
+ exercise: undefined,
83
+ msg: "The slug property is not a valid slug. It must start and end with a letter and be less than 50 characters.",
84
+ });
85
+ }
77
86
  // check if the duration property is in the configuration object
78
- if (!((_f = config.config) === null || _f === void 0 ? void 0 : _f.duration))
87
+ if (!((_h = config.config) === null || _h === void 0 ? void 0 : _h.duration))
79
88
  warnings.push({
80
89
  exercise: undefined,
81
90
  msg: "The duration property is not in the configuration object",
82
91
  });
83
92
  // check if the difficulty property is in the configuration object
84
- if (!((_g = config.config) === null || _g === void 0 ? void 0 : _g.difficulty))
93
+ if (!((_j = config.config) === null || _j === void 0 ? void 0 : _j.difficulty))
85
94
  warnings.push({
86
95
  exercise: undefined,
87
96
  msg: "The difficulty property is not in the configuration object",
88
97
  });
89
98
  // check if the bugs_link property is in the configuration object
90
- if (!((_h = config.config) === null || _h === void 0 ? void 0 : _h.bugsLink))
91
- errors.push({
92
- exercise: undefined,
93
- msg: "The bugsLink property is not in the configuration object",
94
- });
95
- // check if the video_solutions property is in the configuration object
96
- if (((_j = config.config) === null || _j === void 0 ? void 0 : _j.videoSolutions) === undefined)
99
+ if (!((_k = config.config) === null || _k === void 0 ? void 0 : _k.bugsLink)) {
100
+ if (strict)
101
+ errors.push({
102
+ exercise: undefined,
103
+ msg: "The bugsLink property is not in the configuration object",
104
+ });
105
+ else
106
+ warnings.push({
107
+ exercise: undefined,
108
+ msg: "The bugsLink property is not in the configuration object",
109
+ });
110
+ }
111
+ // check if the video_solutions property is in the configuration object
112
+ if (((_l = config.config) === null || _l === void 0 ? void 0 : _l.videoSolutions) === undefined)
97
113
  warnings.push({
98
114
  exercise: undefined,
99
115
  msg: "The videoSolutions property is not in the configuration object",
100
116
  });
101
117
  // These two lines check if the 'repository' property is inside the configuration object.
102
118
  console_1.default.debug("Checking if the repository property is inside the configuration object...");
103
- if (!((_k = config.config) === null || _k === void 0 ? void 0 : _k.repository))
104
- errors.push({
105
- exercise: undefined,
106
- msg: "The repository property is not in the configuration object",
107
- });
119
+ if (!((_m = config.config) === null || _m === void 0 ? void 0 : _m.repository)) {
120
+ if (strict)
121
+ errors.push({
122
+ exercise: undefined,
123
+ msg: "The repository property is not in the configuration object",
124
+ });
125
+ else
126
+ warnings.push({
127
+ exercise: undefined,
128
+ msg: "The repository property is not in the configuration object",
129
+ });
130
+ }
108
131
  else
109
- audit_1.default.isUrl((_l = config.config) === null || _l === void 0 ? void 0 : _l.repository, errors, counter);
132
+ audit_1.default.isUrl((_o = config.config) === null || _o === void 0 ? void 0 : _o.repository, errors, counter);
110
133
  // These two lines check if the 'description' property is inside the configuration object.
111
134
  console_1.default.debug("Checking if the description property is inside the configuration object...");
112
- if (!((_m = config.config) === null || _m === void 0 ? void 0 : _m.description))
135
+ if (!((_p = config.config) === null || _p === void 0 ? void 0 : _p.description))
113
136
  errors.push({
114
137
  exercise: undefined,
115
138
  msg: "The description property is not in the configuration object",
116
139
  });
117
- if (!((_o = config.config) === null || _o === void 0 ? void 0 : _o.projectType))
140
+ if (!((_q = config.config) === null || _q === void 0 ? void 0 : _q.projectType))
118
141
  errors.push({
119
142
  exercise: undefined,
120
- msg: "The projectType property mandatory in the configuration object",
143
+ msg: "The projectType property is mandatory in the configuration object",
121
144
  });
122
145
  if (errors.length === 0)
123
146
  console_1.default.log("The config file is ok");
@@ -306,7 +329,7 @@ class AuditCommand extends SessionCommand_1.default {
306
329
  });
307
330
  }
308
331
  }
309
- catch (_p) {
332
+ catch (_r) {
310
333
  errors.push({
311
334
  exercise: undefined,
312
335
  msg: `The link of the "preview" is broken: ${learnjson.preview}`,
@@ -346,6 +369,10 @@ learnpack audit checks for the following information in a repository:
346
369
  12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
347
370
  `;
348
371
  AuditCommand.flags = {
349
- // name: flags.string({char: 'n', description: 'name to print'}),
372
+ strict: command_1.flags.boolean({
373
+ char: "s",
374
+ description: "strict mode",
375
+ default: false,
376
+ }),
350
377
  };
351
378
  exports.default = AuditCommand;
@@ -25,6 +25,7 @@ const PARAMS = {
25
25
  max_fkgl: 8,
26
26
  max_words: 200,
27
27
  max_rewrite_attempts: 3,
28
+ max_title_length: 50,
28
29
  };
29
30
  const whichTargetAudience = async () => {
30
31
  const res = await prompts([
@@ -133,6 +134,9 @@ const initializeInteractiveCreation = async (rigoToken, courseInfo) => {
133
134
  duration,
134
135
  };
135
136
  };
137
+ process.emitWarning = (warning) => {
138
+ console_1.default.debug("A Warning was emitted by Node.js: ", warning);
139
+ };
136
140
  const appendContentIndex = async () => {
137
141
  const choices = await prompts([
138
142
  {
@@ -292,6 +296,12 @@ const getChoices = async (empty) => {
292
296
  name: "title",
293
297
  initial: "My Interactive Tutorial",
294
298
  message: "Title for your tutorial? Press enter to leave as it is",
299
+ validate: (value) => {
300
+ if (value.length > PARAMS.max_title_length) {
301
+ return `Title must be less than ${PARAMS.max_title_length} characters`;
302
+ }
303
+ return true;
304
+ },
295
305
  },
296
306
  {
297
307
  type: "text",
@@ -1,11 +1,13 @@
1
1
  import SessionCommand from "../utils/SessionCommand";
2
- export default class BuildCommand extends SessionCommand {
2
+ declare class BuildCommand extends SessionCommand {
3
3
  static description: string;
4
4
  static flags: {
5
5
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
6
+ strict: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
6
7
  };
7
8
  init(): Promise<void>;
8
9
  run(): Promise<void>;
9
10
  copyDirectory(src: string, dest: string): void;
10
11
  removeDirectory(dir: string): void;
11
12
  }
13
+ export default BuildCommand;
@@ -20,10 +20,12 @@ const RIGOBOT_HOST = "https://rigobot.herokuapp.com";
20
20
  // const RIGOBOT_HOST =
21
21
  // "https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us116.gitpod.io"
22
22
  const uploadZipEndpont = RIGOBOT_HOST + "/v1/learnpack/upload";
23
- const runAudit = () => {
23
+ const runAudit = (strict) => {
24
24
  try {
25
25
  console_1.default.info("Running learnpack audit before publishing...");
26
- (0, child_process_1.execSync)("learnpack audit", { stdio: "inherit" });
26
+ (0, child_process_1.execSync)(`learnpack audit ${strict ? "--strict" : ""}`, {
27
+ stdio: "inherit",
28
+ });
27
29
  }
28
30
  catch (error) {
29
31
  console_1.default.error("Failed to audit with learnpack");
@@ -68,6 +70,9 @@ class BuildCommand extends SessionCommand_1.default {
68
70
  async run() {
69
71
  var _a, _b, _c;
70
72
  const buildDir = path.join(process.cwd(), "build");
73
+ const { flags } = this.parse(BuildCommand);
74
+ const strict = flags.strict;
75
+ console_1.default.debug("Strict mode: ", strict);
71
76
  // this.configManager?.clean()
72
77
  const configObject = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.get();
73
78
  let sessionPayload = await session_1.default.getPayload();
@@ -98,7 +103,7 @@ class BuildCommand extends SessionCommand_1.default {
98
103
  console_1.default.info("Cleaning configuration files");
99
104
  (_b = this.configManager) === null || _b === void 0 ? void 0 : _b.clean();
100
105
  // build exerises
101
- runAudit();
106
+ runAudit(strict);
102
107
  console_1.default.debug("Building exercises");
103
108
  (_c = this.configManager) === null || _c === void 0 ? void 0 : _c.buildIndex();
104
109
  }
@@ -267,5 +272,18 @@ class BuildCommand extends SessionCommand_1.default {
267
272
  BuildCommand.description = "Builds the project by copying necessary files and directories into a zip file";
268
273
  BuildCommand.flags = {
269
274
  help: command_1.flags.help({ char: "h" }),
275
+ strict: command_1.flags.boolean({
276
+ char: "s",
277
+ description: "strict mode",
278
+ default: false,
279
+ }),
280
+ };
281
+ BuildCommand.flags = {
282
+ strict: command_1.flags.boolean({
283
+ char: "s",
284
+ description: "strict mode",
285
+ default: false,
286
+ }),
287
+ help: command_1.flags.help({ char: "h" }),
270
288
  };
271
289
  exports.default = BuildCommand;
@@ -12,5 +12,6 @@ declare const _default: {
12
12
  writeFile: (filePath: string, content: string) => void;
13
13
  showErrors: (errors: IAuditErrors[], counter: ICounter | undefined) => Promise<unknown>;
14
14
  showWarnings: (warnings: IAuditErrors[]) => Promise<unknown>;
15
+ checkSlug: (slug: string) => boolean;
15
16
  };
16
17
  export default _default;
@@ -38,6 +38,20 @@ const checkForEmptySpaces = (str) => {
38
38
  }
39
39
  return isEmpty;
40
40
  };
41
+ const checkSlug = (slug) => {
42
+ // Validate that the length of the slug is less than 50 characters
43
+ // The slug must start with a letter
44
+ if (slug.length > 50) {
45
+ return false;
46
+ }
47
+ if (!/^[A-Za-z]/.test(slug)) {
48
+ return false;
49
+ }
50
+ if (!/[A-Za-z]$/.test(slug)) {
51
+ return false;
52
+ }
53
+ return true;
54
+ };
41
55
  const checkLearnpackClean = (configObj, errors) => {
42
56
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
43
57
  if ((((_a = configObj.config) === null || _a === void 0 ? void 0 : _a.outputPath) &&
@@ -278,4 +292,5 @@ exports.default = {
278
292
  writeFile,
279
293
  showErrors,
280
294
  showWarnings,
295
+ checkSlug,
281
296
  };
@@ -1 +1 @@
1
- {"version":"5.0.38","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
1
+ {"version":"5.0.40","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false}},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@learnpack/learnpack",
3
3
  "description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
4
- "version": "5.0.38",
4
+ "version": "5.0.40",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -10,6 +10,7 @@ import { IFrontmatter } from "../models/front-matter"
10
10
  import { IAuditErrors } from "../models/audit"
11
11
  import { ICounter } from "../models/counter"
12
12
  import { IFindings } from "../models/findings"
13
+ import { flags } from "@oclif/command"
13
14
 
14
15
  // eslint-disable-next-line
15
16
  const fetch = require("node-fetch")
@@ -21,6 +22,9 @@ class AuditCommand extends SessionCommand {
21
22
  }
22
23
 
23
24
  async run() {
25
+ const { flags }: { flags: { strict: boolean } } = this.parse(AuditCommand)
26
+
27
+ const strict = flags.strict
24
28
  Console.log("Running command audit...")
25
29
 
26
30
  // Get configuration object.
@@ -101,6 +105,14 @@ class AuditCommand extends SessionCommand {
101
105
  exercise: undefined,
102
106
  msg: "The slug property is not in the configuration object",
103
107
  })
108
+
109
+ if (config!.config?.slug && !Audit.checkSlug(config!.config?.slug)) {
110
+ errors.push({
111
+ exercise: undefined,
112
+ msg: "The slug property is not a valid slug. It must start and end with a letter and be less than 50 characters.",
113
+ })
114
+ }
115
+
104
116
  // check if the duration property is in the configuration object
105
117
  if (!config!.config?.duration)
106
118
  warnings.push({
@@ -114,12 +126,20 @@ class AuditCommand extends SessionCommand {
114
126
  msg: "The difficulty property is not in the configuration object",
115
127
  })
116
128
  // check if the bugs_link property is in the configuration object
117
- if (!config!.config?.bugsLink)
118
- errors.push({
119
- exercise: undefined,
120
- msg: "The bugsLink property is not in the configuration object",
121
- })
122
- // check if the video_solutions property is in the configuration object
129
+ if (!config!.config?.bugsLink) {
130
+ if (strict)
131
+ errors.push({
132
+ exercise: undefined,
133
+ msg: "The bugsLink property is not in the configuration object",
134
+ })
135
+ else
136
+ warnings.push({
137
+ exercise: undefined,
138
+ msg: "The bugsLink property is not in the configuration object",
139
+ })
140
+ }
141
+
142
+ // check if the video_solutions property is in the configuration object
123
143
  if (config!.config?.videoSolutions === undefined)
124
144
  warnings.push({
125
145
  exercise: undefined,
@@ -130,12 +150,18 @@ class AuditCommand extends SessionCommand {
130
150
  Console.debug(
131
151
  "Checking if the repository property is inside the configuration object..."
132
152
  )
133
- if (!config!.config?.repository)
134
- errors.push({
135
- exercise: undefined,
136
- msg: "The repository property is not in the configuration object",
137
- })
138
- else
153
+ if (!config!.config?.repository) {
154
+ if (strict)
155
+ errors.push({
156
+ exercise: undefined,
157
+ msg: "The repository property is not in the configuration object",
158
+ })
159
+ else
160
+ warnings.push({
161
+ exercise: undefined,
162
+ msg: "The repository property is not in the configuration object",
163
+ })
164
+ } else
139
165
  Audit.isUrl(config!.config?.repository, errors, counter)
140
166
 
141
167
  // These two lines check if the 'description' property is inside the configuration object.
@@ -150,7 +176,7 @@ Audit.isUrl(config!.config?.repository, errors, counter)
150
176
  if (!config!.config?.projectType)
151
177
  errors.push({
152
178
  exercise: undefined,
153
- msg: "The projectType property mandatory in the configuration object",
179
+ msg: "The projectType property is mandatory in the configuration object",
154
180
  })
155
181
 
156
182
  if (errors.length === 0)
@@ -443,7 +469,11 @@ learnpack audit checks for the following information in a repository:
443
469
  `
444
470
 
445
471
  AuditCommand.flags = {
446
- // name: flags.string({char: 'n', description: 'name to print'}),
472
+ strict: flags.boolean({
473
+ char: "s",
474
+ description: "strict mode",
475
+ default: false,
476
+ }),
447
477
  }
448
478
 
449
479
  export default AuditCommand
@@ -47,6 +47,7 @@ const PARAMS = {
47
47
  max_fkgl: 8,
48
48
  max_words: 200,
49
49
  max_rewrite_attempts: 3,
50
+ max_title_length: 50,
50
51
  }
51
52
 
52
53
  const whichTargetAudience = async () => {
@@ -221,6 +222,10 @@ const initializeInteractiveCreation = async (
221
222
  }
222
223
  }
223
224
 
225
+ process.emitWarning = (warning: string) => {
226
+ Console.debug("A Warning was emitted by Node.js: ", warning)
227
+ }
228
+
224
229
  const appendContentIndex = async () => {
225
230
  const choices = await prompts([
226
231
  {
@@ -425,9 +430,17 @@ const getChoices = async (empty: boolean) => {
425
430
  },
426
431
  {
427
432
  type: "text",
433
+
428
434
  name: "title",
429
435
  initial: "My Interactive Tutorial",
430
436
  message: "Title for your tutorial? Press enter to leave as it is",
437
+ validate: (value: string) => {
438
+ if (value.length > PARAMS.max_title_length) {
439
+ return `Title must be less than ${PARAMS.max_title_length} characters`
440
+ }
441
+
442
+ return true
443
+ },
431
444
  },
432
445
  {
433
446
  type: "text",
@@ -24,10 +24,12 @@ const RIGOBOT_HOST = "https://rigobot.herokuapp.com"
24
24
  // "https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us116.gitpod.io"
25
25
  const uploadZipEndpont = RIGOBOT_HOST + "/v1/learnpack/upload"
26
26
 
27
- const runAudit = () => {
27
+ const runAudit = (strict: boolean) => {
28
28
  try {
29
29
  Console.info("Running learnpack audit before publishing...")
30
- execSync("learnpack audit", { stdio: "inherit" })
30
+ execSync(`learnpack audit ${strict ? "--strict" : ""}`, {
31
+ stdio: "inherit",
32
+ })
31
33
  } catch (error) {
32
34
  Console.error("Failed to audit with learnpack")
33
35
 
@@ -71,12 +73,17 @@ const selectAcademy = async (academies: TAcademy[]) => {
71
73
  return response.academy
72
74
  }
73
75
 
74
- export default class BuildCommand extends SessionCommand {
76
+ class BuildCommand extends SessionCommand {
75
77
  static description =
76
78
  "Builds the project by copying necessary files and directories into a zip file"
77
79
 
78
80
  static flags = {
79
81
  help: flags.help({ char: "h" }),
82
+ strict: flags.boolean({
83
+ char: "s",
84
+ description: "strict mode",
85
+ default: false,
86
+ }),
80
87
  }
81
88
 
82
89
  async init() {
@@ -86,6 +93,10 @@ export default class BuildCommand extends SessionCommand {
86
93
 
87
94
  async run() {
88
95
  const buildDir = path.join(process.cwd(), "build")
96
+
97
+ const { flags }: { flags: { strict: boolean } } = this.parse(BuildCommand)
98
+ const strict = flags.strict
99
+ Console.debug("Strict mode: ", strict)
89
100
  // this.configManager?.clean()
90
101
 
91
102
  const configObject = this.configManager?.get()
@@ -134,7 +145,7 @@ export default class BuildCommand extends SessionCommand {
134
145
  Console.info("Cleaning configuration files")
135
146
  this.configManager?.clean()
136
147
  // build exerises
137
- runAudit()
148
+ runAudit(strict)
138
149
 
139
150
  Console.debug("Building exercises")
140
151
  this.configManager?.buildIndex()
@@ -334,3 +345,14 @@ export default class BuildCommand extends SessionCommand {
334
345
  }
335
346
  }
336
347
  }
348
+
349
+ BuildCommand.flags = {
350
+ strict: flags.boolean({
351
+ char: "s",
352
+ description: "strict mode",
353
+ default: false,
354
+ }),
355
+ help: flags.help({ char: "h" }),
356
+ }
357
+
358
+ export default BuildCommand
@@ -55,6 +55,25 @@ const checkForEmptySpaces = (str: string) => {
55
55
  return isEmpty
56
56
  }
57
57
 
58
+ const checkSlug = (slug: string) => {
59
+ // Validate that the length of the slug is less than 50 characters
60
+ // The slug must start with a letter
61
+
62
+ if (slug.length > 50) {
63
+ return false
64
+ }
65
+
66
+ if (!/^[A-Za-z]/.test(slug)) {
67
+ return false
68
+ }
69
+
70
+ if (!/[A-Za-z]$/.test(slug)) {
71
+ return false
72
+ }
73
+
74
+ return true
75
+ }
76
+
58
77
  const checkLearnpackClean = (configObj: IConfigObj, errors: IAuditErrors[]) => {
59
78
  if (
60
79
  (configObj.config?.outputPath &&
@@ -369,4 +388,5 @@ export default {
369
388
  writeFile,
370
389
  showErrors,
371
390
  showWarnings,
391
+ checkSlug,
372
392
  }
@@ -26,4 +26,4 @@ jobs:
26
26
  with:
27
27
  node-version: ${{ matrix.node-version }}
28
28
  - run: npm install @learnpack/learnpack@latest -g
29
- - run: learnpack audit
29
+ - run: learnpack audit --strict
@@ -26,4 +26,4 @@ jobs:
26
26
  with:
27
27
  node-version: ${{ matrix.node-version }}
28
28
  - run: npm install @learnpack/learnpack@latest -g
29
- - run: learnpack audit
29
+ - run: learnpack audit --strict