把这个支持8.0的blockly插件打包,使之能和unpkg的blockly库一同使用。 blockly版本:unpkg.com_blockly@10.0.2_blockly.min.js
npm install
npx webpack --mode production
将生成的js文件放入blocklyTemplate_web/core文件夹下。
原版blockly的变量很奇怪,没有显式的声明,从toolbox中拉出某个块都可能顺便声明一个变量,对项目不友好。很想实现appinventor里的变量,即代码块声明全局变量,引入局部变量。于是找到了这个插件。
插件用了es6,所以我将原来的web版项目迁移到npm版。迁移完毕,准备引入插件时才发现,插件支持blockly8.0,而在迁移过程中我清楚blockly的api发生了很大的变化,unpkg和npm的使用方法有些不一样。而看插件的代码,风格更像web版。于是我选择将其打包供web版使用,而不是迁移项目去适应它。
以下提供打包源项目的基本操作。关于适配和修bug,参见modify
首先需要把源码改为基于unpkg版本的blockly。观察源码,只有两种import和blockly库有关:
import * as Blockly from 'blockly/core';
而unpkg版本刚好暴露了Blockly这个变量。
import pkg from 'blockly/javascript';
if (pkg) {
// We might be loaded into an environment that doesn't have Blockly's JavaScript generator.
const {javascriptGenerator} = pkg;
...
}
出现于generators文件夹。观察变量javascriptGenerator的使用,和unpkg中的Blockly.Javascript一样。这样只要引入Blockly就好了。于是改成了这样:
import * as Blockly from 'blockly/core';
;(function(){
const javascriptGenerator = Blockly.JavaScript;
...
})();
遗憾的是,unpkg版本的blockly不能用es6 import。意味着我不能在打包前运行代码。但是我猜,打包一部分必然不会检查整个项目能否运行。果不其然。所以我只要在webpack配置中明确输入和输出就好了。
输入是blockly库,用external将其排除在打包范围外,同时命名和unpkg版本一致:
externals: {
'blockly/core': 'Blockly',
}
输出是一个库,观察github提供的demo,插件的使用最主要的有这两句:
import * as LexicalVariables from '@mit-app-inventor/blockly-block-lexical-variables';
LexicalVariables.init(workspace);
想要在打包后的js中这样用,需要这样配置webpack:
output: {
// 其他选项...
library: 'LexicalVariables',
},
这将告诉Webpack在打包后将@mit-app-inventor/blockly-block-lexical-variables模块绑定到一个名为LexicalVariables的全局变量上。这样就可以用LexicalVariables.xxx调用库的所有功能啦。
对于后来导出的其他功能,此法也将其置于LexicalVariables之下,根据export的形式调用即可。详见blocklyTemplate_web文件夹下的实际使用代码。
npx webpack --mode production
一次成功!(太顺利了吧啊啊啊啊啊啊!!!) 将dist文件夹下生成的js用普通的<script>引入html即可。
提供此文件夹供修改源码并重新打包,因为源码有很多毛病。
修改control_for的输入名称 插件提供的control_for和原版的不兼容(输入字段上),导致使用插件后需要修改toolbox。我决定修改源码(即修改了name属性)。
修改语言 我用中文的,翻译了一部分内容。
javascriptGenerator.forBlock['controls_do_then_return'] = function(block) {
const doWhat = javascriptGenerator.statementToCode(block, 'STM') || '';
const returnWhat = javascriptGenerator.valueToCode(block, 'VALUE',
javascriptGenerator.ORDER_NONE) || '';
const code = `(()=>{\n${doWhat}\treturn ${returnWhat};\n})()`
return [code, javascriptGenerator.ORDER_ATOMIC];
};
为procedure改bug
块“procedures_callreturn”生成代码会报错,说没有toLowerCase这个属性。我猜是getName的时候返回了null,说明没有找到这个。全部注释了这个块的代码生成逻辑,但是依旧报错。于是我把procedure中所有叫“PROCNAME”的全部换成了“NAME”,问题解决。
这个procedure块问题很多,还有:定义必须在所有调用之后删除,不然报错。改成“NAME”后这个问题也被解决了。
【后记】发现要覆盖原来的generator必须用Blockly.JavaScript.forBlock[’’],所以插件中重新定义的generator全部没有生效。所以最正确的做法是加上forBlock,而不是改为“NAME”去迁就默认的generator。这样修改解决了另一个bug:无法传参。2023 8 24全面修改。
export {getVariableName} from './generators/lexical-variables.js';
export * as WarningHandler from './warningHandler.js';
在extraBlocks.js的RenameVar_mutator定义中使用了这两个属性,使用方法见注释。
在Serial块中,想使用局部变量,所以导出了:
export * as Shared from './shared.js';
export {Substitution} from './substitution.js'
{
"type": "field_parameter_flydown", // 查lexicalVariable插件的filed定义的fromJson函数可知传参
"name": "VAR",
"text": "data",
"is_editable": true
}
相关的使用在serialBlocks.js的serial_connect块定义中。
修改controls_forEach的代码生成 应为2023/10/22加入了字典,数组也可以设置属性了。 源码用了 for in 遍历列表,但是这样可能会导致遍历了属性。用 for of 可以避免。
即,变量系统前缀混乱,lexicalVariable_plugin_webpack\shared.js中两个必须都是false,且命名为保留字不会自动更改。
解决方法有两个:一是修改这个插件的bug,即用前缀来避免与保留字冲突;二是走blockly的变量系统,得到不冲突的名字。
我选择了后者,一是因为前者实在太乱了,后者更有体系;二是函数定义时若有传参,blockly生成的传参是改变了变量名的,但函数体内的lexicalVariable却不走blockly的体系。修改blockly源码更为困难,所以修改插件源码。
2023/10/29尝试用以下代码解决:
generator.nameDB_.getDistinctName('可能非法的变量名', Blockly.Names.NameType.VARIABLE);
解决失败,因为此方法保证每次调用得到的都不一样。
2023/10/31成功解决,方法是:
Blockly.JavaScript.nameDB_.getName('要修改的变量名', Blockly.Names.NameType.VARIABLE);
此方法要求在定义块的时候增加保留词。