Skip to content

Commit

Permalink
Include extension list in exported .sprite3 files
Browse files Browse the repository at this point in the history
  • Loading branch information
GarboMuffin committed Jul 26, 2023
1 parent d188c0d commit 126f5a3
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 22 deletions.
44 changes: 31 additions & 13 deletions src/serialization/sb3.js
Original file line number Diff line number Diff line change
Expand Up @@ -610,24 +610,16 @@ const serialize = function (runtime, targetId, {allowOptimization = true} = {})

const serializedTargets = flattenedOriginalTargets.map(t => serializeTarget(t, extensions));

if (targetId) {
return serializedTargets[0];
}

obj.targets = serializedTargets;

obj.monitors = serializeMonitors(runtime.getMonitorState(), runtime, extensions);

// Assemble extension list
obj.extensions = Array.from(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:
// {
// "extension_id": "https://...",
// "other_id": "https://..."
// "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
Expand All @@ -642,10 +634,36 @@ const serialize = function (runtime, targetId, {allowOptimization = true} = {})
}
// Only save this object if any URLs would actually be saved.
if (Object.keys(urlsToSave).length !== 0) {
obj.extensionURLs = urlsToSave;
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;
}

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);

// Assemble metadata
const meta = Object.create(null);
meta.semver = '3.0.0';
Expand Down
33 changes: 24 additions & 9 deletions test/integration/tw_serialize_extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const RenderedTarget = require('../../src/sprites/rendered-target');
const Sprite = require('../../src/sprites/sprite');

test('Serializes custom extensions', t => {
t.plan(6);

const vm = new VirtualMachine();

// Trick the extension manager into thinking a couple extensions are loaded.
Expand All @@ -13,21 +15,34 @@ test('Serializes custom extensions', t => {
vm.extensionManager._loadedExtensions.set('test1', 'test.0.0');
vm.extensionManager._loadedExtensions.set('test2', 'test.1.0');

// Create a block that uses the first extension
const sprite = new Sprite(null, vm.runtime);
const target = new RenderedTarget(sprite, vm.runtime);
target.blocks.createBlock({
const targetUsingBlock = new RenderedTarget(new Sprite(null, vm.runtime), vm.runtime);
vm.runtime.addTarget(targetUsingBlock);
targetUsingBlock.blocks.createBlock({
id: 'a',
opcode: 'test1_something'
});
vm.runtime.addTarget(target);

const targetNotUsingBlock = new RenderedTarget(new Sprite(null, vm.runtime), vm.runtime);
vm.runtime.addTarget(targetNotUsingBlock);

// test2 isn't used, so it shouldn't be included in the JSON
const serialized = JSON.parse(vm.toJSON());
t.same(serialized.extensions, ['test1']);
t.same(serialized.extensionURLs, {

const serializedProject = JSON.parse(vm.toJSON());
t.same(serializedProject.extensions, ['test1'], 'save extension IDs for project');
t.same(serializedProject.extensionURLs, {
test1: 'https://example.com/test1.js'
});
}, 'save extension URLs for project');

const serializedTargetWithBlock = JSON.parse(vm.toJSON(targetUsingBlock.id));
t.same(serializedTargetWithBlock.extensions, ['test1'], 'save extension IDs for sprite');
t.same(serializedTargetWithBlock.extensionURLs, {
test1: 'https://example.com/test1.js'
}, 'save extension URLs for sprite');

// other sprite uses no extensions, so don't want extension stuff in the JSON
const serializedTargetWithoutBlock = JSON.parse(vm.toJSON(targetNotUsingBlock.id));
t.notOk('extensions' in serializedTargetWithoutBlock, 'dont save extension IDs for empty sprite');
t.notOk('extensionURLs' in serializedTargetWithoutBlock, 'dont save extension URLs for empty sprite');

t.end();
});

0 comments on commit 126f5a3

Please sign in to comment.