From 3a2d61e7322e72b0909f226b8f1aaeb216cb02fc Mon Sep 17 00:00:00 2001 From: Muffin Date: Sun, 17 Mar 2024 00:45:12 -0500 Subject: [PATCH] Run inputs through sb3fix if validate() fails Closes https://github.com/TurboWarp/scratch-vm/issues/200 Some of the above tests that check for validate() rejecting invalid inputs would ideally fail now, but sb3fix is still an experiment. We haven't seen projects that are invalid in these ways in the wild so not a priority. --- lib/validate.js | 31 ++++++++++++++++++++++++++++++- package-lock.json | 17 +++++++++++++++++ package.json | 1 + test/unit/validate.js | 28 ++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/lib/validate.js b/lib/validate.js index 5c078c1..0b65d93 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -7,7 +7,7 @@ var sprite2Schema = require('./sprite2_schema.json'); var sprite3Schema = require('./sprite3_schema.json'); ajv.addSchema(sb2Defs).addSchema(sb3Defs); -module.exports = function (isSprite, input, callback) { +var validate = function (isSprite, input, callback) { var validateSb3 = ajv.compile(isSprite ? sprite3Schema : sb3Schema); var isValidSb3 = validateSb3(input); if (isValidSb3) { @@ -30,3 +30,32 @@ module.exports = function (isSprite, input, callback) { callback(validationErrors); }; + +module.exports = function (isSprite, input, callback) { + // If validate fails, try using sb3fix to salvage the input + validate(isSprite, input, function (err, result) { + if (!err) { + callback(null, result); + return; + } + + try { + // Load slightly lazily as this is an edge case + // eslint-disable-next-line global-require + var sb3fix = require('@turbowarp/sb3fix'); + + var fixed = sb3fix.fixJSON(input); + validate(isSprite, fixed, function (err2, result2) { + if (err2) { + // Original validation error will be most useful + callback(err); + } else { + callback(null, result2); + } + }); + } catch (sb3fixError) { + // The original validation error will be most useful + callback(err); + } + }); +}; diff --git a/package-lock.json b/package-lock.json index 2533563..598b1b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MPL-2.0", "dependencies": { "@turbowarp/json": "^0.1.1", + "@turbowarp/sb3fix": "^0.2.0", "ajv": "6.3.0", "jszip": "^3.10.1", "pify": "4.0.1" @@ -2480,6 +2481,14 @@ "resolved": "https://registry.npmjs.org/@turbowarp/json/-/json-0.1.2.tgz", "integrity": "sha512-9nWywp+0SH7ROVzQPQQO9gMWBikahsqyMWp1Ku8VV0q+q6bnx6dS0aNPTjqTtF2GHAY55hcREsqKzaoUdWBSwg==" }, + "node_modules/@turbowarp/sb3fix": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@turbowarp/sb3fix/-/sb3fix-0.2.0.tgz", + "integrity": "sha512-OxTjwUnbXcFgMmF5DGdMVgYEYUNbsYqFmJgyPP5Mbo/Tm4j+cTb0MvMsc7ITmZQq4plxtOkjG4t3XNJF2liLZQ==", + "dependencies": { + "jszip": "^3.10.1" + } + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -20269,6 +20278,14 @@ "resolved": "https://registry.npmjs.org/@turbowarp/json/-/json-0.1.2.tgz", "integrity": "sha512-9nWywp+0SH7ROVzQPQQO9gMWBikahsqyMWp1Ku8VV0q+q6bnx6dS0aNPTjqTtF2GHAY55hcREsqKzaoUdWBSwg==" }, + "@turbowarp/sb3fix": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@turbowarp/sb3fix/-/sb3fix-0.2.0.tgz", + "integrity": "sha512-OxTjwUnbXcFgMmF5DGdMVgYEYUNbsYqFmJgyPP5Mbo/Tm4j+cTb0MvMsc7ITmZQq4plxtOkjG4t3XNJF2liLZQ==", + "requires": { + "jszip": "^3.10.1" + } + }, "@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", diff --git a/package.json b/package.json index c44152a..37d6a47 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@turbowarp/json": "^0.1.1", + "@turbowarp/sb3fix": "^0.2.0", "ajv": "6.3.0", "jszip": "^3.10.1", "pify": "4.0.1" diff --git a/test/unit/validate.js b/test/unit/validate.js index 684b2c3..a1eeafb 100644 --- a/test/unit/validate.js +++ b/test/unit/validate.js @@ -118,3 +118,31 @@ test('sb3 errors listed before sb2 errors', function (t) { t.end(); }); }); + +test('Uses sb3fix if validation fails', function (t) { + var invalidSprite = { + isStage: false, + name: 'Sprite1', + variables: {}, + lists: {}, + broadcasts: {}, + blocks: {}, + comments: {}, + currentCostume: 0, + costumes: [], // should not be empty! + sounds: [], + volume: 100, + visible: true, + x: 0, + y: 0, + size: 100, + direction: 90, + draggable: false, + rotationStyle: 'all around' + }; + validate(true, invalidSprite, function (err, res) { + t.equal(err, null); + t.equal(res.costumes.length, 1); // sb3fix will add a costume + t.end(); + }); +});