feature(blocks): Export block definitions (#5908)

* refactor: Provide a BlockDefinition type

* refactor: Split defineBlocksWithJsonArray

  Split defineBlocksWithJsonArray into:

  - createBlockDefinitionsFromJsonArray, which creates BlockDefinitions
    from a (possibly JSON-originated) POJsO array, having no side-effects
    except possibly issuing warnings to the console.
  - defineBlocks, which add any dictionary of BlockDefinitions to
    the Blocks dictionary.

* feat(blocks): Export block definitions per-module

  - Define all blocks in a local blocks dictionary, often using
    createBlockDefinitionFromJsonArray, without registering them.
  - Separately, use defineBlocks to register the exported
    BlockDefinitions at the end of each Blockly.blocks.*
    module.
  - In Blockly.blocks.all, create a blocks export that combines all
    of the blocks exports from the individual blocks modules.

* chore: have format script run clang-format on blocks/ too
This commit is contained in:
Christopher Allen
2022-01-31 21:41:29 +00:00
committed by GitHub
parent 74ef1cbf52
commit ffb8907db8
16 changed files with 208 additions and 71 deletions

View File

@@ -316,7 +316,7 @@ const Block = function(workspace, prototypeName, opt_id) {
this.type = prototypeName;
const prototype = Blocks[prototypeName];
if (!prototype || typeof prototype !== 'object') {
throw TypeError('Unknown block type: ' + prototypeName);
throw TypeError('Invalid block definition for type: ' + prototypeName);
}
object.mixin(this, prototype);
}

View File

@@ -16,11 +16,18 @@
goog.module('Blockly.blocks');
/**
* A block definition. For now this very lose, but it can potentially
* be refined e.g. by replacing this typedef with a class definition.
* @typedef {!Object}
*/
let BlockDefinition;
exports.BlockDefinition = BlockDefinition;
/**
* A mapping of block type names to block prototype objects.
* @type {!Object<string,!Object>}
* @type {!Object<string,!BlockDefinition>}
* @alias Blockly.blocks.Blocks
*/
const Blocks = Object.create(null);
exports.Blocks = Blocks;

View File

@@ -17,7 +17,8 @@
*/
goog.module('Blockly.common');
const {Blocks} = goog.require('Blockly.blocks');
/* eslint-disable-next-line no-unused-vars */
const {BlockDefinition, Blocks} = goog.require('Blockly.blocks');
/* eslint-disable-next-line no-unused-vars */
const {Connection} = goog.requireType('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
@@ -210,27 +211,55 @@ const jsonInitFactory = function(jsonDef) {
* @alias Blockly.common.defineBlocksWithJsonArray
*/
const defineBlocksWithJsonArray = function(jsonArray) {
defineBlocks(createBlockDefinitionsFromJsonArray(jsonArray));
};
exports.defineBlocksWithJsonArray = defineBlocksWithJsonArray;
/**
* Define blocks from an array of JSON block definitions, as might be generated
* by the Blockly Developer Tools.
* @param {!Array<!Object>} jsonArray An array of JSON block definitions.
* @return {!Object<string, !BlockDefinition>} A map of the block
* definitions created.
* @alias Blockly.common.defineBlocksWithJsonArray
*/
const createBlockDefinitionsFromJsonArray = function(jsonArray) {
const /** @type {!Object<string,!BlockDefinition>} */ blocks = {};
for (let i = 0; i < jsonArray.length; i++) {
const elem = jsonArray[i];
if (!elem) {
console.warn(
'Block definition #' + i + ' in JSON array is ' + elem + '. ' +
'Skipping.');
} else {
const typename = elem.type;
if (!typename) {
console.warn(
'Block definition #' + i +
' in JSON array is missing a type attribute. Skipping.');
} else {
if (Blocks[typename]) {
console.warn(
'Block definition #' + i + ' in JSON array' +
' overwrites prior definition of "' + typename + '".');
}
Blocks[typename] = {init: jsonInitFactory(elem)};
}
console.warn(`Block definition #${i} in JSON array is ${elem}. Skipping`);
continue;
}
const type = elem.type;
if (!type) {
console.warn(
`Block definition #${i} in JSON array is missing a type attribute. ` +
'Skipping.');
continue;
}
blocks[type] = {init: jsonInitFactory(elem)};
}
return blocks;
};
exports.createBlockDefinitionsFromJsonArray =
createBlockDefinitionsFromJsonArray;
/**
* Add the specified block definitions to the block definitions
* dictionary (Blockly.Blocks).
* @param {!Object<string,!BlockDefinition>} blocks A map of block
* type names to block definitions.
* @alias Blockly.common.defineBlocks
*/
const defineBlocks = function(blocks) {
// Iterate over own enumerable properties.
for (const type of Object.keys(blocks)) {
const definition = blocks[type];
if (type in Blocks) {
console.warn(`Block definiton "${type}" overwrites previous definition.`);
}
Blocks[type] = definition;
}
};
exports.defineBlocksWithJsonArray = defineBlocksWithJsonArray;
exports.defineBlocks = defineBlocks;