feat(schemas): add support for sprite files for 2.0 and 3.0

refactor schemas to support sprite2 and sprite3 files, while reusing rules in common with sb2 and
sb3 files

BREAKING CHANGE: Change to main API which now takes an additional boolean argument indicating
whether a sprite or project is being validated
This commit is contained in:
Karishma Chadha 2018-05-02 17:10:52 -04:00
parent 5daad968a4
commit b808e7447f
11 changed files with 759 additions and 616 deletions

View file

@ -7,20 +7,21 @@ var validate = require('./lib/validate');
/**
* Unpacks, parses, validates, and analyzes Scratch projects. If successful,
* will return a valid Scratch project object with appended metadata.
* @param {boolean} isSprite Whether this is a sprite (true) or whole project (false)
* @param {Buffer | string} input Buffer or string representing project
* @param {Function} callback Returns error or project data
*/
module.exports = function (input, callback) {
module.exports = function (isSprite, input, callback) {
// First unpack the input (need this outside of the async waterfall so that
// unpackedProject can be refered to again)
unpack(input, function (err, unpackedProject) {
unpack(isSprite, input, function (err, unpackedProject) {
if (err) return callback(err);
async.waterfall([
function (cb) {
parse(unpackedProject[0], cb);
},
validate
validate.bind(null, isSprite)
], function (error, validatedInput) {
// One more callback wrapper so that we can re-package everything
// with the possible zip returned from unpack

View file

@ -8,10 +8,11 @@
* @return {void}
*/
module.exports = function (input, callback) {
var result;
try {
var result = JSON.parse(input);
callback(null, result);
result = JSON.parse(input);
} catch (e) {
return callback(e.toString());
}
return callback(null, result);
};

191
lib/sb2_definitions.json Normal file
View file

@ -0,0 +1,191 @@
{
"$id": "https://scratch.mit.edu/sb2_definitions.json",
"$schema": "http://json-schema.org/schema#",
"description": "Scratch 2.0 Project and Sprite Definitions",
"definitions": {
"scripts": {
"type": "array"
},
"sounds": {
"type": "array",
"properties": {
"soundName": {
"type": "string"
},
"soundID": {
"type": "number"
},
"md5": {
"type": "string"
},
"sampleCount": {
"type": "number"
},
"rate": {
"type": "number"
},
"format": {
"type": "string"
}
},
"required": [
"soundName",
"soundID",
"md5",
"sampleCount",
"rate",
"format"
]
},
"costumes": {
"type": "array",
"properties": {
"costumeName": {
"type": "string"
},
"baseLayerID": {
"type": "number"
},
"baseLayerMD5": {
"type": "string"
},
"bitmapResolution": {
"type": "number"
},
"rotationCenterX": {
"type": "number"
},
"rotationCenterY": {
"type": "number"
}
},
"required": [
"costumeName",
"baseLayerID",
"baseLayerMD5",
"bitmapResolution",
"rotationCenterX",
"rotationCenterY"
]
},
"variables": {
"type": "array",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
},
"isPersistent": {
"type": "boolean"
}
},
"required": [
"name",
"value"
]
},
"sprite_object": {
"type": "object",
"properties": {
"objName": {
"type": "string"
},
"variables": {"$ref": "#/definitions/variables"},
"sounds": {"$ref":"#/definitions/sounds"},
"costumes": {"$ref":"#/definitions/costumes"},
"currentCostumeIndex": {
"type": "number",
"minimum": 0
}
},
"additionalProperties": true,
"required": [
"objName",
"sounds",
"costumes",
"currentCostumeIndex"
]
},
"stage_object" : {
"type": "object",
"properties": {
"objName": {
"type": "string"
},
"variables": {"$ref": "#/definitions/variables"},
"lists": {
"type": "array",
"properties": {
"listName": {
"type": "string"
},
"contents": {
"type": "array"
},
"isPersistent": {
"type": "boolean"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"width": {
"type": "number"
},
"height": {
"type": "number"
},
"visible": {
"type": "boolean"
}
},
"required": [
"listName",
"contents"
]
},
"scripts": {"$ref": "#/definitions/scripts"},
"sounds": {"$ref": "#/definitions/sounds"},
"costumes": {"$ref": "#/definitions/costumes"},
"currentCostumeIndex": {
"type": "number",
"minimum": 0
},
"penLayerMD5": {
"oneOf":[
{"type": "string"},
{"type": "null"}
]
},
"penLayerID": {
"type": "number",
"minimum": -1
},
"tempoBPM": {
"type": "number"
},
"videoAlpha": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"children": {
"type": "array",
"items": {"$ref": "#/definitions/sprite_object"}
}
},
"required": [
"objName",
"costumes",
"currentCostumeIndex",
"penLayerMD5",
"tempoBPM",
"children"
]
}
}
}

View file

@ -1,207 +1,54 @@
{
"$id": "https://scratch.mit.edu",
"description": "Scratch project schema",
"$id": "https://scratch.mit.edu/sb2_schema.json",
"$schema": "http://json-schema.org/schema#",
"description": "Scratch 2.0 project schema",
"type": "object",
"properties": {
"objName": {
"type": "string"
},
"variables": {
"type": "array",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
},
"isPersistent": {
"type": "boolean"
}
},
"required": [
"name",
"value"
]
},
"lists": {
"type": "array",
"properties": {
"listName": {
"type": "string"
},
"contents": {
"type": "array"
},
"isPersistent": {
"type": "boolean"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"width": {
"type": "number"
},
"height": {
"type": "number"
},
"visible": {
"type": "boolean"
}
},
"required": [
"listName",
"contents"
]
},
"scripts": {
"type": "array"
},
"sounds": {
"type": "array",
"properties": {
"soundName": {
"type": "string"
},
"soundID": {
"type": "number"
},
"md5": {
"type": "string"
},
"sampleCount": {
"type": "number"
},
"rate": {
"type": "number"
},
"format": {
"type": "string"
}
},
"required": [
"soundName",
"soundID",
"md5",
"sampleCount",
"rate",
"format"
]
},
"costumes": {
"type": "array",
"properties": {
"costumeName": {
"type": "string"
},
"baseLayerID": {
"type": "number"
},
"baseLayerMD5": {
"type": "string"
},
"bitmapResolution": {
"type": "number"
},
"rotationCenterX": {
"type": "number"
},
"rotationCenterY": {
"type": "number"
}
},
"required": [
"costumeName",
"baseLayerID",
"baseLayerMD5",
"bitmapResolution",
"rotationCenterX",
"rotationCenterY"
]
},
"currentCostumeIndex": {
"type": "number",
"minimum": 0
},
"penLayerMD5": {
"oneOf":[
{"type": "string"},
{"type": "null"}
]
},
"penLayerID": {
"type": "number",
"minimum": -1
},
"tempoBPM": {
"type": "number"
},
"videoAlpha": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"children": {
"type": "array",
"properties": {},
"required": [
"objName",
"sounds",
"costumes",
"currentCostumeIndex"
]
},
"info": {
"allOf": [
{"$ref": "sb2_definitions.json#/definitions/stage_object"},
{
"type": "object",
"properties": {
"flashVersion": {
"type": "string"
},
"swfVersion": {
"type": "string"
},
"userAgent": {
"type": "string"
},
"videoOn": {
"type": "boolean"
},
"savedExtensions": {
"type": "array",
"info": {
"type": "object",
"properties": {
"extensionName": {
"flashVersion": {
"type": "string"
},
"blockSpecs": {
"type": "array"
},
"menus": {
"type": "object"
},
"javascriptURL": {
"swfVersion": {
"type": "string"
},
"userAgent": {
"type": "string"
},
"videoOn": {
"type": "boolean"
},
"savedExtensions": {
"type": "array",
"properties": {
"extensionName": {
"type": "string"
},
"blockSpecs": {
"type": "array"
},
"menus": {
"type": "object"
},
"javascriptURL": {
"type": "string"
}
},
"required": [
"extensionName",
"blockSpecs",
"menus",
"javascriptURL"
]
}
},
"required": [
"extensionName",
"blockSpecs",
"menus",
"javascriptURL"
]
}
}
}
}
},
"required": [
"objName",
"costumes",
"currentCostumeIndex",
"penLayerMD5",
"tempoBPM",
"children",
"info"
]
}

404
lib/sb3_definitions.json Normal file
View file

@ -0,0 +1,404 @@
{
"$id": "https://scratch.mit.edu/sb3_definitions.json",
"$schema": "http://json-schema.org/schema#",
"description": "Scratch 3.0 Project and Sprite Schema Definitions",
"definitions": {
"optionalString": {
"oneOf": [
{"type": "string"},
{"type": "null"}
]
},
"boolOrBoolString": {
"oneOf": [
{"type": "string",
"enum": ["true", "false"]},
{"type": "boolean"}
]
},
"stringOrNumber": {
"oneOf": [
{"type": "string"},
{"type": "number"}
]
},
"scalarVal": {
"oneOf": [
{"$ref":"#/definitions/stringOrNumber"},
{"type": "boolean"}
]
},
"assetId": {
"type": "string",
"pattern": "^[a-fA-F0-9]{32}$"
},
"costume": {
"type": "object",
"properties": {
"assetId": { "$ref": "#/definitions/assetId"},
"bitmapResolution": {
"type": "integer"
},
"dataFormat": {
"type": "string",
"enum": ["png", "svg", "jpeg", "jpg", "bmp", "gif"]
},
"md5ext": {
"type": "string",
"pattern": "^[a-fA-F0-9]{32}\\.[a-zA-Z]+$"
},
"name": {
"type": "string"
},
"rotationCenterX": {
"type": "number",
"description": "This property is not required, but is highly recommended."
},
"rotationCenterY": {
"type": "number",
"description": "This property is not required, but is highly recommended."
}
},
"required": [
"assetId",
"dataFormat",
"name"
]
},
"sound": {
"type": "object",
"properties": {
"assetId": { "$ref": "#/definitions/assetId"},
"dataFormat": {
"type": "string",
"enum": ["wav", "wave", "mp3"]
},
"md5ext": {
"type": "string",
"pattern": "^[a-fA-F0-9]{32}\\.[a-zA-Z0-9]+$"
},
"name": {
"type": "string"
},
"rate": {
"type": "integer"
},
"sampleCount": {
"type": "integer"
}
},
"required": [
"assetId",
"dataFormat",
"name"
]
},
"scalar_variable": {
"type": "array",
"items": [
{"type": "string", "description": "name of the variable"},
{"$ref":"#/definitions/scalarVal", "description": "value of the variable"}
],
"additionalItems": {"type": "boolean", "enum": [true], "description": "Whether this is a cloud variable"},
"maxItems": 3
},
"list": {
"type": "array",
"items": [
{"type":"string", "description": "name of the list"},
{
"type": "array",
"description": "contents of the list",
"items": {"$ref":"#/definitions/scalarVal"}
}
],
"additionalItems": false
},
"broadcast_message": {
"type": "string",
"description": "the message being broadcasted"
},
"num_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [4,5,6,7,8]
},
{"$ref":"#/definitions/stringOrNumber"}
],
"additionalItems": false
},
"color_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [9]
},
{
"type": "string",
"pattern": "^#[a-fA-F0-9]{6}$"
}
],
"additionalItems": false
},
"text_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [10]
},
{"$ref":"#/definitions/stringOrNumber"}
],
"additionalItems": false
},
"broadcast_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [11]
},
{"type": "string", "description": "broadcast message"},
{"type": "string", "description": "broadcast message id"}
],
"additionalItems": false
},
"variable_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [12]
},
{"type": "string", "description": "variable name"},
{"type": "string", "description": "variable id"}
],
"additionalItems": {
"type": "number"
},
"minItems": 3,
"maxItems": 5
},
"list_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [13]
},
{"type": "string", "description": "list name"},
{"type": "string", "description": "list id"}
],
"additionalItems": {
"type": "number"
},
"minItems": 3,
"maxItems": 5
},
"topLevelPrimitive": {
"oneOf": [
{"$ref":"#/definitions/variable_primitive"},
{"$ref":"#/definitions/list_primitive"}
]
},
"inputPrimitive": {
"oneOf": [
{"$ref":"#/definitions/num_primitive"},
{"$ref":"#/definitions/color_primitive"},
{"$ref":"#/definitions/text_primitive"},
{"$ref":"#/definitions/broadcast_primitive"},
{"$ref":"#/definitions/variable_primitive"},
{"$ref":"#/definitions/list_primitive"}
]
},
"block": {
"type": "object",
"properties": {
"opcode": {
"type": "string"
},
"inputs": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": [
{
"type":"number",
"enum":[1,2,3],
"description": "1 = unobscured shadow, 2 = no shadow, 3 = obscured shadow"
}
],
"additionalItems": {
"oneOf": [
{"$ref":"#/definitions/optionalString"},
{"$ref":"#/definitions/inputPrimitive"}
]
}
}
},
"fields": {
"type": "object"
},
"next": {"$ref":"#/definitions/optionalString"},
"topLevel": {
"type": "boolean"
},
"parent": {"$ref":"#/definitions/optionalString"},
"shadow": {
"type": "boolean"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"mutation": {
"type": "object",
"properties": {
"tagName": {
"type": "string",
"enum": ["mutation"]
},
"children": {
"type": "array"
},
"proccode": {
"type": "string"
},
"argumentids": {
"type": "string"
},
"warp": {"$ref":"#/definitions/boolOrBoolString"},
"hasnext": {"$ref":"#/definitions/boolOrBoolString"}
}
}
},
"required": [
"opcode"
]
},
"stage": {
"type": "object",
"description": "Description of property (and/or property/value pairs) that are unique to the stage.",
"properties": {
"name": {
"type": "string",
"enum": ["Stage"]
},
"isStage": {
"type": "boolean",
"enum": [true]
},
"tempo": {
"type": "number"
},
"videoTransparency": {
"type": "number"
},
"videoState": {
"type": "string",
"enum": ["on", "off", "on-flipped"]
}
},
"required": [
"name",
"isStage"
]
},
"sprite": {
"type": "object",
"description": "Description of property (and/or property/value pairs) for sprites.",
"properties": {
"name": {
"type": "string",
"not": {"enum": ["Stage", "stage"]}
},
"isStage": {
"type": "boolean",
"enum": [false]
},
"visible": {
"type": "boolean"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"size": {
"type": "number"
},
"direction": {
"type": "number"
},
"draggable": {
"type": "boolean"
},
"rotationStyle": {
"type": "string",
"enum": ["all around", "don't rotate", "left-right"]
}
},
"required": [
"name",
"isStage"
]
},
"target": {
"type": "object",
"description" : "Properties common to both Scratch 3.0 Stage and Sprite",
"properties": {
"currentCostume": {
"type": "integer",
"minimum": 0
},
"blocks": {
"type": "object",
"additionalProperties": {
"oneOf": [
{"$ref":"#/definitions/block"},
{"$ref":"#/definitions/topLevelPrimitive"}
]
}
},
"variables": {
"type": "object",
"additionalProperties": {"$ref":"#/definitions/scalar_variable"}
},
"lists": {
"type": "object",
"additionalProperties": {"$ref":"#/definitions/list"}
},
"broadcasts": {
"type": "object",
"additionalProperties": {"$ref":"#/definitions/broadcast_message"}
},
"costumes": {
"type": "array",
"items": {"$ref":"#/definitions/costume"},
"minItems": 1,
"uniqueItems": true
},
"sounds": {
"type": "array",
"items": {"$ref":"#/definitions/sound"},
"uniqueItems": true
},
"volume": {
"type": "number"
}
},
"required": [
"variables",
"costumes",
"sounds",
"blocks"
]
}
}
}

