我正在尝试使用 Haxe 生成一个 Javascript 文件,其中每个静态函数/变量都被命名为模块级字段(
package_ClassName_fieldName
而不是 package_ClassName.fieldName
)。不过,我也希望能够使用构建宏,据我所知,这些宏不能用于实际的模块级字段。有什么方法可以两全其美吗?
我试过使用构建宏来更改类类型:
import haxe.macro.Context;
import haxe.macro.Expr; // Field
import haxe.macro.Type; // ClassKind, ClassType
class Macro {
public static macro function convertClassToModuleLevelFields() : Array<Field> {
var ct:ClassType = Context.getLocalClass().get();
ct.kind = ClassKind.KModuleFields(ct.module);
return Context.getBuildFields();
}
}
...但似乎并没有改变什么。
Context.defineModule
和 Context.defineType
似乎很适合将静态类字段重新定义为模块级字段,但我似乎无法将类的 ClassType
转换为等效的 TypeDefinition
。 Compiler.setCustomJSGenerator
听起来也很有希望,但 JSGenApi.generateValue
总是使用点。有什么办法吗?
如果这是为了与代码交互,您可以为字段提供一个带有宏的
@:expose
元数据:
import haxe.macro.Compiler;
import haxe.macro.Context;
import haxe.macro.Expr;
class Macro {
public static macro function flatten():Array<Field> {
var lt = Context.getLocalType();
var pos = Context.currentPos();
var pack = switch (lt) {
case TInst(_.get() => ct, _): ct.pack.concat([ct.name]);
default: return null;
}
var fields = Context.getBuildFields();
var changed = false;
for (field in fields) {
var acc = field.access;
if (acc == null || !acc.contains(AStatic)) continue;
if (field.meta == null) field.meta = [];
if (field.meta.filter(m -> m.name == ":expose").length > 0) continue;
var name = field.name;
var nativeMeta = field.meta.filter(m -> m.name == ":native")[0];
if (nativeMeta != null
&& nativeMeta.params != null
&& nativeMeta.params.length > 0
) switch (nativeMeta.params[0].expr) {
case EConst(CString(s)): name = s; // @:native("newName") static ...
default:
}
field.meta.push({
name: ":expose",
params: [macro $v{pack.concat([name]).join("_")}],
pos: pos,
});
changed = true;
}
return changed ? fields : null;
}
}
然后
@:build(Macro.flatten())
@:keep class Test {
static function one() {
}
static function two() {
}
static function main() {
Browser.console.log("hi!");
}
}
会产生
var Test = function() { };
Test.one = $hx_exports["Test_one"] = function() {
};
Test.two = $hx_exports["Test_two"] = function() {
};
Test.main = $hx_exports["Test_main"] = function() {
$global.console.log("hi!");
};
并且您可以通过
window.Test_one
或 global.Test_one
(取决于运行 JS 的位置)访问您的函数。
你也可以使用
--macro addGlobalMetadata("mypackage", "@:build(Macro.flatten())")
向包中的所有类添加
@:build
,但请注意这样做会将它们包含在构建中,即使 DCE 通常会使用它们(错误?功能?)。
如果这是为了优化为 Closure 生成的代码(github 问题),您会想要使用 CustomJSGenerator,但要为某些/所有表达式编写自己的打印机,而不是使用
JSGenApi.generateValue
。我曾经为此维护一个生成器,但最终放弃了它,因为在大型项目中收益并不那么明显。