@learnpack/learnpack 2.1.0 → 2.1.3
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 +11 -11
- package/lib/commands/audit.d.ts +1 -1
- package/lib/commands/audit.js +48 -47
- package/lib/managers/config/defaults.d.ts +1 -0
- package/lib/managers/config/defaults.js +15 -14
- package/lib/managers/config/index.js +4 -10
- package/lib/models/config.d.ts +1 -0
- package/lib/utils/watcher.js +3 -3
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/audit.ts +463 -462
- package/src/commands/init.ts +172 -172
- package/src/managers/config/defaults.ts +34 -33
- package/src/managers/config/index.ts +4 -11
- package/src/models/config.ts +1 -0
- package/src/utils/fileQueue.ts +198 -198
- package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.es.md +24 -24
- package/src/utils/templates/incremental/.learn/exercises/01-hello-world/README.md +24 -24
- package/src/utils/watcher.ts +25 -26
package/src/commands/init.ts
CHANGED
@@ -1,172 +1,172 @@
|
|
1
|
-
import { flags } from "@oclif/command";
|
2
|
-
import BaseCommand from "../utils/BaseCommand";
|
3
|
-
|
4
|
-
// eslint-disable-next-line
|
5
|
-
import * as fs from "fs-extra";
|
6
|
-
import * as prompts from "prompts";
|
7
|
-
import cli from "cli-ux";
|
8
|
-
import * as eta from "eta";
|
9
|
-
|
10
|
-
import Console from "../utils/console";
|
11
|
-
import { ValidationError } from "../utils/errors";
|
12
|
-
import defaults from "../managers/config/defaults";
|
13
|
-
|
14
|
-
import * as path from "path";
|
15
|
-
|
16
|
-
class InitComand extends BaseCommand {
|
17
|
-
static description =
|
18
|
-
"Create a new learning package: Book, Tutorial or Exercise";
|
19
|
-
|
20
|
-
static flags = {
|
21
|
-
...BaseCommand.flags,
|
22
|
-
grading: flags.help({ char: "h" }),
|
23
|
-
};
|
24
|
-
|
25
|
-
async run() {
|
26
|
-
const { flags } = this.parse(InitComand);
|
27
|
-
|
28
|
-
// if the folder/file .learn or .breathecode aleady exists
|
29
|
-
await alreadyInitialized();
|
30
|
-
|
31
|
-
const choices = await prompts([
|
32
|
-
{
|
33
|
-
type: "select",
|
34
|
-
name: "grading",
|
35
|
-
message: "Is the auto-grading going to be isolated or incremental?",
|
36
|
-
choices: [
|
37
|
-
{
|
38
|
-
title: "Incremental: Build on top of each other like a tutorial",
|
39
|
-
value: "incremental",
|
40
|
-
},
|
41
|
-
{ title: "Isolated: Small separated exercises", value: "isolated" },
|
42
|
-
{
|
43
|
-
title: "No grading: No feedback or testing whatsoever",
|
44
|
-
value: null,
|
45
|
-
},
|
46
|
-
],
|
47
|
-
},
|
48
|
-
{
|
49
|
-
type: "text",
|
50
|
-
name: "title",
|
51
|
-
initial: "My Interactive Tutorial",
|
52
|
-
message: "Title for your tutorial? Press enter to leave as it is",
|
53
|
-
},
|
54
|
-
{
|
55
|
-
type: "text",
|
56
|
-
name: "description",
|
57
|
-
initial: "",
|
58
|
-
message: "Description for your tutorial? Press enter to leave blank",
|
59
|
-
},
|
60
|
-
{
|
61
|
-
type: "select",
|
62
|
-
name: "difficulty",
|
63
|
-
message: "How difficulty will be to complete the tutorial?",
|
64
|
-
choices: [
|
65
|
-
{ title: "Begginer (no previous experience)", value: "beginner" },
|
66
|
-
{ title: "Easy (just a bit of experience required)", value: "easy" },
|
67
|
-
{
|
68
|
-
title: "Intermediate (you need experience)",
|
69
|
-
value: "intermediate",
|
70
|
-
},
|
71
|
-
{ title: "Hard (master the topic)", value: "hard" },
|
72
|
-
],
|
73
|
-
},
|
74
|
-
{
|
75
|
-
type: "text",
|
76
|
-
name: "duration",
|
77
|
-
initial: "1",
|
78
|
-
message: "How many hours avg it takes to complete (number)?",
|
79
|
-
validate: (value: string) => {
|
80
|
-
const n = Math.floor(Number(value));
|
81
|
-
return (
|
82
|
-
n !== Number.POSITIVE_INFINITY && String(n) === value && n >= 0
|
83
|
-
);
|
84
|
-
},
|
85
|
-
},
|
86
|
-
]);
|
87
|
-
|
88
|
-
const packageInfo = {
|
89
|
-
...defaults.config,
|
90
|
-
grading: choices.grading,
|
91
|
-
difficulty: choices.difficulty,
|
92
|
-
duration: parseInt(choices.duration),
|
93
|
-
description: choices.description,
|
94
|
-
title: choices.title,
|
95
|
-
slug: choices.title
|
96
|
-
.toLowerCase()
|
97
|
-
.replace(/ /g, "-")
|
98
|
-
.replace(/[^\w-]+/g, ""),
|
99
|
-
};
|
100
|
-
|
101
|
-
cli.action.start("Initializing package");
|
102
|
-
|
103
|
-
const languages = ["en", "es"];
|
104
|
-
|
105
|
-
const templatesDir = path.resolve(
|
106
|
-
__dirname,
|
107
|
-
"../../src/utils/templates/" + choices.grading || "no-grading"
|
108
|
-
);
|
109
|
-
if (!fs.existsSync(templatesDir))
|
110
|
-
throw ValidationError(`Template ${templatesDir} does not exists`);
|
111
|
-
await fs.copySync(templatesDir, "./");
|
112
|
-
|
113
|
-
// Creating README files
|
114
|
-
// eslint-disable-next-line
|
115
|
-
languages.forEach((language) => {
|
116
|
-
const readmeFilename = `README${language !== "en" ? `.${language}` : ""}`;
|
117
|
-
fs.writeFileSync(
|
118
|
-
`./${readmeFilename}.md`,
|
119
|
-
eta.render(
|
120
|
-
fs.readFileSync(
|
121
|
-
path.resolve(__dirname, `${templatesDir}/${readmeFilename}.ejs`),
|
122
|
-
"utf-8"
|
123
|
-
),
|
124
|
-
packageInfo
|
125
|
-
)
|
126
|
-
);
|
127
|
-
if (fs.existsSync(`./${readmeFilename}.ejs`))
|
128
|
-
fs.removeSync(`./${readmeFilename}.ejs`);
|
129
|
-
});
|
130
|
-
|
131
|
-
if (!fs.existsSync("./.gitignore"))
|
132
|
-
fs.copyFile(
|
133
|
-
path.resolve(__dirname, "../../src/utils/templates/gitignore.txt"),
|
134
|
-
"./.gitignore"
|
135
|
-
);
|
136
|
-
fs.writeFileSync("./learn.json", JSON.stringify(packageInfo, null, 2));
|
137
|
-
|
138
|
-
cli.action.stop();
|
139
|
-
Console.success(`😋 Package initialized successfully`);
|
140
|
-
Console.help(
|
141
|
-
`Start the exercises by running the following command on your terminal: $ learnpack start`
|
142
|
-
);
|
143
|
-
}
|
144
|
-
}
|
145
|
-
|
146
|
-
const alreadyInitialized = () =>
|
147
|
-
new Promise((resolve, reject) => {
|
148
|
-
fs.readdir("./", function (err: any, files: any) {
|
149
|
-
files = files.filter((f: any) =>
|
150
|
-
[".learn", "learn.json", "bc.json", ".breathecode"].includes(f)
|
151
|
-
);
|
152
|
-
if (err) {
|
153
|
-
reject(ValidationError(err.message));
|
154
|
-
throw ValidationError(err.message);
|
155
|
-
} else if (files.length > 0) {
|
156
|
-
reject(
|
157
|
-
ValidationError(
|
158
|
-
"It seems the package is already initialized because we've found the following files: " +
|
159
|
-
files.join(",")
|
160
|
-
)
|
161
|
-
);
|
162
|
-
throw ValidationError(
|
163
|
-
"It seems the package is already initialized because we've found the following files: " +
|
164
|
-
files.join(",")
|
165
|
-
);
|
166
|
-
}
|
167
|
-
|
168
|
-
resolve(false);
|
169
|
-
});
|
170
|
-
});
|
171
|
-
|
172
|
-
export default InitComand;
|
1
|
+
import { flags } from "@oclif/command";
|
2
|
+
import BaseCommand from "../utils/BaseCommand";
|
3
|
+
|
4
|
+
// eslint-disable-next-line
|
5
|
+
import * as fs from "fs-extra";
|
6
|
+
import * as prompts from "prompts";
|
7
|
+
import cli from "cli-ux";
|
8
|
+
import * as eta from "eta";
|
9
|
+
|
10
|
+
import Console from "../utils/console";
|
11
|
+
import { ValidationError } from "../utils/errors";
|
12
|
+
import defaults from "../managers/config/defaults";
|
13
|
+
|
14
|
+
import * as path from "path";
|
15
|
+
|
16
|
+
class InitComand extends BaseCommand {
|
17
|
+
static description =
|
18
|
+
"Create a new learning package: Book, Tutorial or Exercise";
|
19
|
+
|
20
|
+
static flags = {
|
21
|
+
...BaseCommand.flags,
|
22
|
+
grading: flags.help({ char: "h" }),
|
23
|
+
};
|
24
|
+
|
25
|
+
async run() {
|
26
|
+
const { flags } = this.parse(InitComand);
|
27
|
+
|
28
|
+
// if the folder/file .learn or .breathecode aleady exists
|
29
|
+
await alreadyInitialized();
|
30
|
+
|
31
|
+
const choices = await prompts([
|
32
|
+
{
|
33
|
+
type: "select",
|
34
|
+
name: "grading",
|
35
|
+
message: "Is the auto-grading going to be isolated or incremental?",
|
36
|
+
choices: [
|
37
|
+
{
|
38
|
+
title: "Incremental: Build on top of each other like a tutorial",
|
39
|
+
value: "incremental",
|
40
|
+
},
|
41
|
+
{ title: "Isolated: Small separated exercises", value: "isolated" },
|
42
|
+
{
|
43
|
+
title: "No grading: No feedback or testing whatsoever",
|
44
|
+
value: null,
|
45
|
+
},
|
46
|
+
],
|
47
|
+
},
|
48
|
+
{
|
49
|
+
type: "text",
|
50
|
+
name: "title",
|
51
|
+
initial: "My Interactive Tutorial",
|
52
|
+
message: "Title for your tutorial? Press enter to leave as it is",
|
53
|
+
},
|
54
|
+
{
|
55
|
+
type: "text",
|
56
|
+
name: "description",
|
57
|
+
initial: "",
|
58
|
+
message: "Description for your tutorial? Press enter to leave blank",
|
59
|
+
},
|
60
|
+
{
|
61
|
+
type: "select",
|
62
|
+
name: "difficulty",
|
63
|
+
message: "How difficulty will be to complete the tutorial?",
|
64
|
+
choices: [
|
65
|
+
{ title: "Begginer (no previous experience)", value: "beginner" },
|
66
|
+
{ title: "Easy (just a bit of experience required)", value: "easy" },
|
67
|
+
{
|
68
|
+
title: "Intermediate (you need experience)",
|
69
|
+
value: "intermediate",
|
70
|
+
},
|
71
|
+
{ title: "Hard (master the topic)", value: "hard" },
|
72
|
+
],
|
73
|
+
},
|
74
|
+
{
|
75
|
+
type: "text",
|
76
|
+
name: "duration",
|
77
|
+
initial: "1",
|
78
|
+
message: "How many hours avg it takes to complete (number)?",
|
79
|
+
validate: (value: string) => {
|
80
|
+
const n = Math.floor(Number(value));
|
81
|
+
return (
|
82
|
+
n !== Number.POSITIVE_INFINITY && String(n) === value && n >= 0
|
83
|
+
);
|
84
|
+
},
|
85
|
+
},
|
86
|
+
]);
|
87
|
+
|
88
|
+
const packageInfo = {
|
89
|
+
...defaults.config,
|
90
|
+
grading: choices.grading,
|
91
|
+
difficulty: choices.difficulty,
|
92
|
+
duration: parseInt(choices.duration),
|
93
|
+
description: choices.description,
|
94
|
+
title: choices.title,
|
95
|
+
slug: choices.title
|
96
|
+
.toLowerCase()
|
97
|
+
.replace(/ /g, "-")
|
98
|
+
.replace(/[^\w-]+/g, ""),
|
99
|
+
};
|
100
|
+
|
101
|
+
cli.action.start("Initializing package");
|
102
|
+
|
103
|
+
const languages = ["en", "es"];
|
104
|
+
|
105
|
+
const templatesDir = path.resolve(
|
106
|
+
__dirname,
|
107
|
+
"../../src/utils/templates/" + choices.grading || "no-grading"
|
108
|
+
);
|
109
|
+
if (!fs.existsSync(templatesDir))
|
110
|
+
throw ValidationError(`Template ${templatesDir} does not exists`);
|
111
|
+
await fs.copySync(templatesDir, "./");
|
112
|
+
|
113
|
+
// Creating README files
|
114
|
+
// eslint-disable-next-line
|
115
|
+
languages.forEach((language) => {
|
116
|
+
const readmeFilename = `README${language !== "en" ? `.${language}` : ""}`;
|
117
|
+
fs.writeFileSync(
|
118
|
+
`./${readmeFilename}.md`,
|
119
|
+
eta.render(
|
120
|
+
fs.readFileSync(
|
121
|
+
path.resolve(__dirname, `${templatesDir}/${readmeFilename}.ejs`),
|
122
|
+
"utf-8"
|
123
|
+
),
|
124
|
+
packageInfo
|
125
|
+
)
|
126
|
+
);
|
127
|
+
if (fs.existsSync(`./${readmeFilename}.ejs`))
|
128
|
+
fs.removeSync(`./${readmeFilename}.ejs`);
|
129
|
+
});
|
130
|
+
|
131
|
+
if (!fs.existsSync("./.gitignore"))
|
132
|
+
fs.copyFile(
|
133
|
+
path.resolve(__dirname, "../../src/utils/templates/gitignore.txt"),
|
134
|
+
"./.gitignore"
|
135
|
+
);
|
136
|
+
fs.writeFileSync("./learn.json", JSON.stringify(packageInfo, null, 2));
|
137
|
+
|
138
|
+
cli.action.stop();
|
139
|
+
Console.success(`😋 Package initialized successfully`);
|
140
|
+
Console.help(
|
141
|
+
`Start the exercises by running the following command on your terminal: $ learnpack start`
|
142
|
+
);
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
const alreadyInitialized = () =>
|
147
|
+
new Promise((resolve, reject) => {
|
148
|
+
fs.readdir("./", function (err: any, files: any) {
|
149
|
+
files = files.filter((f: any) =>
|
150
|
+
[".learn", "learn.json", "bc.json", ".breathecode"].includes(f)
|
151
|
+
);
|
152
|
+
if (err) {
|
153
|
+
reject(ValidationError(err.message));
|
154
|
+
throw ValidationError(err.message);
|
155
|
+
} else if (files.length > 0) {
|
156
|
+
reject(
|
157
|
+
ValidationError(
|
158
|
+
"It seems the package is already initialized because we've found the following files: " +
|
159
|
+
files.join(",")
|
160
|
+
)
|
161
|
+
);
|
162
|
+
throw ValidationError(
|
163
|
+
"It seems the package is already initialized because we've found the following files: " +
|
164
|
+
files.join(",")
|
165
|
+
);
|
166
|
+
}
|
167
|
+
|
168
|
+
resolve(false);
|
169
|
+
});
|
170
|
+
});
|
171
|
+
|
172
|
+
export default InitComand;
|
@@ -1,33 +1,34 @@
|
|
1
|
-
export default {
|
2
|
-
config: {
|
3
|
-
port: 3000,
|
4
|
-
editor: {
|
5
|
-
mode: null, // [standalone, preview]
|
6
|
-
agent: null, // [vscode,
|
7
|
-
version: null,
|
8
|
-
},
|
9
|
-
dirPath:
|
10
|
-
configPath:
|
11
|
-
outputPath:
|
12
|
-
publicPath:
|
13
|
-
publicUrl: null,
|
14
|
-
language:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
1
|
+
export default {
|
2
|
+
config: {
|
3
|
+
port: 3000,
|
4
|
+
editor: {
|
5
|
+
mode: null, // [standalone, preview]
|
6
|
+
agent: null, // [vscode, gitpod, localhost]
|
7
|
+
version: null,
|
8
|
+
},
|
9
|
+
dirPath: "./.learn",
|
10
|
+
configPath: "./learn.json",
|
11
|
+
outputPath: "./.learn/dist",
|
12
|
+
publicPath: "/preview",
|
13
|
+
publicUrl: null,
|
14
|
+
language: "auto",
|
15
|
+
autoPlay: true,
|
16
|
+
grading: "isolated", // [isolated, incremental]
|
17
|
+
exercisesPath: "./", // path to the folder that contains the exercises
|
18
|
+
webpackTemplate: null, // if you want webpack to use an HTML template
|
19
|
+
disableGrading: false,
|
20
|
+
disabledActions: [], // Possible: 'build', 'test' or 'reset'
|
21
|
+
actions: [], // ⚠️ deprecated, leave empty )
|
22
|
+
entries: {
|
23
|
+
html: "index.html",
|
24
|
+
vanillajs: "index.js",
|
25
|
+
react: "app.jsx",
|
26
|
+
node: "app.js",
|
27
|
+
python3: "app.py",
|
28
|
+
java: "app.java",
|
29
|
+
},
|
30
|
+
},
|
31
|
+
address: "http://localhost",
|
32
|
+
currentExercise: null,
|
33
|
+
exercises: [],
|
34
|
+
};
|
@@ -30,12 +30,7 @@ const chalk = require("chalk");
|
|
30
30
|
/* exercise folder name standard */
|
31
31
|
|
32
32
|
const getConfigPath = () => {
|
33
|
-
const possibleFileNames = [
|
34
|
-
"learn.json",
|
35
|
-
".learn/learn.json",
|
36
|
-
"bc.json",
|
37
|
-
".breathecode/bc.json",
|
38
|
-
];
|
33
|
+
const possibleFileNames = ["learn.json", ".learn/learn.json"];
|
39
34
|
const config = possibleFileNames.find(file => fs.existsSync(file)) || null;
|
40
35
|
if (config && fs.existsSync(".breathecode"))
|
41
36
|
return { config, base: ".breathecode" };
|
@@ -75,7 +70,6 @@ export default async ({
|
|
75
70
|
|
76
71
|
if (confPath) {
|
77
72
|
const bcContent = fs.readFileSync(confPath.config);
|
78
|
-
|
79
73
|
let hiddenBcContent = {};
|
80
74
|
if (fs.existsSync(confPath.base + "/config.json")) {
|
81
75
|
hiddenBcContent = fs.readFileSync(confPath.base + "/config.json");
|
@@ -131,7 +125,6 @@ export default async ({
|
|
131
125
|
|
132
126
|
// auto detect agent (if possible)
|
133
127
|
if (shell.which("gp") && configObj && configObj.config) {
|
134
|
-
configObj.config.editor.agent = "gitpod";
|
135
128
|
configObj.address = getGitpodAddress();
|
136
129
|
configObj.config.publicUrl = `https://${
|
137
130
|
configObj.config.port
|
@@ -144,8 +137,8 @@ export default async ({
|
|
144
137
|
configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`;
|
145
138
|
|
146
139
|
// Assign default editor mode if not set already
|
147
|
-
if (configObj.config && mode
|
148
|
-
configObj.config.editor.mode =
|
140
|
+
if (configObj.config && !mode) {
|
141
|
+
configObj.config.editor.mode = mode as TMode;
|
149
142
|
}
|
150
143
|
|
151
144
|
if (configObj.config && !configObj.config.editor.mode)
|
@@ -160,7 +153,7 @@ configObj.config.editor.version = version;
|
|
160
153
|
"https://raw.githubusercontent.com/learnpack/coding-ide/learnpack/package.json"
|
161
154
|
);
|
162
155
|
const packageJSON = await resp.json();
|
163
|
-
configObj.config.editor.version = packageJSON.version || "1.0.
|
156
|
+
configObj.config.editor.version = packageJSON.version || "1.0.73";
|
164
157
|
}
|
165
158
|
|
166
159
|
if (configObj.config) {
|
package/src/models/config.ts
CHANGED
@@ -54,6 +54,7 @@ export interface IConfig {
|
|
54
54
|
exercisesPath: string;
|
55
55
|
disableGrading: boolean; // TODO: Deprecate
|
56
56
|
actions: Array<string>; // TODO: Deprecate
|
57
|
+
autoPlay: boolean;
|
57
58
|
// TODO: nameExerciseValidation
|
58
59
|
disabledActions?: Array<TConfigAction>;
|
59
60
|
compiler: TCompiler;
|