Major refactor of project loading. Removed loadProjectLocal, in favor of going back to single API (loadProject). loadProject uses scratch-parser to unpack and validate project. Scratch-parser is getting updated to return validated project, with appended project metadata, and unpacked zip (JSZip object) if provided a buffer.

This commit is contained in:
kchadha 2018-03-14 14:18:24 -04:00
parent 2c551d1739
commit 22be2b15f3

View file

@ -177,34 +177,22 @@ class VirtualMachine extends EventEmitter {
}
/**
* Load a project from a Scratch 2.0 JSON representation.
* @param {?string} json JSON string representing the project.
* Load a Scratch project from a .sb, .sb2, .sb3 or json string.
* @param {Buffer} input A buffe or json string representing the project to load.
* @return {!Promise} Promise that resolves after targets are installed.
*/
loadProject (json) {
// @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0.
return this.fromJSON(json);
}
loadProject (input) {
// Clear the current runtime
this.clear();
/**
* Load a project from a Scratch 3.0 sb3 file containing a project json
* and all of the sound and costume files.
* @param {Buffer} inputBuffer A buffer representing the project to load.
* @return {!Promise} Promise that resolves after targets are installed.
*/
loadProjectLocal (inputBuffer) {
// TODO need to handle sb2 files as well, and will possibly merge w/
// above function
return JSZip.loadAsync(inputBuffer)
.then(sb3File => {
sb3File.file('project.json').async('string')
.then(json => {
// TODO error handling for unpacking zip/not finding project.json
json = JSON.parse(json); // TODO catch errors here (validation)
return sb3.deserialize(json, this.runtime, sb3File)
.then(({targets, extensions}) =>
this.installTargets(targets, extensions, true));
});
return validate(input)
.then(validatedInput => this.deserializeProject(validatedInput[0], validatedInput[1]))
.catch(error => {
// Intentionally rejecting here (want errors to be handled by caller)
if (error.hasOwnProperty('validationError')) {
return Promise.reject(JSON.stringify(error));
}
return Promise.reject(error);
});
}
@ -233,6 +221,8 @@ class VirtualMachine extends EventEmitter {
const costumeDescs = serializeCostumes(this.runtime);
const projectJson = this.toJSON();
// TODO want to eventually move zip creation out of here, and perhaps
// into scratch-storage
const zip = new JSZip();
// Put everything in a zip file
@ -260,45 +250,23 @@ class VirtualMachine extends EventEmitter {
/**
* Load a project from a Scratch JSON representation.
* @param {string} json JSON string representing a project.
* @param {string} projectJSON JSON string representing a project.
* @param {?JSZip} zip Optional zipped project containing assets to be loaded.
* @returns {Promise} Promise that resolves after the project has loaded
*/
fromJSON (json) {
// Clear the current runtime
this.clear();
// Validate & parse
if (typeof json !== 'string' && typeof json !== 'object') {
throw new Error('Failed to parse project. Invalid type supplied to fromJSON.');
}
// Establish version, deserialize, and load into runtime
// @todo Support Scratch 1.4
// @todo This is an extremely naïve / dangerous way of determining version.
// See `scratch-parser` for a more sophisticated validation
// methodology that should be adapted for use here
let deserializer;
let validatedProject;
const possibleSb3 = typeof json === 'string' ? JSON.parse(json) : json;
if ((typeof possibleSb3.meta !== 'undefined') && (typeof possibleSb3.meta.semver !== 'undefined')) {
deserializer = sb3;
validatedProject = possibleSb3;
} else {
// scratch-parser expects a json string or a buffer
const possibleSb2 = typeof json === 'object' ? JSON.stringify(json) : json;
validate(possibleSb2, (err, project) => {
if (err) {
throw new Error(
`The given project could not be validated, parsing failed with error: ${JSON.stringify(err)}`);
} else {
deserializer = sb2;
validatedProject = project;
}
});
}
return deserializer.deserialize(validatedProject, this.runtime)
deserializeProject (projectJSON, zip) {
const runtime = this.runtime;
const deserializePromise = function () {
const projectVersion = projectJSON.projectVersion;
if (projectVersion === 2) {
return sb2.deserialize(projectJSON, runtime);
}
if (projectVersion === 3) {
return sb3.deserialize(projectJSON, runtime, zip);
}
return Promise.reject('Unable to verify Scratch Project version.');
};
return deserializePromise()
.then(({targets, extensions}) =>
this.installTargets(targets, extensions, true));
}