leopard-js / sb-edit

Javascript library for manipulating Scratch project files
MIT License
54 stars 15 forks source link

Fix break on Appel v1.4 #58

Closed Fordi closed 3 years ago

Fordi commented 3 years ago

When attempting to compile Appel v1.4 (remixed and exported to Appel v1.4 remix.sb3.zip) (I was zipping around several projects to see what works), this error occurs:

TypeError: Cannot read property '0' of undefined
    at /opt/develop/sb-edit/lib/io/leopard/toLeopard.js:893:19
    at Array.map (<anonymous>)
    at _loop_1 (/opt/develop/sb-edit/lib/io/leopard/toLeopard.js:887:93)
    at Project.toLeopard (/opt/develop/sb-edit/lib/io/leopard/toLeopard.js:915:9)
    at /opt/develop/sb-edit/sample/compile.js:8:13

My analysis indicates it's possible that the find call on the variable name entries returns undefined in several spots from the Appel project (75 instances).

This PR is a quick fix that allows the project to compile with sb-edit, but I'm unsure of the consequences of passing the undefined through in the larger context of compiling the app - a compiled Appel v1.4 does not actually run correctly, with graphical problems, and throwing the following error repeatedly:

Uncaught TypeError: Cannot destructure property 'text' of 't' as it is undefined.
    at y._renderBubble (SpeechBubbleSkin.js:87)
    at new y (SpeechBubbleSkin.js:28)
    at w.getSkin (SkinCache.js:53)
    at j.renderSprite (Renderer.js:421)
    at j._renderLayers (Renderer.js:236)
    at j.update (Renderer.js:296)
    at G.render (Project.js:99)
    at G._renderLoop (Project.js:111)

...though, it's entirely possible I'm compiling incorrectly. Here's my compiler code:

const { Project } = require('../lib/index.js');
const { promises: { readFile, writeFile, mkdir } } = require('fs');
const { join, dirname } = require('path');
(async () => {
    const project = await Project.fromSb3(await readFile(join(__dirname, "Appel v1.4 remix.sb3")));
    const tree = project.toLeopard({ printWidth: 100 });
    const tasks = [];
    tasks.push(...Object.keys(tree).map(async (filename) => {
        const content = tree[filename];
        await mkdir(join(__dirname, dirname(filename)), { recursive: true });
        await writeFile(join(__dirname, filename), content);
    }));
    const writeAsset = (owner, type) => async ({ asset, name, ext }) => {
        const filename = join(owner, type, `${name}.${ext}`);
        await mkdir(join(__dirname, dirname(filename)), { recursive: true });
        await writeFile(join(__dirname, filename), new Uint8Array(asset));
    };        
    tasks.push(...project.stage.costumes.map(writeAsset('Stage', 'costumes')));
    tasks.push(...project.stage.sounds.map(writeAsset('Stage', 'sounds')));
    project.sprites.forEach(({ name: spriteName, costumes, sounds }) => {
        tasks.push(...costumes.map(writeAsset(spriteName, 'costumes')));
        tasks.push(...sounds.map(writeAsset(spriteName, 'sounds')));
    });
    await Promise.all(tasks);
    console.log('Done!');
})();

I then attempt to run the output with npx http-serve .; xdg-open http://localhost:8080.