From 9e0549ba8b8e55b619ee87dc5fa71caee4a65cd4 Mon Sep 17 00:00:00 2001 From: Muffin Date: Tue, 25 Jul 2023 23:26:00 -0500 Subject: [PATCH] Fix regression where extensions used by monitors were not saved Caught by test --- src/serialization/sb3.js | 87 +++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 444b15bf667..cfd568bb1ec 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -580,6 +580,45 @@ const serializeMonitors = function (monitors, runtime, extensions) { .toArray(); }; +/** + * @param {any} obj Project or target JSON. Modified in place. + * @param {Set} extensionIds + * @param {Runtime} runtime + * @param {boolean} isSprite + * @returns {void} nothing, operates in-place + */ +const serializeExtensionMetadata = (obj, extensionIds, runtime, isSprite) => { + const serializedExtensions = Array.from(extensionIds); + if (serializedExtensions.length || !isSprite) { + obj.extensions = serializedExtensions; + } + + // Save list of URLs to load the current extensions + // Extension manager only exists when runtime is wrapped by VirtualMachine + if (runtime.extensionManager) { + // We'll save the extensions in the format: + // { + // "extensionid": "https://...", + // "otherid": "https://..." + // } + // Which lets the VM know which URLs correspond to which IDs, which is useful when the project + // is being loaded. For example, if the extension is eventually converted to a builtin extension + // or if it is already loaded, then it doesn't need to fetch the script again. + const extensionURLs = runtime.extensionManager.getExtensionURLs(); + const urlsToSave = {}; + for (const extension of extensionIds) { + const url = extensionURLs[extension]; + if (typeof url === 'string') { + urlsToSave[extension] = url; + } + } + // Only save this object if any URLs would actually be saved. + if (Object.keys(urlsToSave).length !== 0) { + obj.extensionURLs = urlsToSave; + } + } +}; + /** * Serializes the specified VM runtime. * @param {!Runtime} runtime VM runtime instance to be serialized. @@ -610,60 +649,18 @@ const serialize = function (runtime, targetId, {allowOptimization = true} = {}) const serializedTargets = flattenedOriginalTargets.map(t => serializeTarget(t, extensions)); - const serialiedExtensionList = Array.from(extensions); - - // Save list of URLs to load the current extensions - // Extension manager only exists when runtime is wrapped by VirtualMachine - let serializedExtensionURLs = null; - if (runtime.extensionManager) { - // We'll save the extensions in the format: - // { - // "extensionid": "https://...", - // "otherid": "https://..." - // } - // Which lets the VM know which URLs correspond to which IDs, which is useful when the project - // is being loaded. For example, if the extension is eventually converted to a builtin extension - // or if it is already loaded, then it doesn't need to fetch the script again. - const extensionURLs = runtime.extensionManager.getExtensionURLs(); - const urlsToSave = {}; - for (const extension of extensions) { - const url = extensionURLs[extension]; - if (typeof url === 'string') { - urlsToSave[extension] = url; - } - } - // Only save this object if any URLs would actually be saved. - if (Object.keys(urlsToSave).length !== 0) { - serializedExtensionURLs = urlsToSave; - } - } - if (targetId) { const target = serializedTargets[0]; - - // Scratch doesn't save extensions for sprites, so don't add if not needed. - if (serialiedExtensionList.length) { - target.extensions = serialiedExtensionList; - } - if (serializedExtensionURLs) { - target.extensionURLs = serializedExtensionURLs; - } - + serializeExtensionMetadata(target, extensions, runtime, true); return serializedTargets[0]; } - // This is part of Scratch - obj.extensions = serialiedExtensionList; - - // This is not part of Scratch, so don't add if not needed. - if (serializedExtensionURLs) { - obj.extensionURLs = serializedExtensionURLs; - } - obj.targets = serializedTargets; obj.monitors = serializeMonitors(runtime.getMonitorState(), runtime, extensions); + serializeExtensionMetadata(obj, extensions, runtime, false); + // Assemble metadata const meta = Object.create(null); meta.semver = '3.0.0';