scorm-review 1.0.0 → 1.1.0
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/dist/cli.js +143 -58
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2894,8 +2894,8 @@ var require_utils = __commonJS({
|
|
|
2894
2894
|
var result = transform[inputType][outputType](input);
|
|
2895
2895
|
return result;
|
|
2896
2896
|
};
|
|
2897
|
-
exports2.resolve = function(
|
|
2898
|
-
var parts =
|
|
2897
|
+
exports2.resolve = function(path3) {
|
|
2898
|
+
var parts = path3.split("/");
|
|
2899
2899
|
var result = [];
|
|
2900
2900
|
for (var index = 0; index < parts.length; index++) {
|
|
2901
2901
|
var part = parts[index];
|
|
@@ -8725,18 +8725,18 @@ var require_object = __commonJS({
|
|
|
8725
8725
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
8726
8726
|
this.files[name] = object;
|
|
8727
8727
|
};
|
|
8728
|
-
var parentFolder = function(
|
|
8729
|
-
if (
|
|
8730
|
-
|
|
8728
|
+
var parentFolder = function(path3) {
|
|
8729
|
+
if (path3.slice(-1) === "/") {
|
|
8730
|
+
path3 = path3.substring(0, path3.length - 1);
|
|
8731
8731
|
}
|
|
8732
|
-
var lastSlash =
|
|
8733
|
-
return lastSlash > 0 ?
|
|
8732
|
+
var lastSlash = path3.lastIndexOf("/");
|
|
8733
|
+
return lastSlash > 0 ? path3.substring(0, lastSlash) : "";
|
|
8734
8734
|
};
|
|
8735
|
-
var forceTrailingSlash = function(
|
|
8736
|
-
if (
|
|
8737
|
-
|
|
8735
|
+
var forceTrailingSlash = function(path3) {
|
|
8736
|
+
if (path3.slice(-1) !== "/") {
|
|
8737
|
+
path3 += "/";
|
|
8738
8738
|
}
|
|
8739
|
-
return
|
|
8739
|
+
return path3;
|
|
8740
8740
|
};
|
|
8741
8741
|
var folderAdd = function(name, createFolders) {
|
|
8742
8742
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -9702,9 +9702,9 @@ var require_load = __commonJS({
|
|
|
9702
9702
|
var require_lib3 = __commonJS({
|
|
9703
9703
|
"node_modules/jszip/lib/index.js"(exports2, module2) {
|
|
9704
9704
|
"use strict";
|
|
9705
|
-
function
|
|
9706
|
-
if (!(this instanceof
|
|
9707
|
-
return new
|
|
9705
|
+
function JSZip3() {
|
|
9706
|
+
if (!(this instanceof JSZip3)) {
|
|
9707
|
+
return new JSZip3();
|
|
9708
9708
|
}
|
|
9709
9709
|
if (arguments.length) {
|
|
9710
9710
|
throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
|
|
@@ -9713,7 +9713,7 @@ var require_lib3 = __commonJS({
|
|
|
9713
9713
|
this.comment = null;
|
|
9714
9714
|
this.root = "";
|
|
9715
9715
|
this.clone = function() {
|
|
9716
|
-
var newObj = new
|
|
9716
|
+
var newObj = new JSZip3();
|
|
9717
9717
|
for (var i in this) {
|
|
9718
9718
|
if (typeof this[i] !== "function") {
|
|
9719
9719
|
newObj[i] = this[i];
|
|
@@ -9722,25 +9722,28 @@ var require_lib3 = __commonJS({
|
|
|
9722
9722
|
return newObj;
|
|
9723
9723
|
};
|
|
9724
9724
|
}
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
return new
|
|
9725
|
+
JSZip3.prototype = require_object();
|
|
9726
|
+
JSZip3.prototype.loadAsync = require_load();
|
|
9727
|
+
JSZip3.support = require_support();
|
|
9728
|
+
JSZip3.defaults = require_defaults();
|
|
9729
|
+
JSZip3.version = "3.10.1";
|
|
9730
|
+
JSZip3.loadAsync = function(content, options) {
|
|
9731
|
+
return new JSZip3().loadAsync(content, options);
|
|
9732
9732
|
};
|
|
9733
|
-
|
|
9734
|
-
module2.exports =
|
|
9733
|
+
JSZip3.external = require_external();
|
|
9734
|
+
module2.exports = JSZip3;
|
|
9735
9735
|
}
|
|
9736
9736
|
});
|
|
9737
9737
|
|
|
9738
9738
|
// src/cli.ts
|
|
9739
|
-
var
|
|
9740
|
-
var
|
|
9739
|
+
var import_fs2 = __toESM(require("fs"));
|
|
9740
|
+
var import_path2 = __toESM(require("path"));
|
|
9741
|
+
var import_jszip2 = __toESM(require_lib3());
|
|
9741
9742
|
|
|
9742
9743
|
// src/bundler.ts
|
|
9743
9744
|
var import_jszip = __toESM(require_lib3());
|
|
9745
|
+
var import_fs = __toESM(require("fs"));
|
|
9746
|
+
var import_path = __toESM(require("path"));
|
|
9744
9747
|
|
|
9745
9748
|
// src/bundler-template.ts
|
|
9746
9749
|
var UNPACKER_SCRIPT = `
|
|
@@ -9932,6 +9935,56 @@ async function bundleScormZip(zipBuffer, annotateScript) {
|
|
|
9932
9935
|
processedHtml = stripExistingBundle(processedHtml);
|
|
9933
9936
|
return buildOutputHtml(processedHtml, manifest, annotateScript);
|
|
9934
9937
|
}
|
|
9938
|
+
async function bundleScormFolder(folderPath, annotateScript) {
|
|
9939
|
+
function collectFiles(dir, baseDir) {
|
|
9940
|
+
const results = [];
|
|
9941
|
+
for (const entry of import_fs.default.readdirSync(dir)) {
|
|
9942
|
+
const fullPath = import_path.default.join(dir, entry);
|
|
9943
|
+
const stat = import_fs.default.statSync(fullPath);
|
|
9944
|
+
if (stat.isDirectory()) {
|
|
9945
|
+
results.push(...collectFiles(fullPath, baseDir));
|
|
9946
|
+
} else {
|
|
9947
|
+
results.push(fullPath);
|
|
9948
|
+
}
|
|
9949
|
+
}
|
|
9950
|
+
return results;
|
|
9951
|
+
}
|
|
9952
|
+
const allFiles = collectFiles(folderPath, folderPath);
|
|
9953
|
+
const relFiles = allFiles.map((f) => import_path.default.relative(folderPath, f).replace(/\\/g, "/"));
|
|
9954
|
+
function findEntryHtmlInFiles(files) {
|
|
9955
|
+
if (files.includes("index.html")) return "index.html";
|
|
9956
|
+
if (files.includes("Index.html")) return "Index.html";
|
|
9957
|
+
const nested = files.find((f) => /^[^/]+\/index\.html$/i.test(f));
|
|
9958
|
+
if (nested) return nested;
|
|
9959
|
+
const anyHtml = files.find((f) => /\.html?$/i.test(f));
|
|
9960
|
+
return anyHtml ?? null;
|
|
9961
|
+
}
|
|
9962
|
+
const entryRelPath = findEntryHtmlInFiles(relFiles);
|
|
9963
|
+
if (!entryRelPath) throw new Error("No HTML entry point found in the folder.");
|
|
9964
|
+
const entryAbsPath = import_path.default.join(folderPath, entryRelPath);
|
|
9965
|
+
const templateHtml = import_fs.default.readFileSync(entryAbsPath, "utf-8");
|
|
9966
|
+
const entryDir = entryRelPath.includes("/") ? entryRelPath.slice(0, entryRelPath.lastIndexOf("/") + 1) : "";
|
|
9967
|
+
const manifest = {};
|
|
9968
|
+
const uuidMap = {};
|
|
9969
|
+
for (const relFile of relFiles) {
|
|
9970
|
+
if (relFile === entryRelPath) continue;
|
|
9971
|
+
if (relFile.endsWith("imsmanifest.xml")) continue;
|
|
9972
|
+
if (relFile.endsWith(".bat")) continue;
|
|
9973
|
+
if (import_path.default.basename(relFile) === ".DS_Store") continue;
|
|
9974
|
+
const absPath = import_path.default.join(folderPath, relFile);
|
|
9975
|
+
const mime = getMime(relFile);
|
|
9976
|
+
const data = import_fs.default.readFileSync(absPath).toString("base64");
|
|
9977
|
+
const uuid = makeUUID();
|
|
9978
|
+
manifest[uuid] = { mime, data, compressed: false };
|
|
9979
|
+
const normalizedRel = relFile.replace(/\\/g, "/");
|
|
9980
|
+
const relPath = entryDir && normalizedRel.startsWith(entryDir) ? normalizedRel.slice(entryDir.length) : normalizedRel;
|
|
9981
|
+
uuidMap[relPath] = uuid;
|
|
9982
|
+
if (relPath !== normalizedRel) uuidMap[normalizedRel] = uuid;
|
|
9983
|
+
}
|
|
9984
|
+
let processedHtml = replaceAssetRefs(templateHtml, uuidMap);
|
|
9985
|
+
processedHtml = stripExistingBundle(processedHtml);
|
|
9986
|
+
return buildOutputHtml(processedHtml, manifest, annotateScript);
|
|
9987
|
+
}
|
|
9935
9988
|
function bundlePlainHtml(htmlBuffer, annotateScript) {
|
|
9936
9989
|
let html = htmlBuffer.toString("utf-8");
|
|
9937
9990
|
html = stripExistingBundle(html);
|
|
@@ -12838,25 +12891,36 @@ function printHelp() {
|
|
|
12838
12891
|
scorm-review \u2014 Convert SCORM packages to self-contained review HTML files
|
|
12839
12892
|
|
|
12840
12893
|
Usage:
|
|
12841
|
-
scorm-review <input> Convert a .zip or .html file
|
|
12842
|
-
scorm-review <input> -o <output> Specify output
|
|
12894
|
+
scorm-review <input> Convert a .zip, folder, or .html file
|
|
12895
|
+
scorm-review <input> -o <output> Specify output base name (no extension)
|
|
12843
12896
|
scorm-review --help Show this help message
|
|
12844
12897
|
|
|
12845
12898
|
Examples:
|
|
12846
12899
|
scorm-review ./my-course.zip
|
|
12847
|
-
scorm-review ./my-course
|
|
12900
|
+
scorm-review ./my-course/
|
|
12848
12901
|
scorm-review ./page.html
|
|
12902
|
+
scorm-review ./my-course.zip -o ./output/my-course
|
|
12849
12903
|
|
|
12850
|
-
Output:
|
|
12851
|
-
|
|
12852
|
-
|
|
12904
|
+
Output (zip or folder input):
|
|
12905
|
+
<name>_review_file.html Self-contained review HTML
|
|
12906
|
+
<name>_review.zip Zip file containing the HTML (for easy sharing)
|
|
12853
12907
|
`);
|
|
12854
12908
|
}
|
|
12855
|
-
function
|
|
12856
|
-
if (outputFlag) return
|
|
12857
|
-
const
|
|
12858
|
-
const
|
|
12859
|
-
|
|
12909
|
+
function resolveOutputBase(inputPath, outputFlag) {
|
|
12910
|
+
if (outputFlag) return import_path2.default.resolve(outputFlag);
|
|
12911
|
+
const resolved = import_path2.default.resolve(inputPath);
|
|
12912
|
+
const dir = import_path2.default.dirname(resolved);
|
|
12913
|
+
const base = import_path2.default.basename(resolved.replace(/[\\/]+$/, ""), import_path2.default.extname(resolved));
|
|
12914
|
+
return import_path2.default.join(dir, base);
|
|
12915
|
+
}
|
|
12916
|
+
async function createReviewZip(htmlContent, htmlFilename) {
|
|
12917
|
+
const zip = new import_jszip2.default();
|
|
12918
|
+
zip.file(htmlFilename, htmlContent);
|
|
12919
|
+
return zip.generateAsync({
|
|
12920
|
+
type: "nodebuffer",
|
|
12921
|
+
compression: "DEFLATE",
|
|
12922
|
+
compressionOptions: { level: 6 }
|
|
12923
|
+
});
|
|
12860
12924
|
}
|
|
12861
12925
|
async function main() {
|
|
12862
12926
|
const args = process.argv.slice(2);
|
|
@@ -12864,50 +12928,71 @@ async function main() {
|
|
|
12864
12928
|
printHelp();
|
|
12865
12929
|
process.exit(0);
|
|
12866
12930
|
}
|
|
12867
|
-
let
|
|
12868
|
-
let
|
|
12931
|
+
let inputPath;
|
|
12932
|
+
let outputFlag;
|
|
12869
12933
|
for (let i = 0; i < args.length; i++) {
|
|
12870
12934
|
if (args[i] === "-o" || args[i] === "--output") {
|
|
12871
|
-
|
|
12935
|
+
outputFlag = args[i + 1];
|
|
12872
12936
|
i++;
|
|
12873
12937
|
} else {
|
|
12874
|
-
|
|
12938
|
+
inputPath = args[i];
|
|
12875
12939
|
}
|
|
12876
12940
|
}
|
|
12877
|
-
if (!
|
|
12878
|
-
console.error("Error: No input
|
|
12941
|
+
if (!inputPath) {
|
|
12942
|
+
console.error("Error: No input specified.\n");
|
|
12879
12943
|
printHelp();
|
|
12880
12944
|
process.exit(1);
|
|
12881
12945
|
}
|
|
12882
|
-
const resolvedInput =
|
|
12883
|
-
if (!
|
|
12884
|
-
console.error(`Error:
|
|
12946
|
+
const resolvedInput = import_path2.default.resolve(inputPath.replace(/[\\/]+$/, ""));
|
|
12947
|
+
if (!import_fs2.default.existsSync(resolvedInput)) {
|
|
12948
|
+
console.error(`Error: Path not found: ${resolvedInput}`);
|
|
12885
12949
|
process.exit(1);
|
|
12886
12950
|
}
|
|
12887
|
-
const
|
|
12888
|
-
|
|
12889
|
-
|
|
12951
|
+
const stat = import_fs2.default.statSync(resolvedInput);
|
|
12952
|
+
const isFolder = stat.isDirectory();
|
|
12953
|
+
const ext = isFolder ? "" : import_path2.default.extname(resolvedInput).toLowerCase();
|
|
12954
|
+
if (!isFolder && ![".zip", ".html", ".htm"].includes(ext)) {
|
|
12955
|
+
console.error(`Error: Unsupported file type "${ext}". Only .zip, .html, .htm, or a folder are supported.`);
|
|
12890
12956
|
process.exit(1);
|
|
12891
12957
|
}
|
|
12892
|
-
const
|
|
12893
|
-
|
|
12894
|
-
|
|
12958
|
+
const outputBase = resolveOutputBase(resolvedInput, outputFlag);
|
|
12959
|
+
const htmlFilename = import_path2.default.basename(outputBase) + "_review_file.html";
|
|
12960
|
+
const htmlOutputPath = outputBase + "_review_file.html";
|
|
12961
|
+
const zipOutputPath = outputBase + "_review.zip";
|
|
12962
|
+
console.log(`Converting: ${import_path2.default.basename(resolvedInput)}${isFolder ? "/" : ""}`);
|
|
12963
|
+
console.log(`HTML: ${htmlOutputPath}`);
|
|
12964
|
+
if (!ext || ext === ".zip") {
|
|
12965
|
+
console.log(`ZIP: ${zipOutputPath}`);
|
|
12966
|
+
}
|
|
12895
12967
|
console.log("");
|
|
12968
|
+
import_fs2.default.mkdirSync(import_path2.default.dirname(outputBase), { recursive: true });
|
|
12896
12969
|
try {
|
|
12897
|
-
const fileBuffer = import_fs.default.readFileSync(resolvedInput);
|
|
12898
12970
|
let outputHtml;
|
|
12899
|
-
if (
|
|
12900
|
-
process.stdout.write("Processing SCORM
|
|
12971
|
+
if (isFolder) {
|
|
12972
|
+
process.stdout.write("Processing SCORM folder...");
|
|
12973
|
+
outputHtml = await bundleScormFolder(resolvedInput, ANNOTATE_SCRIPT);
|
|
12974
|
+
} else if (ext === ".zip") {
|
|
12975
|
+
process.stdout.write("Processing SCORM zip...");
|
|
12976
|
+
const fileBuffer = import_fs2.default.readFileSync(resolvedInput);
|
|
12901
12977
|
outputHtml = await bundleScormZip(fileBuffer, ANNOTATE_SCRIPT);
|
|
12902
12978
|
} else {
|
|
12903
12979
|
process.stdout.write("Processing HTML file...");
|
|
12980
|
+
const fileBuffer = import_fs2.default.readFileSync(resolvedInput);
|
|
12904
12981
|
outputHtml = bundlePlainHtml(fileBuffer, ANNOTATE_SCRIPT);
|
|
12905
12982
|
}
|
|
12906
12983
|
console.log(" done.");
|
|
12907
|
-
|
|
12908
|
-
const
|
|
12984
|
+
import_fs2.default.writeFileSync(htmlOutputPath, outputHtml, "utf-8");
|
|
12985
|
+
const htmlSizeMb = (Buffer.byteLength(outputHtml, "utf-8") / 1024 / 1024).toFixed(1);
|
|
12909
12986
|
console.log(`
|
|
12910
|
-
\u2713
|
|
12987
|
+
\u2713 HTML: ${htmlOutputPath} (${htmlSizeMb} MB)`);
|
|
12988
|
+
if (isFolder || ext === ".zip") {
|
|
12989
|
+
process.stdout.write("Creating review zip...");
|
|
12990
|
+
const zipBuffer = await createReviewZip(outputHtml, htmlFilename);
|
|
12991
|
+
import_fs2.default.writeFileSync(zipOutputPath, zipBuffer);
|
|
12992
|
+
const zipSizeMb = (zipBuffer.length / 1024 / 1024).toFixed(1);
|
|
12993
|
+
console.log(` done.`);
|
|
12994
|
+
console.log(`\u2713 ZIP: ${zipOutputPath} (${zipSizeMb} MB)`);
|
|
12995
|
+
}
|
|
12911
12996
|
} catch (err) {
|
|
12912
12997
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12913
12998
|
console.error(`
|