Merge branch 'develop' into fix-asset-naming

This commit is contained in:
Paul Kaplan 2017-07-25 10:48:25 -04:00 committed by GitHub
commit 0cf05647d2
7 changed files with 143 additions and 41 deletions

View file

@ -2,6 +2,9 @@ language: node_js
node_js: node_js:
- 6 - 6
- node - node
env:
- NPM_SCRIPT="tap:unit -- --jobs=4"
- NPM_SCRIPT="tap:integration -- --jobs=4"
sudo: false sudo: false
cache: cache:
directories: directories:
@ -9,27 +12,29 @@ cache:
install: install:
- npm install - npm install
- npm update - npm update
after_script: script: npm run $NPM_SCRIPT
- | jobs:
# RELEASE_BRANCHES and NPM_TOKEN defined in Travis settings panel include:
declare exitCode - env: NPM_SCRIPT=lint
$(npm bin)/travis-after-all node_js: 6
exitCode=$? - stage: release
if [[ node_js: 6
# Execute after all jobs finish successfully env: NPM_SCRIPT=build
$exitCode = 0 && before_deploy:
# Only release on release branches - npm --no-git-tag-version version $($(npm bin)/json -f package.json version)-prerelease.$(date +%s)
$RELEASE_BRANCHES =~ $TRAVIS_BRANCH && - git config --global user.email $(git log --pretty=format:"%ae" -n1)
# Don't release on PR builds - git config --global user.name $(git log --pretty=format:"%an" -n1)
$TRAVIS_PULL_REQUEST = "false" deploy:
]]; then - provider: script
# Authenticate NPM "on":
echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc all_branches: true
# Set version to timestamp condition: $RELEASE_BRANCHES =~ $TRAVIS_BRANCH
npm --no-git-tag-version version $($(npm bin)/json -f package.json version)-prerelease.$(date +%s) skip_cleanup: true
npm publish script: npm run --silent deploy -- -x -r $GH_PAGES_REPO
# Publish to gh-pages as most recent committer - provider: npm
git config --global user.email $(git log --pretty=format:"%ae" -n1) "on":
git config --global user.name $(git log --pretty=format:"%an" -n1) all_branches: true
npm run --silent deploy -- -x -r $GH_PAGES_REPO condition: $RELEASE_BRANCHES =~ $TRAVIS_BRANCH
fi skip_cleanup: true
email: $NPM_EMAIL
api_key: $NPM_TOKEN

View file

@ -15,10 +15,11 @@
"coverage": "./node_modules/.bin/tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov", "coverage": "./node_modules/.bin/tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov",
"deploy": "touch playground/.nojekyll && ./node_modules/.bin/gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"", "deploy": "touch playground/.nojekyll && ./node_modules/.bin/gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"",
"lint": "./node_modules/.bin/eslint .", "lint": "./node_modules/.bin/eslint .",
"prepublish": "npm run build", "prepublish": "in-publish && npm run build || not-in-publish",
"prepublish-watch": "npm run watch",
"start": "./node_modules/.bin/webpack-dev-server", "start": "./node_modules/.bin/webpack-dev-server",
"tap": "./node_modules/.bin/tap ./test/{unit,integration}/*.js", "tap": "./node_modules/.bin/tap ./test/{unit,integration}/*.js",
"tap:unit": "./node_modules/.bin/tap ./test/unit/*.js",
"tap:integration": "./node_modules/.bin/tap ./test/integration/*.js",
"test": "npm run lint && npm run tap", "test": "npm run lint && npm run tap",
"watch": "./node_modules/.bin/webpack --progress --colors --watch", "watch": "./node_modules/.bin/webpack --progress --colors --watch",
"version": "./node_modules/.bin/json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\"" "version": "./node_modules/.bin/json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\""
@ -38,6 +39,7 @@
"highlightjs": "^9.8.0", "highlightjs": "^9.8.0",
"htmlparser2": "3.9.2", "htmlparser2": "3.9.2",
"immutable": "3.8.1", "immutable": "3.8.1",
"in-publish": "^2.0.0",
"json": "^9.0.4", "json": "^9.0.4",
"lodash.defaultsdeep": "4.6.0", "lodash.defaultsdeep": "4.6.0",
"minilog": "3.1.0", "minilog": "3.1.0",
@ -50,7 +52,6 @@
"socket.io-client": "1.7.3", "socket.io-client": "1.7.3",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"tap": "^10.2.0", "tap": "^10.2.0",
"travis-after-all": "^1.4.4",
"webpack": "^2.4.1", "webpack": "^2.4.1",
"webpack-dev-server": "^2.4.1" "webpack-dev-server": "^2.4.1"
} }

View file

