@learnpack/learnpack 5.0.331 → 5.0.332
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 +76 -9
- package/package.json +1 -1
- package/src/commands/publish.ts +117 -15
package/lib/commands/publish.js
CHANGED
|
@@ -20,21 +20,51 @@ 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);
|
|
30
60
|
if (!exists) {
|
|
31
61
|
console_1.default.info("Asset does not exist in this academy, creating it");
|
|
32
62
|
const asset = await api_1.default.createAsset(sessionPayload.token, {
|
|
33
63
|
slug: slug,
|
|
34
|
-
title:
|
|
64
|
+
title: assetTitle,
|
|
35
65
|
lang: selectedLang,
|
|
36
66
|
graded: true,
|
|
37
|
-
description:
|
|
67
|
+
description: assetDescription,
|
|
38
68
|
learnpack_deploy_url: learnpackDeployUrl,
|
|
39
69
|
technologies: learnJson.technologies.map((tech) => tech.toLowerCase().replace(/\s+/g, "-")),
|
|
40
70
|
url: learnpackDeployUrl,
|
|
@@ -60,9 +90,9 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
|
|
|
60
90
|
const asset = await api_1.default.updateAsset(sessionPayload.token, slug, {
|
|
61
91
|
graded: true,
|
|
62
92
|
learnpack_deploy_url: learnpackDeployUrl,
|
|
63
|
-
title:
|
|
93
|
+
title: assetTitle,
|
|
64
94
|
category: category,
|
|
65
|
-
description:
|
|
95
|
+
description: assetDescription,
|
|
66
96
|
all_translations,
|
|
67
97
|
});
|
|
68
98
|
try {
|
|
@@ -82,6 +112,35 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
|
|
|
82
112
|
}
|
|
83
113
|
};
|
|
84
114
|
exports.handleAssetCreation = handleAssetCreation;
|
|
115
|
+
const createMultiLangAssetFromDisk = async (sessionPayload, learnJson, deployUrl) => {
|
|
116
|
+
const availableLangs = getAvailableLangs(learnJson);
|
|
117
|
+
if (availableLangs.length === 0) {
|
|
118
|
+
console_1.default.error("No languages found in learn.json.title. Add at least one language (e.g. title.en).");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const all_translations = [];
|
|
122
|
+
for (const lang of availableLangs) {
|
|
123
|
+
const readmePath = path.join(process.cwd(), `README${(0, creatorUtilities_1.getReadmeExtension)(lang)}`);
|
|
124
|
+
let indexReadmeString = "";
|
|
125
|
+
try {
|
|
126
|
+
if (fs.existsSync(readmePath)) {
|
|
127
|
+
indexReadmeString = fs.readFileSync(readmePath, "utf-8");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console_1.default.error("Error reading index readme:", error);
|
|
132
|
+
indexReadmeString = "";
|
|
133
|
+
}
|
|
134
|
+
const b64IndexReadme = Buffer.from(indexReadmeString).toString("base64");
|
|
135
|
+
// eslint-disable-next-line no-await-in-loop
|
|
136
|
+
const asset = await (0, exports.handleAssetCreation)(sessionPayload, learnJson, lang, deployUrl, b64IndexReadme, all_translations);
|
|
137
|
+
if (!asset) {
|
|
138
|
+
console_1.default.debug("Could not create/update asset for lang", lang);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
all_translations.push(asset.slug);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
85
144
|
const runAudit = (strict) => {
|
|
86
145
|
try {
|
|
87
146
|
console_1.default.info("Running learnpack audit before publishing...");
|
|
@@ -245,8 +304,11 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
245
304
|
const buildManifestPWA = path.join(buildDir, "manifest.webmanifest");
|
|
246
305
|
if (fs.existsSync(indexHtmlPath)) {
|
|
247
306
|
let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8");
|
|
248
|
-
const
|
|
249
|
-
const
|
|
307
|
+
const selectedLang = getDefaultLang(learnJson);
|
|
308
|
+
const description = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.description, selectedLang) ||
|
|
309
|
+
"LearnPack is awesome!";
|
|
310
|
+
const title = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, selectedLang) ||
|
|
311
|
+
"LearnPack: Interactive Learning as a Service";
|
|
250
312
|
const previewUrl = learnJson.preview ||
|
|
251
313
|
"https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/public/learnpack.svg";
|
|
252
314
|
// Replace placeholders and the <title>Old title </title> tag for a new tag with the title
|
|
@@ -265,7 +327,12 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
265
327
|
}
|
|
266
328
|
if (fs.existsSync(manifestPWA)) {
|
|
267
329
|
let manifestPWAContent = fs.readFileSync(manifestPWA, "utf-8");
|
|
268
|
-
|
|
330
|
+
const selectedLang = getDefaultLang(learnJson);
|
|
331
|
+
const courseTitle = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, selectedLang) ||
|
|
332
|
+
getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, "us") ||
|
|
333
|
+
getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, "en") ||
|
|
334
|
+
"LearnPack";
|
|
335
|
+
manifestPWAContent = manifestPWAContent.replace("{{course_title}}", courseTitle);
|
|
269
336
|
const courseShortName = { answer: "testing-tutorial" };
|
|
270
337
|
// const courseShortName = await generateCourseShortName(rigoToken, {
|
|
271
338
|
// learnJSON: JSON.stringify(learnJson),
|
|
@@ -314,7 +381,7 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
314
381
|
console.log(res.data);
|
|
315
382
|
fs.unlinkSync(zipFilePath);
|
|
316
383
|
this.removeDirectory(buildDir);
|
|
317
|
-
await (
|
|
384
|
+
await createMultiLangAssetFromDisk({ token: sessionPayload.token, rigobotToken: rigoToken }, learnJson, res.data.url);
|
|
318
385
|
}
|
|
319
386
|
catch (error) {
|
|
320
387
|
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.332",
|
|
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,7 +67,19 @@ 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)
|
|
@@ -45,10 +88,10 @@ export const handleAssetCreation = async (
|
|
|
45
88
|
Console.info("Asset does not exist in this academy, creating it")
|
|
46
89
|
const asset = await api.createAsset(sessionPayload.token, {
|
|
47
90
|
slug: slug,
|
|
48
|
-
title:
|
|
91
|
+
title: assetTitle,
|
|
49
92
|
lang: selectedLang,
|
|
50
93
|
graded: true,
|
|
51
|
-
description:
|
|
94
|
+
description: assetDescription,
|
|
52
95
|
learnpack_deploy_url: learnpackDeployUrl,
|
|
53
96
|
technologies: learnJson.technologies.map((tech: string) =>
|
|
54
97
|
tech.toLowerCase().replace(/\s+/g, "-")
|
|
@@ -81,9 +124,9 @@ export const handleAssetCreation = async (
|
|
|
81
124
|
const asset = await api.updateAsset(sessionPayload.token, slug, {
|
|
82
125
|
graded: true,
|
|
83
126
|
learnpack_deploy_url: learnpackDeployUrl,
|
|
84
|
-
title:
|
|
127
|
+
title: assetTitle,
|
|
85
128
|
category: category,
|
|
86
|
-
description:
|
|
129
|
+
description: assetDescription,
|
|
87
130
|
all_translations,
|
|
88
131
|
})
|
|
89
132
|
try {
|
|
@@ -106,6 +149,58 @@ export const handleAssetCreation = async (
|
|
|
106
149
|
}
|
|
107
150
|
}
|
|
108
151
|
|
|
152
|
+
const createMultiLangAssetFromDisk = async (
|
|
153
|
+
sessionPayload: { token: string; rigobotToken: string },
|
|
154
|
+
learnJson: any,
|
|
155
|
+
deployUrl: string
|
|
156
|
+
) => {
|
|
157
|
+
const availableLangs = getAvailableLangs(learnJson)
|
|
158
|
+
|
|
159
|
+
if (availableLangs.length === 0) {
|
|
160
|
+
Console.error(
|
|
161
|
+
"No languages found in learn.json.title. Add at least one language (e.g. title.en)."
|
|
162
|
+
)
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const all_translations: string[] = []
|
|
167
|
+
for (const lang of availableLangs) {
|
|
168
|
+
const readmePath = path.join(
|
|
169
|
+
process.cwd(),
|
|
170
|
+
`README${getReadmeExtension(lang)}`
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
let indexReadmeString = ""
|
|
174
|
+
try {
|
|
175
|
+
if (fs.existsSync(readmePath)) {
|
|
176
|
+
indexReadmeString = fs.readFileSync(readmePath, "utf-8")
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
Console.error("Error reading index readme:", error)
|
|
180
|
+
indexReadmeString = ""
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const b64IndexReadme = Buffer.from(indexReadmeString).toString("base64")
|
|
184
|
+
|
|
185
|
+
// eslint-disable-next-line no-await-in-loop
|
|
186
|
+
const asset = await handleAssetCreation(
|
|
187
|
+
sessionPayload,
|
|
188
|
+
learnJson,
|
|
189
|
+
lang,
|
|
190
|
+
deployUrl,
|
|
191
|
+
b64IndexReadme,
|
|
192
|
+
all_translations
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if (!asset) {
|
|
196
|
+
Console.debug("Could not create/update asset for lang", lang)
|
|
197
|
+
continue
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
all_translations.push(asset.slug)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
109
204
|
const runAudit = (strict: boolean) => {
|
|
110
205
|
try {
|
|
111
206
|
Console.info("Running learnpack audit before publishing...")
|
|
@@ -362,9 +457,13 @@ class BuildCommand extends SessionCommand {
|
|
|
362
457
|
if (fs.existsSync(indexHtmlPath)) {
|
|
363
458
|
let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8")
|
|
364
459
|
|
|
365
|
-
const
|
|
460
|
+
const selectedLang = getDefaultLang(learnJson)
|
|
461
|
+
const description =
|
|
462
|
+
getLocalizedValue(learnJson?.description, selectedLang) ||
|
|
463
|
+
"LearnPack is awesome!"
|
|
366
464
|
const title =
|
|
367
|
-
learnJson
|
|
465
|
+
getLocalizedValue(learnJson?.title, selectedLang) ||
|
|
466
|
+
"LearnPack: Interactive Learning as a Service"
|
|
368
467
|
|
|
369
468
|
const previewUrl =
|
|
370
469
|
learnJson.preview ||
|
|
@@ -386,9 +485,15 @@ class BuildCommand extends SessionCommand {
|
|
|
386
485
|
|
|
387
486
|
if (fs.existsSync(manifestPWA)) {
|
|
388
487
|
let manifestPWAContent = fs.readFileSync(manifestPWA, "utf-8")
|
|
488
|
+
const selectedLang = getDefaultLang(learnJson)
|
|
489
|
+
const courseTitle =
|
|
490
|
+
getLocalizedValue(learnJson?.title, selectedLang) ||
|
|
491
|
+
getLocalizedValue(learnJson?.title, "us") ||
|
|
492
|
+
getLocalizedValue(learnJson?.title, "en") ||
|
|
493
|
+
"LearnPack"
|
|
389
494
|
manifestPWAContent = manifestPWAContent.replace(
|
|
390
495
|
"{{course_title}}",
|
|
391
|
-
|
|
496
|
+
courseTitle
|
|
392
497
|
)
|
|
393
498
|
|
|
394
499
|
const courseShortName = { answer: "testing-tutorial" }
|
|
@@ -454,13 +559,10 @@ class BuildCommand extends SessionCommand {
|
|
|
454
559
|
fs.unlinkSync(zipFilePath)
|
|
455
560
|
this.removeDirectory(buildDir)
|
|
456
561
|
|
|
457
|
-
await
|
|
458
|
-
sessionPayload,
|
|
562
|
+
await createMultiLangAssetFromDisk(
|
|
563
|
+
{ token: sessionPayload.token, rigobotToken: rigoToken },
|
|
459
564
|
learnJson,
|
|
460
|
-
|
|
461
|
-
res.data.url,
|
|
462
|
-
"",
|
|
463
|
-
[]
|
|
565
|
+
res.data.url
|
|
464
566
|
)
|
|
465
567
|
} catch (error) {
|
|
466
568
|
if (axios.isAxiosError(error)) {
|