We, at Bocoup, propose sb-util, a JavaScript and CLI utility that allows developers to query a Scratch Project for collections of sprites, blocks, assets, and project metadata.
sb-util will accomplish this by consuming .sb3 files generated by Scratch. .sb3 files are used to save and load projects, and within an .sb3 file there is a project.json, which is the JSON representation of the entities within a Scratch project. sb-util will provide an API that allows developers to query the project.json for project information.
The resulting tool should be usable in test suites, scripts, and applications.
- Loading a Scratch Project
- ScratchProject
- SpriteCollection
- Sprite
- BlockCollection
- Block
- AssetCollection
- Asset
API methods that have been implmented are marked with implemented.
sb-util exposes loading functions to asynchronously instantiate a ScratchProject object. These loading functions handle file I/O and HTTP request handling, decoupling that process from the ScratchProject object itself.
loadSb3(sb3File)
Parameter(s): sb3File. String representing local file location of an *.sb3 file or a URI to an *.sb3 file
Return: Promise. This Promise object will resolve to a ScratchProject
const sp = await loadSb3('foo.sb3');
loadProjectJson(projectJsonFile)
Parameter(s): projectJsonFile. String representing local file location of an project.json file or a URI to an project.json file
Return: Promise. This Promise object will resolve to a ScratchProject
const sp = await loadProjectJson('project.json');
loadCloudId(cloudId)
Parameter(s): cloudId. Number representing a Cloud ID in Scratch
Return: Promise. This Promise object will resolve to a ScratchProject
const sp = await loadCloudId(123456);
ScratchProject(projectJSON) - implemented
A ScratchProject gets initialized by an object, represented by the project.json. The assetFetcher is an optional constructor argument that represents an object is responsible for retrieving asset buffers for an Asset object.
const { ScratchProject } - require('sb-util');
// Use the above loading methods or directly instantiate:
const sp = new ScratchProject(projectJson);
Methods
assets()
Return: AssetCollection representing all the assets of a project
let assets = sp.assets()
blocks() -- implemented
Return: BlockCollection representing all the blocks in the project. This BlockCollection can be further filtered to get specific blocks
let blocks = sp.blocks();
sprites(...args) - implemented
Return: SpriteCollection representing all sprites in the project. A selector syntax can be passed to this function to get sprites that meet the syntax criteria
let sprites = sp.sprites();
let stage = sp.sprites('[isStage=true]').pop();
let sprite1 = sp.sprites('[name="Cat"]').pop();
stage() - implemented
Return: Sprite a stage
let stage = sp.stage();
variables() Return: a list of Variable objects in the project
let vars = sp.variables();
A SpriteCollection represents an iterable collection of objects that represent Sprites. Array methods such as map(), filter(), and pop() are available.
Methods
first() - implemented
Return: the first element in the SpriteCollection, null if the collection is empty
prop(attribute) - implemented
Parameter: attribute string.
Return: the value of the given attribute for the first element in the SpriteCollection, undefined if the prop does not exist, null if the SpriteCollection is empty
Attributes available to pass: name, isStage, variables, lists, broadcasts, blocks, comments, currentCostume, costumes, sounds, volume, layerOrder, temp, videoTransparency, videoState, textToSpeechLanguage, visible, x, y, size, direction, draggable, rotationStyle
query(selector) - implemented
Parameter(s): selector string in the CSS selector syntax style.
Return: SpriteCollection
let stage = sp.sprites('[isStage=true]');
let sprite1 = sp.sprites('[name="Cat"]');
Possible selector syntax, in attribute selector style:
Sprite Attribute | Selector Syntax |
---|---|
isStage | [isStage={true or false}] |
layerOrder | [layerOrder={a number}] |
draggable | [draggable={true or false}] |
rotationStyle | [rotationStyle={"all around" or "left-right" or "don't rotate"}] |
A Sprite is a singleton of SpriteCollection, with additional methods that are specific to a single Sprite. A Sprite can be a stage or an individual sprite.
Methods
prop(attribute) - implemented
Parameter: attribute string.
Return: any value for a given attribute
const currentCostume = sprite.prop('currentCostume');
Attributes available to pass: name, isStage, variables, lists, broadcasts, blocks, comments, currentCostume, costumes, sounds, volume, layerOrder, temp, videoTransparency, videoState, textToSpeechLanguage, visible, x, y, size, direction, draggable, rotationStyle
blocks() - implemented
Return: BlockCollection
const sprite = sp.sprites('[name="Cat"]');
const blocks = sprite.blocks();
assets()
Return: AssetCollection
const assets = sprite.assets();
position() - implemented
Return: the (X, Y) cooredinates of a Sprite in Object notation
const { x, y } = sprite.position();
broadcasts() - implemented
Return: a list of Objects representing a broadcast, which contains an id and a message
const broadcasts = sprite.broadcasts();
// A mapping example
Object.entries(broadcasts).map(([key, value]) => ({ messageId: key, message: value }));
lists() - implemented
Return: a list of Objects representing a list, which contains an id, name, and an Array of values
const lists = sprite.lists();
Object.entries(lists).map(([key, value]) => console.log({key, listName: value[0], values: value[1]}));
A BlockCollection represents and iterable collection of objects that represent Blocks. Array methods such as map(), filter(), and pop() are available.
Methods
first() - implemented
Return: the first element in the BlockCollection, null if the collection is empty
query(selector) - partially implemented
Parameter(s): selector, a string with the convention similar to CSS selector syntax
Return: BlockCollection
This is a mapping of Block object attributes to selector syntax:
Block Attribute | CSS-like Selector Syntax | Status |
---|---|---|
opcode (Full set of opcodes) | Type selector. blocks.query('event_whenflagclicked') or blocks.query('control_if_else') |
implemented |
block type (Full set of block types) | Class selector. blocks.query('.motion') or blocks.query('.sensing') |
implemented |
block shape (Full set of block shapes) | Pseudo class selector. blocks.query(':hat') or blocks.query(':reporter') |
not implemented |
The selector syntax can be combined for more fine-grained filtering.
Get all motion reporter blocks
const motionReporterBlocks = blocks.query('.motion :reporter');
renderToText()
Return: a text representation of blocks that are connected. The output will be a consumable JSON format, which can be written to a file and also be converted to YAML
const sprite1Blocks = sp.sprites({name: 'Sprite1'}).blocks();
const blockTextRepresentation = sprite1Blocks.renderToText()
// Output
{
"blockGroups" : [
// First group of blocks
{
"output": [
"event_whenflagclicked",
{
"control_if_else" : {
"if": {
"condition" : {
"sensing_keypressed": {
"sensing_keyoptions": "space"
}
}
},
"then": [
{
"data_changevariableby": {
"variable": "my variable",
"value": 10
}
},
{
"motion_gotoxy": {
"X": "my variable",
"Y": 10
}
}
],
"else": [
{
"control_wait_until": {
"sensing_keypressed": {
"sensing_keyoptions": "space"
}
}
}
]
}
}
]
},
// Second group of blocks
{
"output": { ... }
}
]
}
A Block is a singleton of BlockCollection. It has additional methods, specific to the data held by an individual block.
Methods
prop(attribute) - implemented
Parameter: attribute string.
Return: any value for a given attribute
Attributes available to pass: opcode, next, parent, inputs, fields, shadow, topLevel
args(selector)
This method is similar to query. args returns the inputs or fields (depending on the query string) of a block using one of the strings defined below. Certain query strings can return an input or a field.
A sample of selector values for the args method is defined in this table:
Inputs | Fields | Both Input and Field |
---|---|---|
X, Y, DURATION, MESSAGE, SECS CONDITION, SUBSTACK, OPERAND, TIMES, CHANGE, FROM, VALUE, BROADCAST_INPUT, BACKDROP, VOLUME, NUM1, NUM2 | EFFECT, BROADCAST_OPTION, VARIABLE, STOP_OPTION | COSTUME, TOUCHINGOBJECTMENU, TO, SOUND_MENU |
const condition = block.args('CONDITION');
const variable = block.args('VARIABLE');
const operand = block.args('OPERAND');
substacks()
Returns the substacks for the block as an list of Objects representing a substack. If a block is not capable of having a substack, the list will be empty.
An AssetCollection represents an interable collection of objects that represent Assets, which are static files included in an *.sb3 file or somwhere the user designates, used for costumes and sounds.
Methods
query(selector)
Parameter(s): A string in the CSS selector style
Return: AssetCollection
Possible selector syntax:
Asset Attribute | Selector Syntax |
---|---|
name | Attribute selector. assets.query('name="83a9787d4cb6f3b7632b4ddfebf74367.wav") |
dataFormat | Type Selector. assets.query('wav') |
An Asset is a singleton of AssetCollection.
Methods
toBuffer() Return: Promise to the file buffer of this Asset
Coming soon
sb-util is implemented in TypeScript and will be available as a JavaScript library on npm and as a CLI tool.
npm install
npm run build
npm test
This project uses https://prettier.io to format code and https://eslint.org/ for linting (catching errors). To format and lint, run
npm run lint