Closure Compiler 编译的 es6 模块库没有导出符号

No exported symbols with es6 modules library compiled by Closure Compiler

起点: 许多js文件被Closure Compiler(ADVANCED_OPTIMIZATIONS级别)编译成功(没有warning/error)在一个单独的库文件中。

在这些 js 文件中:

一些 HTML 文件包括库和一些未编译的 js 成功访问所有 whatever 在这个库中定义。

我想要的:移动到 es6 模块语法

我对每个js文件做了什么:

编译成功(没有warning/error,还是ADVANCED_OPTIMIZATIONS级别)。

问题: 现在,对于相同的 HTML 文件,库中定义的所有 whatever 都被 "undefined" 看到浏览器。实际上,当我在控制台中键入 Object.keys( window ) 时,我可以看到编译器更改了所有符号名称(aabaca 等)但是 none 我导出的符号 whatever.

示例:demoVisitors 是库中定义的 const array,外部需要。 之前在库文件中,我可以看到 ... w("demoVisitors",[Oa,La,Ma,Na]); ... 并且内容在 HTML 页面中正确可见。 es6 模块更改后,我可以看到尝试 1 的 ... H("demoVisitors$$module$filemane",Oa); ...filename 是定义 demoVisitors 的文件名)和尝试 2 的 H("demoVisitors",[Na,Ka,La,Ma]);demoVisitors 在同一页面的浏览器中未定义。

经过进一步调查,我找到了解决方案。

虽然在浏览器中加载时在控制台中没有任何错误(当然 undefined whatever 除外),但我的库没有执行。我只是将闭包库移动到要编译的文件堆栈之前,然后浏览器正确执行我的库,并正确导出我的符号。详情见下文。

导出符号的 3 种方式在已编译的 es6 模块中有效:/** @export */ whatevergoog.exportSymbol('whatever', whatever)window['whatever'] = whatever。第一个 2 是第三个的便捷方式(对于根符号)。

然而 /** @export */ myClass 生成了一个不友好的未混淆名称,如 myClass$$module$source-filename-with-path。 为了获得未混淆的名称 myClass,避免在我的代码中使用 goog 函数并巧妙地启用两种 compiled/uncompiled 模式,我删除了 /** @export */ 并在 class myClass { ... }。这是我的 "own" 函数,直接受到闭包库中定义的 exportSymbol 函数的启发。这仅对像 classes 这样的根符号是必需的,您可以为 class.

中定义的所有符号(属性、函数等)保留 /** @export */

这里是源代码:

export function unobfuscateSymbol(publicPath, object, objectToExportTo = window) {
    // Same code can be used compiled and not compiled so unobfuscation only in case of obfuscation
    if (unobfuscateSymbol.name !== 'unobfuscateSymbol') {
        const /** Array<string> */ parts = publicPath.split('.');
        let /** Object */ objToExportTo = objectToExportTo;
        let /** string */ part;
        const /** number */ nbOfParts = parts.length;
        for (let /** number */ i = 0; i < nbOfParts; i += 1) {
            part = parts[i];
            if ((i === (nbOfParts - 1)) && object) {
                objToExportTo[part] = object;
            } else if (objectToExportTo[part] && objectToExportTo[part] !== Object.prototype[part]) {
                objToExportTo = objectToExportTo[part];
            } else {
                objToExportTo[part] = {};
                objToExportTo = objToExportTo[part];
            }
        }
    }
}

我是如何详细确定问题的:

  1. 为了理解导出问题,我尝试在浏览器中加载我的 HTML 测试页面时在闭包库中定义的 exportSymbol 函数中放置一个断点:没有中断...

  2. 我通过添加 console.log("my library is being executed") 仔细检查了这个执行问题:我能够在 goog.require/goog.provide 我的图书馆的版本,但不是 es6 import/export 版本。没有执行,当然没有符号导出。

  3. 我用 IIFE 包装了我的库。 Closure compiler argument: --output_wrapper "(function(){%output%})()" and an execution error message in my library 出现在浏览器控制台中。我发现 Closure 库的基本命名空间 goog.global 在休息时间是 undefined

  4. 我把闭包库移到了要编译的文件栈前面。关闭编译器参数:–js closure-lib-path/base.js –js myfile1.js –js myfile2.js … 以确保编译的 goog 内容在第一个 /** @export */ 编译生成的第一个 exportSymbol 调用之前。

  5. 然后在浏览器中执行和导出就可以了。我刚刚添加了如上所述的 unobfuscateSymbol 函数以获得与我的库的 goog.require/goog.provide 版本相同的友好导出名称。