@learnpack/learnpack 2.0.0 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +10 -10
- package/oclif.manifest.json +1 -1
- package/package.json +3 -2
- package/src/commands/download.ts +26 -26
- package/src/commands/init.ts +4 -1
- package/src/commands/logout.ts +11 -11
- package/src/commands/start.ts +8 -3
- package/src/commands/test.ts +4 -2
- package/src/managers/config/exercise.ts +17 -10
- package/src/managers/config/index.ts +13 -12
- package/src/managers/server/routes.ts +12 -7
- package/src/utils/BaseCommand.ts +1 -1
- package/src/utils/api.ts +6 -6
- package/src/utils/fileQueue.ts +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/2.0.
|
24
|
+
@learnpack/learnpack/2.0.4 win32-x64 node-v16.14.0
|
25
25
|
$ learnpack --help [COMMAND]
|
26
26
|
USAGE
|
27
27
|
$ learnpack COMMAND
|
@@ -74,7 +74,7 @@ DESCRIPTION
|
|
74
74
|
12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
|
75
75
|
```
|
76
76
|
|
77
|
-
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.
|
77
|
+
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.4/src\commands\audit.ts)_
|
78
78
|
|
79
79
|
## `learnpack clean`
|
80
80
|
|
@@ -89,7 +89,7 @@ DESCRIPTION
|
|
89
89
|
Extra documentation goes here
|
90
90
|
```
|
91
91
|
|
92
|
-
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.
|
92
|
+
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.4/src\commands\clean.ts)_
|
93
93
|
|
94
94
|
## `learnpack download [PACKAGE]`
|
95
95
|
|
@@ -107,7 +107,7 @@ DESCRIPTION
|
|
107
107
|
Extra documentation goes here
|
108
108
|
```
|
109
109
|
|
110
|
-
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.
|
110
|
+
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.4/src\commands\download.ts)_
|
111
111
|
|
112
112
|
## `learnpack help [COMMAND]`
|
113
113
|
|
@@ -138,7 +138,7 @@ OPTIONS
|
|
138
138
|
-h, --grading show CLI help
|
139
139
|
```
|
140
140
|
|
141
|
-
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.
|
141
|
+
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.4/src\commands\init.ts)_
|
142
142
|
|
143
143
|
## `learnpack login [PACKAGE]`
|
144
144
|
|
@@ -156,7 +156,7 @@ DESCRIPTION
|
|
156
156
|
Extra documentation goes here
|
157
157
|
```
|
158
158
|
|
159
|
-
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.
|
159
|
+
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.4/src\commands\login.ts)_
|
160
160
|
|
161
161
|
## `learnpack logout [PACKAGE]`
|
162
162
|
|
@@ -174,7 +174,7 @@ DESCRIPTION
|
|
174
174
|
Extra documentation goes here
|
175
175
|
```
|
176
176
|
|
177
|
-
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.
|
177
|
+
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.4/src\commands\logout.ts)_
|
178
178
|
|
179
179
|
## `learnpack plugins`
|
180
180
|
|
@@ -309,7 +309,7 @@ DESCRIPTION
|
|
309
309
|
Extra documentation goes here
|
310
310
|
```
|
311
311
|
|
312
|
-
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.
|
312
|
+
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.4/src\commands\publish.ts)_
|
313
313
|
|
314
314
|
## `learnpack start`
|
315
315
|
|
@@ -330,7 +330,7 @@ OPTIONS
|
|
330
330
|
-w, --watch Watch for file changes
|
331
331
|
```
|
332
332
|
|
333
|
-
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.
|
333
|
+
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.4/src\commands\start.ts)_
|
334
334
|
|
335
335
|
## `learnpack test [EXERCISESLUG]`
|
336
336
|
|
@@ -344,5 +344,5 @@ ARGUMENTS
|
|
344
344
|
EXERCISESLUG The name of the exercise to test
|
345
345
|
```
|
346
346
|
|
347
|
-
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.
|
347
|
+
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v2.0.4/src\commands\test.ts)_
|
348
348
|
<!-- commandsstop -->
|
package/oclif.manifest.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":"2.0.
|
1
|
+
{"version":"2.0.4","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":[]},"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":{"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":"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}]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"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":"[standalone, gitpod]","options":["standalone","gitpod"]},"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":{},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]}}}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@learnpack/learnpack",
|
3
3
|
"description": "Create, sell or download and take learning amazing learning packages",
|
4
|
-
"version": "2.0.
|
4
|
+
"version": "2.0.4",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"bin": {
|
7
7
|
"learnpack": "bin/run"
|
@@ -90,7 +90,8 @@
|
|
90
90
|
"oclif"
|
91
91
|
],
|
92
92
|
"license": "UNLICENSED",
|
93
|
-
"main": "
|
93
|
+
"main": "../bin/index.js",
|
94
|
+
"types": "../bin/index.d.ts",
|
94
95
|
"oclif": {
|
95
96
|
"commands": "./src/commands",
|
96
97
|
"bin": "learnpack",
|
package/src/commands/download.ts
CHANGED
@@ -1,62 +1,62 @@
|
|
1
|
-
import {Command /* , flags */} from
|
1
|
+
import { Command /* , flags */ } from "@oclif/command";
|
2
2
|
// import fetch from 'node-fetch'
|
3
|
-
import {clone} from
|
4
|
-
import Console from
|
5
|
-
import api from
|
6
|
-
import {askPackage} from
|
3
|
+
import { clone } from "../managers/file";
|
4
|
+
import Console from "../utils/console";
|
5
|
+
import api from "../utils/api";
|
6
|
+
import { askPackage } from "../ui/download";
|
7
7
|
// const BaseCommand = require('../utils/BaseCommand');
|
8
8
|
|
9
9
|
class DownloadCommand extends Command {
|
10
|
-
|
11
10
|
static description = `Describe the command here
|
12
11
|
...
|
13
12
|
Extra documentation goes here
|
14
|
-
|
13
|
+
`;
|
15
14
|
|
16
|
-
static flags:any = {
|
15
|
+
static flags: any = {
|
17
16
|
// name: flags.string({char: 'n', description: 'name to print'}),
|
18
|
-
}
|
17
|
+
};
|
19
18
|
|
20
19
|
static args = [
|
21
20
|
{
|
22
|
-
name:
|
21
|
+
name: "package", // name of arg to show in help and reference with args[name]
|
23
22
|
required: false, // make the arg required with `required: true`
|
24
|
-
description:
|
23
|
+
description:
|
24
|
+
"The unique string that identifies this package on learnpack", // help description
|
25
25
|
hidden: false, // hide this arg from help
|
26
26
|
},
|
27
|
-
]
|
27
|
+
];
|
28
28
|
// async init() {
|
29
29
|
// const {flags} = this.parse(DownloadCommand)
|
30
30
|
// await this.initSession(flags)
|
31
31
|
// }
|
32
32
|
|
33
33
|
async run() {
|
34
|
-
const {/* flags, */ args} = this.parse(DownloadCommand)
|
34
|
+
const { /* flags, */ args } = this.parse(DownloadCommand);
|
35
35
|
// start watching for file changes
|
36
|
-
let _package: string = args.package
|
36
|
+
let _package: string = args.package;
|
37
37
|
if (!_package) {
|
38
|
-
_package = (await askPackage()) as string
|
38
|
+
_package = (await askPackage()) as string;
|
39
39
|
}
|
40
40
|
|
41
41
|
if (!_package) {
|
42
|
-
return null
|
42
|
+
return null;
|
43
43
|
}
|
44
44
|
|
45
45
|
try {
|
46
|
-
const packageInfo = await api.getAllPackages({slug: _package})
|
46
|
+
const packageInfo = await api.getAllPackages({ slug: _package });
|
47
47
|
if (packageInfo.results.length === 0)
|
48
|
-
Console.error(`Package ${_package} not found`)
|
48
|
+
Console.error(`Package ${_package} not found`);
|
49
49
|
else
|
50
50
|
clone(packageInfo.results[0].repository)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
.then(_result => {
|
52
|
+
Console.success("Successfully downloaded");
|
53
|
+
Console.info(
|
54
|
+
`You can now CD into the folder like this: $ cd ${_package}`
|
55
|
+
);
|
56
|
+
})
|
57
|
+
.catch(error => Console.error(error.message || error));
|
58
58
|
} catch {}
|
59
59
|
}
|
60
60
|
}
|
61
61
|
|
62
|
-
export default DownloadCommand
|
62
|
+
export default DownloadCommand;
|
package/src/commands/init.ts
CHANGED
@@ -92,7 +92,10 @@ class InitComand extends BaseCommand {
|
|
92
92
|
duration: parseInt(choices.duration),
|
93
93
|
description: choices.description,
|
94
94
|
title: choices.title,
|
95
|
-
slug: choices.title
|
95
|
+
slug: choices.title
|
96
|
+
.toLowerCase()
|
97
|
+
.replace(/ /g, "-")
|
98
|
+
.replace(/[^\w-]+/g, ""),
|
96
99
|
};
|
97
100
|
|
98
101
|
cli.action.start("Initializing package");
|
package/src/commands/logout.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
// import {Command, flags} from '@oclif/command'
|
2
2
|
// import { prompt } from "enquirer"
|
3
3
|
// import fetch from 'node-fetch'
|
4
|
-
import SessionCommand from
|
5
|
-
import SessionManager from
|
4
|
+
import SessionCommand from "../utils/SessionCommand";
|
5
|
+
import SessionManager from "../managers/session";
|
6
6
|
// import Console from '../utils/console'
|
7
7
|
// import { replace } from 'node-emoji'
|
8
8
|
// import { validURL } from "../utils/validators"
|
@@ -12,32 +12,32 @@ class LogoutCommand extends SessionCommand {
|
|
12
12
|
static description = `Describe the command here
|
13
13
|
...
|
14
14
|
Extra documentation goes here
|
15
|
-
|
15
|
+
`;
|
16
16
|
|
17
17
|
static flags: any = {
|
18
18
|
// name: flags.string({char: 'n', description: 'name to print'}),
|
19
|
-
}
|
19
|
+
};
|
20
20
|
|
21
21
|
static args = [
|
22
22
|
{
|
23
|
-
name:
|
23
|
+
name: "package", // name of arg to show in help and reference with args[name]
|
24
24
|
required: false, // make the arg required with `required: true`
|
25
25
|
description:
|
26
|
-
|
26
|
+
"The unique string that identifies this package on learnpack", // help description
|
27
27
|
hidden: false, // hide this arg from help
|
28
28
|
},
|
29
|
-
]
|
29
|
+
];
|
30
30
|
|
31
31
|
async init() {
|
32
|
-
const {flags} = this.parse(LogoutCommand)
|
33
|
-
await this.initSession(flags)
|
32
|
+
const { flags } = this.parse(LogoutCommand);
|
33
|
+
await this.initSession(flags);
|
34
34
|
}
|
35
35
|
|
36
36
|
async run() {
|
37
37
|
// const {flags, args} = this.parse(LogoutCommand)
|
38
38
|
|
39
|
-
SessionManager.destroy()
|
39
|
+
SessionManager.destroy();
|
40
40
|
}
|
41
41
|
}
|
42
42
|
|
43
|
-
export default LogoutCommand
|
43
|
+
export default LogoutCommand;
|
package/src/commands/start.ts
CHANGED
@@ -74,7 +74,9 @@ export default class StartCommand extends SessionCommand {
|
|
74
74
|
`Grading: ${config?.grading} ${
|
75
75
|
config?.disabledActions?.includes("test") ? "(disabled)" : ""
|
76
76
|
}, editor: ${config?.editor.mode} ${config?.editor.version}, for ${
|
77
|
-
Array.isArray(configObject?.exercises) ?
|
77
|
+
Array.isArray(configObject?.exercises) ?
|
78
|
+
configObject?.exercises.length :
|
79
|
+
0
|
78
80
|
} exercises found`
|
79
81
|
);
|
80
82
|
|
@@ -221,8 +223,11 @@ export default class StartCommand extends SessionCommand {
|
|
221
223
|
setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000);
|
222
224
|
|
223
225
|
// start watching for file changes
|
224
|
-
|
225
|
-
if (StartCommand.flags.watch)
|
226
|
+
|
227
|
+
if (StartCommand.flags.watch)
|
228
|
+
this.configManager.watchIndex(_exercises =>
|
229
|
+
socket.reload(null, _exercises)
|
230
|
+
);
|
226
231
|
}
|
227
232
|
}
|
228
233
|
}
|
package/src/commands/test.ts
CHANGED
@@ -11,7 +11,7 @@ class TestCommand extends SessionCommand {
|
|
11
11
|
const { flags } = this.parse(TestCommand);
|
12
12
|
await this.initSession(flags);
|
13
13
|
}
|
14
|
-
|
14
|
+
|
15
15
|
async run() {
|
16
16
|
const {
|
17
17
|
args: { exerciseSlug },
|
@@ -23,7 +23,9 @@ class TestCommand extends SessionCommand {
|
|
23
23
|
let exercises: IExercise[] | undefined = [];
|
24
24
|
|
25
25
|
// test all exercises
|
26
|
-
!exerciseSlug ?
|
26
|
+
!exerciseSlug ?
|
27
|
+
(exercises = this.configManager?.getAllExercises()) :
|
28
|
+
(exercises = [this.configManager!.getExercise(exerciseSlug)]);
|
27
29
|
|
28
30
|
const exercisesQueue = new ExercisesQueue(exercises);
|
29
31
|
|
@@ -42,14 +42,14 @@ export const exercise = (
|
|
42
42
|
const parts = file.split(".");
|
43
43
|
|
44
44
|
if (parts.length === 3)
|
45
|
-
|
45
|
+
translations[parts[1]] = file;
|
46
46
|
else
|
47
|
-
|
47
|
+
translations.us = file;
|
48
48
|
}
|
49
49
|
|
50
50
|
// if the slug is a dot, it means there is not "exercises" folder, and its just a single README.md
|
51
51
|
if (slug === ".")
|
52
|
-
|
52
|
+
slug = "default-index";
|
53
53
|
|
54
54
|
const detected = detect(configObject, files);
|
55
55
|
|
@@ -61,24 +61,31 @@ export const exercise = (
|
|
61
61
|
language: detected?.language,
|
62
62
|
entry: detected?.entry ? path + "/" + detected.entry : null, // full path to the exercise entry
|
63
63
|
title: slug || "Exercise",
|
64
|
-
graded: files.some(
|
64
|
+
graded: files.some(
|
65
|
+
file =>
|
66
|
+
file.toLowerCase().startsWith("test.") ||
|
67
|
+
file.toLowerCase().startsWith("tests.")
|
68
|
+
),
|
65
69
|
files: filterFiles(files, path),
|
66
70
|
// if the exercises was on the config before I may keep the status done
|
67
71
|
done:
|
68
72
|
Array.isArray(exercises) &&
|
69
73
|
typeof exercises[position] !== "undefined" &&
|
70
|
-
path.slice(Math.max(0, path.indexOf("exercises/") + 10)) ===
|
74
|
+
path.slice(Math.max(0, path.indexOf("exercises/") + 10)) ===
|
75
|
+
exercises[position].slug ?
|
76
|
+
exercises[position].done :
|
77
|
+
false,
|
71
78
|
getReadme: function (lang = null) {
|
72
79
|
if (lang === "us")
|
73
|
-
|
74
|
-
|
80
|
+
lang = null; // <-- english is default, no need to append it to the file name
|
81
|
+
|
75
82
|
if (!fs.existsSync(`${this.path}/README${lang ? "." + lang : ""}.md`)) {
|
76
83
|
Console.error(
|
77
84
|
`Language ${lang} not found for exercise ${slug}, switching to default language`
|
78
85
|
);
|
79
86
|
|
80
87
|
if (lang)
|
81
|
-
|
88
|
+
lang = null;
|
82
89
|
|
83
90
|
if (!fs.existsSync(`${this.path}/README${lang ? "." + lang : ""}.md`))
|
84
91
|
throw new Error(
|
@@ -141,7 +148,7 @@ export const exercise = (
|
|
141
148
|
const _path = `${configObject?.confPath?.base}/reports/${this.slug}.json`;
|
142
149
|
|
143
150
|
if (!fs.existsSync(_path))
|
144
|
-
|
151
|
+
return {};
|
145
152
|
|
146
153
|
const content = fs.readFileSync(_path);
|
147
154
|
const data = JSON.parse(`${content}`);
|
@@ -154,7 +161,7 @@ export const exercise = (
|
|
154
161
|
|
155
162
|
export const validateExerciseDirectoryName = (str: string) => {
|
156
163
|
if (str === "./")
|
157
|
-
|
164
|
+
return true;
|
158
165
|
// TODO: Add nameValidationREgex from the config
|
159
166
|
const regex = /^(\d{2,3}(\.\d{1,2})?-([\dA-Za-z]+(-|_)?)+)$/;
|
160
167
|
return regex.test(str);
|
@@ -124,7 +124,9 @@ export default async ({
|
|
124
124
|
|
125
125
|
Console.debug("This is your configuration object: ", {
|
126
126
|
...configObj,
|
127
|
-
exercises: configObj.exercises ?
|
127
|
+
exercises: configObj.exercises ?
|
128
|
+
configObj.exercises.map(e => e.slug) :
|
129
|
+
[],
|
128
130
|
});
|
129
131
|
|
130
132
|
// auto detect agent (if possible)
|
@@ -151,8 +153,7 @@ export default async ({
|
|
151
153
|
configObj.config.editor.agent === "localhost" ? "standalone" : "preview";
|
152
154
|
|
153
155
|
if (version && configObj.config)
|
154
|
-
|
155
|
-
|
156
|
+
configObj.config.editor.version = version;
|
156
157
|
else if (configObj.config && configObj.config.editor.version === null) {
|
157
158
|
Console.debug("Config version not found, downloading default.");
|
158
159
|
const resp = await fetch(
|
@@ -177,7 +178,7 @@ export default async ({
|
|
177
178
|
python3: "python",
|
178
179
|
};
|
179
180
|
if (map[_l])
|
180
|
-
|
181
|
+
return map[_l];
|
181
182
|
return _l;
|
182
183
|
};
|
183
184
|
|
@@ -185,7 +186,7 @@ export default async ({
|
|
185
186
|
language = alias(language);
|
186
187
|
|
187
188
|
if (this.validLanguages[language])
|
188
|
-
|
189
|
+
return true;
|
189
190
|
|
190
191
|
Console.debug(`Validating engine for ${language} compilation`);
|
191
192
|
let result = shell.exec("learnpack plugins", { silent: true });
|
@@ -249,7 +250,7 @@ export default async ({
|
|
249
250
|
const exercise = (configObj.exercises || []).find(
|
250
251
|
ex => ex.slug === slug
|
251
252
|
);
|
252
|
-
if (!exercise)
|
253
|
+
if (!exercise)
|
253
254
|
throw ValidationError(`Exercise ${slug} not found`);
|
254
255
|
|
255
256
|
return exercise;
|
@@ -269,7 +270,7 @@ throw ValidationError(`Exercise ${slug} not found`);
|
|
269
270
|
exercise.files.forEach((f: IFile) => {
|
270
271
|
if (configObj.config) {
|
271
272
|
const _path = configObj.config.outputPath + "/" + f.name;
|
272
|
-
if (f.hidden === false && fs.existsSync(_path))
|
273
|
+
if (f.hidden === false && fs.existsSync(_path))
|
273
274
|
fs.unlinkSync(_path);
|
274
275
|
}
|
275
276
|
});
|
@@ -314,7 +315,7 @@ fs.unlinkSync(_path);
|
|
314
315
|
if (name === path.basename(configObj?.config?.dirPath || ""))
|
315
316
|
return false;
|
316
317
|
// ignore folders that start with a dot
|
317
|
-
if (name.charAt(0) === "." || name.charAt(0) === "_")
|
318
|
+
if (name.charAt(0) === "." || name.charAt(0) === "_")
|
318
319
|
return false;
|
319
320
|
|
320
321
|
return fs.lstatSync(source).isDirectory();
|
@@ -326,7 +327,7 @@ return false;
|
|
326
327
|
.map(name => path.join(source, name))
|
327
328
|
.filter(isDirectory);
|
328
329
|
// add the .learn folder
|
329
|
-
if (!fs.existsSync(confPath.base))
|
330
|
+
if (!fs.existsSync(confPath.base))
|
330
331
|
fs.mkdirSync(confPath.base);
|
331
332
|
// add the outout folder where webpack will publish the the html/css/js files
|
332
333
|
if (
|
@@ -359,7 +360,7 @@ fs.mkdirSync(confPath.base);
|
|
359
360
|
.then((/* eventname, filename */) => {
|
360
361
|
Console.debug("Changes detected on your exercises");
|
361
362
|
this.buildIndex();
|
362
|
-
if (onChange)
|
363
|
+
if (onChange)
|
363
364
|
onChange();
|
364
365
|
})
|
365
366
|
.catch(error => {
|
@@ -385,7 +386,7 @@ onChange();
|
|
385
386
|
} as IConfigManager;
|
386
387
|
};
|
387
388
|
|
388
|
-
function deepMerge(...sources: any): any {
|
389
|
+
function deepMerge(...sources: any): any {
|
389
390
|
let acc: any = {};
|
390
391
|
for (const source of sources) {
|
391
392
|
if (Array.isArray(source)) {
|
@@ -401,7 +402,7 @@ function deepMerge(...sources: any): any {
|
|
401
402
|
value = deepMerge(acc[key], value);
|
402
403
|
}
|
403
404
|
|
404
|
-
if (value !== undefined)
|
405
|
+
if (value !== undefined)
|
405
406
|
acc = { ...acc, [key]: value };
|
406
407
|
}
|
407
408
|
}
|
@@ -67,8 +67,8 @@ export default async function (
|
|
67
67
|
// symbolic link to maintain path compatiblity
|
68
68
|
const fetchStaticAsset = withHandler((req, res) => {
|
69
69
|
const filePath = `${config?.dirPath}/assets/${req.params.filePath}`;
|
70
|
-
if (!fs.existsSync(filePath))
|
71
|
-
throw new Error("File not found: " + filePath);
|
70
|
+
if (!fs.existsSync(filePath))
|
71
|
+
throw new Error("File not found: " + filePath);
|
72
72
|
const content = fs.readFileSync(filePath);
|
73
73
|
res.write(content);
|
74
74
|
res.end();
|
@@ -138,18 +138,23 @@ throw new Error("File not found: " + filePath);
|
|
138
138
|
dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug);
|
139
139
|
|
140
140
|
type TEntry = "python3" | "html" | "node" | "react" | "java";
|
141
|
+
|
142
|
+
const entries = new Set(
|
143
|
+
Object.keys(config?.entries!).map(
|
144
|
+
lang => config?.entries[lang as TEntry]
|
145
|
+
)
|
146
|
+
);
|
141
147
|
|
142
|
-
// eslint-disable-next-line
|
143
|
-
const entries = new Set(Object.keys(config?.entries!).map(
|
144
|
-
lang => config?.entries[lang as TEntry]
|
145
|
-
));
|
146
148
|
// if we are in incremental grading, the entry file can by dinamically detected
|
147
149
|
// based on the changes the student is making during the exercise
|
148
150
|
if (config?.grading === "incremental") {
|
149
151
|
const scanedFiles = fs.readdirSync("./");
|
150
152
|
|
151
153
|
// update the file hierarchy with updates
|
152
|
-
exercise.files = [
|
154
|
+
exercise.files = [
|
155
|
+
...exercise.files.filter(f => f.name.includes("test.")),
|
156
|
+
...filterFiles(scanedFiles),
|
157
|
+
];
|
153
158
|
Console.debug(`Exercise updated files: `, exercise.files);
|
154
159
|
}
|
155
160
|
|
package/src/utils/BaseCommand.ts
CHANGED
package/src/utils/api.ts
CHANGED
@@ -32,11 +32,11 @@ const fetch = async (url: string, options: IOptions = {}) => {
|
|
32
32
|
headers: { ...headers, ...options.headers },
|
33
33
|
} as any);
|
34
34
|
|
35
|
-
if (resp.status >= 200 && resp.status < 300)
|
35
|
+
if (resp.status >= 200 && resp.status < 300)
|
36
36
|
return await resp.json();
|
37
37
|
if (resp.status === 401)
|
38
38
|
throw APIError("Invalid authentication credentials", 401);
|
39
|
-
else if (resp.status === 404)
|
39
|
+
else if (resp.status === 404)
|
40
40
|
throw APIError("Package not found", 404);
|
41
41
|
else if (resp.status >= 500)
|
42
42
|
throw APIError("Impossible to connect with the server", 500);
|
@@ -54,7 +54,7 @@ throw APIError("Package not found", 404);
|
|
54
54
|
} else {
|
55
55
|
throw APIError("Uknown error");
|
56
56
|
}
|
57
|
-
} else
|
57
|
+
} else
|
58
58
|
throw APIError("Uknown error");
|
59
59
|
} catch (error) {
|
60
60
|
Console.error((error as TypeError).message);
|
@@ -91,7 +91,7 @@ const publish = async (config: any) => {
|
|
91
91
|
];
|
92
92
|
|
93
93
|
const payload: { [key: string]: string } = {};
|
94
|
-
for (const k of keys)
|
94
|
+
for (const k of keys)
|
95
95
|
config[k] ? (payload[k] = config[k]) : null;
|
96
96
|
try {
|
97
97
|
console.log("Package to publish:", payload);
|
@@ -138,7 +138,7 @@ const getPackage = async (slug: string) => {
|
|
138
138
|
} catch (error) {
|
139
139
|
if ((error as any).status === 404)
|
140
140
|
Console.error(`Package ${slug} does not exist`);
|
141
|
-
else
|
141
|
+
else
|
142
142
|
Console.error(`Package ${slug} does not exist`);
|
143
143
|
Console.debug(error);
|
144
144
|
throw error;
|
@@ -155,7 +155,7 @@ const getLangs = async () => {
|
|
155
155
|
} catch (error) {
|
156
156
|
if ((error as any).status === 404)
|
157
157
|
Console.error("Package slug does not exist");
|
158
|
-
else
|
158
|
+
else
|
159
159
|
Console.error("Package slug does not exist");
|
160
160
|
Console.debug(error);
|
161
161
|
throw error;
|
package/src/utils/fileQueue.ts
CHANGED
@@ -30,7 +30,7 @@ const loadDispatcher = (opts: any) => {
|
|
30
30
|
let exists = fs.existsSync(opts.path);
|
31
31
|
if (opts.create) {
|
32
32
|
if (exists)
|
33
|
-
|
33
|
+
actions.push({ name: "reset", time: now() });
|
34
34
|
fs.writeFileSync(opts.path, JSON.stringify(actions), { flag: "w" });
|
35
35
|
exists = true;
|
36
36
|
}
|