View file

@ -2,410 +2,6 @@
"$id": "https://scratch.mit.edu/sb3_schema.json",
"$schema": "http://json-schema.org/schema#",
"description": "Scratch 3.0 Project Schema",
"definitions": {
"optionalString": {
"oneOf": [
{"type": "string"},
{"type": "null"}
]
},
"boolOrBoolString": {
"oneOf": [
{"type": "string",
"enum": ["true", "false"]},
{"type": "boolean"}
]
},
"stringOrNumber": {
"oneOf": [
{"type": "string"},
{"type": "number"}
]
},
"scalarVal": {
"oneOf": [
{"$ref":"#/definitions/stringOrNumber"},
{"type": "boolean"}
]
},
"assetId": {
"type": "string",
"pattern": "^[a-fA-F0-9]{32}$"
},
"costume": {
"type": "object",
"properties": {
"assetId": { "$ref": "#/definitions/assetId"},
"bitmapResolution": {
"type": "integer"
},
"dataFormat": {
"type": "string",
"enum": ["png", "svg", "jpeg", "jpg", "bmp", "gif"]
},
"md5ext": {
"type": "string",
"pattern": "^[a-fA-F0-9]{32}\\.[a-zA-Z]+$"
},
"name": {
"type": "string"
},
"rotationCenterX": {
"type": "number"
},
"rotationCenterY": {
"type": "number"
}
},
"required": [
"assetId",
"dataFormat",
"name",
"rotationCenterX",
"rotationCenterY"
]
},
"sound": {
"type": "object",
"properties": {
"assetId": { "$ref": "#/definitions/assetId"},
"dataFormat": {
"type": "string",
"enum": ["wav", "wave", "mp3"]
},
"format": {
"type": "string",
"enum": ["", "adpcm"]
},
"md5ext": {
"type": "string",
"pattern": "^[a-fA-F0-9]{32}\\.[a-zA-Z0-9]+$"
},
"name": {
"type": "string"
},
"rate": {
"type": "integer"
},
"sampleCount": {
"type": "integer"
}
},
"required": [
"assetId",
"dataFormat",
"format",
"name"
]
},
"scalar_variable": {
"type": "array",
"items": [
{"type": "string", "description": "name of the variable"},
{"$ref":"#/definitions/scalarVal", "description": "value of the variable"}
],
"additionalItems": {"type": "boolean", "enum": [true], "description": "Whether this is a cloud variable"},
"maxItems": 3
},
"list": {
"type": "array",
"items": [
{"type":"string", "description": "name of the list"},
{
"type": "array",
"description": "contents of the list",
"items": {"$ref":"#/definitions/scalarVal"}
}
],
"additionalItems": false
},
"broadcast_message": {
"type": "string",
"description": "the message being broadcasted"
},
"num_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [4,5,6,7,8]
},
{"$ref":"#/definitions/stringOrNumber"}
],
"additionalItems": false
},
"color_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [9]
},
{
"type": "string",
"pattern": "^#[a-fA-F0-9]{6}$"
}
],
"additionalItems": false
},
"text_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [10]
},
{"$ref":"#/definitions/stringOrNumber"}
],
"additionalItems": false
},
"broadcast_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [11]
},
{"type": "string", "description": "broadcast message"},
{"type": "string", "description": "broadcast message id"}
],
"additionalItems": false
},
"variable_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [12]
},
{"type": "string", "description": "variable name"},
{"type": "string", "description": "variable id"}
],
"additionalItems": {
"type": "number"
},
"minItems": 3,
"maxItems": 5
},
"list_primitive": {
"type": "array",
"items": [
{
"type": "number",
"enum": [13]
},
{"type": "string", "description": "list name"},
{"type": "string", "description": "list id"}
],
"additionalItems": {
"type": "number"
},
"minItems": 3,
"maxItems": 5
},
"topLevelPrimitive": {
"oneOf": [
{"$ref":"#/definitions/variable_primitive"},
{"$ref":"#/definitions/list_primitive"}
]
},
"inputPrimitive": {
"oneOf": [
{"$ref":"#/definitions/num_primitive"},
{"$ref":"#/definitions/color_primitive"},
{"$ref":"#/definitions/text_primitive"},
{"$ref":"#/definitions/broadcast_primitive"},
{"$ref":"#/definitions/variable_primitive"},
{"$ref":"#/definitions/list_primitive"}
]
},
"block": {
"type": "object",
"properties": {
"opcode": {
"type": "string"
},
"inputs": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": [
{
"type":"number",
"enum":[1,2,3],
"description": "1 = unobscured shadow, 2 = no shadow, 3 = obscured shadow"
}
],
"additionalItems": {
"oneOf": [
{"$ref":"#/definitions/optionalString"},
{"$ref":"#/definitions/inputPrimitive"}
]
}
}
},
"fields": {
"type": "object"
},
"next": {"$ref":"#/definitions/optionalString"},
"topLevel": {
"type": "boolean"
},
"parent": {"$ref":"#/definitions/optionalString"},
"shadow": {
"type": "boolean"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"mutation": {
"type": "object",
"properties": {
"tagName": {
"type": "string",
"enum": ["mutation"]
},
"children": {
"type": "array"
},
"proccode": {
"type": "string"
},
"argumentids": {
"type": "string"
},
"warp": {"$ref":"#/definitions/boolOrBoolString"},
"hasnext": {"$ref":"#/definitions/boolOrBoolString"}
}
}
},
"required": [
"opcode"
]
},
"stage": {
"type": "object",
"description": "Description of property (and/or property/value pairs) that are unique to the stage.",
"properties": {
"name": {
"type": "string",
"enum": ["Stage"]
},
"isStage": {
"type": "boolean",
"enum": [true]
},
"tempo": {
"type": "number"
},
"videoTransparency": {
"type": "number"
},
"videoState": {
"type": "string",
"enum": ["on", "off", "on-flipped"]
}
},
"required": [
"name",
"isStage"
]
},
"sprite": {
"type": "object",
"description": "Description of property (and/or property/value pairs) for sprites.",
"properties": {
"name": {
"type": "string",
"not": {"enum": ["Stage", "stage"]}
},
"isStage": {
"type": "boolean",
"enum": [false]
},
"visible": {
"type": "boolean"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"size": {
"type": "number"
},
"direction": {
"type": "number"
},
"draggable": {
"type": "boolean"
},
"rotationStyle": {
"type": "string",
"enum": ["all around", "don't rotate", "left-right"]
}
},
"required": [
"name",
"isStage"
]
},
"target": {
"type": "object",
"description" : "Properties common to both Scratch 3.0 Stage and Sprite",
"properties": {
"currentCostume": {
"type": "integer",
"minimum": 0
},
"blocks": {
"type": "object",
"additionalProperties": {
"oneOf": [
{"$ref":"#/definitions/block"},
{"$ref":"#/definitions/topLevelPrimitive"}
]
}
},
"variables": {
"type": "object",
"additionalProperties": {"$ref":"#/definitions/scalar_variable"}
},
"lists": {
"type": "object",
"additionalProperties": {"$ref":"#/definitions/list"}
},
"broadcasts": {
"type": "object",
"additionalProperties": {"$ref":"#/definitions/broadcast_message"}
},
"costumes": {
"type": "array",
"items": {"$ref":"#/definitions/costume"},
"minItems": 1,
"uniqueItems": true
},
"sounds": {
"type": "array",
"items": {"$ref":"#/definitions/sound"},
"uniqueItems": true
},
"volume": {
"type": "number"
}
},
"required": [
"variables",
"costumes",
"sounds",
"blocks"
]
}
},
"type": "object",
"properties": {
"meta": {
@ -427,21 +23,20 @@
"semver"
]
},
"targets": {
"type": "array",
"items": [
{
"allOf": [
{"$ref": "#/definitions/stage" },
{"$ref": "#/definitions/target"}
{"$ref": "sb3_definitions.json#/definitions/stage" },
{"$ref": "sb3_definitions.json#/definitions/target"}
]
}
],
"additionalItems": {
"allOf": [
{"$ref": "#/definitions/sprite"},
{"$ref": "#/definitions/target"}
{"$ref": "sb3_definitions.json#/definitions/sprite"},
{"$ref": "sb3_definitions.json#/definitions/target"}
]
}
}

