scorm-review 1.0.0 → 1.2.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 +148 -311
- 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;
|
|
@@ -9736,287 +9736,103 @@ var require_lib3 = __commonJS({
|
|
|
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
9741
|
|
|
9742
9742
|
// src/bundler.ts
|
|
9743
9743
|
var import_jszip = __toESM(require_lib3());
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
d.id = '__bundler_err';
|
|
9755
|
-
d.style.cssText = 'position:fixed;bottom:12px;left:12px;right:12px;font:12px/1.4 ui-monospace,monospace;background:#2a1215;color:#ff8a80;padding:10px 14px;border-radius:8px;border:1px solid #5c2b2e;z-index:99999;white-space:pre-wrap;max-height:40vh;overflow:auto';
|
|
9756
|
-
d.textContent = (d.textContent ? d.textContent + '\\n' : '') +
|
|
9757
|
-
'[bundle] ' + (e.message || e.type) +
|
|
9758
|
-
(e.filename ? ' (' + e.filename.slice(0,60) + ':' + e.lineno + ')' : '');
|
|
9759
|
-
}, true);
|
|
9760
|
-
|
|
9761
|
-
try {
|
|
9762
|
-
var manifestEl = document.querySelector('script[type="__bundler/manifest"]');
|
|
9763
|
-
var templateEl = document.querySelector('script[type="__bundler/template"]');
|
|
9764
|
-
if (!manifestEl || !templateEl) {
|
|
9765
|
-
setStatus('Error: missing bundle data');
|
|
9766
|
-
return;
|
|
9767
|
-
}
|
|
9768
|
-
|
|
9769
|
-
var manifest = JSON.parse(manifestEl.textContent);
|
|
9770
|
-
var template = JSON.parse(templateEl.textContent);
|
|
9771
|
-
|
|
9772
|
-
var uuids = Object.keys(manifest);
|
|
9773
|
-
setStatus('Unpacking ' + uuids.length + ' assets...');
|
|
9774
|
-
|
|
9775
|
-
var blobUrls = {};
|
|
9776
|
-
await Promise.all(uuids.map(async function(uuid) {
|
|
9777
|
-
var entry = manifest[uuid];
|
|
9778
|
-
try {
|
|
9779
|
-
var binaryStr = atob(entry.data);
|
|
9780
|
-
var bytes = new Uint8Array(binaryStr.length);
|
|
9781
|
-
for (var i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
9782
|
-
var finalBytes = bytes;
|
|
9783
|
-
if (entry.compressed && typeof DecompressionStream !== 'undefined') {
|
|
9784
|
-
var ds = new DecompressionStream('gzip');
|
|
9785
|
-
var writer = ds.writable.getWriter();
|
|
9786
|
-
var reader = ds.readable.getReader();
|
|
9787
|
-
writer.write(bytes); writer.close();
|
|
9788
|
-
var chunks = [], totalLen = 0;
|
|
9789
|
-
while (true) {
|
|
9790
|
-
var res = await reader.read();
|
|
9791
|
-
if (res.done) break;
|
|
9792
|
-
chunks.push(res.value); totalLen += res.value.length;
|
|
9793
|
-
}
|
|
9794
|
-
finalBytes = new Uint8Array(totalLen);
|
|
9795
|
-
var offset = 0;
|
|
9796
|
-
for (var ci = 0; ci < chunks.length; ci++) { finalBytes.set(chunks[ci], offset); offset += chunks[ci].length; }
|
|
9797
|
-
}
|
|
9798
|
-
blobUrls[uuid] = URL.createObjectURL(new Blob([finalBytes], { type: entry.mime }));
|
|
9799
|
-
} catch(err) {
|
|
9800
|
-
console.error('Failed to decode asset ' + uuid + ':', err);
|
|
9801
|
-
blobUrls[uuid] = URL.createObjectURL(new Blob([], { type: entry.mime }));
|
|
9802
|
-
}
|
|
9803
|
-
}));
|
|
9804
|
-
|
|
9805
|
-
setStatus('Rendering...');
|
|
9806
|
-
for (var uuid of uuids) template = template.split(uuid).join(blobUrls[uuid]);
|
|
9807
|
-
|
|
9808
|
-
template = template.replace(/\\s+integrity="[^"]*"/gi, '').replace(/\\s+crossorigin="[^"]*"/gi, '');
|
|
9809
|
-
|
|
9810
|
-
var resourceScript = '<script>window.__resources = ' +
|
|
9811
|
-
JSON.stringify({}).split('</' + 'script>').join('<\\\\/' + 'script>') +
|
|
9812
|
-
';</' + 'script>';
|
|
9813
|
-
|
|
9814
|
-
var headOpen = template.match(/<head[^>]*>/i);
|
|
9815
|
-
if (headOpen) {
|
|
9816
|
-
var hi = headOpen.index + headOpen[0].length;
|
|
9817
|
-
template = template.slice(0, hi) + resourceScript + template.slice(hi);
|
|
9818
|
-
}
|
|
9819
|
-
|
|
9820
|
-
var doc = new DOMParser().parseFromString(template, 'text/html');
|
|
9821
|
-
document.documentElement.replaceWith(doc.documentElement);
|
|
9822
|
-
var dead = Array.from(document.scripts);
|
|
9823
|
-
for (var old of dead) {
|
|
9824
|
-
var s = document.createElement('script');
|
|
9825
|
-
for (var a of old.attributes) s.setAttribute(a.name, a.value);
|
|
9826
|
-
s.textContent = old.textContent;
|
|
9827
|
-
if ((s.type === 'text/babel' || s.type === 'text/jsx') && s.src) {
|
|
9828
|
-
var r = await fetch(s.src);
|
|
9829
|
-
s.textContent = await r.text();
|
|
9830
|
-
s.removeAttribute('src');
|
|
9831
|
-
}
|
|
9832
|
-
var p = s.src ? new Promise(function(resolve) { s.onload = s.onerror = resolve; }) : null;
|
|
9833
|
-
old.replaceWith(s);
|
|
9834
|
-
if (p) await p;
|
|
9835
|
-
}
|
|
9836
|
-
if (window.Babel && typeof window.Babel.transformScriptTags === 'function') {
|
|
9837
|
-
window.Babel.transformScriptTags();
|
|
9838
|
-
}
|
|
9839
|
-
|
|
9840
|
-
// Inject annotate review tool
|
|
9841
|
-
window.AnnotateConfig = {
|
|
9842
|
-
project: document.title || 'scorm-review',
|
|
9843
|
-
note: 'Please review all slides. Check layout, wording, and content.',
|
|
9844
|
-
startOpen: false,
|
|
9845
|
-
theme: 'auto'
|
|
9846
|
-
};
|
|
9847
|
-
await new Promise(function(resolve) {
|
|
9848
|
-
var ann = document.createElement('script');
|
|
9849
|
-
ann.id = '__annotate_inline';
|
|
9850
|
-
ann.textContent = window.__ANNOTATE_SCRIPT__;
|
|
9851
|
-
document.head.appendChild(ann);
|
|
9852
|
-
resolve();
|
|
9853
|
-
});
|
|
9854
|
-
|
|
9855
|
-
} catch(err) {
|
|
9856
|
-
setStatus('Error unpacking: ' + err.message);
|
|
9857
|
-
console.error('Bundle unpack error:', err);
|
|
9858
|
-
}
|
|
9859
|
-
});
|
|
9860
|
-
`;
|
|
9861
|
-
var THUMBNAIL_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect width="100" height="100" fill="#6366f1"></rect><text x="50" y="58" font-size="32" font-family="Arial" font-weight="bold" text-anchor="middle" fill="#fff">\u25B6</text></svg>`;
|
|
9862
|
-
|
|
9863
|
-
// src/bundler.ts
|
|
9864
|
-
var MIME_MAP = {
|
|
9865
|
-
// Images
|
|
9866
|
-
png: "image/png",
|
|
9867
|
-
jpg: "image/jpeg",
|
|
9868
|
-
jpeg: "image/jpeg",
|
|
9869
|
-
gif: "image/gif",
|
|
9870
|
-
webp: "image/webp",
|
|
9871
|
-
svg: "image/svg+xml",
|
|
9872
|
-
ico: "image/x-icon",
|
|
9873
|
-
avif: "image/avif",
|
|
9874
|
-
// Audio / Video
|
|
9875
|
-
mp3: "audio/mpeg",
|
|
9876
|
-
mp4: "video/mp4",
|
|
9877
|
-
ogg: "audio/ogg",
|
|
9878
|
-
ogv: "video/ogg",
|
|
9879
|
-
wav: "audio/wav",
|
|
9880
|
-
webm: "video/webm",
|
|
9881
|
-
m4a: "audio/m4a",
|
|
9882
|
-
// Fonts
|
|
9883
|
-
woff: "font/woff",
|
|
9884
|
-
woff2: "font/woff2",
|
|
9885
|
-
ttf: "font/ttf",
|
|
9886
|
-
otf: "font/otf",
|
|
9887
|
-
eot: "application/vnd.ms-fontobject",
|
|
9888
|
-
// Scripts / Styles
|
|
9889
|
-
js: "application/javascript",
|
|
9890
|
-
mjs: "application/javascript",
|
|
9891
|
-
css: "text/css",
|
|
9892
|
-
json: "application/json",
|
|
9893
|
-
xml: "application/xml",
|
|
9894
|
-
// Docs
|
|
9895
|
-
pdf: "application/pdf"
|
|
9896
|
-
};
|
|
9897
|
-
function getMime(filename) {
|
|
9898
|
-
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
9899
|
-
return MIME_MAP[ext] ?? "application/octet-stream";
|
|
9900
|
-
}
|
|
9901
|
-
function makeUUID() {
|
|
9902
|
-
const hex = () => Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0");
|
|
9903
|
-
return `${hex()}-${hex().slice(0, 4)}-4${hex().slice(0, 3)}-${hex().slice(0, 4)}-${hex()}${hex().slice(0, 4)}`;
|
|
9744
|
+
var import_fs = __toESM(require("fs"));
|
|
9745
|
+
var import_path = __toESM(require("path"));
|
|
9746
|
+
function findEntryHtml(files) {
|
|
9747
|
+
const norm = files.map((f) => f.replace(/\\/g, "/"));
|
|
9748
|
+
if (norm.includes("index.html")) return "index.html";
|
|
9749
|
+
if (norm.includes("Index.html")) return "Index.html";
|
|
9750
|
+
const nested = norm.find((f) => /^[^/]+\/index\.html$/i.test(f));
|
|
9751
|
+
if (nested) return nested;
|
|
9752
|
+
const anyHtml = norm.find((f) => /\.html?$/i.test(f));
|
|
9753
|
+
return anyHtml ?? null;
|
|
9904
9754
|
}
|
|
9905
|
-
function
|
|
9906
|
-
|
|
9755
|
+
function injectCoreviewTag(html) {
|
|
9756
|
+
const scriptTag = `<script src="./coreview.js" data-project="scorm-review" data-note="Please review all slides. Check layout, wording, and content." data-start-open="false"></script>`;
|
|
9757
|
+
html = html.replace(/<script[^>]*src="[^"]*coreview\.js"[^>]*><\/script>\s*/gi, "");
|
|
9758
|
+
if (/<\/body>/i.test(html)) {
|
|
9759
|
+
return html.replace(/<\/body>/i, `${scriptTag}
|
|
9760
|
+
</body>`);
|
|
9761
|
+
}
|
|
9762
|
+
return html + "\n" + scriptTag;
|
|
9907
9763
|
}
|
|
9908
|
-
async function bundleScormZip(zipBuffer,
|
|
9764
|
+
async function bundleScormZip(zipBuffer, coreviewScript) {
|
|
9909
9765
|
const zip = await import_jszip.default.loadAsync(zipBuffer);
|
|
9910
|
-
const
|
|
9766
|
+
const fileNames = Object.keys(zip.files).filter((f) => !zip.files[f].dir);
|
|
9767
|
+
const entryPath = findEntryHtml(fileNames);
|
|
9911
9768
|
if (!entryPath) throw new Error("No HTML entry point found in the zip file.");
|
|
9912
|
-
const entryFile = zip.file(entryPath);
|
|
9913
|
-
if (!entryFile) throw new Error(`Entry file "${entryPath}" could not be read.`);
|
|
9914
|
-
const templateHtml = await entryFile.async("string");
|
|
9915
9769
|
const entryDir = entryPath.includes("/") ? entryPath.slice(0, entryPath.lastIndexOf("/") + 1) : "";
|
|
9916
|
-
const
|
|
9917
|
-
|
|
9918
|
-
const
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
const relPath = entryDir && file.name.startsWith(entryDir) ? file.name.slice(entryDir.length) : file.name;
|
|
9928
|
-
uuidMap[relPath] = uuid;
|
|
9929
|
-
if (relPath !== file.name) uuidMap[file.name] = uuid;
|
|
9930
|
-
}
|
|
9931
|
-
let processedHtml = replaceAssetRefs(templateHtml, uuidMap);
|
|
9932
|
-
processedHtml = stripExistingBundle(processedHtml);
|
|
9933
|
-
return buildOutputHtml(processedHtml, manifest, annotateScript);
|
|
9934
|
-
}
|
|
9935
|
-
function bundlePlainHtml(htmlBuffer, annotateScript) {
|
|
9936
|
-
let html = htmlBuffer.toString("utf-8");
|
|
9937
|
-
html = stripExistingBundle(html);
|
|
9938
|
-
return injectAnnotate(html, annotateScript);
|
|
9939
|
-
}
|
|
9940
|
-
function findEntryHtml(zip) {
|
|
9941
|
-
const files = Object.keys(zip.files);
|
|
9942
|
-
if (zip.files["index.html"]) return "index.html";
|
|
9943
|
-
if (zip.files["Index.html"]) return "Index.html";
|
|
9944
|
-
const nested = files.find((f) => /^[^/]+\/index\.html$/i.test(f));
|
|
9945
|
-
if (nested) return nested;
|
|
9946
|
-
const anyHtml = files.find((f) => /\.html?$/i.test(f) && !zip.files[f].dir);
|
|
9947
|
-
return anyHtml ?? null;
|
|
9770
|
+
const entryFile = zip.file(entryPath);
|
|
9771
|
+
if (!entryFile) throw new Error(`Could not read entry file: ${entryPath}`);
|
|
9772
|
+
const originalHtml = await entryFile.async("string");
|
|
9773
|
+
const modifiedHtml = injectCoreviewTag(originalHtml);
|
|
9774
|
+
zip.file(entryPath, modifiedHtml);
|
|
9775
|
+
zip.file(`${entryDir}coreview.js`, coreviewScript);
|
|
9776
|
+
return zip.generateAsync({
|
|
9777
|
+
type: "nodebuffer",
|
|
9778
|
+
compression: "DEFLATE",
|
|
9779
|
+
compressionOptions: { level: 6 }
|
|
9780
|
+
});
|
|
9948
9781
|
}
|
|
9949
|
-
function
|
|
9950
|
-
|
|
9951
|
-
|
|
9952
|
-
const
|
|
9953
|
-
|
|
9954
|
-
|
|
9782
|
+
async function bundleScormFolder(folderPath, coreviewScript) {
|
|
9783
|
+
function collectFiles(dir) {
|
|
9784
|
+
const results = [];
|
|
9785
|
+
for (const entry of import_fs.default.readdirSync(dir)) {
|
|
9786
|
+
const fullPath = import_path.default.join(dir, entry);
|
|
9787
|
+
const stat = import_fs.default.statSync(fullPath);
|
|
9788
|
+
if (stat.isDirectory()) {
|
|
9789
|
+
results.push(...collectFiles(fullPath));
|
|
9790
|
+
} else {
|
|
9791
|
+
results.push(fullPath);
|
|
9792
|
+
}
|
|
9793
|
+
}
|
|
9794
|
+
return results;
|
|
9955
9795
|
}
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9796
|
+
const allFiles = collectFiles(folderPath);
|
|
9797
|
+
const relFiles = allFiles.map((f) => import_path.default.relative(folderPath, f).replace(/\\/g, "/"));
|
|
9798
|
+
const entryRelPath = findEntryHtml(relFiles);
|
|
9799
|
+
if (!entryRelPath) throw new Error("No HTML entry point found in the folder.");
|
|
9800
|
+
const zip = new import_jszip.default();
|
|
9801
|
+
for (const relFile of relFiles) {
|
|
9802
|
+
if (import_path.default.basename(relFile) === ".DS_Store") continue;
|
|
9803
|
+
const absPath = import_path.default.join(folderPath, relFile);
|
|
9804
|
+
if (relFile === entryRelPath) {
|
|
9805
|
+
const originalHtml = import_fs.default.readFileSync(absPath, "utf-8");
|
|
9806
|
+
const modifiedHtml = injectCoreviewTag(originalHtml);
|
|
9807
|
+
zip.file(relFile, modifiedHtml);
|
|
9808
|
+
} else {
|
|
9809
|
+
zip.file(relFile, import_fs.default.readFileSync(absPath));
|
|
9810
|
+
}
|
|
9811
|
+
}
|
|
9812
|
+
const entryDir = entryRelPath.includes("/") ? entryRelPath.slice(0, entryRelPath.lastIndexOf("/") + 1) : "";
|
|
9813
|
+
zip.file(`${entryDir}coreview.js`, coreviewScript);
|
|
9814
|
+
return zip.generateAsync({
|
|
9815
|
+
type: "nodebuffer",
|
|
9816
|
+
compression: "DEFLATE",
|
|
9817
|
+
compressionOptions: { level: 6 }
|
|
9818
|
+
});
|
|
9966
9819
|
}
|
|
9967
|
-
function
|
|
9968
|
-
const
|
|
9969
|
-
|
|
9970
|
-
window.AnnotateConfig = {
|
|
9971
|
-
|
|
9972
|
-
note: 'Please review this document. Check layout, wording, and content.',
|
|
9973
|
-
startOpen: false,
|
|
9974
|
-
theme: 'auto'
|
|
9975
|
-
};
|
|
9976
|
-
${annotateScript}
|
|
9820
|
+
function bundlePlainHtml(htmlBuffer, coreviewScript) {
|
|
9821
|
+
const html = htmlBuffer.toString("utf-8");
|
|
9822
|
+
const scriptTag = `<script id="__coreview_inline">
|
|
9823
|
+
window.AnnotateConfig = { project: document.title || 'review', note: 'Please review this document.', startOpen: false, theme: 'auto' };
|
|
9824
|
+
${coreviewScript}
|
|
9977
9825
|
</script>`;
|
|
9978
|
-
|
|
9979
|
-
|
|
9826
|
+
const cleaned = html.replace(/<script[^>]*id="__coreview_inline"[^>]*>[\s\S]*?<\/script>\s*/gi, "");
|
|
9827
|
+
if (/<\/body>/i.test(cleaned)) {
|
|
9828
|
+
return cleaned.replace(/<\/body>/i, `${scriptTag}
|
|
9980
9829
|
</body>`);
|
|
9981
9830
|
}
|
|
9982
|
-
return
|
|
9983
|
-
}
|
|
9984
|
-
function buildOutputHtml(templateHtml, manifest, annotateScript) {
|
|
9985
|
-
const titleMatch = templateHtml.match(/<title[^>]*>([^<]*)<\/title>/i);
|
|
9986
|
-
const title = titleMatch ? titleMatch[1].trim() : "SCORM Review";
|
|
9987
|
-
const manifestJson = JSON.stringify(manifest);
|
|
9988
|
-
const templateJson = JSON.stringify(templateHtml);
|
|
9989
|
-
const annotateEscaped = escapeForJsonString(annotateScript);
|
|
9990
|
-
return `<!DOCTYPE html>
|
|
9991
|
-
<html>
|
|
9992
|
-
<head>
|
|
9993
|
-
<meta charset="utf-8">
|
|
9994
|
-
<title>${title}</title>
|
|
9995
|
-
<style>
|
|
9996
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9997
|
-
body { background: #faf9f5; display: flex; align-items: center; justify-content: center; min-height: 100vh; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
|
|
9998
|
-
#__bundler_loading { position: fixed; bottom: 20px; right: 20px; font: 13px/1.4 -apple-system, BlinkMacSystemFont, sans-serif; color: #666; background: #fff; padding: 8px 14px; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.12); z-index: 10000; }
|
|
9999
|
-
#__bundler_thumbnail { position: fixed; inset: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: #faf9f5; z-index: 9999; }
|
|
10000
|
-
#__bundler_thumbnail svg { width: 120px; height: 120px; }
|
|
10001
|
-
</style>
|
|
10002
|
-
</head>
|
|
10003
|
-
<body>
|
|
10004
|
-
<div id="__bundler_thumbnail">
|
|
10005
|
-
${THUMBNAIL_SVG}
|
|
10006
|
-
</div>
|
|
10007
|
-
<div id="__bundler_loading">Unpacking...</div>
|
|
10008
|
-
|
|
10009
|
-
<script>window.__ANNOTATE_SCRIPT__ = "${annotateEscaped}";</script>
|
|
10010
|
-
<script>${UNPACKER_SCRIPT}</script>
|
|
10011
|
-
|
|
10012
|
-
<script type="__bundler/manifest">${manifestJson}</script>
|
|
10013
|
-
<script type="__bundler/template">${templateJson}</script>
|
|
10014
|
-
</body>
|
|
10015
|
-
</html>`;
|
|
9831
|
+
return cleaned + "\n" + scriptTag;
|
|
10016
9832
|
}
|
|
10017
9833
|
|
|
10018
9834
|
// src/cli.ts
|
|
10019
|
-
var
|
|
9835
|
+
var COREVIEW_SCRIPT = `\uFEFF/* =============================================================================\r
|
|
10020
9836
|
* coreview.js \u2014 a drop-in visual review & annotation layer for any website.\r
|
|
10021
9837
|
* Open-source edition \xB7 local-only \xB7 zero backend.\r
|
|
10022
9838
|
*\r
|
|
@@ -12835,28 +12651,33 @@ var ANNOTATE_SCRIPT = `\uFEFF/* ================================================
|
|
|
12835
12651
|
`;
|
|
12836
12652
|
function printHelp() {
|
|
12837
12653
|
console.log(`
|
|
12838
|
-
scorm-review \u2014
|
|
12654
|
+
scorm-review \u2014 Inject the coreview review tool into SCORM packages
|
|
12839
12655
|
|
|
12840
12656
|
Usage:
|
|
12841
|
-
scorm-review <input> Convert a .zip or .html file
|
|
12657
|
+
scorm-review <input> Convert a .zip, folder, or .html file
|
|
12842
12658
|
scorm-review <input> -o <output> Specify output file path
|
|
12843
12659
|
scorm-review --help Show this help message
|
|
12844
12660
|
|
|
12845
12661
|
Examples:
|
|
12846
12662
|
scorm-review ./my-course.zip
|
|
12847
|
-
scorm-review ./my-course
|
|
12663
|
+
scorm-review ./my-course/
|
|
12848
12664
|
scorm-review ./page.html
|
|
12665
|
+
scorm-review ./my-course.zip -o ./output/my-course_review.zip
|
|
12849
12666
|
|
|
12850
12667
|
Output:
|
|
12851
|
-
|
|
12852
|
-
|
|
12668
|
+
.zip or folder input \u2192 <name>_review.zip (SCORM package with coreview.js added)
|
|
12669
|
+
.html input \u2192 <name>_review.html (HTML with coreview injected inline)
|
|
12853
12670
|
`);
|
|
12854
12671
|
}
|
|
12855
|
-
function resolveOutputPath(
|
|
12856
|
-
if (outputFlag) return
|
|
12857
|
-
const
|
|
12858
|
-
const
|
|
12859
|
-
|
|
12672
|
+
function resolveOutputPath(inputPath, isFolder, ext, outputFlag) {
|
|
12673
|
+
if (outputFlag) return import_path2.default.resolve(outputFlag);
|
|
12674
|
+
const resolved = import_path2.default.resolve(inputPath.replace(/[\\/]+$/, ""));
|
|
12675
|
+
const dir = import_path2.default.dirname(resolved);
|
|
12676
|
+
const base = import_path2.default.basename(resolved, isFolder ? "" : ext);
|
|
12677
|
+
if (ext === ".html" || ext === ".htm") {
|
|
12678
|
+
return import_path2.default.join(dir, base + "_review.html");
|
|
12679
|
+
}
|
|
12680
|
+
return import_path2.default.join(dir, base + "_review.zip");
|
|
12860
12681
|
}
|
|
12861
12682
|
async function main() {
|
|
12862
12683
|
const args = process.argv.slice(2);
|
|
@@ -12864,50 +12685,66 @@ async function main() {
|
|
|
12864
12685
|
printHelp();
|
|
12865
12686
|
process.exit(0);
|
|
12866
12687
|
}
|
|
12867
|
-
let
|
|
12868
|
-
let
|
|
12688
|
+
let inputPath;
|
|
12689
|
+
let outputFlag;
|
|
12869
12690
|
for (let i = 0; i < args.length; i++) {
|
|
12870
12691
|
if (args[i] === "-o" || args[i] === "--output") {
|
|
12871
|
-
|
|
12692
|
+
outputFlag = args[i + 1];
|
|
12872
12693
|
i++;
|
|
12873
12694
|
} else {
|
|
12874
|
-
|
|
12695
|
+
inputPath = args[i];
|
|
12875
12696
|
}
|
|
12876
12697
|
}
|
|
12877
|
-
if (!
|
|
12878
|
-
console.error("Error: No input
|
|
12698
|
+
if (!inputPath) {
|
|
12699
|
+
console.error("Error: No input specified.\n");
|
|
12879
12700
|
printHelp();
|
|
12880
12701
|
process.exit(1);
|
|
12881
12702
|
}
|
|
12882
|
-
const resolvedInput =
|
|
12883
|
-
if (!
|
|
12884
|
-
console.error(`Error:
|
|
12703
|
+
const resolvedInput = import_path2.default.resolve(inputPath.replace(/[\\/]+$/, ""));
|
|
12704
|
+
if (!import_fs2.default.existsSync(resolvedInput)) {
|
|
12705
|
+
console.error(`Error: Path not found: ${resolvedInput}`);
|
|
12885
12706
|
process.exit(1);
|
|
12886
12707
|
}
|
|
12887
|
-
const
|
|
12888
|
-
|
|
12889
|
-
|
|
12708
|
+
const stat = import_fs2.default.statSync(resolvedInput);
|
|
12709
|
+
const isFolder = stat.isDirectory();
|
|
12710
|
+
const ext = isFolder ? "" : import_path2.default.extname(resolvedInput).toLowerCase();
|
|
12711
|
+
if (!isFolder && ![".zip", ".html", ".htm"].includes(ext)) {
|
|
12712
|
+
console.error(`Error: Unsupported file type "${ext}". Only .zip, .html, .htm, or a folder are supported.`);
|
|
12890
12713
|
process.exit(1);
|
|
12891
12714
|
}
|
|
12892
|
-
const
|
|
12893
|
-
console.log(`
|
|
12894
|
-
console.log(`Output:
|
|
12715
|
+
const outputPath = resolveOutputPath(inputPath, isFolder, ext, outputFlag);
|
|
12716
|
+
console.log(`Input: ${import_path2.default.basename(resolvedInput)}${isFolder ? "/" : ""}`);
|
|
12717
|
+
console.log(`Output: ${outputPath}`);
|
|
12895
12718
|
console.log("");
|
|
12719
|
+
import_fs2.default.mkdirSync(import_path2.default.dirname(outputPath), { recursive: true });
|
|
12896
12720
|
try {
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12721
|
+
if (isFolder) {
|
|
12722
|
+
process.stdout.write("Processing SCORM folder...");
|
|
12723
|
+
const zipBuffer = await bundleScormFolder(resolvedInput, COREVIEW_SCRIPT);
|
|
12724
|
+
console.log(" done.");
|
|
12725
|
+
import_fs2.default.writeFileSync(outputPath, zipBuffer);
|
|
12726
|
+
const sizeMb = (zipBuffer.length / 1024 / 1024).toFixed(1);
|
|
12727
|
+
console.log(`
|
|
12728
|
+
\u2713 Created: ${outputPath} (${sizeMb} MB)`);
|
|
12729
|
+
} else if (ext === ".zip") {
|
|
12730
|
+
process.stdout.write("Processing SCORM zip...");
|
|
12731
|
+
const fileBuffer = import_fs2.default.readFileSync(resolvedInput);
|
|
12732
|
+
const zipBuffer = await bundleScormZip(fileBuffer, COREVIEW_SCRIPT);
|
|
12733
|
+
console.log(" done.");
|
|
12734
|
+
import_fs2.default.writeFileSync(outputPath, zipBuffer);
|
|
12735
|
+
const sizeMb = (zipBuffer.length / 1024 / 1024).toFixed(1);
|
|
12736
|
+
console.log(`
|
|
12737
|
+
\u2713 Created: ${outputPath} (${sizeMb} MB)`);
|
|
12902
12738
|
} else {
|
|
12903
12739
|
process.stdout.write("Processing HTML file...");
|
|
12904
|
-
|
|
12740
|
+
const fileBuffer = import_fs2.default.readFileSync(resolvedInput);
|
|
12741
|
+
const outputHtml = bundlePlainHtml(fileBuffer, COREVIEW_SCRIPT);
|
|
12742
|
+
console.log(" done.");
|
|
12743
|
+
import_fs2.default.writeFileSync(outputPath, outputHtml, "utf-8");
|
|
12744
|
+
const sizeMb = (Buffer.byteLength(outputHtml, "utf-8") / 1024 / 1024).toFixed(1);
|
|
12745
|
+
console.log(`
|
|
12746
|
+
\u2713 Created: ${outputPath} (${sizeMb} MB)`);
|
|
12905
12747
|
}
|
|
12906
|
-
console.log(" done.");
|
|
12907
|
-
import_fs.default.writeFileSync(resolvedOutput, outputHtml, "utf-8");
|
|
12908
|
-
const sizeMb = (Buffer.byteLength(outputHtml, "utf-8") / 1024 / 1024).toFixed(1);
|
|
12909
|
-
console.log(`
|
|
12910
|
-
\u2713 Created: ${resolvedOutput} (${sizeMb} MB)`);
|
|
12911
12748
|
} catch (err) {
|
|
12912
12749
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12913
12750
|
console.error(`
|