@learnpack/learnpack 4.0.5 → 4.0.7
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +10 -10
- package/lib/commands/start.js +14 -3
- package/lib/managers/config/defaults.d.ts +6 -0
- package/lib/managers/config/defaults.js +6 -0
- package/lib/managers/config/index.js +168 -7
- package/lib/managers/file.js +12 -12
- package/lib/managers/socket.js +1 -1
- package/lib/models/config.d.ts +11 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/start.ts +17 -5
- package/src/managers/config/defaults.ts +49 -43
- package/src/managers/config/index.ts +206 -8
- package/src/managers/file.ts +16 -14
- package/src/managers/socket.ts +2 -6
- package/src/models/config.ts +72 -68
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/4.0.
|
24
|
+
@learnpack/learnpack/4.0.7 win32-x64 node-v20.16.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/v4.0.
|
77
|
+
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.7/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/v4.0.
|
92
|
+
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.7/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/v4.0.
|
110
|
+
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.7/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/v4.0.
|
141
|
+
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.7/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/v4.0.
|
159
|
+
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.7/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/v4.0.
|
177
|
+
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.7/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/v4.0.
|
312
|
+
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.7/src\commands\publish.ts)_
|
313
313
|
|
314
314
|
## `learnpack start`
|
315
315
|
|
@@ -330,7 +330,7 @@ OPTIONS
|
|
330
330
|
-w, --watch Watch for file changes
|
331
331
|
```
|
332
332
|
|
333
|
-
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.
|
333
|
+
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.7/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/v4.0.
|
347
|
+
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v4.0.7/src\commands\test.ts)_
|
348
348
|
<!-- commandsstop -->
|
349
349
|
|
350
350
|
> > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
|
package/lib/commands/start.js
CHANGED
@@ -22,10 +22,9 @@ class StartCommand extends SessionCommand_1.default {
|
|
22
22
|
await this.initSession(flags);
|
23
23
|
}
|
24
24
|
async run() {
|
25
|
-
var _a, _b, _c, _d, _e, _f;
|
25
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
26
26
|
// get configuration object
|
27
27
|
const configObject = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.get();
|
28
|
-
// TODO: console.log(this.config.platform); get the platfrom from Oclif
|
29
28
|
const hasXDG = await osOperations_1.eventManager.checkXDGInstalled();
|
30
29
|
const installedPlugins = this.config.plugins.map(plugin => {
|
31
30
|
return `${plugin.pjson.name}`;
|
@@ -51,7 +50,7 @@ class StartCommand extends SessionCommand_1.default {
|
|
51
50
|
// listen to socket commands
|
52
51
|
if (config && this.configManager) {
|
53
52
|
const server = await server_1.default(configObject, this.configManager, process.env.NODE_ENV === "test");
|
54
|
-
server.setMaxListeners(
|
53
|
+
server.setMaxListeners(30);
|
55
54
|
// I should call a method to get the EventListener
|
56
55
|
const dispatcher = fileQueue_1.default.dispatcher({
|
57
56
|
create: true,
|
@@ -78,6 +77,18 @@ class StartCommand extends SessionCommand_1.default {
|
|
78
77
|
}
|
79
78
|
}
|
80
79
|
socket_1.default.start(config, server, false);
|
80
|
+
if ((_g = config === null || config === void 0 ? void 0 : config.warnings) === null || _g === void 0 ? void 0 : _g.agent) {
|
81
|
+
const message = config.warnings.agent;
|
82
|
+
setTimeout(() => {
|
83
|
+
socket_1.default.dialog(message);
|
84
|
+
}, 2000);
|
85
|
+
}
|
86
|
+
if ((_h = config === null || config === void 0 ? void 0 : config.warnings) === null || _h === void 0 ? void 0 : _h.extension) {
|
87
|
+
const message = config.warnings.extension;
|
88
|
+
setTimeout(() => {
|
89
|
+
socket_1.default.dialog(message);
|
90
|
+
}, 3000);
|
91
|
+
}
|
81
92
|
socket_1.default.on("open", (data) => {
|
82
93
|
console_1.default.debug("Opening these files: ", data);
|
83
94
|
const files = misc_1.prioritizeHTMLFile(data.files);
|
@@ -5,7 +5,6 @@ const fs = require("fs");
|
|
5
5
|
const shell = require("shelljs");
|
6
6
|
const child_process_1 = require("child_process");
|
7
7
|
const util_1 = require("util");
|
8
|
-
const execAsync = util_1.promisify(child_process_1.exec);
|
9
8
|
const console_1 = require("../../utils/console");
|
10
9
|
const watcher_1 = require("../../utils/watcher");
|
11
10
|
const errors_1 = require("../../utils/errors");
|
@@ -13,6 +12,7 @@ const defaults_1 = require("./defaults");
|
|
13
12
|
const exercise_1 = require("./exercise");
|
14
13
|
const file_1 = require("../file");
|
15
14
|
/* exercise folder name standard */
|
15
|
+
const execAsync = util_1.promisify(child_process_1.exec);
|
16
16
|
// eslint-disable-next-line
|
17
17
|
const fetch = require("node-fetch");
|
18
18
|
// eslint-disable-next-line
|
@@ -60,7 +60,7 @@ const getCodespacesNamespace = () => {
|
|
60
60
|
return codespace_name;
|
61
61
|
};
|
62
62
|
exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
63
|
-
var _a, _b, _c, _d;
|
63
|
+
var _a, _b, _c, _d, _e, _f;
|
64
64
|
const confPath = getConfigPath();
|
65
65
|
console_1.default.debug("This is the config path: ", confPath);
|
66
66
|
let configObj = {};
|
@@ -108,11 +108,22 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
108
108
|
// auto detect agent (if possible)
|
109
109
|
const codespaces_workspace = getCodespacesNamespace();
|
110
110
|
console_1.default.debug("This is the codespace namespace: ", codespaces_workspace);
|
111
|
+
if (!configObj.config.editor) {
|
112
|
+
configObj.config.editor = {};
|
113
|
+
}
|
111
114
|
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);
|
115
|
+
if ((_e = (_d = configObj.config) === null || _d === void 0 ? void 0 : _d.editor) === null || _e === void 0 ? void 0 : _e.agent) {
|
116
|
+
if (configObj.config.suggestions) {
|
117
|
+
configObj.config.suggestions.agent = configObj.config.editor.agent;
|
118
|
+
}
|
119
|
+
else {
|
120
|
+
configObj.config.suggestions = { agent: configObj.config.editor.agent };
|
121
|
+
}
|
122
|
+
}
|
112
123
|
if (shell.which("gp") && configObj && configObj.config) {
|
113
124
|
console_1.default.debug("Gitpod detected");
|
114
125
|
configObj.address = getGitpodAddress();
|
115
|
-
configObj.config.publicUrl = `https://${configObj.config.port}-${(
|
126
|
+
configObj.config.publicUrl = `https://${configObj.config.port}-${(_f = configObj.address) === null || _f === void 0 ? void 0 : _f.slice(8)}`;
|
116
127
|
configObj.config.editor.agent = "vscode";
|
117
128
|
}
|
118
129
|
else if (configObj.config && Boolean(codespaces_workspace)) {
|
@@ -125,11 +136,32 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
125
136
|
console_1.default.debug("Neither Gitpod nor Codespaces detected, using localhost.");
|
126
137
|
configObj.address = `http://localhost:${configObj.config.port}`;
|
127
138
|
configObj.config.publicUrl = `http://localhost:${configObj.config.port}`;
|
128
|
-
}
|
129
|
-
if (!configObj.config.editor.agent) {
|
130
139
|
configObj.config.editor.agent =
|
131
140
|
process.env.TERM_PROGRAM === "vscode" ? "vscode" : "os";
|
132
141
|
}
|
142
|
+
if (configObj.config.editor.agent === "vscode" && isCodeCommandAvailable()) {
|
143
|
+
const extensionName = "learn-pack.learnpack-vscode";
|
144
|
+
if (!isExtensionInstalled(extensionName)) {
|
145
|
+
console_1.default.info("The LearnPack extension is not installed, trying to install it automatically.");
|
146
|
+
if (!installExtension(extensionName)) {
|
147
|
+
configObj.config.warnings = {
|
148
|
+
extension: EXTENSION_INSTALLATION_STEPS,
|
149
|
+
};
|
150
|
+
console_1.default.warning("The LearnPack extension is not installed and this tutorial is meant to be run inside VSCode. Please install the LearnPack extension.");
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
if (configObj.config.suggestions &&
|
155
|
+
configObj.config.editor &&
|
156
|
+
configObj.config.suggestions.agent !== configObj.config.editor.agent) {
|
157
|
+
console_1.default.warning(`The suggested agent "${configObj.config.suggestions.agent}" is different from the detected agent "${configObj.config.editor.agent}"`);
|
158
|
+
try {
|
159
|
+
configObj.config.warnings = Object.assign(Object.assign({}, configObj.config.warnings), { agent: buildAgentWarning(configObj.config.editor.agent, configObj.config.suggestions.agent) });
|
160
|
+
}
|
161
|
+
catch (_g) {
|
162
|
+
console_1.default.error("IMPOSSIBLE TO SET WARNING");
|
163
|
+
}
|
164
|
+
}
|
133
165
|
if (configObj.config && !configObj.config.publicUrl)
|
134
166
|
configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`;
|
135
167
|
if (configObj.config && !configObj.config.editor.mode && mode) {
|
@@ -145,7 +177,7 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
145
177
|
console_1.default.debug("Config version not found, downloading default.");
|
146
178
|
const resp = await fetch("https://raw.githubusercontent.com/learnpack/ide/master/package.json");
|
147
179
|
const packageJSON = await resp.json();
|
148
|
-
configObj.config.editor.version = packageJSON.version || "4.0.
|
180
|
+
configObj.config.editor.version = packageJSON.version || "4.0.2";
|
149
181
|
}
|
150
182
|
configObj.config.dirPath = "./" + confPath.base;
|
151
183
|
configObj.config.exercisesPath = getExercisesPath(confPath.base) || "./";
|
@@ -188,7 +220,9 @@ exports.default = async ({ grading, mode, disableGrading, version, }) => {
|
|
188
220
|
}
|
189
221
|
return {
|
190
222
|
validLanguages: {},
|
191
|
-
get: () =>
|
223
|
+
get: () => {
|
224
|
+
return configObj;
|
225
|
+
},
|
192
226
|
validateEngine: function (language, server, socket) {
|
193
227
|
// eslint-disable-next-line
|
194
228
|
const alias = (_l) => {
|
@@ -374,3 +408,130 @@ function deepMerge(...sources) {
|
|
374
408
|
}
|
375
409
|
return acc;
|
376
410
|
}
|
411
|
+
const buildAgentWarning = (current, suggested) => {
|
412
|
+
const message = `# Agent mismatch!\n
|
413
|
+
|
414
|
+
In LearnPack, the agent is in charge of running the LearnPack interface.
|
415
|
+
|
416
|
+
You're currently using LearnPack through \`${current}\` but the suggested agent is \`${suggested}\`.
|
417
|
+
|
418
|
+
We recommend strongly recommend changing your agent.
|
419
|
+
|
420
|
+
${stepsToChangeAgent(suggested)}
|
421
|
+
`;
|
422
|
+
return message;
|
423
|
+
};
|
424
|
+
const stepsToChangeAgent = (agent) => {
|
425
|
+
if (agent === "vscode") {
|
426
|
+
return `
|
427
|
+
# Steps to Change Agent to VSCode
|
428
|
+
|
429
|
+
1. **Install VSCode**:
|
430
|
+
- Visit the [VSCode website](https://code.visualstudio.com/Download) and download the latest version.
|
431
|
+
- Follow the installation instructions for your operating system.
|
432
|
+
|
433
|
+
2. **Install LearnPack VSCode Extension**:
|
434
|
+
- Open VSCode.
|
435
|
+
- Go to the Extensions view by clicking on the Extensions icon in the Activity Bar on the side of the window or by pressing \`Ctrl+Shift+X\`.
|
436
|
+
- Search for "LearnPack" and click "Install".
|
437
|
+
- If the extension is already installed but disabled, enable it.
|
438
|
+
|
439
|
+
3. **Run LearnPack in VSCode**:
|
440
|
+
- Open your terminal in VSCode.
|
441
|
+
- Navigate to your LearnPack project directory.
|
442
|
+
- Run the following command:
|
443
|
+
\`\`\`sh
|
444
|
+
learnpack start
|
445
|
+
\`\`\`
|
446
|
+
|
447
|
+
We strongly recommend using VSCode for a better learning experience.
|
448
|
+
`;
|
449
|
+
}
|
450
|
+
if (agent === "os") {
|
451
|
+
return `
|
452
|
+
# Steps to Change Agent to OS
|
453
|
+
|
454
|
+
This learning package was designed to run outside of VSCode. We strongly recommend closing VSCode and running the package independently.
|
455
|
+
|
456
|
+
1. **Close VSCode**:
|
457
|
+
- Save your work and close the VSCode application.
|
458
|
+
|
459
|
+
2. **Open a New Terminal**:
|
460
|
+
- Open a terminal or command prompt on your operating system.
|
461
|
+
|
462
|
+
3. **Navigate to Your LearnPack Project Directory**:
|
463
|
+
- Use the \`cd\` command to navigate to the directory where your LearnPack project is located.
|
464
|
+
|
465
|
+
4. **Run LearnPack**:
|
466
|
+
- Run the following command to start LearnPack:
|
467
|
+
\`\`\`sh
|
468
|
+
learnpack start
|
469
|
+
\`\`\`
|
470
|
+
|
471
|
+
We strongly recommend running the package independently for a better learning experience.
|
472
|
+
`;
|
473
|
+
}
|
474
|
+
return "";
|
475
|
+
};
|
476
|
+
/**
|
477
|
+
* Checks if the 'code' command is available in the terminal.
|
478
|
+
*
|
479
|
+
* @returns {boolean} True if the 'code' command is available, false otherwise.
|
480
|
+
*/
|
481
|
+
const isCodeCommandAvailable = () => {
|
482
|
+
return shell.which("code") !== null;
|
483
|
+
};
|
484
|
+
/**
|
485
|
+
* Checks if a specific VSCode extension is installed.
|
486
|
+
*
|
487
|
+
* @param {string} extensionName - The name of the extension to check.
|
488
|
+
* @returns {boolean} True if the extension is installed, false otherwise.
|
489
|
+
*/
|
490
|
+
const isExtensionInstalled = (extensionName) => {
|
491
|
+
if (!isCodeCommandAvailable()) {
|
492
|
+
throw new Error("The 'code' command is not available in the terminal. Please ensure that VSCode is installed and the 'code' command is added to your PATH.");
|
493
|
+
}
|
494
|
+
const result = shell.exec("code --list-extensions", { silent: true });
|
495
|
+
return result.stdout.split("\n").includes(extensionName);
|
496
|
+
};
|
497
|
+
const EXTENSION_INSTALLATION_STEPS = `
|
498
|
+
# Steps to Install LearnPack VSCode Extension
|
499
|
+
|
500
|
+
1. **Open VSCode**:
|
501
|
+
- Launch the Visual Studio Code application on your computer.
|
502
|
+
|
503
|
+
2. **Go to Extensions View**:
|
504
|
+
- Click on the Extensions icon in the Activity Bar on the side of the window.
|
505
|
+
- Alternatively, you can open the Extensions view by pressing \`Ctrl+Shift+X\`.
|
506
|
+
|
507
|
+
3. **Search for LearnPack**:
|
508
|
+
- In the Extensions view, type "LearnPack" into the search bar.
|
509
|
+
|
510
|
+
4. **Install the Extension**:
|
511
|
+
- Find the "LearnPack" extension in the search results and click the "Install" button.
|
512
|
+
- If the extension is already installed but disabled, click the "Enable" button.
|
513
|
+
|
514
|
+
5. **Verify Installation**:
|
515
|
+
- Once installed, you can verify the installation by running the following command in the terminal:
|
516
|
+
\`\`\`sh
|
517
|
+
code --list-extensions | grep learn-pack.learnpack-vscode
|
518
|
+
\`\`\`
|
519
|
+
- If the extension is listed, it means the installation was successful.
|
520
|
+
|
521
|
+
We strongly recommend using the LearnPack extension in VSCode for a better learning experience.
|
522
|
+
`;
|
523
|
+
/**
|
524
|
+
* Installs the LearnPack VSCode extension if the 'code' command is available.
|
525
|
+
*
|
526
|
+
* @param {string} extensionName - The name of the extension to install.
|
527
|
+
* @returns {boolean} True if the installation was successful, false otherwise.
|
528
|
+
*/
|
529
|
+
const installExtension = (extensionName) => {
|
530
|
+
if (!isCodeCommandAvailable()) {
|
531
|
+
throw new Error("The 'code' command is not available in the terminal. Please ensure that VSCode is installed and the 'code' command is added to your PATH.");
|
532
|
+
}
|
533
|
+
const result = shell.exec(`code --install-extension ${extensionName}`, {
|
534
|
+
silent: true,
|
535
|
+
});
|
536
|
+
return result.code === 0;
|
537
|
+
};
|
package/lib/managers/file.js
CHANGED
@@ -40,33 +40,33 @@ exports.downloadEditor = async (version, destination) => {
|
|
40
40
|
const versionPrefix = `${major}.${minor}`;
|
41
41
|
let tags;
|
42
42
|
try {
|
43
|
-
const tagsRes = await fetch("https://
|
43
|
+
const tagsRes = await fetch("https://raw.githubusercontent.com/learnpack/ide/master/versions.json");
|
44
44
|
tags = await tagsRes.json();
|
45
|
-
if (tags
|
46
|
-
throw new
|
45
|
+
if (!Array.isArray(tags)) {
|
46
|
+
throw new TypeError(`Invalid versions.json format`);
|
47
47
|
}
|
48
48
|
}
|
49
49
|
catch (error) {
|
50
|
-
console_1.default.
|
50
|
+
console_1.default.debug("Error reading versions.json, defaulting to version 4.0.2", error);
|
51
51
|
version = "4.0.2";
|
52
|
-
tags = [
|
52
|
+
tags = [`learnpack-${version}.tar.gz`];
|
53
53
|
}
|
54
54
|
let matchingTags;
|
55
55
|
try {
|
56
56
|
if (tags.length === 0 || !tags)
|
57
|
-
throw errors_1.InternalError(`No found tags in
|
57
|
+
throw errors_1.InternalError(`No found tags in versions.json ${tags}`);
|
58
58
|
matchingTags = tags
|
59
|
-
.filter(
|
60
|
-
.sort((a, b) => b.
|
59
|
+
.filter(tag => tag.includes(versionPrefix))
|
60
|
+
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
|
61
61
|
if (matchingTags.length === 0)
|
62
|
-
throw errors_1.InternalError(`No matching version found for prefix ${versionPrefix} in the
|
62
|
+
throw errors_1.InternalError(`No matching version found for prefix ${versionPrefix} in the versions.json`);
|
63
63
|
}
|
64
64
|
catch (error) {
|
65
|
-
console_1.default.
|
65
|
+
console_1.default.debug("Error processing tags, defaulting to version 4.0.2", error);
|
66
66
|
version = "4.0.2";
|
67
|
-
matchingTags = [
|
67
|
+
matchingTags = [`learnpack-${version}.tar.gz`];
|
68
68
|
}
|
69
|
-
const latestVersion = matchingTags[0]
|
69
|
+
const latestVersion = matchingTags[0]
|
70
70
|
.replace("learnpack-", "")
|
71
71
|
.replace(".tar.gz", "");
|
72
72
|
const versionNumber = parseInt(latestVersion.split(".")[0]);
|
package/lib/managers/socket.js
CHANGED
@@ -102,7 +102,7 @@ const SocketManager = {
|
|
102
102
|
});
|
103
103
|
},
|
104
104
|
reload: function (files = null, exercises = null) {
|
105
|
-
this.emit("reload", (files === null || files === void 0 ? void 0 : files.join("")) || ""
|
105
|
+
this.emit("reload", (files === null || files === void 0 ? void 0 : files.join("")) || "", exercises);
|
106
106
|
},
|
107
107
|
openWindow: function (url = "") {
|
108
108
|
fileQueue_1.default.dispatcher().enqueue(fileQueue_1.default.events.OPEN_WINDOW, url);
|
package/lib/models/config.d.ts
CHANGED
@@ -8,10 +8,9 @@ export declare type TCompiler = "webpack" | "vanillajs" | "react" | "html" | "no
|
|
8
8
|
export interface IConfigPath {
|
9
9
|
base: string;
|
10
10
|
}
|
11
|
-
declare type TAgent = "vscode" | "os";
|
12
11
|
export interface IEditor {
|
13
12
|
mode?: TMode;
|
14
|
-
version
|
13
|
+
version?: string;
|
15
14
|
agent?: TAgent;
|
16
15
|
hideTerminal?: boolean;
|
17
16
|
}
|
@@ -56,9 +55,19 @@ export interface IConfig {
|
|
56
55
|
skills: Array<string>;
|
57
56
|
telemetry?: TTelemetryUrls;
|
58
57
|
variables?: TVariables;
|
58
|
+
suggestions: TSuggestions;
|
59
|
+
warnings: TWarnings;
|
59
60
|
runHook: (...agrs: Array<any>) => void;
|
60
61
|
testingFinishedCallback: (arg: any | undefined) => void;
|
61
62
|
}
|
63
|
+
export declare type TAgent = "os" | "vscode" | null;
|
64
|
+
declare type TSuggestions = {
|
65
|
+
agent: TAgent;
|
66
|
+
};
|
67
|
+
declare type TWarnings = {
|
68
|
+
agent?: string;
|
69
|
+
extension?: string;
|
70
|
+
};
|
62
71
|
declare type TVariable = {
|
63
72
|
type: "command" | "string";
|
64
73
|
source: string;
|
package/oclif.manifest.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":"4.0.
|
1
|
+
{"version":"4.0.7","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
@@ -77,7 +77,6 @@ export default class StartCommand extends SessionCommand {
|
|
77
77
|
// get configuration object
|
78
78
|
const configObject = this.configManager?.get()
|
79
79
|
|
80
|
-
// TODO: console.log(this.config.platform); get the platfrom from Oclif
|
81
80
|
const hasXDG = await eventManager.checkXDGInstalled()
|
82
81
|
|
83
82
|
const installedPlugins = this.config.plugins.map(plugin => {
|
@@ -86,6 +85,7 @@ export default class StartCommand extends SessionCommand {
|
|
86
85
|
|
87
86
|
if (configObject) {
|
88
87
|
const { config } = configObject
|
88
|
+
|
89
89
|
// build exerises
|
90
90
|
this.configManager?.buildIndex()
|
91
91
|
|
@@ -112,9 +112,7 @@ export default class StartCommand extends SessionCommand {
|
|
112
112
|
this.exit(1)
|
113
113
|
}
|
114
114
|
|
115
|
-
const appAlreadyExists = checkIfDirectoryExists(
|
116
|
-
`${config?.dirPath}/_app`
|
117
|
-
)
|
115
|
+
const appAlreadyExists = checkIfDirectoryExists(`${config?.dirPath}/_app`)
|
118
116
|
|
119
117
|
if (!appAlreadyExists) {
|
120
118
|
// download app and decompress
|
@@ -137,7 +135,7 @@ export default class StartCommand extends SessionCommand {
|
|
137
135
|
this.configManager,
|
138
136
|
process.env.NODE_ENV === "test"
|
139
137
|
)
|
140
|
-
server.setMaxListeners(
|
138
|
+
server.setMaxListeners(30)
|
141
139
|
|
142
140
|
// I should call a method to get the EventListener
|
143
141
|
const dispatcher = queue.dispatcher({
|
@@ -172,6 +170,20 @@ export default class StartCommand extends SessionCommand {
|
|
172
170
|
|
173
171
|
socket.start(config, server, false)
|
174
172
|
|
173
|
+
if (config?.warnings?.agent) {
|
174
|
+
const message = config.warnings.agent
|
175
|
+
setTimeout(() => {
|
176
|
+
socket.dialog(message)
|
177
|
+
}, 2000)
|
178
|
+
}
|
179
|
+
|
180
|
+
if (config?.warnings?.extension) {
|
181
|
+
const message = config.warnings.extension
|
182
|
+
setTimeout(() => {
|
183
|
+
socket.dialog(message)
|
184
|
+
}, 3000)
|
185
|
+
}
|
186
|
+
|
175
187
|
socket.on("open", (data: IGitpodData) => {
|
176
188
|
Console.debug("Opening these files: ", data)
|
177
189
|
|
@@ -1,43 +1,49 @@
|
|
1
|
-
import * as os from "os"
|
2
|
-
export default {
|
3
|
-
config: {
|
4
|
-
port: 3000,
|
5
|
-
os: (function () {
|
6
|
-
return os.platform()
|
7
|
-
})(),
|
8
|
-
editor: {
|
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
|
12
|
-
},
|
13
|
-
dirPath: "./.learn",
|
14
|
-
configPath: "./learn.json",
|
15
|
-
outputPath: "./.learn/dist",
|
16
|
-
publicPath: "/preview",
|
17
|
-
publicUrl: null,
|
18
|
-
contact: "https://github.com/learnpack/learnpack/issues/new",
|
19
|
-
language: "auto",
|
20
|
-
autoPlay: true,
|
21
|
-
projectType: "tutorial", // [tutorial, project]
|
22
|
-
grading: "isolated", // [isolated, incremental]
|
23
|
-
exercisesPath: "./", // path to the folder that contains the exercises
|
24
|
-
webpackTemplate: null, // if you want webpack to use an HTML template
|
25
|
-
disableGrading: false,
|
26
|
-
disabledActions: [], // Possible: 'build', 'test' or 'reset'
|
27
|
-
actions: [], // ⚠️ deprecated, leave empty )
|
28
|
-
entries: {
|
29
|
-
html: "index.html",
|
30
|
-
vanillajs: "index.js",
|
31
|
-
react: "app.jsx",
|
32
|
-
node: "app.js",
|
33
|
-
python3: "app.py",
|
34
|
-
java: "app.java",
|
35
|
-
},
|
36
|
-
},
|
37
|
-
address: "http://localhost",
|
38
|
-
currentExercise: null,
|
39
|
-
exercises: [],
|
40
|
-
bugsLink: null,
|
41
|
-
videoSolutions: false,
|
42
|
-
|
43
|
-
|
1
|
+
import * as os from "os"
|
2
|
+
export default {
|
3
|
+
config: {
|
4
|
+
port: 3000,
|
5
|
+
os: (function () {
|
6
|
+
return os.platform()
|
7
|
+
})(),
|
8
|
+
editor: {
|
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
|
12
|
+
},
|
13
|
+
dirPath: "./.learn",
|
14
|
+
configPath: "./learn.json",
|
15
|
+
outputPath: "./.learn/dist",
|
16
|
+
publicPath: "/preview",
|
17
|
+
publicUrl: null,
|
18
|
+
contact: "https://github.com/learnpack/learnpack/issues/new",
|
19
|
+
language: "auto",
|
20
|
+
autoPlay: true,
|
21
|
+
projectType: "tutorial", // [tutorial, project]
|
22
|
+
grading: "isolated", // [isolated, incremental]
|
23
|
+
exercisesPath: "./", // path to the folder that contains the exercises
|
24
|
+
webpackTemplate: null, // if you want webpack to use an HTML template
|
25
|
+
disableGrading: false,
|
26
|
+
disabledActions: [], // Possible: 'build', 'test' or 'reset'
|
27
|
+
actions: [], // ⚠️ deprecated, leave empty )
|
28
|
+
entries: {
|
29
|
+
html: "index.html",
|
30
|
+
vanillajs: "index.js",
|
31
|
+
react: "app.jsx",
|
32
|
+
node: "app.js",
|
33
|
+
python3: "app.py",
|
34
|
+
java: "app.java",
|
35
|
+
},
|
36
|
+
},
|
37
|
+
address: "http://localhost",
|
38
|
+
currentExercise: null,
|
39
|
+
exercises: [],
|
40
|
+
bugsLink: null,
|
41
|
+
videoSolutions: false,
|
42
|
+
suggestions: {
|
43
|
+
agent: null,
|
44
|
+
},
|
45
|
+
warnings: {
|
46
|
+
agent: null,
|
47
|
+
},
|
48
|
+
localhostOnly: false, // if true, the exercise should not run on gitpod or codespaces but show an error message encouraging the user to run locally
|
49
|
+
}
|
@@ -4,8 +4,6 @@ import * as shell from "shelljs"
|
|
4
4
|
import { exec } from "child_process"
|
5
5
|
import { promisify } from "util"
|
6
6
|
|
7
|
-
const execAsync = promisify(exec)
|
8
|
-
|
9
7
|
import Console from "../../utils/console"
|
10
8
|
import watch from "../../utils/watcher"
|
11
9
|
import {
|
@@ -18,7 +16,12 @@ import defaults from "./defaults"
|
|
18
16
|
import { exercise } from "./exercise"
|
19
17
|
|
20
18
|
import { rmSync } from "../file"
|
21
|
-
import {
|
19
|
+
import {
|
20
|
+
IConfigObj,
|
21
|
+
TConfigObjAttributes,
|
22
|
+
TMode,
|
23
|
+
TAgent,
|
24
|
+
} from "../../models/config"
|
22
25
|
import {
|
23
26
|
IConfigManagerAttributes,
|
24
27
|
IConfigManager,
|
@@ -26,6 +29,7 @@ import {
|
|
26
29
|
import { IFile } from "../../models/file"
|
27
30
|
|
28
31
|
/* exercise folder name standard */
|
32
|
+
const execAsync = promisify(exec)
|
29
33
|
|
30
34
|
// eslint-disable-next-line
|
31
35
|
const fetch = require("node-fetch")
|
@@ -158,11 +162,23 @@ throw InternalError("Config object not found")
|
|
158
162
|
const codespaces_workspace = getCodespacesNamespace()
|
159
163
|
Console.debug("This is the codespace namespace: ", codespaces_workspace)
|
160
164
|
|
165
|
+
if (!configObj.config.editor) {
|
166
|
+
configObj.config.editor = {}
|
167
|
+
}
|
168
|
+
|
161
169
|
Console.debug(
|
162
170
|
"This is the agent, and this should be null an the beginning: ",
|
163
171
|
configObj.config?.editor?.agent
|
164
172
|
)
|
165
173
|
|
174
|
+
if (configObj.config?.editor?.agent) {
|
175
|
+
if (configObj.config.suggestions) {
|
176
|
+
configObj.config.suggestions.agent = configObj.config.editor.agent
|
177
|
+
} else {
|
178
|
+
configObj.config.suggestions = { agent: configObj.config.editor.agent }
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
166
182
|
if (shell.which("gp") && configObj && configObj.config) {
|
167
183
|
Console.debug("Gitpod detected")
|
168
184
|
configObj.address = getGitpodAddress()
|
@@ -179,13 +195,51 @@ throw InternalError("Config object not found")
|
|
179
195
|
Console.debug("Neither Gitpod nor Codespaces detected, using localhost.")
|
180
196
|
configObj.address = `http://localhost:${configObj.config.port}`
|
181
197
|
configObj.config.publicUrl = `http://localhost:${configObj.config.port}`
|
182
|
-
}
|
183
|
-
|
184
|
-
if (!configObj.config.editor.agent) {
|
185
198
|
configObj.config.editor.agent =
|
186
199
|
process.env.TERM_PROGRAM === "vscode" ? "vscode" : "os"
|
187
200
|
}
|
188
201
|
|
202
|
+
if (configObj.config.editor.agent === "vscode" && isCodeCommandAvailable()) {
|
203
|
+
const extensionName = "learn-pack.learnpack-vscode"
|
204
|
+
|
205
|
+
if (!isExtensionInstalled(extensionName)) {
|
206
|
+
Console.info(
|
207
|
+
"The LearnPack extension is not installed, trying to install it automatically."
|
208
|
+
)
|
209
|
+
|
210
|
+
if (!installExtension(extensionName)) {
|
211
|
+
configObj.config.warnings = {
|
212
|
+
extension: EXTENSION_INSTALLATION_STEPS,
|
213
|
+
}
|
214
|
+
Console.warning(
|
215
|
+
"The LearnPack extension is not installed and this tutorial is meant to be run inside VSCode. Please install the LearnPack extension."
|
216
|
+
)
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
if (
|
222
|
+
configObj.config.suggestions &&
|
223
|
+
configObj.config.editor &&
|
224
|
+
configObj.config.suggestions.agent !== configObj.config.editor.agent
|
225
|
+
) {
|
226
|
+
Console.warning(
|
227
|
+
`The suggested agent "${configObj.config.suggestions.agent}" is different from the detected agent "${configObj.config.editor.agent}"`
|
228
|
+
)
|
229
|
+
|
230
|
+
try {
|
231
|
+
configObj.config.warnings = {
|
232
|
+
...configObj.config.warnings, // Ensure we don't overwrite other warnings
|
233
|
+
agent: buildAgentWarning(
|
234
|
+
configObj.config.editor.agent,
|
235
|
+
configObj.config.suggestions.agent
|
236
|
+
),
|
237
|
+
}
|
238
|
+
} catch {
|
239
|
+
Console.error("IMPOSSIBLE TO SET WARNING")
|
240
|
+
}
|
241
|
+
}
|
242
|
+
|
189
243
|
if (configObj.config && !configObj.config.publicUrl)
|
190
244
|
configObj.config.publicUrl = `${configObj.address}:${configObj.config.port}`
|
191
245
|
|
@@ -206,7 +260,7 @@ configObj.config.editor.version = version
|
|
206
260
|
"https://raw.githubusercontent.com/learnpack/ide/master/package.json"
|
207
261
|
)
|
208
262
|
const packageJSON = await resp.json()
|
209
|
-
configObj.config.editor.version = packageJSON.version || "4.0.
|
263
|
+
configObj.config.editor.version = packageJSON.version || "4.0.2"
|
210
264
|
}
|
211
265
|
|
212
266
|
configObj.config.dirPath = "./" + confPath.base
|
@@ -258,7 +312,9 @@ return
|
|
258
312
|
|
259
313
|
return {
|
260
314
|
validLanguages: {},
|
261
|
-
get: () =>
|
315
|
+
get: () => {
|
316
|
+
return configObj
|
317
|
+
},
|
262
318
|
validateEngine: function (language: string, server: any, socket: any) {
|
263
319
|
// eslint-disable-next-line
|
264
320
|
const alias = (_l: string) => {
|
@@ -500,3 +556,145 @@ acc = { ...acc, [key]: value }
|
|
500
556
|
|
501
557
|
return acc
|
502
558
|
}
|
559
|
+
|
560
|
+
const buildAgentWarning = (current: TAgent, suggested: TAgent): string => {
|
561
|
+
const message = `# Agent mismatch!\n
|
562
|
+
|
563
|
+
In LearnPack, the agent is in charge of running the LearnPack interface.
|
564
|
+
|
565
|
+
You're currently using LearnPack through \`${current}\` but the suggested agent is \`${suggested}\`.
|
566
|
+
|
567
|
+
We recommend strongly recommend changing your agent.
|
568
|
+
|
569
|
+
${stepsToChangeAgent(suggested)}
|
570
|
+
`
|
571
|
+
|
572
|
+
return message
|
573
|
+
}
|
574
|
+
|
575
|
+
const stepsToChangeAgent = (agent: TAgent): string => {
|
576
|
+
if (agent === "vscode") {
|
577
|
+
return `
|
578
|
+
# Steps to Change Agent to VSCode
|
579
|
+
|
580
|
+
1. **Install VSCode**:
|
581
|
+
- Visit the [VSCode website](https://code.visualstudio.com/Download) and download the latest version.
|
582
|
+
- Follow the installation instructions for your operating system.
|
583
|
+
|
584
|
+
2. **Install LearnPack VSCode Extension**:
|
585
|
+
- Open VSCode.
|
586
|
+
- Go to the Extensions view by clicking on the Extensions icon in the Activity Bar on the side of the window or by pressing \`Ctrl+Shift+X\`.
|
587
|
+
- Search for "LearnPack" and click "Install".
|
588
|
+
- If the extension is already installed but disabled, enable it.
|
589
|
+
|
590
|
+
3. **Run LearnPack in VSCode**:
|
591
|
+
- Open your terminal in VSCode.
|
592
|
+
- Navigate to your LearnPack project directory.
|
593
|
+
- Run the following command:
|
594
|
+
\`\`\`sh
|
595
|
+
learnpack start
|
596
|
+
\`\`\`
|
597
|
+
|
598
|
+
We strongly recommend using VSCode for a better learning experience.
|
599
|
+
`
|
600
|
+
}
|
601
|
+
|
602
|
+
if (agent === "os") {
|
603
|
+
return `
|
604
|
+
# Steps to Change Agent to OS
|
605
|
+
|
606
|
+
This learning package was designed to run outside of VSCode. We strongly recommend closing VSCode and running the package independently.
|
607
|
+
|
608
|
+
1. **Close VSCode**:
|
609
|
+
- Save your work and close the VSCode application.
|
610
|
+
|
611
|
+
2. **Open a New Terminal**:
|
612
|
+
- Open a terminal or command prompt on your operating system.
|
613
|
+
|
614
|
+
3. **Navigate to Your LearnPack Project Directory**:
|
615
|
+
- Use the \`cd\` command to navigate to the directory where your LearnPack project is located.
|
616
|
+
|
617
|
+
4. **Run LearnPack**:
|
618
|
+
- Run the following command to start LearnPack:
|
619
|
+
\`\`\`sh
|
620
|
+
learnpack start
|
621
|
+
\`\`\`
|
622
|
+
|
623
|
+
We strongly recommend running the package independently for a better learning experience.
|
624
|
+
`
|
625
|
+
}
|
626
|
+
|
627
|
+
return ""
|
628
|
+
}
|
629
|
+
|
630
|
+
/**
|
631
|
+
* Checks if the 'code' command is available in the terminal.
|
632
|
+
*
|
633
|
+
* @returns {boolean} True if the 'code' command is available, false otherwise.
|
634
|
+
*/
|
635
|
+
const isCodeCommandAvailable = (): boolean => {
|
636
|
+
return shell.which("code") !== null
|
637
|
+
}
|
638
|
+
|
639
|
+
/**
|
640
|
+
* Checks if a specific VSCode extension is installed.
|
641
|
+
*
|
642
|
+
* @param {string} extensionName - The name of the extension to check.
|
643
|
+
* @returns {boolean} True if the extension is installed, false otherwise.
|
644
|
+
*/
|
645
|
+
const isExtensionInstalled = (extensionName: string): boolean => {
|
646
|
+
if (!isCodeCommandAvailable()) {
|
647
|
+
throw new Error(
|
648
|
+
"The 'code' command is not available in the terminal. Please ensure that VSCode is installed and the 'code' command is added to your PATH."
|
649
|
+
)
|
650
|
+
}
|
651
|
+
|
652
|
+
const result = shell.exec("code --list-extensions", { silent: true })
|
653
|
+
return result.stdout.split("\n").includes(extensionName)
|
654
|
+
}
|
655
|
+
|
656
|
+
const EXTENSION_INSTALLATION_STEPS = `
|
657
|
+
# Steps to Install LearnPack VSCode Extension
|
658
|
+
|
659
|
+
1. **Open VSCode**:
|
660
|
+
- Launch the Visual Studio Code application on your computer.
|
661
|
+
|
662
|
+
2. **Go to Extensions View**:
|
663
|
+
- Click on the Extensions icon in the Activity Bar on the side of the window.
|
664
|
+
- Alternatively, you can open the Extensions view by pressing \`Ctrl+Shift+X\`.
|
665
|
+
|
666
|
+
3. **Search for LearnPack**:
|
667
|
+
- In the Extensions view, type "LearnPack" into the search bar.
|
668
|
+
|
669
|
+
4. **Install the Extension**:
|
670
|
+
- Find the "LearnPack" extension in the search results and click the "Install" button.
|
671
|
+
- If the extension is already installed but disabled, click the "Enable" button.
|
672
|
+
|
673
|
+
5. **Verify Installation**:
|
674
|
+
- Once installed, you can verify the installation by running the following command in the terminal:
|
675
|
+
\`\`\`sh
|
676
|
+
code --list-extensions | grep learn-pack.learnpack-vscode
|
677
|
+
\`\`\`
|
678
|
+
- If the extension is listed, it means the installation was successful.
|
679
|
+
|
680
|
+
We strongly recommend using the LearnPack extension in VSCode for a better learning experience.
|
681
|
+
`
|
682
|
+
|
683
|
+
/**
|
684
|
+
* Installs the LearnPack VSCode extension if the 'code' command is available.
|
685
|
+
*
|
686
|
+
* @param {string} extensionName - The name of the extension to install.
|
687
|
+
* @returns {boolean} True if the installation was successful, false otherwise.
|
688
|
+
*/
|
689
|
+
const installExtension = (extensionName: string): boolean => {
|
690
|
+
if (!isCodeCommandAvailable()) {
|
691
|
+
throw new Error(
|
692
|
+
"The 'code' command is not available in the terminal. Please ensure that VSCode is installed and the 'code' command is added to your PATH."
|
693
|
+
)
|
694
|
+
}
|
695
|
+
|
696
|
+
const result = shell.exec(`code --install-extension ${extensionName}`, {
|
697
|
+
silent: true,
|
698
|
+
})
|
699
|
+
return result.code === 0
|
700
|
+
}
|
package/src/managers/file.ts
CHANGED
@@ -29,6 +29,7 @@ export const decompress = (sourcePath: string, destinationPath: string) =>
|
|
29
29
|
}
|
30
30
|
)
|
31
31
|
})
|
32
|
+
|
32
33
|
export const downloadEditor = async (
|
33
34
|
version: string | undefined,
|
34
35
|
destination: string
|
@@ -54,41 +55,42 @@ export const downloadEditor = async (
|
|
54
55
|
let tags
|
55
56
|
try {
|
56
57
|
const tagsRes = await fetch(
|
57
|
-
"https://
|
58
|
+
"https://raw.githubusercontent.com/learnpack/ide/master/versions.json"
|
58
59
|
)
|
59
60
|
tags = await tagsRes.json()
|
60
61
|
|
61
|
-
if (tags
|
62
|
-
throw new
|
62
|
+
if (!Array.isArray(tags)) {
|
63
|
+
throw new TypeError(`Invalid versions.json format`)
|
63
64
|
}
|
64
65
|
} catch (error) {
|
65
|
-
Console.
|
66
|
+
Console.debug(
|
67
|
+
"Error reading versions.json, defaulting to version 4.0.2",
|
68
|
+
error
|
69
|
+
)
|
66
70
|
version = "4.0.2"
|
67
|
-
tags = [
|
71
|
+
tags = [`learnpack-${version}.tar.gz`]
|
68
72
|
}
|
69
73
|
|
70
74
|
let matchingTags
|
71
75
|
try {
|
72
76
|
if (tags.length === 0 || !tags)
|
73
|
-
throw InternalError(`No found tags in
|
77
|
+
throw InternalError(`No found tags in versions.json ${tags}`)
|
74
78
|
|
75
79
|
matchingTags = tags
|
76
|
-
.filter(
|
77
|
-
.sort((a
|
78
|
-
b.name.localeCompare(a.name, undefined, { numeric: true })
|
79
|
-
)
|
80
|
+
.filter(tag => tag.includes(versionPrefix))
|
81
|
+
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }))
|
80
82
|
|
81
83
|
if (matchingTags.length === 0)
|
82
84
|
throw InternalError(
|
83
|
-
`No matching version found for prefix ${versionPrefix} in the
|
85
|
+
`No matching version found for prefix ${versionPrefix} in the versions.json`
|
84
86
|
)
|
85
87
|
} catch (error) {
|
86
|
-
Console.
|
88
|
+
Console.debug("Error processing tags, defaulting to version 4.0.2", error)
|
87
89
|
version = "4.0.2"
|
88
|
-
matchingTags = [
|
90
|
+
matchingTags = [`learnpack-${version}.tar.gz`]
|
89
91
|
}
|
90
92
|
|
91
|
-
const latestVersion = matchingTags[0]
|
93
|
+
const latestVersion = matchingTags[0]
|
92
94
|
.replace("learnpack-", "")
|
93
95
|
.replace(".tar.gz", "")
|
94
96
|
const versionNumber = parseInt(latestVersion.split(".")[0])
|
package/src/managers/socket.ts
CHANGED
@@ -144,11 +144,7 @@ actions = [actions]
|
|
144
144
|
files: Array<string> | null = null,
|
145
145
|
exercises: Array<string> | null = null
|
146
146
|
) {
|
147
|
-
this.emit(
|
148
|
-
"reload",
|
149
|
-
files?.join("") || "" /* TODO: Check it out this */,
|
150
|
-
exercises!
|
151
|
-
)
|
147
|
+
this.emit("reload", files?.join("") || "", exercises!)
|
152
148
|
},
|
153
149
|
openWindow: function (url = "") {
|
154
150
|
queue.dispatcher().enqueue(queue.events.OPEN_WINDOW, url)
|
@@ -195,7 +191,7 @@ actions = [actions]
|
|
195
191
|
}
|
196
192
|
|
197
193
|
// eslint-disable-next-line
|
198
|
-
this.config?.disabledActions?.forEach((a) => this.removeAllowed(a))
|
194
|
+
this.config?.disabledActions?.forEach((a) => this.removeAllowed(a))
|
199
195
|
|
200
196
|
this.socket?.emit("compiler", {
|
201
197
|
action,
|
package/src/models/config.ts
CHANGED
@@ -1,18 +1,13 @@
|
|
1
1
|
import { IExercise } from "./exercise-obj"
|
2
2
|
import { TTelemetryUrls } from "../managers/telemetry"
|
3
3
|
|
4
|
-
export type TGrading = "isolated" | "incremental" | "no-grading"
|
4
|
+
export type TGrading = "isolated" | "incremental" | "no-grading"
|
5
5
|
|
6
|
-
export type TMode = "preview" | "extension"
|
6
|
+
export type TMode = "preview" | "extension"
|
7
7
|
|
8
|
-
export type TConfigAction =
|
9
|
-
| "test"
|
10
|
-
| "build"
|
11
|
-
| "tutorial"
|
12
|
-
| "reset"
|
13
|
-
| "generate";
|
8
|
+
export type TConfigAction = "test" | "build" | "tutorial" | "reset" | "generate"
|
14
9
|
|
15
|
-
export type TConfigObjAttributes = "config" | "exercises" | "grading"
|
10
|
+
export type TConfigObjAttributes = "config" | "exercises" | "grading"
|
16
11
|
|
17
12
|
export type TCompiler =
|
18
13
|
| "webpack"
|
@@ -21,82 +16,91 @@ export type TCompiler =
|
|
21
16
|
| "html"
|
22
17
|
| "node"
|
23
18
|
| "python"
|
24
|
-
| "css"
|
19
|
+
| "css"
|
25
20
|
|
26
21
|
export interface IConfigPath {
|
27
|
-
base: string
|
22
|
+
base: string
|
28
23
|
}
|
29
24
|
|
30
|
-
type TAgent = "vscode" | "os";
|
31
|
-
|
32
25
|
export interface IEditor {
|
33
|
-
mode?: TMode
|
34
|
-
version
|
35
|
-
agent?: TAgent
|
36
|
-
hideTerminal?: boolean
|
26
|
+
mode?: TMode
|
27
|
+
version?: string
|
28
|
+
agent?: TAgent
|
29
|
+
hideTerminal?: boolean
|
37
30
|
}
|
38
31
|
|
39
32
|
export interface TEntries {
|
40
|
-
python3?: string
|
41
|
-
html?: string
|
42
|
-
node?: string
|
43
|
-
react?: string
|
44
|
-
java?: string
|
33
|
+
python3?: string
|
34
|
+
html?: string
|
35
|
+
node?: string
|
36
|
+
react?: string
|
37
|
+
java?: string
|
45
38
|
}
|
46
39
|
|
47
40
|
export interface IConfig {
|
48
|
-
os: string
|
49
|
-
port?: string
|
50
|
-
repository?: string
|
51
|
-
description?: string
|
52
|
-
slug?: string
|
53
|
-
dirPath: string
|
54
|
-
preview?: string
|
55
|
-
entries: TEntries
|
56
|
-
grading: TGrading
|
57
|
-
configPath: string
|
58
|
-
translations: Array<string
|
59
|
-
outputPath?: string
|
60
|
-
editor: IEditor
|
61
|
-
language: string
|
62
|
-
title: string
|
63
|
-
duration: number
|
64
|
-
difficulty?: string
|
65
|
-
exercisesPath: string
|
66
|
-
disableGrading: boolean
|
67
|
-
actions: Array<string
|
68
|
-
autoPlay: boolean
|
69
|
-
projectType?: string
|
41
|
+
os: string
|
42
|
+
port?: string
|
43
|
+
repository?: string
|
44
|
+
description?: string
|
45
|
+
slug?: string
|
46
|
+
dirPath: string
|
47
|
+
preview?: string // Picture thumbnail
|
48
|
+
entries: TEntries
|
49
|
+
grading: TGrading
|
50
|
+
configPath: string
|
51
|
+
translations: Array<string>
|
52
|
+
outputPath?: string
|
53
|
+
editor: IEditor
|
54
|
+
language: string
|
55
|
+
title: string
|
56
|
+
duration: number
|
57
|
+
difficulty?: string
|
58
|
+
exercisesPath: string
|
59
|
+
disableGrading: boolean // TODO: Deprecate
|
60
|
+
actions: Array<string> // TODO: Deprecate
|
61
|
+
autoPlay: boolean
|
62
|
+
projectType?: string
|
70
63
|
// TODO: nameExerciseValidation
|
71
|
-
contact?: string
|
72
|
-
disabledActions?: Array<TConfigAction
|
73
|
-
compiler: TCompiler
|
74
|
-
compilers: Array<TCompiler
|
75
|
-
publicPath: string
|
76
|
-
publicUrl?: string
|
77
|
-
bugsLink?: string
|
78
|
-
videoSolutions?: boolean
|
79
|
-
skills: Array<string
|
80
|
-
telemetry?: TTelemetryUrls
|
81
|
-
variables?: TVariables
|
64
|
+
contact?: string
|
65
|
+
disabledActions?: Array<TConfigAction>
|
66
|
+
compiler: TCompiler
|
67
|
+
compilers: Array<TCompiler>
|
68
|
+
publicPath: string
|
69
|
+
publicUrl?: string
|
70
|
+
bugsLink?: string
|
71
|
+
videoSolutions?: boolean
|
72
|
+
skills: Array<string>
|
73
|
+
telemetry?: TTelemetryUrls
|
74
|
+
variables?: TVariables
|
75
|
+
suggestions: TSuggestions
|
76
|
+
warnings: TWarnings
|
77
|
+
runHook: (...agrs: Array<any>) => void
|
78
|
+
testingFinishedCallback: (arg: any | undefined) => void
|
79
|
+
}
|
80
|
+
export type TAgent = "os" | "vscode" | null
|
81
|
+
|
82
|
+
type TSuggestions = {
|
83
|
+
agent: TAgent
|
84
|
+
}
|
82
85
|
|
83
|
-
|
84
|
-
|
86
|
+
type TWarnings = {
|
87
|
+
agent?: string
|
88
|
+
extension?: string
|
85
89
|
}
|
86
90
|
|
87
91
|
type TVariable = {
|
88
|
-
type: "command" | "string"
|
89
|
-
source: string
|
90
|
-
}
|
92
|
+
type: "command" | "string"
|
93
|
+
source: string
|
94
|
+
}
|
91
95
|
type TVariables = {
|
92
|
-
[key: string]: TVariable | string
|
93
|
-
}
|
96
|
+
[key: string]: TVariable | string
|
97
|
+
}
|
94
98
|
|
95
99
|
export interface IConfigObj {
|
96
|
-
session?: number
|
97
|
-
currentExercise?: any
|
98
|
-
config?: IConfig
|
99
|
-
exercises?: Array<IExercise
|
100
|
-
confPath?: IConfigPath
|
101
|
-
address?: string
|
100
|
+
session?: number
|
101
|
+
currentExercise?: any
|
102
|
+
config?: IConfig
|
103
|
+
exercises?: Array<IExercise>
|
104
|
+
confPath?: IConfigPath
|
105
|
+
address?: string // Maybe
|
102
106
|
}
|