Flatten class fields like module-level fields?

问题描述 投票:0回答:1

我正在尝试使用 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
总是使用点。有什么办法吗?

haxe
1个回答
0
投票

如果这是为了与代码交互,您可以为字段提供一个带有宏的

@: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
。我曾经为此维护一个生成器,但最终放弃了它,因为在大型项目中收益并不那么明显。

© www.soinside.com 2019 - 2024. All rights reserved.