scratch-analysis
Advanced tools
Comparing version 2.2.0 to 2.2.1
@@ -71,2 +71,24 @@ const utility = require('./utility'); | ||
/** | ||
* Extract summary information about backdrops including | ||
* count, list of backdrop names and list of backdrop hashes. | ||
* Backdrops are a subset of all costumes. | ||
* Backdrops are a costumes from the stage object. | ||
* @param {object} project Project object (SB2 format) | ||
* @return {object} Summary information | ||
*/ | ||
const backdrops = function (project) { | ||
let stageCostumes = project.costumes; | ||
if (!Array.isArray(stageCostumes)) { | ||
return {count: 0, id: [], hash: []}; | ||
} | ||
return { | ||
count: stageCostumes.length, | ||
id: stageCostumes.map((sc) => sc.costumeName), | ||
hash: stageCostumes.map((sc) => sc.baseLayerMD5) | ||
}; | ||
}; | ||
/** | ||
* Extract number of sprites from a project object. Will attempt to ignore | ||
@@ -231,2 +253,4 @@ * "children" which are not sprites. | ||
meta.backdrops = backdrops(project); | ||
meta.cloud = cloud(project, meta.variables.id); | ||
@@ -233,0 +257,0 @@ |
@@ -19,2 +19,27 @@ const utility = require('./utility'); | ||
const costumes = function (targets) { | ||
// Storage objects | ||
let occurrences = 0; | ||
let nameList = []; | ||
let hashList = []; | ||
for (let t in targets) { | ||
for (let a in targets[t].costumes) { | ||
const costume = targets[t].costumes[a]; | ||
occurrences++; | ||
nameList.push(costume.name); | ||
let hash = costume.md5ext || `${costume.assetId}.${costume.dataFormat}`; | ||
hashList.push(hash); | ||
} | ||
} | ||
// field are named this way to keep backward compatibility | ||
return { | ||
count: occurrences, | ||
id: nameList, | ||
hash: hashList | ||
}; | ||
}; | ||
const variables = function (targets, attribute) { | ||
@@ -96,6 +121,18 @@ // Storage objects | ||
const block = targets[t].blocks[a]; | ||
let opcode = block.opcode; | ||
// Check for primitive blocks which don't have the opcode field | ||
if (typeof opcode === 'undefined') { | ||
switch (block[0]) { | ||
case (12): | ||
opcode = 'data_variable'; | ||
break; | ||
case (13): | ||
opcode = 'data_listcontents'; | ||
break; | ||
} | ||
} | ||
// Get opcode and check variable manipulation for the presence of | ||
// cloud variables | ||
let opcode = block.opcode; | ||
if (opcode === 'data_setvariableto' || opcode === 'data_changevariableby') { | ||
@@ -138,2 +175,6 @@ if (isArgCloudVar(block.fields.VARIABLE[1])) { | ||
const stageTargets = function (targets) { | ||
return targets.filter((target) => target.isStage); | ||
}; | ||
module.exports = function (project, callback) { | ||
@@ -147,3 +188,5 @@ const meta = { | ||
sounds: extract(project.targets, 'sounds', 'name', 'md5ext'), | ||
costumes: extract(project.targets, 'costumes', 'name', 'md5ext'), | ||
costumes: costumes(project.targets), | ||
// backdrops are costumes on the stage target | ||
backdrops: costumes(stageTargets(project.targets)), | ||
sprites: sprites(project.targets), | ||
@@ -150,0 +193,0 @@ blocks: blocks(project.targets), |
{ | ||
"name": "scratch-analysis", | ||
"version": "2.2.0", | ||
"version": "2.2.1", | ||
"description": "Analysis tool for summarizing the structure, composition, and complexity of Scratch programs.", | ||
@@ -24,4 +24,5 @@ "main": "lib/index.js", | ||
"eslint-config-scratch": "^7.0.0", | ||
"scratch-semantic-release-config": "1.0.8", | ||
"tap": "^16.2.0" | ||
} | ||
} |
@@ -72,2 +72,10 @@ { | ||
"rotationCenterY": 53 | ||
}, | ||
{ | ||
"assetId": "d27716e022fb5f747d7b09fe6eeeca06", | ||
"name": "costume_without_md5ext", | ||
"bitmapResolution": 1, | ||
"dataFormat": "svg", | ||
"rotationCenterX": 71, | ||
"rotationCenterY": 107 | ||
} | ||
@@ -74,0 +82,0 @@ ], |
@@ -5,2 +5,5 @@ const fs = require('fs'); | ||
const analysis = require('../../lib/index'); | ||
// using the sb2 directly to bypass scratch-parser and excersise | ||
// logic targeting broken project files | ||
const sb2 = require('../../lib/sb2'); | ||
@@ -17,2 +20,6 @@ const defaultObject = fs.readFileSync( | ||
const invalidCostumes = fs.readFileSync( | ||
path.resolve(__dirname, '../fixtures/sb2/invalid-costumes.json') | ||
); | ||
test('default (object)', t => { | ||
@@ -61,2 +68,11 @@ analysis(defaultObject, (err, result) => { | ||
t.type(result.backdrops, 'object'); | ||
t.equal(result.backdrops.count, 1); | ||
t.same(result.backdrops.id, [ | ||
'backdrop1' | ||
]); | ||
t.same(result.backdrops.hash, [ | ||
'739b5e2a2435f6e1ec2993791b423146.png' | ||
]); | ||
t.type(result.sprites, 'object'); | ||
@@ -125,2 +141,11 @@ t.equal(result.sprites.count, 1); | ||
t.type(result.backdrops, 'object'); | ||
t.equal(result.backdrops.count, 1); | ||
t.same(result.backdrops.id, [ | ||
'backdrop1' | ||
]); | ||
t.same(result.backdrops.hash, [ | ||
'739b5e2a2435f6e1ec2993791b423146.png' | ||
]); | ||
t.type(result.sprites, 'object'); | ||
@@ -192,2 +217,11 @@ t.equal(result.sprites.count, 1); | ||
t.type(result.backdrops, 'object'); | ||
t.equal(result.backdrops.count, 1); | ||
t.same(result.backdrops.id, [ | ||
'backdrop1' | ||
]); | ||
t.same(result.backdrops.hash, [ | ||
'5b465b3b07d39019109d8dc6d6ee6593.svg' | ||
]); | ||
t.type(result.sprites, 'object'); | ||
@@ -265,1 +299,16 @@ t.equal(result.sprites.count, 1); | ||
}); | ||
test('stage with invalid costumes', t => { | ||
const project = JSON.parse(invalidCostumes); | ||
sb2(project, (err, result) => { | ||
t.ok(typeof err === 'undefined' || err === null); | ||
t.type(result, 'object'); | ||
t.type(result.backdrops, 'object'); | ||
t.equal(result.backdrops.count, 0); | ||
t.same(result.backdrops.id, []); | ||
t.same(result.backdrops.hash, []); | ||
t.end(); | ||
}); | ||
}); |
@@ -24,2 +24,6 @@ const fs = require('fs'); | ||
const primitiveVariableAndListBlocks = fs.readFileSync( | ||
path.resolve(__dirname, '../fixtures/sb3/primitiveVariableAndListBlocks.json') | ||
); | ||
test('default (object)', t => { | ||
@@ -58,7 +62,8 @@ analysis(defaultObject, (err, result) => { | ||
t.type(result.costumes, 'object'); | ||
t.equal(result.costumes.count, 3); | ||
t.equal(result.costumes.count, 4); | ||
t.same(result.costumes.id, [ | ||
'backdrop1', | ||
'costume1', | ||
'costume2' | ||
'costume2', | ||
'costume_without_md5ext' | ||
]); | ||
@@ -68,5 +73,15 @@ t.same(result.costumes.hash, [ | ||
'b7853f557e4426412e64bb3da6531a99.svg', | ||
'e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg' | ||
'e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg', | ||
'd27716e022fb5f747d7b09fe6eeeca06.svg' | ||
]); | ||
t.type(result.backdrops, 'object'); | ||
t.equal(result.backdrops.count, 1); | ||
t.same(result.backdrops.id, [ | ||
'backdrop1' | ||
]); | ||
t.same(result.backdrops.hash, [ | ||
'cd21514d0531fdffb22204e0ec5ed84a.svg' | ||
]); | ||
t.type(result.sprites, 'object'); | ||
@@ -124,7 +139,8 @@ t.equal(result.sprites.count, 1); | ||
t.type(result.costumes, 'object'); | ||
t.equal(result.costumes.count, 3); | ||
t.equal(result.costumes.count, 4); | ||
t.same(result.costumes.id, [ | ||
'backdrop1', | ||
'costume1', | ||
'costume2' | ||
'costume2', | ||
'costume_without_md5ext' | ||
]); | ||
@@ -134,5 +150,15 @@ t.same(result.costumes.hash, [ | ||
'b7853f557e4426412e64bb3da6531a99.svg', | ||
'e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg' | ||
'e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg', | ||
'd27716e022fb5f747d7b09fe6eeeca06.svg' | ||
]); | ||
t.type(result.backdrops, 'object'); | ||
t.equal(result.backdrops.count, 1); | ||
t.same(result.backdrops.id, [ | ||
'backdrop1' | ||
]); | ||
t.same(result.backdrops.hash, [ | ||
'cd21514d0531fdffb22204e0ec5ed84a.svg' | ||
]); | ||
t.type(result.sprites, 'object'); | ||
@@ -207,2 +233,11 @@ t.equal(result.sprites.count, 1); | ||
t.type(result.backdrops, 'object'); | ||
t.equal(result.backdrops.count, 1); | ||
t.same(result.backdrops.id, [ | ||
'backdrop1' | ||
]); | ||
t.same(result.backdrops.hash, [ | ||
'7633d36de03d1df75808f581bbccc742.svg' | ||
]); | ||
t.type(result.sprites, 'object'); | ||
@@ -342,2 +377,11 @@ t.equal(result.sprites.count, 1); | ||
t.type(result.backdrops, 'object'); | ||
t.equal(result.backdrops.count, 1); | ||
t.same(result.backdrops.id, [ | ||
'backdrop1' | ||
]); | ||
t.same(result.backdrops.hash, [ | ||
'cd21514d0531fdffb22204e0ec5ed84a.svg' | ||
]); | ||
t.type(result.sprites, 'object'); | ||
@@ -361,1 +405,35 @@ t.equal(result.sprites.count, 1); | ||
}); | ||
test('correctly handling primitve reporter blocks: list and variable', t => { | ||
analysis(primitiveVariableAndListBlocks, (err, result) => { | ||
t.ok(typeof err === 'undefined' || err === null); | ||
t.type(result, 'object'); | ||
t.type(result.variables, 'object'); | ||
t.equal(result.variables.count, 1); | ||
t.same(result.variables.id, [ | ||
'my_variable' | ||
]); | ||
t.type(result.lists, 'object'); | ||
t.equal(result.lists.count, 1); | ||
t.same(result.lists.id, [ | ||
'my_list' | ||
]); | ||
t.type(result.blocks, 'object'); | ||
t.equal(result.blocks.count, 3); | ||
t.equal(result.blocks.unique, 3); | ||
t.same(result.blocks.id, [ | ||
'data_listcontents', | ||
'motion_changexby', | ||
'data_variable' | ||
]); | ||
t.same(result.blocks.frequency, { | ||
data_listcontents: 1, | ||
motion_changexby: 1, | ||
data_variable: 1 | ||
}); | ||
t.end(); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
7975682
42
1696
5
1