@learnpack/learnpack 2.1.37 → 2.1.39
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 +25 -16
- package/lib/managers/config/defaults.d.ts +2 -0
- package/lib/managers/config/defaults.js +4 -0
- package/lib/managers/config/index.js +32 -27
- 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 +5 -0
- package/lib/utils/osOperations.js +72 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -2
- package/src/commands/start.ts +29 -17
- package/src/managers/config/defaults.ts +7 -3
- package/src/managers/config/index.ts +32 -29
- 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 +79 -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.39 linux-x64 node-v20.11.1
|
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.39/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.39/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.39/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.39/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.39/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.39/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.39/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.39/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.39/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
|
@@ -22,6 +23,7 @@ class StartCommand extends SessionCommand_1.default {
|
|
22
23
|
// get configuration object
|
23
24
|
const configObject = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.get();
|
24
25
|
const config = configObject === null || configObject === void 0 ? void 0 : configObject.config;
|
26
|
+
const hasXDG = await osOperations_1.eventManager.checkXDGInstalled();
|
25
27
|
if (configObject) {
|
26
28
|
const { config } = configObject;
|
27
29
|
// build exerises
|
@@ -38,6 +40,9 @@ class StartCommand extends SessionCommand_1.default {
|
|
38
40
|
// listen to socket commands
|
39
41
|
if (config && this.configManager) {
|
40
42
|
const server = await server_1.default(configObject, this.configManager, process.env.NODE_ENV === "test");
|
43
|
+
server.setMaxListeners(20);
|
44
|
+
// console.log(config, "this is the f*!shell.which("code")!shell.which("code")cking config");
|
45
|
+
// I should call a method to get the EventListener
|
41
46
|
const dispatcher = fileQueue_1.default.dispatcher({
|
42
47
|
create: true,
|
43
48
|
path: `${config.dirPath}/vscode_queue.json`,
|
@@ -66,12 +71,23 @@ class StartCommand extends SessionCommand_1.default {
|
|
66
71
|
socket_1.default.on("open", (data) => {
|
67
72
|
console_1.default.debug("Opening these files: ", data);
|
68
73
|
const files = misc_1.prioritizeHTMLFile(data.files);
|
69
|
-
|
74
|
+
if (config.editor.agent === "os") {
|
75
|
+
osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_FILES, files);
|
76
|
+
}
|
77
|
+
else {
|
78
|
+
dispatcher.enqueue(dispatcher.events.OPEN_FILES, files);
|
79
|
+
}
|
70
80
|
socket_1.default.ready("Ready to compile...");
|
71
81
|
});
|
72
82
|
socket_1.default.on("open_window", (data) => {
|
73
83
|
console_1.default.debug("Opening window: ", data);
|
74
|
-
|
84
|
+
if (config.editor.agent === "os" &&
|
85
|
+
((config.os === "linux" && hasXDG) || config.os !== "linux")) {
|
86
|
+
osOperations_1.eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data);
|
87
|
+
}
|
88
|
+
else {
|
89
|
+
dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data);
|
90
|
+
}
|
75
91
|
socket_1.default.ready("Ready to compile...");
|
76
92
|
});
|
77
93
|
socket_1.default.on("reset", (exercise) => {
|
@@ -87,11 +103,6 @@ class StartCommand extends SessionCommand_1.default {
|
|
87
103
|
setTimeout(() => socket_1.default.ready("Ready to compile..."), 2000);
|
88
104
|
}
|
89
105
|
});
|
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
106
|
socket_1.default.on("build", async (data) => {
|
96
107
|
var _a;
|
97
108
|
const exercise = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.getExercise(data.exerciseSlug);
|
@@ -143,14 +154,12 @@ class StartCommand extends SessionCommand_1.default {
|
|
143
154
|
return true;
|
144
155
|
});
|
145
156
|
const terminate = () => {
|
146
|
-
|
157
|
+
var _a;
|
158
|
+
console_1.default.error("Terminating Learnpack...");
|
147
159
|
telemetry_1.default.submit();
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
dispatcher.enqueue(dispatcher.events.END);
|
152
|
-
process.exit();
|
153
|
-
});
|
160
|
+
(_a = this.configManager) === null || _a === void 0 ? void 0 : _a.noCurrentExercise();
|
161
|
+
dispatcher.enqueue(dispatcher.events.END);
|
162
|
+
process.exit();
|
154
163
|
};
|
155
164
|
server.on("close", terminate);
|
156
165
|
process.on("SIGINT", terminate);
|
@@ -182,8 +191,8 @@ StartCommand.flags = Object.assign(Object.assign({}, SessionCommand_1.default.fl
|
|
182
191
|
default: false,
|
183
192
|
}), editor: command_1.flags.string({
|
184
193
|
char: "e",
|
185
|
-
description: "[
|
186
|
-
options: ["
|
194
|
+
description: "[preview, extension]",
|
195
|
+
options: ["extension", "preview"],
|
187
196
|
}), version: command_1.flags.string({
|
188
197
|
char: "v",
|
189
198
|
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,44 @@ 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)}`;
|
114
|
+
configObj.config.editor.agent = "vscode";
|
109
115
|
}
|
110
116
|
else if (configObj.config && Boolean(codespaces_workspace)) {
|
111
117
|
console_1.default.debug("Codespaces detected: ", codespaces_workspace);
|
112
|
-
configObj.config.editor.agent = "vscode";
|
113
118
|
configObj.address = `https://${codespaces_workspace}.github.dev`;
|
114
119
|
configObj.config.publicUrl = `https://${codespaces_workspace}-${configObj.config.port}.app.github.dev`;
|
115
|
-
|
116
|
-
// } else if (configObj.config && !configObj.config.editor.agent) {
|
120
|
+
configObj.config.editor.agent = "vscode";
|
117
121
|
}
|
118
|
-
else
|
119
|
-
console_1.default.debug("
|
120
|
-
configObj.config.editor.agent = "localhost";
|
122
|
+
else {
|
123
|
+
console_1.default.debug("Neither Gitpod nor Codespaces detected, using localhost.");
|
121
124
|
configObj.address = `http://localhost:${configObj.config.port}`;
|
122
125
|
configObj.config.publicUrl = `http://localhost:${configObj.config.port}`;
|
123
126
|
}
|
124
|
-
|
127
|
+
if (!configObj.config.editor.agent) {
|
128
|
+
configObj.config.editor.agent =
|
129
|
+
process.env.TERM_PROGRAM === "vscode" ? "vscode" : "os";
|
130
|
+
}
|
125
131
|
if (configObj.config && !configObj.config.publicUrl)
|
126
132
|
configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`;
|
127
|
-
|
128
|
-
if (configObj.config && !mode) {
|
133
|
+
if (configObj.config && !configObj.config.editor.mode && mode) {
|
129
134
|
configObj.config.editor.mode = mode;
|
130
135
|
}
|
131
|
-
if (
|
136
|
+
if (!configObj.config.editor.mode) {
|
132
137
|
configObj.config.editor.mode =
|
133
|
-
configObj.config.editor.agent === "
|
134
|
-
|
138
|
+
configObj.config.editor.agent === "vscode" ? "extension" : "preview";
|
139
|
+
}
|
140
|
+
if (version)
|
135
141
|
configObj.config.editor.version = version;
|
136
|
-
else if (configObj.config
|
142
|
+
else if (configObj.config.editor.version === null) {
|
137
143
|
console_1.default.debug("Config version not found, downloading default.");
|
138
144
|
const resp = await fetch("https://raw.githubusercontent.com/learnpack/ide/master/package.json");
|
139
145
|
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) || "./";
|
146
|
+
configObj.config.editor.version = packageJSON.version || "3.1.22";
|
145
147
|
}
|
148
|
+
configObj.config.dirPath = "./" + confPath.base;
|
149
|
+
configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./";
|
146
150
|
return {
|
147
151
|
validLanguages: {},
|
148
152
|
get: () => configObj,
|
@@ -168,6 +172,7 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
168
172
|
return true;
|
169
173
|
}
|
170
174
|
console_1.default.info(`Language engine for ${language} not found, installing...`);
|
175
|
+
// Install the compiler in their new versions
|
171
176
|
result = shell.exec(`learnpack plugins:install learnpack-${language}`, {
|
172
177
|
silent: true,
|
173
178
|
});
|
@@ -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,72 @@
|
|
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 checkXDGInstalled = () => {
|
9
|
+
return new Promise((resolve, reject) => {
|
10
|
+
child_process_1.exec("which xdg-open", (error, stdout, stderr) => {
|
11
|
+
if (error) {
|
12
|
+
resolve(false);
|
13
|
+
}
|
14
|
+
if (stdout) {
|
15
|
+
resolve(true);
|
16
|
+
}
|
17
|
+
else {
|
18
|
+
resolve(false);
|
19
|
+
}
|
20
|
+
});
|
21
|
+
});
|
22
|
+
};
|
23
|
+
const openFile = (filePath) => {
|
24
|
+
const fullPath = path.join(process.cwd(), filePath);
|
25
|
+
let command;
|
26
|
+
switch (os.platform()) {
|
27
|
+
case "darwin": // macOS
|
28
|
+
command = `open ${fullPath}`;
|
29
|
+
break;
|
30
|
+
case "win32": // Windows
|
31
|
+
command = `start ${fullPath}`;
|
32
|
+
break;
|
33
|
+
case "linux": // Linux
|
34
|
+
command = `xdg-open ${fullPath}`;
|
35
|
+
break;
|
36
|
+
default:
|
37
|
+
throw new Error("Unsupported OS");
|
38
|
+
}
|
39
|
+
child_process_1.exec(command, (error, stdout, stderr) => {
|
40
|
+
if (error) {
|
41
|
+
console.error(`exec error: ${error}`);
|
42
|
+
}
|
43
|
+
});
|
44
|
+
};
|
45
|
+
const eventManager = {
|
46
|
+
enqueue: (event, data) => {
|
47
|
+
if (event === "start_exercise") {
|
48
|
+
const exercise = data;
|
49
|
+
const filesToOpen = exercise.files
|
50
|
+
.filter((file) => !file.hidden)
|
51
|
+
.map((file) => {
|
52
|
+
return file.path.replace("\\", "/");
|
53
|
+
});
|
54
|
+
for (const file of filesToOpen) {
|
55
|
+
openFile(file);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
if (event === "open_files") {
|
59
|
+
const files = data;
|
60
|
+
for (const file of files) {
|
61
|
+
const correctedPath = file.replace("\\", "/");
|
62
|
+
openFile(correctedPath);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
if (event === "open_window") {
|
66
|
+
const url = data.url;
|
67
|
+
cli_ux_1.default.open(url);
|
68
|
+
}
|
69
|
+
},
|
70
|
+
checkXDGInstalled,
|
71
|
+
};
|
72
|
+
exports.eventManager = eventManager;
|
package/oclif.manifest.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":"2.1.
|
1
|
+
{"version":"2.1.39","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
@@ -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.1.
|
4
|
+
"version": "2.1.39",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"bin": {
|
7
7
|
"learnpack": "bin/run"
|
@@ -67,7 +67,6 @@
|
|
67
67
|
"fs-extra": "^10.0.1",
|
68
68
|
"globby": "^10.0.2",
|
69
69
|
"husky": ">=6",
|
70
|
-
"learnpack-node": "^0.0.21",
|
71
70
|
"lint-staged": ">=10",
|
72
71
|
"mocha": "^5.2.0",
|
73
72
|
"mock-fs": "^5.1.2",
|
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",
|
@@ -69,6 +70,8 @@ export default class StartCommand extends SessionCommand {
|
|
69
70
|
const configObject = this.configManager?.get()
|
70
71
|
const config = configObject?.config
|
71
72
|
|
73
|
+
const hasXDG = await eventManager.checkXDGInstalled()
|
74
|
+
|
72
75
|
if (configObject) {
|
73
76
|
const { config } = configObject
|
74
77
|
|
@@ -110,7 +113,11 @@ export default class StartCommand extends SessionCommand {
|
|
110
113
|
this.configManager,
|
111
114
|
process.env.NODE_ENV === "test"
|
112
115
|
)
|
116
|
+
server.setMaxListeners(20)
|
117
|
+
|
118
|
+
// console.log(config, "this is the f*!shell.which("code")!shell.which("code")cking config");
|
113
119
|
|
120
|
+
// I should call a method to get the EventListener
|
114
121
|
const dispatcher = queue.dispatcher({
|
115
122
|
create: true,
|
116
123
|
path: `${config.dirPath}/vscode_queue.json`,
|
@@ -148,13 +155,27 @@ export default class StartCommand extends SessionCommand {
|
|
148
155
|
|
149
156
|
const files = prioritizeHTMLFile(data.files)
|
150
157
|
|
151
|
-
|
158
|
+
if (config.editor.agent === "os") {
|
159
|
+
eventManager.enqueue(dispatcher.events.OPEN_FILES, files)
|
160
|
+
} else {
|
161
|
+
dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
|
162
|
+
}
|
163
|
+
|
152
164
|
socket.ready("Ready to compile...")
|
153
165
|
})
|
154
166
|
|
155
167
|
socket.on("open_window", (data: IGitpodData) => {
|
156
168
|
Console.debug("Opening window: ", data)
|
157
|
-
|
169
|
+
|
170
|
+
if (
|
171
|
+
config.editor.agent === "os" &&
|
172
|
+
((config.os === "linux" && hasXDG) || config.os !== "linux")
|
173
|
+
) {
|
174
|
+
eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
|
175
|
+
} else {
|
176
|
+
dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data)
|
177
|
+
}
|
178
|
+
|
158
179
|
socket.ready("Ready to compile...")
|
159
180
|
})
|
160
181
|
|
@@ -175,11 +196,6 @@ export default class StartCommand extends SessionCommand {
|
|
175
196
|
setTimeout(() => socket.ready("Ready to compile..."), 2000)
|
176
197
|
}
|
177
198
|
})
|
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
199
|
|
184
200
|
socket.on("build", async (data: IExerciseData) => {
|
185
201
|
const exercise = this.configManager?.getExercise(data.exerciseSlug)
|
@@ -256,15 +272,11 @@ export default class StartCommand extends SessionCommand {
|
|
256
272
|
})
|
257
273
|
|
258
274
|
const terminate = () => {
|
259
|
-
Console.
|
260
|
-
|
275
|
+
Console.error("Terminating Learnpack...")
|
261
276
|
TelemetryManager.submit()
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
dispatcher.enqueue(dispatcher.events.END)
|
266
|
-
process.exit()
|
267
|
-
})
|
277
|
+
this.configManager?.noCurrentExercise()
|
278
|
+
dispatcher.enqueue(dispatcher.events.END)
|
279
|
+
process.exit()
|
268
280
|
}
|
269
281
|
|
270
282
|
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", "os"] ("os" is the agent when running outside a coding editor
|
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)}`
|
167
|
+
configObj.config.editor.agent = "vscode"
|
163
168
|
} else if (configObj.config && Boolean(codespaces_workspace)) {
|
164
169
|
Console.debug("Codespaces detected: ", codespaces_workspace)
|
165
|
-
configObj.config.editor.agent = "vscode"
|
166
|
-
|
167
170
|
configObj.address = `https://${codespaces_workspace}.github.dev`
|
168
171
|
configObj.config.publicUrl = `https://${codespaces_workspace}-${configObj.config.port}.app.github.dev`
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
Console.debug("Localhost detected")
|
173
|
-
configObj.config.editor.agent = "localhost"
|
172
|
+
configObj.config.editor.agent = "vscode"
|
173
|
+
} else {
|
174
|
+
Console.debug("Neither Gitpod nor Codespaces detected, using localhost.")
|
174
175
|
configObj.address = `http://localhost:${configObj.config.port}`
|
175
176
|
configObj.config.publicUrl = `http://localhost:${configObj.config.port}`
|
176
177
|
}
|
177
178
|
|
178
|
-
|
179
|
+
if (!configObj.config.editor.agent) {
|
180
|
+
configObj.config.editor.agent =
|
181
|
+
process.env.TERM_PROGRAM === "vscode" ? "vscode" : "os"
|
182
|
+
}
|
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,79 @@
|
|
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 checkXDGInstalled = (): Promise<boolean> => {
|
7
|
+
return new Promise((resolve, reject) => {
|
8
|
+
exec("which xdg-open", (error, stdout, stderr) => {
|
9
|
+
if (error) {
|
10
|
+
resolve(false)
|
11
|
+
}
|
12
|
+
|
13
|
+
if (stdout) {
|
14
|
+
resolve(true)
|
15
|
+
} else {
|
16
|
+
resolve(false)
|
17
|
+
}
|
18
|
+
})
|
19
|
+
})
|
20
|
+
}
|
21
|
+
|
22
|
+
const openFile = (filePath: string): void => {
|
23
|
+
const fullPath = path.join(process.cwd(), filePath)
|
24
|
+
let command: string
|
25
|
+
|
26
|
+
switch (os.platform()) {
|
27
|
+
case "darwin": // macOS
|
28
|
+
command = `open ${fullPath}`
|
29
|
+
break
|
30
|
+
case "win32": // Windows
|
31
|
+
command = `start ${fullPath}`
|
32
|
+
break
|
33
|
+
case "linux": // Linux
|
34
|
+
command = `xdg-open ${fullPath}`
|
35
|
+
break
|
36
|
+
default:
|
37
|
+
throw new Error("Unsupported OS")
|
38
|
+
}
|
39
|
+
|
40
|
+
exec(command, (error, stdout, stderr) => {
|
41
|
+
if (error) {
|
42
|
+
console.error(`exec error: ${error}`)
|
43
|
+
}
|
44
|
+
})
|
45
|
+
}
|
46
|
+
|
47
|
+
const eventManager = {
|
48
|
+
enqueue: (event: string, data: any) => {
|
49
|
+
if (event === "start_exercise") {
|
50
|
+
const exercise = data
|
51
|
+
const filesToOpen = exercise.files
|
52
|
+
.filter((file: any) => !file.hidden)
|
53
|
+
.map((file: any) => {
|
54
|
+
return file.path.replace("\\", "/")
|
55
|
+
})
|
56
|
+
|
57
|
+
for (const file of filesToOpen) {
|
58
|
+
openFile(file)
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
if (event === "open_files") {
|
63
|
+
const files = data
|
64
|
+
|
65
|
+
for (const file of files) {
|
66
|
+
const correctedPath = file.replace("\\", "/")
|
67
|
+
openFile(correctedPath)
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
if (event === "open_window") {
|
72
|
+
const url = data.url
|
73
|
+
cli.open(url)
|
74
|
+
}
|
75
|
+
},
|
76
|
+
checkXDGInstalled,
|
77
|
+
}
|
78
|
+
|
79
|
+
export { eventManager }
|