@learnpack/learnpack 2.1.37 → 2.1.38
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +11 -11
- package/lib/commands/start.js +22 -16
- package/lib/managers/config/defaults.d.ts +2 -0
- package/lib/managers/config/defaults.js +4 -0
- package/lib/managers/config/index.js +30 -26
- package/lib/managers/server/index.js +1 -1
- package/lib/managers/server/routes.js +12 -6
- package/lib/managers/telemetry.js +4 -2
- package/lib/models/config.d.ts +5 -2
- package/lib/utils/SessionCommand.d.ts +2 -2
- package/lib/utils/SessionCommand.js +3 -7
- package/lib/utils/api.js +1 -0
- package/lib/utils/osOperations.d.ts +4 -0
- package/lib/utils/osOperations.js +56 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/start.ts +22 -17
- package/src/managers/config/defaults.ts +7 -3
- package/src/managers/config/index.ts +31 -28
- package/src/managers/server/index.ts +2 -2
- package/src/managers/server/routes.ts +6 -2
- package/src/managers/telemetry.ts +6 -2
- package/src/models/config.ts +5 -2
- package/src/utils/SessionCommand.ts +9 -14
- package/src/utils/api.ts +1 -0
- package/src/utils/osOperations.ts +63 -0
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.1.
|
24
|
+
@learnpack/learnpack/2.1.38 darwin-arm64 node-v16.20.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.1.
|
77
|
+
_See code: [src/commands/audit.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/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.1.
|
92
|
+
_See code: [src/commands/clean.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/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.1.
|
110
|
+
_See code: [src/commands/download.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/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.1.
|
141
|
+
_See code: [src/commands/init.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/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.1.
|
159
|
+
_See code: [src/commands/login.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/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.1.
|
177
|
+
_See code: [src/commands/logout.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/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.1.
|
312
|
+
_See code: [src/commands/publish.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/publish.ts)_
|
313
313
|
|
314
314
|
## `learnpack start`
|
315
315
|
|
@@ -322,7 +322,7 @@ USAGE
|
|
322
322
|
OPTIONS
|
323
323
|
-D, --disableGrading disble grading functionality
|
324
324
|
-d, --debug debugger mode for more verbage
|
325
|
-
-e, --editor=
|
325
|
+
-e, --editor=extension|preview [preview, extension]
|
326
326
|
-g, --grading=isolated|incremental [isolated, incremental]
|
327
327
|
-h, --host=host server host
|
328
328
|
-p, --port=port server port
|
@@ -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.1.
|
333
|
+
_See code: [src/commands/start.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/start.ts)_
|
334
334
|
|
335
335
|
## `learnpack test [EXERCISESLUG]`
|
336
336
|
|
@@ -344,7 +344,7 @@ 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.1.
|
347
|
+
_See code: [src/commands/test.ts](https://github.com/learnpack/learnpack-cli/blob/v2.1.38/src/commands/test.ts)_
|
348
348
|
<!-- commandsstop -->
|
349
349
|
|
350
350
|
> > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
|
package/lib/commands/start.js
CHANGED
@@ -10,6 +10,7 @@ const server_1 = require("../managers/server");
|
|
10
10
|
const fileQueue_1 = require("../utils/fileQueue");
|
11
11
|
const file_1 = require("../managers/file");
|
12
12
|
const misc_1 = require("../utils/misc");
|
13
|
+
const osOperations_1 = require("../utils/osOperations");
|
13
14
|
class StartCommand extends SessionCommand_1.default {
|
14
15
|
// 🛑 IMPORTANT
|
15
16
|
// Every command that will use the configManager needs this init method
|
@@ -38,6 +39,9 @@ class StartCommand extends SessionCommand_1.default {
|
|
38
39
|
// listen to socket commands
|
39
40
|
if (config && this.configManager) {
|
40
41
|
const server = await server_1.default(configObject, this.configManager, process.env.NODE_ENV === "test");
|
42
|
+
server.setMaxListeners(20);
|
43
|
+
// console.log(config, "this is the f*!shell.which("code")!shell.which("code")cking config");
|
44
|
+
// I should call a method to get the EventListener
|
41
45
|
const dispatcher = fileQueue_1.default.dispatcher({
|
42
46
|
create: true,
|
43
47
|
path: `${config.dirPath}/vscode_queue.json`,
|
@@ -66,12 +70,21 @@ class StartCommand extends SessionCommand_1.default {
|
|
66
70
|
socket_1.default.on("open", (data) => {
|
67
71
|
console_1.default.debug("Opening these files: ", data);
|
68
72
|
const files = misc_1.prioritizeHTMLFile(data.files);
|
69
|
-
|
73
|
+
if (config.editor.agent === "os") {
|
74
|
+
osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_FILES, files);
|
75
|
+
}
|
76
|
+
else {
|
77
|
+
dispatcher.enqueue(dispatcher.events.OPEN_FILES, files);
|
78
|
+
}
|
70
79
|
socket_1.default.ready("Ready to compile...");
|
71
80
|
});
|
72
81
|
socket_1.default.on("open_window", (data) => {
|
73
82
|
console_1.default.debug("Opening window: ", data);
|
74
|
-
|
83
|
+
// if (config.editor.agent === "os") {
|
84
|
+
// } else {
|
85
|
+
// dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data);
|
86
|
+
// }
|
87
|
+
osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data);
|
75
88
|
socket_1.default.ready("Ready to compile...");
|
76
89
|
});
|
77
90
|
socket_1.default.on("reset", (exercise) => {
|
@@ -87,11 +100,6 @@ class StartCommand extends SessionCommand_1.default {
|
|
87
100
|
setTimeout(() => socket_1.default.ready("Ready to compile..."), 2000);
|
88
101
|
}
|
89
102
|
});
|
90
|
-
// socket.on("preview", (data) => {
|
91
|
-
// Console.debug("Preview triggered, removing the 'preview' action ")
|
92
|
-
// socket.removeAllowed("preview")
|
93
|
-
// socket.log('ready',['Ready to compile...'])
|
94
|
-
// })
|
95
103
|
socket_1.default.on("build", async (data) => {
|
96
104
|
var _a;
|
97
105
|
const exercise = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.getExercise(data.exerciseSlug);
|
@@ -143,14 +151,12 @@ class StartCommand extends SessionCommand_1.default {
|
|
143
151
|
return true;
|
144
152
|
});
|
145
153
|
const terminate = () => {
|
146
|
-
|
154
|
+
var _a;
|
155
|
+
console_1.default.error("Terminating Learnpack...");
|
147
156
|
telemetry_1.default.submit();
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
dispatcher.enqueue(dispatcher.events.END);
|
152
|
-
process.exit();
|
153
|
-
});
|
157
|
+
(_a = this.configManager) === null || _a === void 0 ? void 0 : _a.noCurrentExercise();
|
158
|
+
dispatcher.enqueue(dispatcher.events.END);
|
159
|
+
process.exit();
|
154
160
|
};
|
155
161
|
server.on("close", terminate);
|
156
162
|
process.on("SIGINT", terminate);
|
@@ -182,8 +188,8 @@ StartCommand.flags = Object.assign(Object.assign({}, SessionCommand_1.default.fl
|
|
182
188
|
default: false,
|
183
189
|
}), editor: command_1.flags.string({
|
184
190
|
char: "e",
|
185
|
-
description: "[
|
186
|
-
options: ["
|
191
|
+
description: "[preview, extension]",
|
192
|
+
options: ["extension", "preview"],
|
187
193
|
}), version: command_1.flags.string({
|
188
194
|
char: "v",
|
189
195
|
description: "E.g: 1.0.1",
|
@@ -15,6 +15,12 @@ const fetch = require("node-fetch");
|
|
15
15
|
// eslint-disable-next-line
|
16
16
|
const chalk = require("chalk");
|
17
17
|
/* exercise folder name standard */
|
18
|
+
/**
|
19
|
+
* Retrieves the configuration path for the learnpack package.
|
20
|
+
*
|
21
|
+
* @returns An object containing the configuration file path and the base directory.
|
22
|
+
* @throws NotFoundError if the learn.json file is not found in the current folder.
|
23
|
+
*/
|
18
24
|
const getConfigPath = () => {
|
19
25
|
const possibleFileNames = ["learn.json", ".learn/learn.json"];
|
20
26
|
const config = possibleFileNames.find(file => fs.existsSync(file)) || null;
|
@@ -38,7 +44,7 @@ const getGitpodAddress = () => {
|
|
38
44
|
return "http://localhost";
|
39
45
|
};
|
40
46
|
const getCodespacesNamespace = () => {
|
41
|
-
// https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
|
47
|
+
// Example: https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
|
42
48
|
const codespace_name = shell
|
43
49
|
.exec("echo $CODESPACE_NAME", { silent: true })
|
44
50
|
.stdout.replace(/(\r\n|\n|\r)/gm, "");
|
@@ -52,12 +58,12 @@ const getCodespacesNamespace = () => {
|
|
52
58
|
return codespace_name;
|
53
59
|
};
|
54
60
|
exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
55
|
-
var _a, _b, _c, _d
|
61
|
+
var _a, _b, _c, _d;
|
56
62
|
const confPath = getConfigPath();
|
57
63
|
console_1.default.debug("This is the config path: ", confPath);
|
58
64
|
let configObj = {};
|
59
65
|
if (confPath) {
|
60
|
-
const
|
66
|
+
const learnJsonContent = fs.readFileSync(confPath.config);
|
61
67
|
let hiddenBcContent = {};
|
62
68
|
if (fs.existsSync(confPath.base + "/config.json")) {
|
63
69
|
hiddenBcContent = fs.readFileSync(confPath.base + "/config.json");
|
@@ -65,7 +71,7 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
65
71
|
if (!hiddenBcContent)
|
66
72
|
throw new Error(`Invalid ${confPath.base}/config.json syntax: Unable to parse.`);
|
67
73
|
}
|
68
|
-
const jsonConfig = JSON.parse(`${
|
74
|
+
const jsonConfig = JSON.parse(`${learnJsonContent}`);
|
69
75
|
if (!jsonConfig)
|
70
76
|
throw new Error(`Invalid ${confPath.config} syntax: Unable to parse.`);
|
71
77
|
let session;
|
@@ -83,7 +89,7 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
83
89
|
});
|
84
90
|
}
|
85
91
|
else {
|
86
|
-
throw errors_1.ValidationError("No learn.json file has been found, make sure you are in the
|
92
|
+
throw errors_1.ValidationError("No learn.json file has been found, make sure you are in the correct directory. To start a new LearnPack package run: $ learnpack init my-course-name");
|
87
93
|
}
|
88
94
|
configObj = deepMerge(defaults_1.default || {}, configObj, {
|
89
95
|
config: {
|
@@ -91,9 +97,9 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
91
97
|
configPath: confPath.config,
|
92
98
|
},
|
93
99
|
});
|
94
|
-
if (configObj.config)
|
95
|
-
|
96
|
-
|
100
|
+
if (!configObj.config)
|
101
|
+
throw errors_1.InternalError("Config object not found");
|
102
|
+
configObj.config.outputPath = confPath.base + "/dist";
|
97
103
|
console_1.default.debug("This is your configuration object: ", Object.assign(Object.assign({}, configObj), { exercises: configObj.exercises ?
|
98
104
|
configObj.exercises.map(e => e.slug) :
|
99
105
|
[] }));
|
@@ -103,46 +109,43 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
103
109
|
console_1.default.debug("This is the agent, and this should be null an the beginning: ", (_c = (_b = configObj.config) === null || _b === void 0 ? void 0 : _b.editor) === null || _c === void 0 ? void 0 : _c.agent);
|
104
110
|
if (shell.which("gp") && configObj && configObj.config) {
|
105
111
|
console_1.default.debug("Gitpod detected");
|
106
|
-
configObj.config.editor.agent = "vscode";
|
107
112
|
configObj.address = getGitpodAddress();
|
108
113
|
configObj.config.publicUrl = `https://${configObj.config.port}-${(_d = configObj.address) === null || _d === void 0 ? void 0 : _d.slice(8)}`;
|
109
114
|
}
|
110
115
|
else if (configObj.config && Boolean(codespaces_workspace)) {
|
111
116
|
console_1.default.debug("Codespaces detected: ", codespaces_workspace);
|
112
|
-
configObj.config.editor.agent = "vscode";
|
113
117
|
configObj.address = `https://${codespaces_workspace}.github.dev`;
|
114
118
|
configObj.config.publicUrl = `https://${codespaces_workspace}-${configObj.config.port}.app.github.dev`;
|
115
119
|
// TODO: Why is needed to have an agent?
|
116
|
-
// } else if (configObj.config && !configObj.config.editor.agent) {
|
117
120
|
}
|
118
|
-
else
|
119
|
-
console_1.default.debug("
|
120
|
-
configObj.config.editor.agent = "localhost";
|
121
|
+
else {
|
122
|
+
console_1.default.debug("Neither Gitpod nor Codespaces detected, using localhost.");
|
121
123
|
configObj.address = `http://localhost:${configObj.config.port}`;
|
122
124
|
configObj.config.publicUrl = `http://localhost:${configObj.config.port}`;
|
123
125
|
}
|
124
|
-
|
126
|
+
if (!configObj.config.editor.agent) {
|
127
|
+
configObj.config.editor.agent = shell.which("code") ? "vscode" : "os";
|
128
|
+
}
|
129
|
+
// TODO: We need to set the correct EventListener if the agent is not vscode
|
125
130
|
if (configObj.config && !configObj.config.publicUrl)
|
126
131
|
configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`;
|
127
|
-
|
128
|
-
if (configObj.config && !mode) {
|
132
|
+
if (configObj.config && !configObj.config.editor.mode && mode) {
|
129
133
|
configObj.config.editor.mode = mode;
|
130
134
|
}
|
131
|
-
if (
|
135
|
+
if (!configObj.config.editor.mode) {
|
132
136
|
configObj.config.editor.mode =
|
133
|
-
configObj.config.editor.agent === "
|
134
|
-
|
137
|
+
configObj.config.editor.agent === "vscode" ? "extension" : "preview";
|
138
|
+
}
|
139
|
+
if (version)
|
135
140
|
configObj.config.editor.version = version;
|
136
|
-
else if (configObj.config
|
141
|
+
else if (configObj.config.editor.version === null) {
|
137
142
|
console_1.default.debug("Config version not found, downloading default.");
|
138
143
|
const resp = await fetch("https://raw.githubusercontent.com/learnpack/ide/master/package.json");
|
139
144
|
const packageJSON = await resp.json();
|
140
|
-
configObj.config.editor.version = packageJSON.version || "3.
|
141
|
-
}
|
142
|
-
if (configObj.config) {
|
143
|
-
configObj.config.dirPath = "./" + confPath.base;
|
144
|
-
configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./";
|
145
|
+
configObj.config.editor.version = packageJSON.version || "3.1.22";
|
145
146
|
}
|
147
|
+
configObj.config.dirPath = "./" + confPath.base;
|
148
|
+
configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./";
|
146
149
|
return {
|
147
150
|
validLanguages: {},
|
148
151
|
get: () => configObj,
|
@@ -168,6 +171,7 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
168
171
|
return true;
|
169
172
|
}
|
170
173
|
console_1.default.info(`Language engine for ${language} not found, installing...`);
|
174
|
+
// Install the compiler in their new versions
|
171
175
|
result = shell.exec(`learnpack plugins:install learnpack-${language}`, {
|
172
176
|
silent: true,
|
173
177
|
});
|
@@ -32,7 +32,7 @@ async function default_1(configObj, configManager, isTestingEnvironment = false)
|
|
32
32
|
console_1.default.success(`Exercises are running 😃 Open your browser to start practicing!`);
|
33
33
|
console_1.default.success(`\n Open the exercise on this link:`);
|
34
34
|
console_1.default.log(` ${config === null || config === void 0 ? void 0 : config.publicUrl}`);
|
35
|
-
if ((config === null || config === void 0 ? void 0 : config.editor.mode) === "
|
35
|
+
if ((config === null || config === void 0 ? void 0 : config.editor.mode) === "preview")
|
36
36
|
cli_ux_1.default.open(`${config.publicUrl}`);
|
37
37
|
}
|
38
38
|
});
|
@@ -10,6 +10,7 @@ const fileQueue_1 = require("../../utils/fileQueue");
|
|
10
10
|
const exercise_1 = require("../config/exercise");
|
11
11
|
const session_1 = require("../../managers/session");
|
12
12
|
const telemetry_1 = require("../telemetry");
|
13
|
+
const osOperations_1 = require("../../utils/osOperations");
|
13
14
|
const withHandler = (func) => (req, res) => {
|
14
15
|
try {
|
15
16
|
func(req, res);
|
@@ -112,7 +113,7 @@ async function default_1(app, configObject, configManager) {
|
|
112
113
|
}
|
113
114
|
}));
|
114
115
|
app.get("/exercise/:slug", withHandler((req, res) => {
|
115
|
-
var _a, _b, _c, _d;
|
116
|
+
var _a, _b, _c, _d, _e;
|
116
117
|
// no need to re-start exercise if it's already started
|
117
118
|
if (configObject.currentExercise &&
|
118
119
|
req.params.slug === configObject.currentExercise) {
|
@@ -127,7 +128,12 @@ async function default_1(app, configObject, configManager) {
|
|
127
128
|
if (exercise.position) {
|
128
129
|
telemetry_1.default.registerStepEvent(exercise.position, "open_step", {});
|
129
130
|
}
|
130
|
-
|
131
|
+
if (((_a = configObject.config) === null || _a === void 0 ? void 0 : _a.editor.agent) === "os") {
|
132
|
+
osOperations_1.eventManager.enqueue(dispatcher.events.START_EXERCISE, exercise);
|
133
|
+
}
|
134
|
+
else {
|
135
|
+
dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug);
|
136
|
+
}
|
131
137
|
const entries = new Set(Object.keys(config === null || config === void 0 ? void 0 : config.entries).map(lang => config === null || config === void 0 ? void 0 : config.entries[lang]));
|
132
138
|
// if we are in incremental grading, the entry file can by dinamically detected
|
133
139
|
// based on the changes the student is making during the exercise
|
@@ -159,23 +165,23 @@ async function default_1(app, configObject, configManager) {
|
|
159
165
|
exercise.entry = detected === null || detected === void 0 ? void 0 : detected.entry;
|
160
166
|
console_1.default.debug(`Exercise detected entry: ${detected === null || detected === void 0 ? void 0 : detected.entry} and language ${exercise.language}`);
|
161
167
|
// exercises.graded and exercises.disableGrading deprecated.
|
162
|
-
if (!exercise.graded || (config === null || config === void 0 ? void 0 : config.disableGrading) || ((
|
168
|
+
if (!exercise.graded || (config === null || config === void 0 ? void 0 : config.disableGrading) || ((_b = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _b === void 0 ? void 0 : _b.includes("test"))) {
|
163
169
|
socket_1.default.removeAllowed("test");
|
164
170
|
}
|
165
171
|
else {
|
166
172
|
socket_1.default.addAllowed("test");
|
167
173
|
}
|
168
|
-
if (!exercise.entry || ((
|
174
|
+
if (!exercise.entry || ((_c = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _c === void 0 ? void 0 : _c.includes("build"))) {
|
169
175
|
socket_1.default.removeAllowed("build");
|
170
176
|
}
|
171
177
|
else {
|
172
178
|
socket_1.default.addAllowed("build");
|
173
179
|
}
|
174
180
|
if (exercise.files.filter((f) => !f.name.toLowerCase().includes("readme.") &&
|
175
|
-
!f.name.toLowerCase().includes("test.")).length === 0 || ((
|
181
|
+
!f.name.toLowerCase().includes("test.")).length === 0 || ((_d = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _d === void 0 ? void 0 : _d.includes("reset"))) {
|
176
182
|
socket_1.default.removeAllowed("reset");
|
177
183
|
}
|
178
|
-
else if (!((
|
184
|
+
else if (!((_e = config === null || config === void 0 ? void 0 : config.disabledActions) === null || _e === void 0 ? void 0 : _e.includes("reset"))) {
|
179
185
|
socket_1.default.addAllowed("reset");
|
180
186
|
}
|
181
187
|
socket_1.default.log("ready");
|
@@ -1,6 +1,7 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
const api_1 = require("../utils/api");
|
4
|
+
const console_1 = require("../utils/console");
|
4
5
|
const fs = require("fs");
|
5
6
|
function createUUID() {
|
6
7
|
return (Math.random().toString(36).slice(2, 10) +
|
@@ -14,7 +15,7 @@ const TelemetryManager = {
|
|
14
15
|
urls: {},
|
15
16
|
configPath: "",
|
16
17
|
salute: message => {
|
17
|
-
|
18
|
+
console_1.default.info(message);
|
18
19
|
},
|
19
20
|
start: function (agent, steps, path, tutorialSlug) {
|
20
21
|
this.configPath = path;
|
@@ -44,7 +45,7 @@ const TelemetryManager = {
|
|
44
45
|
this.submit();
|
45
46
|
})
|
46
47
|
.catch(error => {
|
47
|
-
|
48
|
+
console_1.default.debug(error);
|
48
49
|
});
|
49
50
|
}
|
50
51
|
},
|
@@ -167,6 +168,7 @@ const TelemetryManager = {
|
|
167
168
|
});
|
168
169
|
},
|
169
170
|
submit: async function () {
|
171
|
+
console_1.default.debug("Submitting telemetry...");
|
170
172
|
if (!this.current)
|
171
173
|
return Promise.resolve();
|
172
174
|
const url = this.urls.batch;
|
package/lib/models/config.d.ts
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
import { IExercise } from "./exercise-obj";
|
2
2
|
import { TTelemetryUrls } from "../managers/telemetry";
|
3
3
|
export declare type TGrading = "isolated" | "incremental" | "no-grading";
|
4
|
-
export declare type TMode = "preview" | "
|
4
|
+
export declare type TMode = "preview" | "extension";
|
5
5
|
export declare type TConfigAction = "test" | "build" | "tutorial" | "reset" | "generate";
|
6
6
|
export declare type TConfigObjAttributes = "config" | "exercises" | "grading";
|
7
7
|
export declare type TCompiler = "webpack" | "vanillajs" | "vue" | "react" | "css" | "html";
|
8
8
|
export interface IConfigPath {
|
9
9
|
base: string;
|
10
10
|
}
|
11
|
+
declare type TAgent = "vscode" | "os";
|
11
12
|
export interface IEditor {
|
12
13
|
mode?: TMode;
|
13
14
|
version: string;
|
14
|
-
agent?:
|
15
|
+
agent?: TAgent;
|
15
16
|
}
|
16
17
|
export interface TEntries {
|
17
18
|
python3?: string;
|
@@ -21,6 +22,7 @@ export interface TEntries {
|
|
21
22
|
java?: string;
|
22
23
|
}
|
23
24
|
export interface IConfig {
|
25
|
+
os: string;
|
24
26
|
port?: string;
|
25
27
|
repository?: string;
|
26
28
|
description?: string;
|
@@ -62,3 +64,4 @@ export interface IConfigObj {
|
|
62
64
|
confPath?: IConfigPath;
|
63
65
|
address?: string;
|
64
66
|
}
|
67
|
+
export {};
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import BaseCommand from
|
2
|
-
import { IConfigManager } from
|
1
|
+
import BaseCommand from "./BaseCommand";
|
2
|
+
import { IConfigManager } from "../models/config-manager";
|
3
3
|
export default class SessionCommand extends BaseCommand {
|
4
4
|
session: any;
|
5
5
|
configManager: IConfigManager | null;
|
@@ -24,8 +24,8 @@ class SessionCommand extends BaseCommand_1.default {
|
|
24
24
|
}
|
25
25
|
else {
|
26
26
|
if (_private)
|
27
|
-
throw errors_1.AuthError(
|
28
|
-
console_1.default.debug(
|
27
|
+
throw errors_1.AuthError("You need to log in, run the following command to continue: $ learnpack login");
|
28
|
+
console_1.default.debug("No active session available", _private);
|
29
29
|
}
|
30
30
|
}
|
31
31
|
catch (error) {
|
@@ -36,12 +36,8 @@ class SessionCommand extends BaseCommand_1.default {
|
|
36
36
|
this.configManager = await index_1.default(flags);
|
37
37
|
}
|
38
38
|
async catch(err) {
|
39
|
-
console_1.default.debug(
|
39
|
+
console_1.default.debug("COMMAND CATCH", err);
|
40
40
|
throw err;
|
41
41
|
}
|
42
42
|
}
|
43
43
|
exports.default = SessionCommand;
|
44
|
-
// SessionCommand.description = `Describe the command here
|
45
|
-
// ...
|
46
|
-
// Extra documentation goes here
|
47
|
-
// `
|
package/lib/utils/api.js
CHANGED
@@ -0,0 +1,56 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.eventManager = void 0;
|
4
|
+
const child_process_1 = require("child_process");
|
5
|
+
const os = require("os");
|
6
|
+
const path = require("path");
|
7
|
+
const cli_ux_1 = require("cli-ux");
|
8
|
+
const openFile = (filePath) => {
|
9
|
+
const fullPath = path.join(process.cwd(), filePath);
|
10
|
+
let command;
|
11
|
+
switch (os.platform()) {
|
12
|
+
case "darwin": // macOS
|
13
|
+
command = `open ${fullPath}`;
|
14
|
+
break;
|
15
|
+
case "win32": // Windows
|
16
|
+
command = `start ${fullPath}`;
|
17
|
+
break;
|
18
|
+
case "linux": // Linux
|
19
|
+
command = `xdg-open ${fullPath}`;
|
20
|
+
break;
|
21
|
+
default:
|
22
|
+
throw new Error("Unsupported OS");
|
23
|
+
}
|
24
|
+
child_process_1.exec(command, (error, stdout, stderr) => {
|
25
|
+
if (error) {
|
26
|
+
console.error(`exec error: ${error}`);
|
27
|
+
}
|
28
|
+
});
|
29
|
+
};
|
30
|
+
const eventManager = {
|
31
|
+
enqueue: (event, data) => {
|
32
|
+
if (event === "start_exercise") {
|
33
|
+
const exercise = data;
|
34
|
+
const filesToOpen = exercise.files
|
35
|
+
.filter((file) => !file.hidden)
|
36
|
+
.map((file) => {
|
37
|
+
return file.path.replace("\\", "/");
|
38
|
+
});
|
39
|
+
for (const file of filesToOpen) {
|
40
|
+
openFile(file);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
if (event === "open_files") {
|
44
|
+
const files = data;
|
45
|
+
for (const file of files) {
|
46
|
+
const correctedPath = file.replace("\\", "/");
|
47
|
+
openFile(correctedPath);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
if (event === "open_window") {
|
51
|
+
const url = data.url;
|
52
|
+
cli_ux_1.default.open(url);
|
53
|
+
}
|
54
|
+
},
|
55
|
+
};
|
56
|
+
exports.eventManager = eventManager;
|
package/oclif.manifest.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":"2.1.
|
1
|
+
{"version":"2.1.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":[]},"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":"[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":{},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]}}}
|
package/package.json
CHANGED
package/src/commands/start.ts
CHANGED
@@ -16,6 +16,7 @@ import { prioritizeHTMLFile } from "../utils/misc"
|
|
16
16
|
|
17
17
|
import { IGitpodData } from "../models/gitpod-data"
|
18
18
|
import { IExercise, IExerciseData } from "../models/exercise-obj"
|
19
|
+
import { eventManager } from "../utils/osOperations"
|
19
20
|
|
20
21
|
export default class StartCommand extends SessionCommand {
|
21
22
|
static description = "Runs a small server with all the exercise instructions"
|
@@ -37,8 +38,8 @@ export default class StartCommand extends SessionCommand {
|
|
37
38
|
}),
|
38
39
|
editor: flags.string({
|
39
40
|
char: "e",
|
40
|
-
description: "[
|
41
|
-
options: ["
|
41
|
+
description: "[preview, extension]",
|
42
|
+
options: ["extension", "preview"],
|
42
43
|
}),
|
43
44
|
version: flags.string({
|
44
45
|
char: "v",
|
@@ -110,7 +111,11 @@ export default class StartCommand extends SessionCommand {
|
|
110
111
|
this.configManager,
|
111
112
|
process.env.NODE_ENV === "test"
|
112
113
|
)
|
114
|
+
server.setMaxListeners(20)
|
113
115
|
|
116
|
+
// console.log(config, "this is the f*!shell.which("code")!shell.which("code")cking config");
|
117
|
+
|
118
|
+
// I should call a method to get the EventListener
|
114
119
|
const dispatcher = queue.dispatcher({
|
115
120
|
create: true,
|
116
121
|
path: `${config.dirPath}/vscode_queue.json`,
|
@@ -148,13 +153,22 @@ export default class StartCommand extends SessionCommand {
|
|
148
153
|
|
149
154
|
const files = prioritizeHTMLFile(data.files)
|
150
155
|
|
151
|
-
|
156
|
+
if (config.editor.agent === "os") {
|
157
|
+
eventManager.enqueue(dispatcher.events.OPEN_FILES, files)
|
158
|
+
} else {
|
159
|
+
dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
|
160
|
+
}
|
161
|
+
|
152
162
|
socket.ready("Ready to compile...")
|
153
163
|
})
|
154
164
|
|
155
165
|
socket.on("open_window", (data: IGitpodData) => {
|
156
166
|
Console.debug("Opening window: ", data)
|
157
|
-
|
167
|
+
// if (config.editor.agent === "os") {
|
168
|
+
// } else {
|
169
|
+
// dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data);
|
170
|
+
// }
|
171
|
+
eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
|
158
172
|
socket.ready("Ready to compile...")
|
159
173
|
})
|
160
174
|
|
@@ -175,11 +189,6 @@ export default class StartCommand extends SessionCommand {
|
|
175
189
|
setTimeout(() => socket.ready("Ready to compile..."), 2000)
|
176
190
|
}
|
177
191
|
})
|
178
|
-
// socket.on("preview", (data) => {
|
179
|
-
// Console.debug("Preview triggered, removing the 'preview' action ")
|
180
|
-
// socket.removeAllowed("preview")
|
181
|
-
// socket.log('ready',['Ready to compile...'])
|
182
|
-
// })
|
183
192
|
|
184
193
|
socket.on("build", async (data: IExerciseData) => {
|
185
194
|
const exercise = this.configManager?.getExercise(data.exerciseSlug)
|
@@ -256,15 +265,11 @@ export default class StartCommand extends SessionCommand {
|
|
256
265
|
})
|
257
266
|
|
258
267
|
const terminate = () => {
|
259
|
-
Console.
|
260
|
-
|
268
|
+
Console.error("Terminating Learnpack...")
|
261
269
|
TelemetryManager.submit()
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
dispatcher.enqueue(dispatcher.events.END)
|
266
|
-
process.exit()
|
267
|
-
})
|
270
|
+
this.configManager?.noCurrentExercise()
|
271
|
+
dispatcher.enqueue(dispatcher.events.END)
|
272
|
+
process.exit()
|
268
273
|
}
|
269
274
|
|
270
275
|
server.on("close", terminate)
|
@@ -1,10 +1,14 @@
|
|
1
|
+
import * as os from "os"
|
1
2
|
export default {
|
2
3
|
config: {
|
3
4
|
port: 3000,
|
5
|
+
os: (function () {
|
6
|
+
return os.platform()
|
7
|
+
})(),
|
4
8
|
editor: {
|
5
|
-
mode: null, // [
|
6
|
-
agent: null, // [vscode,
|
7
|
-
version: null,
|
9
|
+
mode: null, // [extension, preview]
|
10
|
+
agent: null, // [vscode, null]
|
11
|
+
version: null, // By default downloads the latest version
|
8
12
|
},
|
9
13
|
dirPath: "./.learn",
|
10
14
|
configPath: "./learn.json",
|
@@ -29,6 +29,12 @@ const chalk = require("chalk");
|
|
29
29
|
|
30
30
|
/* exercise folder name standard */
|
31
31
|
|
32
|
+
/**
|
33
|
+
* Retrieves the configuration path for the learnpack package.
|
34
|
+
*
|
35
|
+
* @returns An object containing the configuration file path and the base directory.
|
36
|
+
* @throws NotFoundError if the learn.json file is not found in the current folder.
|
37
|
+
*/
|
32
38
|
const getConfigPath = () => {
|
33
39
|
const possibleFileNames = ["learn.json", ".learn/learn.json"]
|
34
40
|
const config = possibleFileNames.find(file => fs.existsSync(file)) || null
|
@@ -58,7 +64,7 @@ const getGitpodAddress = () => {
|
|
58
64
|
}
|
59
65
|
|
60
66
|
const getCodespacesNamespace = () => {
|
61
|
-
// https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
|
67
|
+
// Example: https://orange-rotary-phone-wxpg49q5gcg4rp-3000.app.github.dev
|
62
68
|
|
63
69
|
const codespace_name = shell
|
64
70
|
.exec("echo $CODESPACE_NAME", { silent: true })
|
@@ -84,13 +90,12 @@ export default async ({
|
|
84
90
|
version,
|
85
91
|
}: IConfigManagerAttributes): Promise<IConfigManager> => {
|
86
92
|
const confPath = getConfigPath()
|
87
|
-
|
88
93
|
Console.debug("This is the config path: ", confPath)
|
89
94
|
|
90
95
|
let configObj: IConfigObj = {}
|
91
96
|
|
92
97
|
if (confPath) {
|
93
|
-
const
|
98
|
+
const learnJsonContent = fs.readFileSync(confPath.config)
|
94
99
|
let hiddenBcContent = {}
|
95
100
|
if (fs.existsSync(confPath.base + "/config.json")) {
|
96
101
|
hiddenBcContent = fs.readFileSync(confPath.base + "/config.json")
|
@@ -101,7 +106,8 @@ export default async ({
|
|
101
106
|
)
|
102
107
|
}
|
103
108
|
|
104
|
-
const jsonConfig = JSON.parse(`${
|
109
|
+
const jsonConfig = JSON.parse(`${learnJsonContent}`)
|
110
|
+
|
105
111
|
if (!jsonConfig)
|
106
112
|
throw new Error(`Invalid ${confPath.config} syntax: Unable to parse.`)
|
107
113
|
|
@@ -121,7 +127,7 @@ export default async ({
|
|
121
127
|
})
|
122
128
|
} else {
|
123
129
|
throw ValidationError(
|
124
|
-
"No learn.json file has been found, make sure you are in the
|
130
|
+
"No learn.json file has been found, make sure you are in the correct directory. To start a new LearnPack package run: $ learnpack init my-course-name"
|
125
131
|
)
|
126
132
|
}
|
127
133
|
|
@@ -131,10 +137,10 @@ export default async ({
|
|
131
137
|
configPath: confPath.config,
|
132
138
|
},
|
133
139
|
})
|
140
|
+
if (!configObj.config)
|
141
|
+
throw InternalError("Config object not found")
|
134
142
|
|
135
|
-
|
136
|
-
configObj.config.outputPath = confPath.base + "/dist"
|
137
|
-
}
|
143
|
+
configObj.config.outputPath = confPath.base + "/dist"
|
138
144
|
|
139
145
|
Console.debug("This is your configuration object: ", {
|
140
146
|
...configObj,
|
@@ -145,7 +151,6 @@ export default async ({
|
|
145
151
|
|
146
152
|
// auto detect agent (if possible)
|
147
153
|
const codespaces_workspace = getCodespacesNamespace()
|
148
|
-
|
149
154
|
Console.debug("This is the codespace namespace: ", codespaces_workspace)
|
150
155
|
|
151
156
|
Console.debug(
|
@@ -155,55 +160,52 @@ export default async ({
|
|
155
160
|
|
156
161
|
if (shell.which("gp") && configObj && configObj.config) {
|
157
162
|
Console.debug("Gitpod detected")
|
158
|
-
configObj.config.editor.agent = "vscode"
|
159
163
|
configObj.address = getGitpodAddress()
|
160
164
|
configObj.config.publicUrl = `https://${
|
161
165
|
configObj.config.port
|
162
166
|
}-${configObj.address?.slice(8)}`
|
163
167
|
} else if (configObj.config && Boolean(codespaces_workspace)) {
|
164
168
|
Console.debug("Codespaces detected: ", codespaces_workspace)
|
165
|
-
configObj.config.editor.agent = "vscode"
|
166
|
-
|
167
169
|
configObj.address = `https://${codespaces_workspace}.github.dev`
|
168
170
|
configObj.config.publicUrl = `https://${codespaces_workspace}-${configObj.config.port}.app.github.dev`
|
169
171
|
// TODO: Why is needed to have an agent?
|
170
|
-
|
171
|
-
|
172
|
-
Console.debug("Localhost detected")
|
173
|
-
configObj.config.editor.agent = "localhost"
|
172
|
+
} else {
|
173
|
+
Console.debug("Neither Gitpod nor Codespaces detected, using localhost.")
|
174
174
|
configObj.address = `http://localhost:${configObj.config.port}`
|
175
175
|
configObj.config.publicUrl = `http://localhost:${configObj.config.port}`
|
176
176
|
}
|
177
177
|
|
178
|
-
|
178
|
+
if (!configObj.config.editor.agent) {
|
179
|
+
configObj.config.editor.agent = shell.which("code") ? "vscode" : "os"
|
180
|
+
}
|
181
|
+
|
182
|
+
// TODO: We need to set the correct EventListener if the agent is not vscode
|
179
183
|
|
180
184
|
if (configObj.config && !configObj.config.publicUrl)
|
181
185
|
configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`
|
182
186
|
|
183
|
-
|
184
|
-
if (configObj.config && !mode) {
|
187
|
+
if (configObj.config && !configObj.config.editor.mode && mode) {
|
185
188
|
configObj.config.editor.mode = mode as TMode
|
186
189
|
}
|
187
190
|
|
188
|
-
if (
|
191
|
+
if (!configObj.config.editor.mode) {
|
189
192
|
configObj.config.editor.mode =
|
190
|
-
configObj.config.editor.agent === "
|
193
|
+
configObj.config.editor.agent === "vscode" ? "extension" : "preview"
|
194
|
+
}
|
191
195
|
|
192
|
-
if (version
|
196
|
+
if (version)
|
193
197
|
configObj.config.editor.version = version
|
194
|
-
else if (configObj.config
|
198
|
+
else if (configObj.config.editor.version === null) {
|
195
199
|
Console.debug("Config version not found, downloading default.")
|
196
200
|
const resp = await fetch(
|
197
201
|
"https://raw.githubusercontent.com/learnpack/ide/master/package.json"
|
198
202
|
)
|
199
203
|
const packageJSON = await resp.json()
|
200
|
-
configObj.config.editor.version = packageJSON.version || "3.
|
204
|
+
configObj.config.editor.version = packageJSON.version || "3.1.22"
|
201
205
|
}
|
202
206
|
|
203
|
-
|
204
|
-
|
205
|
-
configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./"
|
206
|
-
}
|
207
|
+
configObj.config.dirPath = "./" + confPath.base
|
208
|
+
configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./"
|
207
209
|
|
208
210
|
return {
|
209
211
|
validLanguages: {},
|
@@ -237,6 +239,7 @@ return true
|
|
237
239
|
}
|
238
240
|
|
239
241
|
Console.info(`Language engine for ${language} not found, installing...`)
|
242
|
+
// Install the compiler in their new versions
|
240
243
|
result = shell.exec(`learnpack plugins:install learnpack-${language}`, {
|
241
244
|
silent: true,
|
242
245
|
})
|
@@ -46,7 +46,8 @@ server = require("http").Server(app)
|
|
46
46
|
)
|
47
47
|
Console.success(`\n Open the exercise on this link:`)
|
48
48
|
Console.log(` ${config?.publicUrl}`)
|
49
|
-
|
49
|
+
|
50
|
+
if (config?.editor.mode === "preview")
|
50
51
|
cli.open(`${config.publicUrl}`)
|
51
52
|
}
|
52
53
|
})
|
@@ -67,7 +68,6 @@ cli.open(`${config.publicUrl}`)
|
|
67
68
|
server.terminate = (callback: void) => {
|
68
69
|
for (const socket of sockets) {
|
69
70
|
socket.destroy()
|
70
|
-
|
71
71
|
sockets.delete(socket)
|
72
72
|
}
|
73
73
|
|
@@ -12,6 +12,7 @@ import { IConfigManager } from "../../models/config-manager"
|
|
12
12
|
import { IExercise } from "../../models/exercise-obj"
|
13
13
|
import SessionManager from "../../managers/session"
|
14
14
|
import TelemetryManager from "../telemetry"
|
15
|
+
import { eventManager } from "../../utils/osOperations"
|
15
16
|
|
16
17
|
const withHandler =
|
17
18
|
(func: (req: express.Request, res: express.Response) => void) =>
|
@@ -194,7 +195,11 @@ export default async function (
|
|
194
195
|
TelemetryManager.registerStepEvent(exercise.position, "open_step", {})
|
195
196
|
}
|
196
197
|
|
197
|
-
|
198
|
+
if (configObject.config?.editor.agent === "os") {
|
199
|
+
eventManager.enqueue(dispatcher.events.START_EXERCISE, exercise)
|
200
|
+
} else {
|
201
|
+
dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug)
|
202
|
+
}
|
198
203
|
|
199
204
|
type TEntry = "python3" | "html" | "node" | "react" | "java";
|
200
205
|
|
@@ -279,7 +284,6 @@ export default async function (
|
|
279
284
|
}
|
280
285
|
|
281
286
|
socket.log("ready")
|
282
|
-
|
283
287
|
res.json(exercise)
|
284
288
|
})
|
285
289
|
)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import { IFile } from "../models/file"
|
2
2
|
import API from "../utils/api"
|
3
|
+
import Console from "../utils/console"
|
4
|
+
|
3
5
|
const fs = require("fs")
|
4
6
|
|
5
7
|
function createUUID(): string {
|
@@ -112,7 +114,7 @@ const TelemetryManager: ITelemetryManager = {
|
|
112
114
|
urls: {},
|
113
115
|
configPath: "",
|
114
116
|
salute: message => {
|
115
|
-
|
117
|
+
Console.info(message)
|
116
118
|
},
|
117
119
|
|
118
120
|
start: function (agent, steps, path, tutorialSlug) {
|
@@ -143,7 +145,7 @@ const TelemetryManager: ITelemetryManager = {
|
|
143
145
|
this.submit()
|
144
146
|
})
|
145
147
|
.catch(error => {
|
146
|
-
|
148
|
+
Console.debug(error)
|
147
149
|
})
|
148
150
|
}
|
149
151
|
},
|
@@ -293,6 +295,8 @@ const TelemetryManager: ITelemetryManager = {
|
|
293
295
|
})
|
294
296
|
},
|
295
297
|
submit: async function () {
|
298
|
+
Console.debug("Submitting telemetry...")
|
299
|
+
|
296
300
|
if (!this.current)
|
297
301
|
return Promise.resolve()
|
298
302
|
const url = this.urls.batch
|
package/src/models/config.ts
CHANGED
@@ -3,7 +3,7 @@ import { TTelemetryUrls } from "../managers/telemetry"
|
|
3
3
|
|
4
4
|
export type TGrading = "isolated" | "incremental" | "no-grading";
|
5
5
|
|
6
|
-
export type TMode = "preview" | "
|
6
|
+
export type TMode = "preview" | "extension";
|
7
7
|
|
8
8
|
export type TConfigAction =
|
9
9
|
| "test"
|
@@ -26,10 +26,12 @@ export interface IConfigPath {
|
|
26
26
|
base: string;
|
27
27
|
}
|
28
28
|
|
29
|
+
type TAgent = "vscode" | "os";
|
30
|
+
|
29
31
|
export interface IEditor {
|
30
32
|
mode?: TMode;
|
31
33
|
version: string;
|
32
|
-
agent?:
|
34
|
+
agent?: TAgent;
|
33
35
|
}
|
34
36
|
|
35
37
|
export interface TEntries {
|
@@ -41,6 +43,7 @@ export interface TEntries {
|
|
41
43
|
}
|
42
44
|
|
43
45
|
export interface IConfig {
|
46
|
+
os: string;
|
44
47
|
port?: string;
|
45
48
|
repository?: string;
|
46
49
|
description?: string;
|
@@ -1,10 +1,10 @@
|
|
1
1
|
// import { flags } from "@oclif/command";
|
2
|
-
import BaseCommand from
|
3
|
-
import Console from
|
4
|
-
import SessionManager from
|
5
|
-
import configManager from
|
6
|
-
import {AuthError} from
|
7
|
-
import {IConfigManager} from
|
2
|
+
import BaseCommand from "./BaseCommand"
|
3
|
+
import Console from "./console"
|
4
|
+
import SessionManager from "../managers/session"
|
5
|
+
import configManager from "../managers/config/index"
|
6
|
+
import { AuthError } from "./errors"
|
7
|
+
import { IConfigManager } from "../models/config-manager"
|
8
8
|
|
9
9
|
export default class SessionCommand extends BaseCommand {
|
10
10
|
session: any = null
|
@@ -23,9 +23,9 @@ export default class SessionCommand extends BaseCommand {
|
|
23
23
|
} else {
|
24
24
|
if (_private)
|
25
25
|
throw AuthError(
|
26
|
-
|
26
|
+
"You need to log in, run the following command to continue: $ learnpack login"
|
27
27
|
)
|
28
|
-
Console.debug(
|
28
|
+
Console.debug("No active session available", _private)
|
29
29
|
}
|
30
30
|
} catch (error) {
|
31
31
|
Console.error((error as TypeError).message)
|
@@ -37,12 +37,7 @@ export default class SessionCommand extends BaseCommand {
|
|
37
37
|
}
|
38
38
|
|
39
39
|
async catch(err: any) {
|
40
|
-
Console.debug(
|
40
|
+
Console.debug("COMMAND CATCH", err)
|
41
41
|
throw err
|
42
42
|
}
|
43
43
|
}
|
44
|
-
|
45
|
-
// SessionCommand.description = `Describe the command here
|
46
|
-
// ...
|
47
|
-
// Extra documentation goes here
|
48
|
-
// `
|
package/src/utils/api.ts
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
import { exec } from "child_process"
|
2
|
+
import * as os from "os"
|
3
|
+
import * as path from "path"
|
4
|
+
import cli from "cli-ux"
|
5
|
+
|
6
|
+
const openFile = (filePath: string): void => {
|
7
|
+
const fullPath = path.join(process.cwd(), filePath)
|
8
|
+
let command: string
|
9
|
+
|
10
|
+
switch (os.platform()) {
|
11
|
+
case "darwin": // macOS
|
12
|
+
command = `open ${fullPath}`
|
13
|
+
break
|
14
|
+
case "win32": // Windows
|
15
|
+
command = `start ${fullPath}`
|
16
|
+
break
|
17
|
+
case "linux": // Linux
|
18
|
+
command = `xdg-open ${fullPath}`
|
19
|
+
break
|
20
|
+
default:
|
21
|
+
throw new Error("Unsupported OS")
|
22
|
+
}
|
23
|
+
|
24
|
+
exec(command, (error, stdout, stderr) => {
|
25
|
+
if (error) {
|
26
|
+
console.error(`exec error: ${error}`)
|
27
|
+
|
28
|
+
}
|
29
|
+
})
|
30
|
+
}
|
31
|
+
|
32
|
+
const eventManager = {
|
33
|
+
enqueue: (event: string, data: any) => {
|
34
|
+
if (event === "start_exercise") {
|
35
|
+
const exercise = data
|
36
|
+
const filesToOpen = exercise.files
|
37
|
+
.filter((file: any) => !file.hidden)
|
38
|
+
.map((file: any) => {
|
39
|
+
return file.path.replace("\\", "/")
|
40
|
+
})
|
41
|
+
|
42
|
+
for (const file of filesToOpen) {
|
43
|
+
openFile(file)
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
if (event === "open_files") {
|
48
|
+
const files = data
|
49
|
+
|
50
|
+
for (const file of files) {
|
51
|
+
const correctedPath = file.replace("\\", "/")
|
52
|
+
openFile(correctedPath)
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
if (event === "open_window") {
|
57
|
+
const url = data.url
|
58
|
+
cli.open(url)
|
59
|
+
}
|
60
|
+
},
|
61
|
+
}
|
62
|
+
|
63
|
+
export { eventManager }
|