53
lib/sprite2_schema.json Normal file
View file

@ -0,0 +1,53 @@
{
"$id": "https://scratch.mit.edu/sprite2_schema.json",
"description": "Scratch 2.0 project schema",
"type": "object",
"allOf": [
{"$ref": "sb2_definitions.json#/definitions/sprite_object"},
{
"type": "object",
"properties": {
"info": {
"type": "object",
"properties": {
"flashVersion": {
"type": "string"
},
"swfVersion": {
"type": "string"
},
"userAgent": {
"type": "string"
},
"videoOn": {
"type": "boolean"
},
"savedExtensions": {
"type": "array",
"properties": {
"extensionName": {
"type": "string"
},
"blockSpecs": {
"type": "array"
},
"menus": {
"type": "object"
},
"javascriptURL": {
"type": "string"
}
},
"required": [
"extensionName",
"blockSpecs",
"menus",
"javascriptURL"
]
}
}
}
}
}
]
}

42
lib/sprite3_schema.json Normal file
View file

@ -0,0 +1,42 @@
{
"$id": "https://scratch.mit.edu/sprite3_schema.json",
"$schema": "http://json-schema.org/schema#",
"description": "Scratch 3.0 Sprite Schema",
"type": "object",
"properties": {
"meta": {
"type": "object",
"properties": {
"semver": {
"type": "string",
"pattern": "^(3.[0-9]+.[0-9]+)$"
},
"vm": {
"type": "string",
"pattern": "^([0-9]+.[0-9]+.[0-9]+)($|-)"
},
"agent": {
"type": "string"
}
},
"required": [
"semver"
]
},
"targets": {
"type": "array",
"items": [
{
"allOf": [
{"$ref": "sb3_definitions.json#/definitions/sprite"},
{"$ref": "sb3_definitions.json#/definitions/target"}
]
}
],
"additionalItems": false
}
},
"required": [
"targets"
]
}

