@learnpack/learnpack 5.0.331 → 5.0.333
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/publish.js +80 -10
- package/package.json +1 -1
- package/src/commands/publish.ts +123 -17
package/lib/commands/publish.js
CHANGED
|
@@ -20,23 +20,56 @@ const rigoActions_1 = require("../utils/rigoActions");
|
|
|
20
20
|
const misc_1 = require("../utils/misc");
|
|
21
21
|
const creatorUtilities_1 = require("../utils/creatorUtilities");
|
|
22
22
|
const uploadZipEndpont = api_1.RIGOBOT_HOST + "/v1/learnpack/upload";
|
|
23
|
+
const getAvailableLangs = (learnJson) => {
|
|
24
|
+
const langs = Object.keys((learnJson === null || learnJson === void 0 ? void 0 : learnJson.title) || {});
|
|
25
|
+
return langs.filter((l) => typeof l === "string" && l.length > 0);
|
|
26
|
+
};
|
|
27
|
+
const getDefaultLang = (learnJson) => {
|
|
28
|
+
const availableLangs = getAvailableLangs(learnJson);
|
|
29
|
+
if (availableLangs.length === 0)
|
|
30
|
+
return "en";
|
|
31
|
+
return availableLangs.includes("en") ? "en" : availableLangs[0];
|
|
32
|
+
};
|
|
33
|
+
const getLocalizedValue = (translations, lang, fallbackLangs = ["en", "us"]) => {
|
|
34
|
+
if (!translations || typeof translations !== "object")
|
|
35
|
+
return "";
|
|
36
|
+
const direct = translations[lang];
|
|
37
|
+
if (typeof direct === "string" && direct.trim().length > 0)
|
|
38
|
+
return direct;
|
|
39
|
+
for (const fb of fallbackLangs) {
|
|
40
|
+
const v = translations[fb];
|
|
41
|
+
if (typeof v === "string" && v.trim().length > 0)
|
|
42
|
+
return v;
|
|
43
|
+
}
|
|
44
|
+
const firstKey = Object.keys(translations)[0];
|
|
45
|
+
const first = firstKey ? translations[firstKey] : "";
|
|
46
|
+
return typeof first === "string" ? first : "";
|
|
47
|
+
};
|
|
23
48
|
const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, all_translations = []) => {
|
|
24
49
|
const category = "uncategorized";
|
|
25
50
|
try {
|
|
26
51
|
const user = await api_1.default.validateToken(sessionPayload.token);
|
|
27
|
-
|
|
52
|
+
const assetTitle = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, selectedLang);
|
|
53
|
+
const assetDescription = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.description, selectedLang);
|
|
54
|
+
if (!assetTitle) {
|
|
55
|
+
throw new Error(`Missing learn.json title for language "${selectedLang}"`);
|
|
56
|
+
}
|
|
57
|
+
let slug = (0, creatorUtilities_1.slugify)(assetTitle).slice(0, 47);
|
|
28
58
|
slug = `${slug}-${selectedLang}`;
|
|
29
59
|
const { exists } = await api_1.default.doesAssetExists(sessionPayload.token, slug);
|
|
60
|
+
const technologies = Array.isArray(learnJson === null || learnJson === void 0 ? void 0 : learnJson.technologies) ?
|
|
61
|
+
learnJson.technologies :
|
|
62
|
+
[];
|
|
30
63
|
if (!exists) {
|
|
31
64
|
console_1.default.info("Asset does not exist in this academy, creating it");
|
|
32
65
|
const asset = await api_1.default.createAsset(sessionPayload.token, {
|
|
33
66
|
slug: slug,
|
|
34
|
-
title:
|
|
67
|
+
title: assetTitle,
|
|
35
68
|
lang: selectedLang,
|
|
36
69
|
graded: true,
|
|
37
|
-
description:
|
|
70
|
+
description: assetDescription,
|
|
38
71
|
learnpack_deploy_url: learnpackDeployUrl,
|
|
39
|
-
technologies:
|
|
72
|
+
technologies: technologies.map((tech) => String(tech).toLowerCase().replace(/\s+/g, "-")),
|
|
40
73
|
url: learnpackDeployUrl,
|
|
41
74
|
category: category,
|
|
42
75
|
owner: user.id,
|
|
@@ -60,9 +93,9 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
|
|
|
60
93
|
const asset = await api_1.default.updateAsset(sessionPayload.token, slug, {
|
|
61
94
|
graded: true,
|
|
62
95
|
learnpack_deploy_url: learnpackDeployUrl,
|
|
63
|
-
title:
|
|
96
|
+
title: assetTitle,
|
|
64
97
|
category: category,
|
|
65
|
-
description:
|
|
98
|
+
description: assetDescription,
|
|
66
99
|
all_translations,
|
|
67
100
|
});
|
|
68
101
|
try {
|
|
@@ -82,6 +115,35 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
|
|
|
82
115
|
}
|
|
83
116
|
};
|
|
84
117
|
exports.handleAssetCreation = handleAssetCreation;
|
|
118
|
+
const createMultiLangAssetFromDisk = async (sessionPayload, learnJson, deployUrl) => {
|
|
119
|
+
const availableLangs = getAvailableLangs(learnJson);
|
|
120
|
+
if (availableLangs.length === 0) {
|
|
121
|
+
console_1.default.error("No languages found in learn.json.title. Add at least one language (e.g. title.en).");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const all_translations = [];
|
|
125
|
+
for (const lang of availableLangs) {
|
|
126
|
+
const readmePath = path.join(process.cwd(), `README${(0, creatorUtilities_1.getReadmeExtension)(lang)}`);
|
|
127
|
+
let indexReadmeString = "";
|
|
128
|
+
try {
|
|
129
|
+
if (fs.existsSync(readmePath)) {
|
|
130
|
+
indexReadmeString = fs.readFileSync(readmePath, "utf-8");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console_1.default.error("Error reading index readme:", error);
|
|
135
|
+
indexReadmeString = "";
|
|
136
|
+
}
|
|
137
|
+
const b64IndexReadme = Buffer.from(indexReadmeString).toString("base64");
|
|
138
|
+
// eslint-disable-next-line no-await-in-loop
|
|
139
|
+
const asset = await (0, exports.handleAssetCreation)(sessionPayload, learnJson, lang, deployUrl, b64IndexReadme, all_translations);
|
|
140
|
+
if (!asset) {
|
|
141
|
+
console_1.default.debug("Could not create/update asset for lang", lang);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
all_translations.push(asset.slug);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
85
147
|
const runAudit = (strict) => {
|
|
86
148
|
try {
|
|
87
149
|
console_1.default.info("Running learnpack audit before publishing...");
|
|
@@ -245,8 +307,11 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
245
307
|
const buildManifestPWA = path.join(buildDir, "manifest.webmanifest");
|
|
246
308
|
if (fs.existsSync(indexHtmlPath)) {
|
|
247
309
|
let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8");
|
|
248
|
-
const
|
|
249
|
-
const
|
|
310
|
+
const selectedLang = getDefaultLang(learnJson);
|
|
311
|
+
const description = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.description, selectedLang) ||
|
|
312
|
+
"LearnPack is awesome!";
|
|
313
|
+
const title = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, selectedLang) ||
|
|
314
|
+
"LearnPack: Interactive Learning as a Service";
|
|
250
315
|
const previewUrl = learnJson.preview ||
|
|
251
316
|
"https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/public/learnpack.svg";
|
|
252
317
|
// Replace placeholders and the <title>Old title </title> tag for a new tag with the title
|
|
@@ -265,7 +330,12 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
265
330
|
}
|
|
266
331
|
if (fs.existsSync(manifestPWA)) {
|
|
267
332
|
let manifestPWAContent = fs.readFileSync(manifestPWA, "utf-8");
|
|
268
|
-
|
|
333
|
+
const selectedLang = getDefaultLang(learnJson);
|
|
334
|
+
const courseTitle = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, selectedLang) ||
|
|
335
|
+
getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, "us") ||
|
|
336
|
+
getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, "en") ||
|
|
337
|
+
"LearnPack";
|
|
338
|
+
manifestPWAContent = manifestPWAContent.replace("{{course_title}}", courseTitle);
|
|
269
339
|
const courseShortName = { answer: "testing-tutorial" };
|
|
270
340
|
// const courseShortName = await generateCourseShortName(rigoToken, {
|
|
271
341
|
// learnJSON: JSON.stringify(learnJson),
|
|
@@ -314,7 +384,7 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
314
384
|
console.log(res.data);
|
|
315
385
|
fs.unlinkSync(zipFilePath);
|
|
316
386
|
this.removeDirectory(buildDir);
|
|
317
|
-
await (
|
|
387
|
+
await createMultiLangAssetFromDisk({ token: sessionPayload.token, rigobotToken: rigoToken }, learnJson, res.data.url);
|
|
318
388
|
}
|
|
319
389
|
catch (error) {
|
|
320
390
|
if (axios_1.default.isAxiosError(error)) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learnpack/learnpack",
|
|
3
3
|
"description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
|
|
4
|
-
"version": "5.0.
|
|
4
|
+
"version": "5.0.333",
|
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
|
6
6
|
"contributors": [
|
|
7
7
|
{
|
package/src/commands/publish.ts
CHANGED
|
@@ -19,10 +19,41 @@ import api, { getConsumable, RIGOBOT_HOST, TAcademy } from "../utils/api"
|
|
|
19
19
|
import * as prompts from "prompts"
|
|
20
20
|
import { isValidRigoToken } from "../utils/rigoActions"
|
|
21
21
|
import { minutesToISO8601Duration } from "../utils/misc"
|
|
22
|
-
import { slugify } from "../utils/creatorUtilities"
|
|
22
|
+
import { getReadmeExtension, slugify } from "../utils/creatorUtilities"
|
|
23
23
|
|
|
24
24
|
const uploadZipEndpont = RIGOBOT_HOST + "/v1/learnpack/upload"
|
|
25
25
|
|
|
26
|
+
const getAvailableLangs = (learnJson: any): string[] => {
|
|
27
|
+
const langs = Object.keys(learnJson?.title || {})
|
|
28
|
+
return langs.filter((l) => typeof l === "string" && l.length > 0)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getDefaultLang = (learnJson: any): string => {
|
|
32
|
+
const availableLangs = getAvailableLangs(learnJson)
|
|
33
|
+
if (availableLangs.length === 0) return "en"
|
|
34
|
+
return availableLangs.includes("en") ? "en" : availableLangs[0]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const getLocalizedValue = (
|
|
38
|
+
translations: Record<string, any> | undefined,
|
|
39
|
+
lang: string,
|
|
40
|
+
fallbackLangs: string[] = ["en", "us"]
|
|
41
|
+
): string => {
|
|
42
|
+
if (!translations || typeof translations !== "object") return ""
|
|
43
|
+
|
|
44
|
+
const direct = translations[lang]
|
|
45
|
+
if (typeof direct === "string" && direct.trim().length > 0) return direct
|
|
46
|
+
|
|
47
|
+
for (const fb of fallbackLangs) {
|
|
48
|
+
const v = translations[fb]
|
|
49
|
+
if (typeof v === "string" && v.trim().length > 0) return v
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const firstKey = Object.keys(translations)[0]
|
|
53
|
+
const first = firstKey ? translations[firstKey] : ""
|
|
54
|
+
return typeof first === "string" ? first : ""
|
|
55
|
+
}
|
|
56
|
+
|
|
26
57
|
export const handleAssetCreation = async (
|
|
27
58
|
sessionPayload: { token: string; rigobotToken: string },
|
|
28
59
|
learnJson: any,
|
|
@@ -36,22 +67,38 @@ export const handleAssetCreation = async (
|
|
|
36
67
|
try {
|
|
37
68
|
const user = await api.validateToken(sessionPayload.token)
|
|
38
69
|
|
|
39
|
-
|
|
70
|
+
const assetTitle = getLocalizedValue(learnJson?.title, selectedLang)
|
|
71
|
+
const assetDescription = getLocalizedValue(
|
|
72
|
+
learnJson?.description,
|
|
73
|
+
selectedLang
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if (!assetTitle) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Missing learn.json title for language "${selectedLang}"`
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let slug = slugify(assetTitle).slice(0, 47)
|
|
40
83
|
slug = `${slug}-${selectedLang}`
|
|
41
84
|
|
|
42
85
|
const { exists } = await api.doesAssetExists(sessionPayload.token, slug)
|
|
43
86
|
|
|
87
|
+
const technologies: unknown[] = Array.isArray(learnJson?.technologies) ?
|
|
88
|
+
learnJson.technologies :
|
|
89
|
+
[]
|
|
90
|
+
|
|
44
91
|
if (!exists) {
|
|
45
92
|
Console.info("Asset does not exist in this academy, creating it")
|
|
46
93
|
const asset = await api.createAsset(sessionPayload.token, {
|
|
47
94
|
slug: slug,
|
|
48
|
-
title:
|
|
95
|
+
title: assetTitle,
|
|
49
96
|
lang: selectedLang,
|
|
50
97
|
graded: true,
|
|
51
|
-
description:
|
|
98
|
+
description: assetDescription,
|
|
52
99
|
learnpack_deploy_url: learnpackDeployUrl,
|
|
53
|
-
technologies:
|
|
54
|
-
tech.toLowerCase().replace(/\s+/g, "-")
|
|
100
|
+
technologies: technologies.map((tech: unknown) =>
|
|
101
|
+
String(tech).toLowerCase().replace(/\s+/g, "-")
|
|
55
102
|
),
|
|
56
103
|
url: learnpackDeployUrl,
|
|
57
104
|
category: category,
|
|
@@ -81,9 +128,9 @@ export const handleAssetCreation = async (
|
|
|
81
128
|
const asset = await api.updateAsset(sessionPayload.token, slug, {
|
|
82
129
|
graded: true,
|
|
83
130
|
learnpack_deploy_url: learnpackDeployUrl,
|
|
84
|
-
title:
|
|
131
|
+
title: assetTitle,
|
|
85
132
|
category: category,
|
|
86
|
-
description:
|
|
133
|
+
description: assetDescription,
|
|
87
134
|
all_translations,
|
|
88
135
|
})
|
|
89
136
|
try {
|
|
@@ -106,6 +153,58 @@ export const handleAssetCreation = async (
|
|
|
106
153
|
}
|
|
107
154
|
}
|
|
108
155
|
|
|
156
|
+
const createMultiLangAssetFromDisk = async (
|
|
157
|
+
sessionPayload: { token: string; rigobotToken: string },
|
|
158
|
+
learnJson: any,
|
|
159
|
+
deployUrl: string
|
|
160
|
+
) => {
|
|
161
|
+
const availableLangs = getAvailableLangs(learnJson)
|
|
162
|
+
|
|
163
|
+
if (availableLangs.length === 0) {
|
|
164
|
+
Console.error(
|
|
165
|
+
"No languages found in learn.json.title. Add at least one language (e.g. title.en)."
|
|
166
|
+
)
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const all_translations: string[] = []
|
|
171
|
+
for (const lang of availableLangs) {
|
|
172
|
+
const readmePath = path.join(
|
|
173
|
+
process.cwd(),
|
|
174
|
+
`README${getReadmeExtension(lang)}`
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
let indexReadmeString = ""
|
|
178
|
+
try {
|
|
179
|
+
if (fs.existsSync(readmePath)) {
|
|
180
|
+
indexReadmeString = fs.readFileSync(readmePath, "utf-8")
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
Console.error("Error reading index readme:", error)
|
|
184
|
+
indexReadmeString = ""
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const b64IndexReadme = Buffer.from(indexReadmeString).toString("base64")
|
|
188
|
+
|
|
189
|
+
// eslint-disable-next-line no-await-in-loop
|
|
190
|
+
const asset = await handleAssetCreation(
|
|
191
|
+
sessionPayload,
|
|
192
|
+
learnJson,
|
|
193
|
+
lang,
|
|
194
|
+
deployUrl,
|
|
195
|
+
b64IndexReadme,
|
|
196
|
+
all_translations
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
if (!asset) {
|
|
200
|
+
Console.debug("Could not create/update asset for lang", lang)
|
|
201
|
+
continue
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
all_translations.push(asset.slug)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
109
208
|
const runAudit = (strict: boolean) => {
|
|
110
209
|
try {
|
|
111
210
|
Console.info("Running learnpack audit before publishing...")
|
|
@@ -362,9 +461,13 @@ class BuildCommand extends SessionCommand {
|
|
|
362
461
|
if (fs.existsSync(indexHtmlPath)) {
|
|
363
462
|
let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8")
|
|
364
463
|
|
|
365
|
-
const
|
|
464
|
+
const selectedLang = getDefaultLang(learnJson)
|
|
465
|
+
const description =
|
|
466
|
+
getLocalizedValue(learnJson?.description, selectedLang) ||
|
|
467
|
+
"LearnPack is awesome!"
|
|
366
468
|
const title =
|
|
367
|
-
learnJson
|
|
469
|
+
getLocalizedValue(learnJson?.title, selectedLang) ||
|
|
470
|
+
"LearnPack: Interactive Learning as a Service"
|
|
368
471
|
|
|
369
472
|
const previewUrl =
|
|
370
473
|
learnJson.preview ||
|
|
@@ -386,9 +489,15 @@ class BuildCommand extends SessionCommand {
|
|
|
386
489
|
|
|
387
490
|
if (fs.existsSync(manifestPWA)) {
|
|
388
491
|
let manifestPWAContent = fs.readFileSync(manifestPWA, "utf-8")
|
|
492
|
+
const selectedLang = getDefaultLang(learnJson)
|
|
493
|
+
const courseTitle =
|
|
494
|
+
getLocalizedValue(learnJson?.title, selectedLang) ||
|
|
495
|
+
getLocalizedValue(learnJson?.title, "us") ||
|
|
496
|
+
getLocalizedValue(learnJson?.title, "en") ||
|
|
497
|
+
"LearnPack"
|
|
389
498
|
manifestPWAContent = manifestPWAContent.replace(
|
|
390
499
|
"{{course_title}}",
|
|
391
|
-
|
|
500
|
+
courseTitle
|
|
392
501
|
)
|
|
393
502
|
|
|
394
503
|
const courseShortName = { answer: "testing-tutorial" }
|
|
@@ -454,13 +563,10 @@ class BuildCommand extends SessionCommand {
|
|
|
454
563
|
fs.unlinkSync(zipFilePath)
|
|
455
564
|
this.removeDirectory(buildDir)
|
|
456
565
|
|
|
457
|
-
await
|
|
458
|
-
sessionPayload,
|
|
566
|
+
await createMultiLangAssetFromDisk(
|
|
567
|
+
{ token: sessionPayload.token, rigobotToken: rigoToken },
|
|
459
568
|
learnJson,
|
|
460
|
-
|
|
461
|
-
res.data.url,
|
|
462
|
-
"",
|
|
463
|
-
[]
|
|
569
|
+
res.data.url
|
|
464
570
|
)
|
|
465
571
|
} catch (error) {
|
|
466
572
|
if (axios.isAxiosError(error)) {
|