@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 +17 -13
- package/lib/commands/audit.js +48 -21
- package/lib/commands/init.js +10 -0
- package/lib/commands/publish.d.ts +3 -1
- package/lib/commands/publish.js +21 -3
- package/lib/utils/audit.d.ts +1 -0
- package/lib/utils/audit.js +15 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/audit.ts +44 -14
- package/src/commands/init.ts +13 -0
- package/src/commands/publish.ts +26 -4
- package/src/utils/audit.ts +20 -0
- package/src/utils/templates/incremental/.github/workflows/learnpack-audit.yml +1 -1
- package/src/utils/templates/isolated/.github/workflows/learnpack-audit.yml +1 -1
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
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.
|
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.
|
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.
|
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.
|
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
|
package/lib/commands/audit.js
CHANGED
@@ -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 (!((
|
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 (!((
|
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 (!((
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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 (!((
|
104
|
-
|
105
|
-
|
106
|
-
|
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((
|
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 (!((
|
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 (!((
|
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 (
|
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
|
-
|
372
|
+
strict: command_1.flags.boolean({
|
373
|
+
char: "s",
|
374
|
+
description: "strict mode",
|
375
|
+
default: false,
|
376
|
+
}),
|
350
377
|
};
|
351
378
|
exports.default = AuditCommand;
|
package/lib/commands/init.js
CHANGED
@@ -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
|
-
|
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;
|
package/lib/commands/publish.js
CHANGED
@@ -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)(
|
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;
|
package/lib/utils/audit.d.ts
CHANGED
@@ -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;
|
package/lib/utils/audit.js
CHANGED
@@ -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
|
};
|
package/oclif.manifest.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":"5.0.
|
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.
|
4
|
+
"version": "5.0.40",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
package/src/commands/audit.ts
CHANGED
@@ -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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
472
|
+
strict: flags.boolean({
|
473
|
+
char: "s",
|
474
|
+
description: "strict mode",
|
475
|
+
default: false,
|
476
|
+
}),
|
447
477
|
}
|
448
478
|
|
449
479
|
export default AuditCommand
|
package/src/commands/init.ts
CHANGED
@@ -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",
|
package/src/commands/publish.ts
CHANGED
@@ -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(
|
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
|
-
|
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
|
package/src/utils/audit.ts
CHANGED
@@ -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
|
}
|