@ -77,15 +77,16 @@ const flatten = function (blocks) {
* a list of blocks in a branch (e.g., in forever), * a list of blocks in a branch (e.g., in forever),
* or a list of blocks in an argument (e.g., move [pick random...]). * or a list of blocks in an argument (e.g., move [pick random...]).
* @param {Array.<object>} blockList SB2 JSON-format block list. * @param {Array.<object>} blockList SB2 JSON-format block list.
* @param {Function} getVariableId function to retreive a variable's ID based on name
* @return {Array.<object>} Scratch VM-format block list. * @return {Array.<object>} Scratch VM-format block list.
*/ */
const parseBlockList = function (blockList) { const parseBlockList = function (blockList, getVariableId) {
const resultingList = []; const resultingList = [];
let previousBlock = null; // For setting next. let previousBlock = null; // For setting next.
for (let i = 0; i < blockList.length; i++) { for (let i = 0; i < blockList.length; i++) {
const block = blockList[i]; const block = blockList[i];
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
const parsedBlock = parseBlock(block); const parsedBlock = parseBlock(block, getVariableId);
if (typeof parsedBlock === 'undefined') continue; if (typeof parsedBlock === 'undefined') continue;
if (previousBlock) { if (previousBlock) {
parsedBlock.parent = previousBlock.id; parsedBlock.parent = previousBlock.id;
@ -102,14 +103,15 @@ const parseBlockList = function (blockList) {
* This should only handle top-level scripts that include X, Y coordinates. * This should only handle top-level scripts that include X, Y coordinates.
* @param {!object} scripts Scripts object from SB2 JSON. * @param {!object} scripts Scripts object from SB2 JSON.
* @param {!Blocks} blocks Blocks object to load parsed blocks into. * @param {!Blocks} blocks Blocks object to load parsed blocks into.
* @param {Function} getVariableId function to retreive a variable's ID based on name
*/ */
const parseScripts = function (scripts, blocks) { const parseScripts = function (scripts, blocks, getVariableId) {
for (let i = 0; i < scripts.length; i++) { for (let i = 0; i < scripts.length; i++) {
const script = scripts[i]; const script = scripts[i];
const scriptX = script[0]; const scriptX = script[0];
const scriptY = script[1]; const scriptY = script[1];
const blockList = script[2]; const blockList = script[2];
const parsedBlockList = parseBlockList(blockList); const parsedBlockList = parseBlockList(blockList, getVariableId);
if (parsedBlockList[0]) { if (parsedBlockList[0]) {
// Adjust script coordinates to account for // Adjust script coordinates to account for
// larger block size in scratch-blocks. // larger block size in scratch-blocks.
@ -127,6 +129,30 @@ const parseScripts = function (scripts, blocks) {
} }
}; };
/**
* Create a callback for assigning fixed IDs to imported variables
* Generator stores the global variable mapping in a closure
* @param {!string} targetId the id of the target to scope the variable to
* @return {string} variable ID
*/
const generateVariableIdGetter = (function () {
let globalVariableNameMap = {};
const namer = (targetId, name) => `${targetId}-${name}`;
return function (targetId, topLevel) {
// Reset the global variable map if topLevel
if (topLevel) globalVariableNameMap = {};
return function (name) {
if (topLevel) { // Store the name/id pair in the globalVariableNameMap
globalVariableNameMap[name] = namer(targetId, name);
return globalVariableNameMap[name];
}
// Not top-level, so first check the global name map
if (globalVariableNameMap[name]) return globalVariableNameMap[name];
return namer(targetId, name);
};
};
}());
/** /**
* Parse a single "Scratch object" and create all its in-memory VM objects. * Parse a single "Scratch object" and create all its in-memory VM objects.
* @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher. * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher.
@ -180,19 +206,18 @@ const parseScratchObject = function (object, runtime, topLevel) {
soundPromises.push(loadSound(sound, runtime)); soundPromises.push(loadSound(sound, runtime));
} }
} }
// If included, parse any and all scripts/blocks on the object.
if (object.hasOwnProperty('scripts')) {
parseScripts(object.scripts, blocks);
}
// Create the first clone, and load its run-state from JSON. // Create the first clone, and load its run-state from JSON.
const target = sprite.createClone(); const target = sprite.createClone();
const getVariableId = generateVariableIdGetter(target.id, topLevel);
// Load target properties from JSON. // Load target properties from JSON.
if (object.hasOwnProperty('variables')) { if (object.hasOwnProperty('variables')) {
for (let j = 0; j < object.variables.length; j++) { for (let j = 0; j < object.variables.length; j++) {
const variable = object.variables[j]; const variable = object.variables[j];
const newVariable = new Variable( const newVariable = new Variable(
null, getVariableId(variable.name),
variable.name, variable.name,
variable.value, variable.value,
variable.isPersistent variable.isPersistent
@ -200,6 +225,12 @@ const parseScratchObject = function (object, runtime, topLevel) {
target.variables[newVariable.id] = newVariable; target.variables[newVariable.id] = newVariable;
} }
} }
// If included, parse any and all scripts/blocks on the object.
if (object.hasOwnProperty('scripts')) {
parseScripts(object.scripts, blocks, getVariableId);
}
if (object.hasOwnProperty('lists')) { if (object.hasOwnProperty('lists')) {
for (let k = 0; k < object.lists.length; k++) { for (let k = 0; k < object.lists.length; k++) {
const list = object.lists[k]; const list = object.lists[k];
@ -294,9 +325,10 @@ const sb2import = function (json, runtime, optForceSprite) {
/** /**
* Parse a single SB2 JSON-formatted block and its children. * Parse a single SB2 JSON-formatted block and its children.
* @param {!object} sb2block SB2 JSON-formatted block. * @param {!object} sb2block SB2 JSON-formatted block.
* @param {Function} getVariableId function to retreive a variable's ID based on name
* @return {object} Scratch VM format block. * @return {object} Scratch VM format block.
*/ */
const parseBlock = function (sb2block) { const parseBlock = function (sb2block, getVariableId) {
// First item in block object is the old opcode (e.g., 'forward:'). // First item in block object is the old opcode (e.g., 'forward:').
const oldOpcode = sb2block[0]; const oldOpcode = sb2block[0];
// Convert the block using the specMap. See sb2specmap.js. // Convert the block using the specMap. See sb2specmap.js.
@ -341,10 +373,10 @@ const parseBlock = function (sb2block) {
let innerBlocks; let innerBlocks;
if (typeof providedArg[0] === 'object' && providedArg[0]) { if (typeof providedArg[0] === 'object' && providedArg[0]) {
// Block list occupies the input. // Block list occupies the input.
innerBlocks = parseBlockList(providedArg); innerBlocks = parseBlockList(providedArg, getVariableId);
} else { } else {
// Single block occupies the input. // Single block occupies the input.
innerBlocks = [parseBlock(providedArg)]; innerBlocks = [parseBlock(providedArg, getVariableId)];
} }
let previousBlock = null; let previousBlock = null;
for (let j = 0; j < innerBlocks.length; j++) { for (let j = 0; j < innerBlocks.length; j++) {
@ -426,6 +458,11 @@ const parseBlock = function (sb2block) {
name: expectedArg.fieldName, name: expectedArg.fieldName,
value: providedArg value: providedArg
}; };
if (expectedArg.fieldName === 'VARIABLE') {
// Add `id` property to variable fields
activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg);
}
} }
} }
// Special cases to generate mutations. // Special cases to generate mutations.

View file

@ -539,8 +539,11 @@ class VirtualMachine extends EventEmitter {
* of the current editing target's blocks. * of the current editing target's blocks.
*/ */
emitWorkspaceUpdate () { emitWorkspaceUpdate () {
// @todo Include variables scoped to editing target also. const variableMap = Object.assign({},
const variableMap = this.runtime.getTargetForStage().variables; this.runtime.getTargetForStage().variables,
this.editingTarget.variables
);
const variables = Object.keys(variableMap).map(k => variableMap[k]); const variables = Object.keys(variableMap).map(k => variableMap[k]);
const xmlString = `<xml xmlns="http://www.w3.org/1999/xhtml"> const xmlString = `<xml xmlns="http://www.w3.org/1999/xhtml">

BIN
test/fixtures/data.sb2 vendored

Binary file not shown.

View file

@ -51,3 +51,20 @@ test('default', t => {
t.end(); t.end();
}); });
}); });
test('data scoping', t => {
// Get SB2 JSON (string)
const uri = path.resolve(__dirname, '../fixtures/data.sb2');
const file = extract(uri);
const json = JSON.parse(file);
// Create runtime instance & load SB2 into it
const rt = new Runtime();
sb2.deserialize(json, rt).then(targets => {
const globalVariableIds = Object.keys(targets[0].variables);
const localVariableIds = Object.keys(targets[1].variables);
t.equal(targets[0].variables[globalVariableIds[0]].name, 'foo');
t.equal(targets[1].variables[localVariableIds[0]].name, 'local');
t.end();
});
});

View file

@ -109,3 +109,42 @@ test('renameSprite does not increment when renaming to the same name', t => {
t.equal(vm.runtime.targets[0].sprite.name, 'this name'); t.equal(vm.runtime.targets[0].sprite.name, 'this name');
t.end(); t.end();
}); });
test('emitWorkspaceUpdate', t => {
const vm = new VirtualMachine();
vm.runtime.targets = [
{
isStage: true,
variables: {
global: {
toXML: () => 'global'
}
}
}, {
variables: {
unused: {
toXML: () => 'unused'
}
}
}, {
variables: {
local: {
toXML: () => 'local'
}
},
blocks: {
toXML: () => 'blocks'
}
}
];
vm.editingTarget = vm.runtime.targets[2];
let xml = null;
vm.emit = (event, data) => (xml = data.xml);
vm.emitWorkspaceUpdate();
t.notEqual(xml.indexOf('global'), -1);
t.notEqual(xml.indexOf('local'), -1);
t.equal(xml.indexOf('unused'), -1);
t.notEqual(xml.indexOf('blocks'), -1);
t.end();
});