View file

@ -4,11 +4,13 @@ var unzip = require('./unzip');
* If input a buffer, transforms buffer into a UTF-8 string.
* If input is encoded in zip or gzip format, the input will be extracted and decoded.
* If input is a string, passes that string along to the given callback.
* @param {boolean} isSprite Whether the input should be treated as
* a sprite (true) or a whole project (false)
* @param {Buffer | string} input Project data
* @param {Function} callback Error or stringified project data
* @return {void}
*/
module.exports = function (input, callback) {
module.exports = function (isSprite, input, callback) {
if (typeof input === 'string') {
// Pass string to callback
return callback(null, [input, null]);
@ -46,5 +48,5 @@ module.exports = function (input, callback) {
// Return error if legacy encoding detected
if (isLegacy) return callback('Parser only supports Scratch 2.X and above');
unzip(input, isGZip, callback);
unzip(isSprite, input, isGZip, callback);
};

View file

@ -3,12 +3,14 @@ var GZip = require('gzip-js');
/**
* Unpacks a zip or gzip file.
* @param {boolean} isSprite Whether the input should be treated as
* a sprite (true) or whole project (false)
* @param {string} input Zip file provided as a string
* @param {boolean} isGZip Whether the input is a GZip file, otherwise treat as zip
* @param {array} callback Array including both the project and zip archive
* @return {void}
*/
module.exports = function (input, isGZip, callback) {
module.exports = function (isSprite, input, isGZip, callback) {
var msg = 'Failed to unzip and extract project.json, with error: ';
if (isGZip) {
var unpackedProject = null;
@ -21,7 +23,7 @@ module.exports = function (input, isGZip, callback) {
}
return JSZip.loadAsync(input)
.then(function (zip) {
return zip.file('project.json').async('string')
return zip.file(isSprite ? 'sprite.json' : 'project.json').async('string')
.then(function (project) {
return callback(null, [project, zip]);
});

View file

@ -1,10 +1,15 @@
var ajv = require('ajv')();
var sb2schema = require('./sb2_schema.json');
var sb3schema = require('./sb3_schema.json');
var sb2Defs = require('./sb2_definitions.json');
var sb3Defs = require('./sb3_definitions.json');
var sb2Schema = require('./sb2_schema.json');
var sb3Schema = require('./sb3_schema.json');
var sprite2Schema = require('./sprite2_schema.json');
var sprite3Schema = require('./sprite3_schema.json');
ajv.addSchema(sb2Defs).addSchema(sb3Defs);
module.exports = function (input, callback) {
var validateSb2 = ajv.compile(sb2schema);
var validateSb3 = ajv.compile(sb3schema);
module.exports = function (isSprite, input, callback) {
var validateSb2 = ajv.compile(isSprite ? sprite2Schema : sb2Schema);
var validateSb3 = ajv.compile(isSprite ? sprite3Schema : sb3Schema);
var isValidSb2 = validateSb2(input);